First up: TYPES.
I remember coding on a ZX Spectrum and being incredibly jealous of the BBC as they had INTEGER variable types. This option allowed your code to run MUCH faster than using the standard floating point number variable. Now, back then ALL floating point code was done through emulation and so was much, much slower. Nowadays, doubles (which is what Game Maker uses) is all done in hardware, so why do we care? Well first, it's not always done in hardware, so we're back to the CPU doing emulation and that's really bad. In fact, even is we're doing this on a machine with hardware for doubles, integer computation is simply faster, what with the multiple execution pipes and single cycle execution on most hardware, if you can do it in integers, you should at least have the option. After all, how many FOR loops really need floating point? Lastly... How much would this really affect folk who don't care? I'd argue not much. For example...
i = 1.12;
b = 12;
compare that to something like...
INT b;
i = 1.12;
b = 12;
I'd say if you don't want to use them, it's not exactly going to bother you, but if you want to declare the variable (with INT, DIM, VAR or whatever we end up using), then you will not only get a speed boost, but it'll help you debug your code because you KNOW it can't be a fraction! That can be a valuable bit of info.
Second: STRUCTS
I'm wondering how I can even begin to say how important this is. Lets give a couple of examples. (this isn't proper code, so don't jump on errors please!)
BaddieID[i] = id
BaddieGridX[i] = round( id.x/16);
BaddieGridY[i] = round( id.y/16);
BaddieType[i] = enemytype;
BaddieParent[i] = id.parent;
Now, in this little sample, we need multiple arrays to deal with storing all the bits of information I need for processing later. This is pretty common when coding and having multiple, dynamically resizing arrays is a nasty thing. So what can we do to improve this? Welcome to the world of structures.
struct SBaddie{
id,
gridx,
gridy,
type
parent
};
baddie.id = id
baddie.x = round( id.x/16);
baddie.y = round( id.y/16);
baddie.type = enemytype
baddie.parent = parent;
Baddies[i] = baddie;
(I'm deliberately avoiding adding any types here... but you can do)
Now... how I'd hook all this up is unclear, but having a single object (much like a standard, but very lightweight Game Maker object) with variables you can access can mean you can contain data in a single packet. This means you no longer have lots of dynamically resizing arrays (which is always a good thing), but if we are to expand what can be passed into functions, it also allows you to pass a lot of data in a single blob, removing lots of parameter stacking. This is all good, not only from a performance standpoint, but simplifies your think about about variables you're dealing with. No longer are you thinking about lots of individual variables, remembering which ones do what, you now have a single variable with all the information you need and this again simplifies your work.
Structures are good. Structures are VERY good.
Third: FUNCTIONS.
Being able to breakup large functions into smaller common parts is nice, not just from a readability standpoint, but for allowing you to reuse code better. It's a very good skill to learn; making code general so you can use the same function over and over again. This not only teaches coding flexibility, but allows you to start to make an API for certain features. Good APIs are a real skill, and one thats vital to learn if you ever want to progress as a coder. All that said, if you don't really care about coding and it's simply a means to an end, then being able to break your functions up does make code just simpler. Having a function that is pages and pages long is horrible to maintain, and will introduce bugs, so this would also help you reduce bugs as each function is smaller, and easer to think about. So again, its another good one.
Fourth: CONSTANTS
Simple one. PROPER constants. This is mainly an internal thing... but allowing you to define them in code would be great.
Now... I'd also say there are features that would just confuse most folk so we should just avoid them. Things like anonymous delegates, the C++ << style operator, operator overloading, templates, and even to some extent #defines; these are all simply not required inside GML. While I do like #defines, I think they can be so badly used, I'd simply avoid them.
EDIT: Oh... and the other thing I'd LOVE to add; argument passing to the instance_create(x,y,obj) function. This would be brilliant. This would allow you to do stuff like this...
w = instance_create( 10, 10, cBullet, "smallbang.wav", false, sSprite );
I could have used this quite a few times already....




19 Smart arse replys:
So, is any of this getting implemented into Game Maker? I too would love to see GM gain these features.
Have you considered a Python like syntax where "object.function(arg1, arg2)" is equivalent to "function(object, arg1, arg2)"? I find it's a really nice syntax for allowing both global methods and object methods to work in the same way.
Anonymous functions are well-worth whatever confusion they may cause. I've seen newbies invent them on accident trying to solve their problems.
Not sure as to when we'll start changing things, we're pretty busy just now, although it's becoming increasingly important that we add features for ourselves, so it might be sooner than planned.
Well, that's just not python format, C++, C# and pascal (I think - it's been a while!) can all reference objects like this. If you read my previous post you'll see I'd like that format too.
No... def not. These are just messy and confusing to newbies. If for some reason we need callbacks, then simply using a named function would suffice.
"These are just messy and confusing to newbies."
Where on earth are you getting this from? I have seen actual examples of newbies coming up with them on their own, and outside your little world of static, imperative languages they are really quite common, well-understood and even fundamental.
Relegating callbacks to named functions makes code more verbose and spreads out information that should be together. Once people understand named functions, unnamed functions aren't much of a jump - instead of saying "use that function over there" you say "use this one right here."
Just because a couple have managed, doesn't automatically mean they all can. I've seen newbies that struggle to understand functions full stop!
Personally I wouldn't add callbacks if I can get away without it, I'd use what Game Maker already has; events you tag code into. It's a much nicer way to visualise callbacks for newbies.
Also, if for some reason we couldn't use events for callbacks, there are already scripts there, so I'd rather they write a script, and then that's called from a callback. It means there's little or no change to the general look/feel of things, which keeps people happy. In general, folk don't like big changes, it upsets them. :)
Scripts and events can't provide closures. Besides, why should you exclude an extremely common programming construct just because it's hard to understand? People are going to run into it sooner or later and it would be extremely helpful to more advanced users, whom you've explicitly said you'd like to support alongside beginners.
I'm not sure what you mean by "can't provide closures". The current Event system and the code you write for each object IS a callback, in every sense of the word. The mechanics are just hidden from you.
What I want to hide is the programmable way of setting callbacks; Game Maker already has a great, intuitive way of dealing with them, so currently we just don't need them - or rather we don't have to force them onto users.
So while we COULD add them so users could do callbacks for themselves, it's so incredible low on the wish list that I'm not even thinking about it.
If you believe otherwise... then you've to give a few examples that couldn't possible be done any other way....
Sounds like Mike likes your idea about as well as I did, Rusky. In fact, this argument is somewhat of a big deja vu.
Uhm, just a small question:
-what is the difference between "scripts" as they are now and "functions" as you want to be possible?
Only that functions have named / fixed number of arguments?
Also on the topic of pointers being too difficult/bad:
I do agree pointers aren't really the best thing.. But doesn't gamemaker already have them in a sense? - When you create a datastructure, it returns a number, which "points" to the actual datastructure.
Similar, isn't the instance-id of a certain instance not actually a pointer?
They don't point towards memory locations, but they do point.
No, these are "handles" to objects. These are much cleaner than pointers as they can't be altered to point to incorrect/corrupt memory. This is pretty much all you want a user to have access to.
Pointers are RAW memory addresses, and you can simply point to a chunk of memory and tell the compiler it's an object or structure. Pointers are dangerous, particularly for newbies as it's easy to mishandle them.
Closures are callbacks with their free variables bound in their lexical environment- You can access data in the scope of the function definition, whereas scripts don't even have a lexical scope. This makes them infinitely more useful as callbacks, because they can carry information along with them from when the callback is defined to when it needs to be used.
One example of this (also of a newbie inventing anonymous functions on their own) is a filter for the instance list. If you want to find all the instances within some distance, or with some stat in a certain range, or to store these instances in a local collection, you would be rather limited if you couldn't somehow pass some extra data to the callback.
For example:
instance_filter(function (inst) {
if (distance_to_object(inst)) < 500 && object_is_ancestor(obj_foo, inst.object_index) /* && arbitrarily complex logic with access to both the caller and the instances */)
instance_deactivate(inst);
});
A lot of your library could be reimplemented using these techniques, making both GM's and users' code a lot more reusable. Without closures, there would be no way to access the calling scope of instance_filter, and thus you would need a more painful or less reusable solution like an object with a user-defined event, a "caller" argument to the called-back script or set of functions like file_find_*.
Another example is alarms- rather than statically defining a predetermined number of alarms, you could use anonymous functions to dynamically create callbacks with different lexically and temporally local data such as nearby instances. Without anonymous functions here you need to put the callback in an object, rather than in a script that could, for example, be defined in an extension package. Without closures, you need to either use a single set of instance locals to pass data to the alarm, or in the case of multiple firings going on at the same time a more-complicated system to keep track of which set goes to which alarm instance.
While these could technically be done some other way, those other ways are more complicated to understand and maintain, not less. This one feature (and its use in throughout GM) could greatly improve a lot of things.
Err, Blogger killed the code formatting. Try this
I'm not exactly sure what you think this gives over calling a script from within an object context; either that or I'm just not getting it what you mean. Any script called from an object has the objects context, just as though it was being done via an event. So you can do pretty much anything you want from a script, even if that means passing in an object to execute with().
As to this being simpler than using the alarm events in the drag and drop, I think you're on your own there.... Clicking add event, then selecting an alarm is about as simple and intuitive as you're likely to get.
There's also no way I'd implement most of the library like this, it's messy, hard to organise/find code as it's embedded inside other functions, and there's no way it's simpler than what's there already. This would be a total nightmare to absolute beginners. Who wants to try and figure this out when you can simple pick a menu item?!?
Either that.... or I'm seriously not seeing what you're on about...?!?!?
Mike, since all scripts are executed in the current instance's context, what will happen when a method of another instance is called? Which context will it be in?
You're missing two things: closures and locality. As things are, it is impossible to access script, event or trigger-local variables from scripts. It is also impossible to put small callbacks where they belong - at the place they are used. I suppose the locality argument isn't always important for beginners, but I don't think it makes it any worse for them to have everything in the same place.
Using script-based callbacks for the instance_filter example completely ruins locality because you need a separate node in the resource tree for just two lines of code, the new script has less meaningful names (argument0) and can be called in nonsensical places. It also can't be extended to reference, for example, some other instance previously found and stored in a local, unless your callback system provides support for a user-data parameter or the user over-uses object-local variables.
Currently, alarms also have the locality problem. Usually you want to specify that some action should happen some time in the future - in the 1945 example, you say "reload the gun in 30 steps." The action of reloading the gun is only one line and really shouldn't require a completely separate, modal, statically-allocated event. More important, however, is access to locals: if you need to pass information you have now to the alarm event, you have to store it in the object. The static nature of alarms also means that you can only have one at a time per handler - if you want lots of alarms counting down independently to the same event, you're pretty much out of luck.
Finally, I have a really hard time seeing why you think a closure-based library would be messy. With the alarms example again, the user has to create an alarm event and trigger it separately, rather than just saying "do in steps." A closure-based filtering system for instances would be much cleaner than the current collection of functions - rather than explicitly looping using instance_find(), you just say "do to each instance." Rather than being limited to instance_nearest and instance_furthest, you can have instance_whateveryouwant, including instance_basedonlocalscope.
Eh, with(), Rusky?
Your alarm example is definitely an issue. I don't see how a closure could solve it, unless you're proposing that alarm events be instantiable. How would that even fit into the current event system? While I suppose the secondary event ID could just be endlessly incremented, this would create just as much bloat (internally) as if they -had- created an alarm event for each. Moreover, if they're already putting the alarm code inside an unrelated event, why wouldn't they just put it in the step event and monitor the counter themselves?
Where exactly would the code in the closure be stored that an alarm could be instantiated in a key press event but be executed every frame?
"argument passing to the instance_create(x,y,obj) function"
Well, that would be sort of unnecessary unless it runs faster (which it most likely do. a lot) 'cause you can use instance_create(x,y,obj) with a with-statement and execute the codes there. Oh declare instance_create(x,y,obj) as a variable and use var.something=1; but we don't want to have it lying around as a variable.
I love the variable type declaration idea, like;
INT n;
STRING str;
DOUBLE nn;
Those would be the 3 necessary ones, wouldn't it? You blog is very interesting, and you make life at the YoYo office seem much more relaxed and amusing than it looks on the outside! :). Give the team my best!
Post a Comment