A complaint often made about using GameMaker Studio is that the language doesn’t include built-in support for
Objects in an
OOP sense. Now, if you’re not quite familiar with Object-Oriented Programming, you might want to do a little reading up on it before continuing, but this article still may give you some ideas on better ways to organize your own code.
The most important question to ask yourself when you’re looking at this is, “What do I want out of classes? Why do I want to use them?” My answer might not be your answer, and that’s okay, but my motivation is primarily: Modular Code.
I try to adhere to the Single Responsibility Principal as much as possible to keep things neat and clean. It also makes it much easier to try to keep dependency on other “classes” or bits of code low so that the “modules” of code can be picked up from one project and dropped in another with minimal conflict or need for additional classes to come with it. All of this effort is to fulfill two purposes… the code should be easily re-usable (throughout multiple projects, if applicable) and it should be easy to maintain. If a class only has one responsibility it prevents it from getting too large and out of control… in GML in particular, I find myself getting buried under objects and scripts fairly easy, so anything to keep things small and neat helps my headspace.
So, with these goals packed up and ready to go, I’ve found two approaches that work fairly well. Each has their strengths and weakness, so I mix-and-match depending on my situation.
If you’re familiar with OOP already, it’s pretty easy to draw the parallel between GameMaker Studio 2‘s
object resource and the OOP concept of a
class. Objects have instance variables, which resemble members of a class, and they have events, which can resemble member functions. While there are a limited amount of
User Events you can tack onto an object, there’s really not much else for customizing your own “member function” within the object definition. To provide more “member functions” you can write
script_functions that are expected to be called by an
instance of the object, or using the
This syntax allows object functions to just call script as if they’re built-in “member functions” of the object, but for external callers, the syntax is weird a bit of “tribal knowledge” in that you have to know to use
with. Well-documented code definitely helps, but I personally dislike doing anything self-documenting code can’t handle.
My favorite method of doing this is to pass the
instance as the first parameter of the function:
Doing it this way, you run into the situation of the
instance calling these functions with
id. Since most of the functions are going to use
with internally with the passed
instance you’re redundantly
withing yourself. I do wonder if GML optimzes this out in YYC, I’ve never tested.
Any of these methods work, so whichever fits your coding style best is one to try working with but REMEMBER - it’s important that you establish some sort of standard for whether you’re expecting to pass the
instance or call the functions
with it, and to remain consistent with it. Otherwise, you’ll end up in a situation where you’re not quite sure how to use your own code, and you’ll need to be looking up your script code just to remember how it expects things to be called. A headache, tedious, and sometimes even overwhelming.
So that all sounds good, right? Pretty much got yourself a class definition through objects that you can instantiate with instances… it’s a nice little package, and definitely worth it if you need a class with a
step hook, a
draw hook, or maybe a
clean up hook.
Since I do have a drive for modular code, I’m particularly bothered by how “far apart” GameMaker Studio 2 places objects and scripts… I’d love to nest the “object scripts” with the object itself. You CAN create custom views and place them together which is nice, but the feature has been a bit too cumbersome for me to work it into my workflow… so I’ve come up with a different sort’ve work around though I prefer it - it keeps the objects guts in the script resources. Instead of writing code in the events, I just call
__system_name_event_create etc, and write that function. It helps keep all of the code together in the tree that you stubbornly won’t make a custom view for.
There are some downsides to using objects however:
- Events have a bit of an overhead over just a
script_executecall, so I shy away from using the
user events(in fact, I’ve never used
- With an object comes all of its instance variables and whatever overhead is gained by it existing on lists. You can
deactivatethe instance but if you do remember that you lose the ability to use
withwith the instance, and it’s essentially “off the grid” losing much functionality.
- (by lists, I mean, for each Step/Draw event, there is most likely some overhead to iterate over the object, even if you’re not necessarily using all the events)
Another option when deciding how to create your classes is to use a combination of an array and an enum. I tend to favor this method myself. Arrays are quick and easy to make, and fast to access their element values. With some practice, accessing members via array elements eventually becomes a second nature.
In order to define a class, use an enum, ending it with
Num so we know how many members our class has. We can then use
array_create to instantiate the class:
This example will get us a nice
rect_create function that creates the array object and returns it. While you can’t write actual member variables, you can fake “member functions” for these array objects by passing the array object as the first parameter - mimicking some prototype class based languages such as Lua:
Since arrays are passed by reference, you do need to be careful for subtle bugs when you forget to use the accessor operator (
_rect[@ ERect.L] vs
_rect[ERect.L]), and you’ll need to write your own dedicated cloning functions if you want to be able to copy your classes. Generally,
array_copy is ok, but remember your deep copies!
Using array objects like this allows you to keep them entirely within a script resource, organized in their own group as you’re wont to do. This satisfies the drive for modular code for me much more than an object with scripts that are rather detached from it…
Some additional downsides of using arrays include:
- Arrays don’t have events, obviously. If you need to clean up your object when it’s finished with, you’ll need to manually script some mechanism to handle this situation.
- Arrays can’t be iterated using
- Arrays can’t be the target of any of the collision functions (i.e., remember, you’ll lose
- Arrays re-allocate on resizing - it shouldn’t be too much of an issue since you should be creating your arrays with the fixed size from its enum.
Depending on the situation, I’ll use both of these types of classes. If “class” needs any sort of collision or drawing, I’m really talking about an object… and I’ll heavily consider using an object as a class in this situation. If it’s a more lightweight feature, most likely a mixin, then I’ll use an array object. Array objects are generally more suited for small, but plentiful “tasks” or bits of data.
Woah woah, “mixins?” You’re just gonna drop that one in there casually? Alright - so one final trick that I love to do that I wanted to mention was the concept of mixins. Basically, a set of functions to add functionality to another class or, basically, object.
The method of implementing mixins in GML that I use is simply writing a set of similar script functions, giving them an
destroy set of functions to be called in an object’s
clean up events (or event scripts tee-hee). For instance, I commonly use a mixin I call
mover to give basic movement to instances everytime I start a new project (it’s much better than rewriting it for the 90th time). It’s got a set of functions like this:
destroy function is useful if your mixin needs to use datastructures and needs to clean them up… and you can also give your mixins a
step or whatever hook you need since they’re technically living on an object.
If you’re going to use mixins, it’s another thing to consider standardizing - since mixins declare their own instance variables in this method, you’ll want to make sure you have some sort of system of prefixing them as to not conflict with normal instance variables… you’ll notice I threw in the
__mover variables as one example of a way you can prefix them, borrowing the
__ of private functions from the naming standard.
mixins in a manner that I’d call
loose mixins. The primary thing they do is instrusively attach to the instance they’re init’d to, in that the programmer using the
mixin has an understanding that some instance variables are going to be used up, and they’ll most likely be prefixed with the
mixin‘s name. With this,
mixins can be individual components that require things like a tick (or a
step as it is in GML) without having to be their entirely own object.
A couple of examples for things I’ve used:
moverhas several functions like
mover_move_towhich, when used in addition of
mover_stepwill move the instance to the position. it includes easing options and speed options, as well as some light optional 2d side-view physics .
animator_set_animationstakes a map of
EAnimationDataarray objects, using either strings or enums as keys.
animator_playallows the instance to track which animation is playing, query if an animation has completed, as well as tracking an animations position / looping via