60 Ways to make Game Maker projects more maintainable

Game Maker is a great tool; it is especially suited for rapid development and small projects. However, as a project becomes bigger, it becomes more difficult to find things, easier to break it, and generally harder to work on. This is of course true for any production environment, and there are many things you can do to tame the beast of scale. Here are 60 things to make Game Maker projects more maintainable.

General

Use postfixes to prevent name clashes

It is possible (but incorrect) to give, for example, an object and sprite the same name. To prevent this from happening accidentally, use postfixes to distinguish different types of game-elements (sprites, sounds, objects, rooms, and so on). Use postfixes instead of prefixes – this makes it easier to find specific objects in the object tree, and to read your code.

[EDIT]

The following Game Maker file illustrates how name clashes can lead to bugs:

test_name_clash.gmk (19.2KB)

Use folders to structure resources

Generally, the object folder structure should follow the inheritance hierarchy, with further separation as necessary. Other resource folders should reflect the groups of the objects to which they belong, or be subdivided by their function.

Use a Development Standards Document

Compile and work from a development standards document to promote consistency across projects and between developers. At the very least, this document should define:

  • your naming convention;
  • your folder structure;
  • your layering convention (see below);
  • positions of controllers; and
  • icon code (if you use one – see below).

Resources

Do not make the embedded drawing tool part of the pipeline

…if your main drawing tool is an external tool. This prevents every change from becoming an error-prone two-step process.

Use dynamic content

Loading images from files, creating sprites and objects dynamically, and constructing levels programmatically makes it a lot easier to change assets, since the Game Maker file doesn’t need to change whenever the asset changes.

Design art around a grid

Use a grid, and design objects for that grid to simplify world construction and object placement.

GUI

Draw your GUI in a separate room

…and overlay this room over every level.

Use a widget-and-style approach for complicated GUIs

For complicated GUIs, it is worth defining generic widgets (such as buttons or menus) that can be configured using parameters. Keep style elements (colour, font, borders) in separate objects. This makes it easier to change styles in one place, instead of in every widget. It also makes it easier to make the GUI skinnable.

Do as much of the GUI layout programmatically as is possible

Laying out a complicated GUI can be extremely labour intensive, so that changes to the GUI layout can become unmanageable. Using layout functions can greatly improve the ease with which the GUI can be extended or otherwise modified.

Extensions

Make extensive use of Game Maker extensions

Using extensions provide several advantages:

  • extensions make it possible to reuse functions more effectively;
  • extensions make it easier for more people to work together (people making extensions needn’t modify the main Game Maker file); and
  • extensions can be put under separate version control, and so reduce the impact of a roll-back.

Every script function you define should be considered for implementation as a DnD action in an extension. It is useful to have a general usage extension, that you can use across projects, as well as a project-specific extension. Remember to check whether somebody has not already implemented a particular function in an extension!

Document extension actions properly

Don’t leave any of the fields (such as Description, List Text, and Hint Text) in the extension maker empty, and take care to make the List Text and Hint Text display as much field information as possible (by using the @keywords – see the Extension Maker Help Documentation).

Spend time making (or searching for) proper icons for your extension actions

Forgetting what an action does kills maintainability; a descriptive icon will significantly reduce this.

Always define a script function for every DnD action

This way it is easier to unit-test features (see below), and possible to use the functionality from a script.

Actions and Scripts

Learn how to use the merge facility effectively to facilitate for teamwork

This is not Game Maker’s safest feature: there are many pitfalls, for example:

  • be careful for name clashes; and
  • all elements get new IDs (so be careful if you use hard=coded IDs).

However, with careful planning, this feature can help several developers can work together on a project. for example, every developer works on his / her own file, which is merged into a final game.

Avoid complicated branch logic in the GUI action editor

Complicated branch statements are hard to read in the DnD interface, and editing these constructs is error-prone. Put complicated branch logic in a script.

complicated_branches

Avoid assigning too many variables in the GUI action editor

Rather use a script. This way, tweaking the values of several variables becomes a lot easier.

Never use the Execute Code action

Always use the Run Script action, and put the code in a separate script. This way the code is easier to find, reuse, and use in an extension.

if_blocksAlways use blocks to demarcate if-then-else constructs in the DnD interface

…even when a statement block is only one line. This makes it much more convenient to add stements to the block in future, and reduces the risk of introducing a bug because of incorrect blocks.

Make use of named global constants

Using named constants makes code easier to read, less error-prone, and easier to change the value. If you define them in the Global Game Settings dialog, they appear highlighted in your code.

Use expressions as constant values

You can use expressions when you define constants (in the Game Settings dialog). For instance, to define a colour, you can set the value as make_color(255, 128, 64).

constants

Initialise the variables of an object in one place

…either in a single file, or as DnD actions in the Create event of that object. It might be better to do it in a file if you need to document usage.

Declare global variables in one file only

This serve as an easy way to see all the global variables, and is a good place to document their usage.

Always assign function arguments to named variables

Like this:

   1: {
   2:     var width, height;
   3:     width = argument0;
   4:     height = argument1;
   5:
   6:     return width * height;
   7: }

 

 

Notice that you can immediately deduce what the arguments should be, and that this function returns an area.

Use the with-statement as a for-each loop

The with-statement can be used as a for-each loop over the instances of an object (where the variable self serves as the loop counter).

The following two examples illustrate this:

   1: //kills all monsters
   2: with(monster)
   3: {
   4:     instance_destroy();
   5: }

 

 

   1: //Resets all object positions to 0 0
   2: with(all)
   3: {
   4:     x = 0;
   5:     y = 0;
   6: }

 

Objects

Use sprite frames to represent object states,

…rather than using separate sprites or objects per state.

Use inheritance

… and refactor aggressively throughout development. See The Power of Inheritance.

Use tiles instead of sprites for the visual appearance of objects

See for example http://www.youtube.com/watch?v=zM3iQbzM4C4.

Use unique sprites even for invisible and control objects

This makes it easier to place these objects in rooms, and identify them properly. If you cannot spend time making proper visually descriptive icons, use a coding system (based on colour, shape and text).

Avoid creation order dependence…

Creation order dependence is hard to remember, and thus something that is easy to break. Creation order dependence can often be eliminated by removing initialisation code, and making sure that the update function will do the work the first time it is called.

…Or use spawning to control order dependence

When creation order dependence is unavoidable, let objects spawn those objects which should be created after them. This way you control the creation order, but also make it explicit (and thus, not necessary to remember).

Use parameterised objects instead of separate objects where possible…

For example, when the only difference between two objects is their sprites, you could do better to make them the same object, and control the sprite that is used by a parameter. The fewer objects you have, the easier it is to restructure or otherwise change the objects.

…But use separate objects to avoid type checking

If you have to do type checking to distinguish between objects to differentiate behaviour, you should use separate objects. This is especially true when you have to perform group operations.

Use Game Maker objects to mimic record data types

For example, you might create an Abilities object that contains variables for strength and magic power. Every character in the game can make its own Abilities object, which allows you to write code like this:

   1: if (monster.abilities.strength > human.abilities.strength)
   2: {
   3:     kill_human(human);
   4: }

 

Of course, nowhere in the game the user will actually see an Abilities object, nor will it ever be put in a room (as would monsters, for example).

Use object composition

Object composition is a great way to reuse objects, and is a great substitute for multiple inheritance (which is not available in Game Maker). For example, a monster can be composed of its static attributes (strength, aggressiveness), its current state (health, anger) and its items (weapons and potions).

Avoid Using the Test Chance action

Because it is only possible to express a limited set of probabilities with a single Test Chance action, it is not easy to tweak it. Rather use a Test Expression action, with the expression something like:

random(100) <= 60

Idioms

Choose a layering convention

Choose it up-front, document it, and keep to it! For example, choose the layers for the background, static elements, dynamic elements, main character, and effects (such as particles and glows).

Use the controller idiom

Controllers are used to group together logic that does not belong to an in-game-object (such as the player, a monster, or item). Typically, controllers are used to maintain room state and global game state, and to draw interface elements.

Choose a standard position on screen for controllers

This makes them easy to find, and avoids duplication.

Standardise idioms that are relevant to your game

For example, I always use the init-update-draw idiom for each interesting object (i.e. init_monster, update_monster, draw_monster). Recognise the patterns in your own games, document them, and follow these as standards.

Avoid putting code in the room initialisation section

It is usually better to put initialisation code in a controller, and it is necessary if you want the code to execute before other objects are created. It is easy to forget about code in the room initialisation section, which can result in code duplication and hard-to-track bugs.

Time

Use timelines with care

Step numbers can only be hard-coded values. Thus, tweaking a set of timelines can become a maintenance nightmare if their step numbers are somehow related to each other or other game parameters. It might be a better idea to use a script and counter approach, which will allow you to define these relationships (for example, the growling time of a monster is always proportional to it’s size).

Use a script file to document important global issues, such as script entry points, general architecture of the game, and so on.

Design your game around fixed unit time intervals

For example, everything in the game happens in intervals of multiples of ten steps. Using this scheme makes constructing time-dependent events easier for the same reason that using a grid make level-construction easier.

Never use frame rate to change simulation speed

This can lead to ugly glitches in animation. Simulation speed is better controlled with a time scale variable independent of the frame rate.

Always animate sprites at the same frame rate

It is just easier not to have to think about this for every object. And if you need to tweak the frame rates of sprites individually, you can easily find yourself in a maintenance nightmare.

Do not use any text in your graphics

When text is part of a graphic, it is very hard to change it. Also, it means that you need a new graphic for each piece of text, which puts a bottleneck on the amount you can produce. Drawing text with Game Maker is a pain, but it will save a lot of work in the long run, and will make it easier to make the game data-driven and scale gracefully.

Events

Choose a fixed place to call the parent event

You should always call the parent event either first, or last, and do it consistently throughout (the game, or even better – all your games). This reduces the chance of having hard-to-debug logic as a result of calling the parent event, especially along complicated hierarchies.

Never rely on the order of events for something to work

Even though the order is well defined, and can be expected to stay the same for future releases of Game Maker, it is hard to remember this order, and hard to remember that the order for a piece of logic is important. Therefor mistakes are easily made. This is especially true in teamwork, where  reliance on event order might not be communicated properly.

Avoid using custom events when possible…

Custom events are not named (you have to use indices to refer to them), and hence it is hard to remember which event does what.

…But use custom events instead of global states

Custom events can be used to broadcast changes in game conditions. This not only eliminates global variables, but also the if-statements that are necessary to check the global state at every step.

Testing and Debugging

Use asserts

Define an assert function, and use it to confirm game conditions. Using asserts make it much easier to make changes safely.

Always make sure that the number of enums is correct

When you iterate over enums, you usually have a constant that holds the number of enums in a set. It is important to assert that this number is correct (it is easy to forget to update this number when inserting or deleting enums). Do it in a place where it is easy to see how many there should be. Below is an example.

   1: globalvar activity_dict;
   2:
   3: activity_dict[0] = bed;
   4: activity_dict[1] = tv;
   5: activity_dict[2] = stove;
   6: activity_dict[3] = washingmachine;
   7: activity_dict[4] = radio;
   8: activity_dict[5] = noone;
   9: activity_dict[6] = computer;
  10: 
  11: assert(ACTIVITY_COUNT == 7, "The wrong number of activities have been initilised in the script 'init'");

 

Always check that functions do not return noone

When searching for the nearest instance of an object, for example, make sure that the function does not return noone (even when you think it is impossible). At the very least, display a helpful message. It is common that your assumption of impossibility might be violated by future changes.

Use unit tests

Write unit tests for all your script functions. You can also set up unit tests in special test rooms, that will test object behaviour in specific preset conditions. When you are protected with unit tests, you make make changes boldly. Log success and failures to a file.

Make unit tests runnable with command line parameter

This way, unit testing can be automated. Use the log file to detect failures. You can use the parameter_count and parameter_string functions to get the command line parameters fed to the game.

Use a Game Maker file to put in unit tests for the general purpose library

This special file will not only help detect bugs in your extensions, but also serve as a usage document.

Implement a debug menu screen

Implement a screen with special debug functionality, and only display this when the game is run under a special mode. The debug screen should have ways to:

  • run any level;
  • run scratchpad levels (see below); and
  • run unit tests.

Implement a test room for a scratch pad

It is often necessary to test a new feature in a room that is already part of the game. Resist the temptation to test new features in existing rooms. It is easy to mess up a room when implementing a new, untested feature, and it can be hard to remove this feature if it doesn’t work out. Rather, have a minimal room ready that is fully integrated into the game, and use it as a scratchpad to test new features.

Implement debug drawing mode

In many cases your game can be too complex to rely on logging or inspecting variables one by one to perform adequate debugging. Drawing extra information with instances, and using colours and other graphical elements effectively, can make it a lot easier to spot errors. This technique is especially useful for AI and simulation.

Implement cheats

It is not only a useful debugging aid, but also guides the design (the technical design – not the game design!) to be more modular and flexible. A cheat (for example, a God-mode) that is hard to implement, can indicate a problem in the design (for instance, deductions to health is made in several objects, instead of in a single one or just a few).

Use a global game options to switch on and off some features

For example, this can be used to display debug info, or skip some eye-candy to increase load speeds. Do not chaotically comment in and out pieces of code or action logic to achieve this effect: it is error prone in larger projects and in teams.

About the author

Comments

  1. No! Not postfixes! ARRRGHHH! Every programming language that you’ll ever use in your whole life wants the data type BEFORE the variable-name.

    int hello;
    NOT
    hello int;

    Therefore, using postfixes is bad use and will really complicate things when moving on to other languages. DON’T!

  2. “Never use the Execute Code action

    Always use the Run Script action, and put the code in a separate script. This way the code is easier to find, reuse, and use in an extension.”
    That’s just stupid! Scripts should be used when the same code is going to be used at multiple places.

    Execute code should be used everywhere else. Use it instead of D&D. It gives more control, easier to read and easier to modify.

  3. @Chainsawkitten:

    “… Therefore, using postfixes is bad use and will really complicate things when moving on to other languages.”

    I get what you are saying, but dragging a suffix around as a variable name is not the same as a declaration, and I doubt that would hinder progress with another language. A similar situation occurs with GUI programming, where I prefer ok_button and file_menu (instead of button_ok and menu_file). It is just when you have many buttons, it reads easier when every button starts with something unique, especially when using auto complete features of an IDE. The same goes with monster_obj and monster_spr, where it is more important to know that you are dealing with a monster than an object or sprite.

    “… Scripts should be used when the same code is going to be used at multiple places.

    Execute code should be used everywhere else. …”

    This makes sense to promote encapsulation. However, if your code is hidden away in objects, it can become a nuisance to edit, and indeed easy to forget that it is there. For instance, you might easily implement two identical actions for two characters without realising it; when the scripts are arranged properly, this redundancy becomes easy to spot.

    This, to me, outweighs the advantages that using the “execute code” action provides.

    It is always good to have different perspectives on these matters; thanks for your comments!

  4. Obviously it depends on the kinds of projects you attempt with it. We use it for rapid prototyping – with great success. But it does have certain annoying feature limitations that limit the scope of projects, and also how many people can work together.

  5. Hi, I’ve used GM a little, actually I’ve done 2 games with it and I’m doing a 3rd one, but I’ve never been able to debug, I dont understand how. Can anyone send me a tutorial about it, or the link to it. Thanks

  6. The debugging features of GM are limited. You cannot set breakpoints and step through code (as far as I know). You can check the value of variables during runtime, however. To do this, run the game in debug mode (using the red play button). In addition to your game, a small window will appear. You can add watch expressions from here, for instance here.health, and see how they change over time.

  7. It is very use ful and also i like it verrrrrrrry much ,it is difficult but code spot makes it easier.

  8. I know this question comes a bit late, but I was wondering about the following:
    “DRAW YOUR GUI IN A SEPARATE ROOM
    …and overlay this room over every level.”

    How would you go about doing that? I couldn’t find a way to overlay one room over another anywhere!

  9. @OdedS: I really can’t remember how to do that…and a quick search does not reveal anything o_O. I still have a file *somewhere* where I did do it; I will find it and post the method here.

  10. Hi there-

    “USE SPRITE FRAMES TO REPRESENT OBJECT STATES,

    …rather than using separate sprites or objects per state.”

    Could you explain more what you mean here? I’m kind of new to Game Maker (hunkering down and determined to learn) but this tip sounds kind of important, probably because I don’t understand it, haha. I was going to make a character using separate sprites or objects for each state , but you didn’t go into detail how to use “sprite frames” or why I shouldn’t do the former.

    Your help is appreciated. This was a great article btw.

  11. @Roger
    A Sprite is effectively an image container. As such you are able to put more than 1 image into a sprite. Each image then becomes a frame within that sprite. Referencing the image you want is as simple as using the Frame index for that image when referring to the sprite.

  12. Thank you for this helpful article!
    I’m just a bit confused about the composition advices:
    Use Game Maker objects to mimic record data types
    1: if (monster.abilities.strength > human.abilities.strength)
    and Use object composition

    What exactly are you doing here and how? Did I understand correctly that “monster” and “abilities” are BOTH objects? But how do you make an object containing an object? Or is the “abilities” a variable pointing to the obj_abilities? When I tried something like obj_block.CreateEvent: attributes = instance_create(x,y,obj_attributes) it worked like your example. But I’m not sure if this is what you meant in those two tips.

    The instance count would grow significantly, when using this method on a lot of objects or when using many different “sub”objects (like with “object composition”). Aren’t there problems with the performance? And wouldn’t this produce a lot of maintenance work or bugs when cleaning up?
    I’m just learning and wondering how to follow the rule “composition over inheritance” in Game Maker 🙂

    Thanks!

    1. When I used this method, performance was not a problem using this method. It was a while back though; things may have changed (although most likely for the better). My advice is to only worry about it once it dies become a problem.

    1. Hi Chris, I think the article has many useful strategies, at least at the time that I wrote it (5 years ago!) If there are corrections I would be happy to make them. (It’s a shame not more people write on this topic. I did a quick search and there is not a lot available).

      Suh Burb looks very cool, BTW. The physics are impressive.

      1. I’m an experienced developer that’s new to Game Maker. This list was a great getting-started point for me to learn best practices in Game Maker. Thanks so much!

        1. It’s been almost a month since I’ve read your article, and my favorite points to follow are:

          1. Always use the Run Script action, and put the code in a separate script.

          2. Initialize the variables of an object … as DnD actions in the Create event of that object.

          I completely ignored drag-and-drop actions, at first, but using a comment prior to a block of variable assignments in an Object’s create event makes it easy to keep track of member variables.

  13. really good and useful points. I learned programming and game making by my own experience. so I learned these principles too late. I could be more professional now, if someone told me these methods 4 years ago. some of them are really important

Leave a Reply to navin kumar Cancel reply

Your email address will not be published. Required fields are marked *