Jump to content
A 2021 backup has been restored. Forums are closed and work in progress. Join our Discord server for more updates! ×
SoaH City Message Board

Mercury's Game Maker Resources


Do you use Game Maker?  

27 members have voted

  1. 1. Do you use Game Maker, and if so, what version?

    • Game Maker 7 or below
    • Game Maker 8.0
    • Game Maker 8.1
    • Game Maker Studio
    • I do not use Game Maker


Recommended Posts

Though I am slowly and surely moving away from Game Maker for the greener pastures of C#, I still use Game Maker 8.0 fairly regularly, not least because I remain committed to releasing a Game Maker compatible AeStHete package for those fan game creators who want to use it.

 

Having spent a lot of time with other languages and IDEs, I always find myself wishing the workflow was faster in Game Maker. It's impossible to make it perfect, but over the years I have found some workarounds and little hacks that make me happier using it.

 

In the interest of benefiting those who also still use Game Maker, I'll be sharing some of my secrets in this thread. Note that everything I will post is only tested in Game Maker 8.0 unless specified otherwise.

 

Also, for the sake of my own curiosity, I have included a poll. Please vote to help me get a sense of how Game Maker is used around here.

 

~ Mercury <3

 

Posts:

Edited by Mercury
Link to comment
Share on other sites

[Double posting to make an initial post]

 

The Problem

 

One of the things I hate the most about Game Maker's workflow is the necessity of adding code actions to events in the object form.

 

For any moderately advanced user who has graduated completely from the drag 'n' drop actions, the bulk of their work in Game Maker is writing code. This code can either be in code actions or scripts.

 

In Game Maker 8.0, scripts have the clear advantage over code actions when it comes to usability. They can be easily searched and checked for errors with the Scripts drop-down menu, and their code windows don't lock up the IDE. Indeed, these advantages are so great that I have ceased to use code actions altogether and I now rely entirely on scripts.

 

How do I do this? I simply create a group structure for my scripts that reflects the structure of my project. For example:

 


[] PlayerObject

PlayerObjectCreate

PlayerObjectDestroy

PlayerObjectStep

PlayerObjectDraw

[] Functions

PlayerObjectJump

PlayerObjectDie

[] EnemyObject

EnemyObjectCreate


 

It then becomes necessary to attach these scripts to the objects they belong to, a process which involves 1) opening the object form for the object in question; 2) clicking add event; 3) choosing the appropriate event; 4) going to the control tab; 5) dragging the "Execute Script" action into the action field; 6) clicking the drop-down menu to choose the corresponding script, which can be quite involved in itself as I drill down through my hierarchy to the script I want .

 

Going through all of the rigmarole above just to get the advantages of scripts over code actions is still worth it in my estimation, because not being able to search or error check large portions of your project's code is absolutely horrid. But it is a bottleneck for workflow, especially when defining behaviour for a new object and having to add scripts for many events in a row.

 

Using scripts in lieu of code actions in this way has another drawback: Scripts are less efficient, as they introduce the overhead of calling a function. This overhead is negligible in most cases, but for a large project that needs to be optimised - for example a Sonic game - it's not ideal.

 

The Solution

 

Fortunately I have found a way to avoid both problems and make a "scripts only" workflow viable.

 

My solution takes advantage of a few quirks and obscure functions of Game Maker, so I will explain each in turn before describing it.

  1. Game Maker does not strictly enforce valid names for scripts or other resources. You can include whitespace or punctuation in a resource name and be perfectly alright, as long as you don't need to reference the resource within code.
  2. Game Maker has functions for returning the names of resources, including scripts (script_get_name).
  3. Game Maker has a function for returning the body text of a script (script_get_text).
  4. Game Maker has a function for programmatically adding code to an object's events in the form of a string (object_event_add). Once the string has been added as code, it runs as efficiently as a code action that has been added manually in the IDE.
  5. Game Maker has a function for executing a string, allowing a project to evaluate GML at runtime (execute_string).
The 5 features above are all I need. I've written a script called bind_scripts(), which does the following:

  • Iterates through all the scripts in the project (by counting from 0 to a maximum index (which can be provided as an argument but defaults to 1000) and testing if a script of that index exists), checking their names for whitespace, i.e. chr(32).
  • If a script's name contains whitespace, it is assumed that it is not intended for use as a script, but as code for an event in an object. The name is then parsed to determine which object and event.
  • The body text of the script is then added to the appropriate object and event.
The result of calling bind_scripts() is that any script in the project with a name that conforms to the following structure will be bound to an object as an event:

 

MyObject ev_draw

MyObject ev_create

MyObject ev_destroy

 

In other words, a script with the name "MyObject ev_draw" would be bound to the draw event of the object called "MyObject".

 

For a complex event, both arguments must be provided in the script name, like so:

 

MyObject ev_step ev_step_normal

MyObject ev_mouse ev_mouse_enter

MyObject ev_alarm 0

MyObject ev_collision AnotherObject

MyObject ev_keypress vk_space

 

In the interest of allowing a little sugar, certain characters will be ignored, allowing the user to format the script names in many ways according to preference. Some examples:

 

MyObject (ev_step, ev_step_normal)

MyObject | ev_step | ev_step_normal

MyObject [ev_step - ev_step_normal]

MyObject: ev_step / ev_step_normal

 

One has to be aware, however, that typos in the object name or event constants can cause errors or undesired behaviour, so careless typers will need to pay especial attention.

 

Obviously bind_scripts() should be called before anything else happens, so that all the objects' events are bound before they need to be performed.

 

If you are wondering about whether or not the binding process will considerably slow down a project at its start, I tested it on my current project with over 250 scripts and it finished in 30 milliseconds. Ultimately, if it becomes a concern for any reason, all the scripts could be manually bound through a copy and paste process when your project is ready to be released, but at least the automatic process would save you a lot of trouble during development. However, the time is overwhelmingly dwarfed by the default loading time of any Game Maker project, so it should not be a worry.

 

Usage:



bind_scripts(); // uses default values, only checks up to script index 1000
bind_script(10000); // uses custom script index
bind_scripts(1000, true); // pass true as the second argument to clear events before binding scripts to them


tl;dr

 

This custom function will bind any script with a correctly formatted name to the object and event specified by that name. You can use it to work with scripts exclusively, eschewing the long process of adding code actions altogether, or use it only as a supplement. It requires a certain amount of familiarity and understanding of Game Maker, so unless you consider yourself moderately advanced, use it with care.

 

Here are downloads of the script and also a GEX extension version.

  • Like 1
Link to comment
Share on other sites

Guest Mr Lange

Mercury I'm as grateful for this as I am sad. You code in C# and everything and you're wasting all this on Game Maker which already has functional Sonic engines. Meanwhile Unity desperately needs and is more than deserving of a Sonic engine.

Link to comment
Share on other sites

Way to stay on topic guys. We really need to stop complaining about the platforms people use to make their games it is not productive, at least not here. If its a conversation worth having make a thread out of it, rather than derailing Mercury's topic with complaints.

 

As for us GM users this is will be extremely useful. It would be too much work to convert time twisted to this system, but I'll likely use it for my next project. Do you have a GMC account Mercury? This is worth posting over there I'd say. I can do it for you if you like.

  • Like 1
Link to comment
Share on other sites

I'll say this:

  • My 7 years of experience in Game Maker shouldn't go to waste. Now that I'm moving away from it I simply want to disseminate some of my knowledge for others.
  • The last SAGExpo was absolutely dominated by GM games, and I think that'll be a trend for another couple years at least. Therefore, this knowledge may be put to good use.
  • I hate Unity.  :P

As for us GM users this is will be extremely useful. It would be too much work to convert time twisted to this system, but I'll likely use it for my next project. Do you have a GMC account Mercury? This is worth posting over there I'd say. I can do it for you if you like.

I've never had the stomach for the GMC, but you can share this wherever if you'd like.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...
This is a significant difference between Game Maker Studio and Game Maker 8.0 that really gave me trouble. You see, in Game Maker 8.0, arguments passed to a function or script are always evaluated in a strict left-to-right order. Say you wanted to read some data from a file directly into a function like so:
some_function(file_bin_read_byte(file), file_bin_read_byte(file), file_bin_read_byte(file), file_bin_read_byte(file));
This would work as expected in Game Maker 8.0, with each byte being read from the file in sequence and winding up in some_function() as argument0, argument1, argument2, and argument3 as expected. But in Game Maker Studio, however, the evaluation order is not guaranteed, and - in my tests - seems to be precisely reversed. Thus, argument0 winds up as the fourth byte, argument1 as the third byte, argument2 as the second byte, and argument3 as the first byte.
 
Weird as this is, it appears as though it's not going to change any time soon (explanation here). It really should have been given more attention in the documentation, because it can wreak inexplicable havoc in legacy code.
 
Fortunately, it can be worked around easily, if clumsily:
byte[0] = file_bin_read_byte(file);
byte[1] = file_bin_read_byte(file);
byte[2] = file_bin_read_byte(file);
byte[3] = file_bin_read_byte(file);

some_function(byte[0], byte[1], byte[2], byte[3]);

 

  • Like 1
Link to comment
Share on other sites

Anyone who fooled around with MS Paint in the '90s as much as I did is intimately familiar with the Invert Colors effect. Today it still has its uses; it's pretty handy for flashing enemies or bosses for example.

 

It's actually pretty simple to achieve the same effect in Game Maker 8.0, even without shaders.

 

Inverting a Rectangular Region

 

As long as the drawing color is set to c_white, you can invert a rectangular region simply by drawing a rectangle with a certain blend mode.

draw_set_blend_mode_ext(bm_inv_dest_color, bm_zero);
draw_rectangle(left, top, right, bottom, false);
draw_set_blend_mode(bm_normal);

I recommend turning the above into a script, perhaps named draw_invert_region(), for ease of use.

 

Inverting an Image

 

Often you'll want to invert an image, though, not an exact rectangle. The trick to doing this is to invert a region, draw the image in that region, and then invert the same exact region again. This sandwiches the image between two inversions, resulting in every pixel in the image being inverted but the pixels behind the image are returned to normal.

draw_set_color(c_white);
draw_invert_region(view_xview, view_yview, view_xview + view_wview, view_yview + view_hview);
draw_sprite(sprite_index, image_index, x, y);
draw_invert_region(view_xview, view_yview, view_xview + view_wview, view_yview + view_hview);

Further Effects

 

It's possible to sweeten up plain old inversions in order to create some nicer effects. By drawing the image with a blend color, you can give it different tones. Draw with a dark purple blend color to get a golden yellow inverted image; draw with an orange or red blend color to get an icy blue inverted image.

 

If you want an example, here's a GMK you can fool with.

  • Like 3
Link to comment
Share on other sites

×
×
  • Create New...