GM 8.1
Home Up GM Studio GM 8.1

 

Automatic sprite group assignment

A programming technique for GM5.3A (possibly relevant to 6.0) that allows a group of sprites to be assigned to an object by assigning the first sprite of the group to the object.

Note that this technique is not supported by GM HTML5 and will probably not work in GM9. Put simply the developers have stated that the "execute_string" function will be removed meaning there is no way to turn the name string into a sprite ID.

On the bright side the GM source format is becoming more accessible to external utilities so it may be possible to write a code generator to provide comparable functionality.

In certain types of game (platform game) an object may use one of a number of sprites depending on its movement direction, or other game events. A player in Super Bouncy Ball consists of nine sprites. These are standing still, moving, jumping and squashed facing right and left, plus an additional facing forward sprite used to indicate the active player. There are multiple player objects each needing a different set of sprites. In games where an object only has one sprite it is easy to create other copies or descendants of the object with a different sprite. Where an object uses multiple sprites the sprite names are usually hard-coded into events, making them difficult to change.

The technique described here has been tested in UNREGISTERED GM5.3A and still works. I had been under the impression that "execute_string" was only available to registered users but it appears to be available to all. The technique still works in version 8.1 but is unlikely to work in anything newer than version 8.1.

Other game types benefit as well as platform games. Many games feature objects that can rotate. It used to be considered difficult to have rotation and animation in the same object*. It has been demonstrated that rotation and animation can be combined by using an array of sprites such that the multiple images of the sprites contain various rotations and the series of sprites contain successive frames of animation (used in water panic), or each sprite may be fully animated at one rotation so that the series of sprites contains a full rotation (used in the curve platform demo).

Automating sprite group assignment:

Firstly the sprites need to be named in a specific way.

Sprites organised in folders A spriteset.

There is one master sprite, then all the sub-sprites have the same name but with suffixes added.

The other player sets (shown as groups) have different names but the same suffixes.

In the constructor* create event of the player object there is the following code:

//auto_sprite code
//takes name of object's sprite and adds suffixes
//to fill a spriteset
var name;
name=sprite_get_name(sprite_index)
execute_string("stand_right="+name+"_s_r")
execute_string("stand_left="+name+"_s_l")
execute_string("walk_right="+name+"_m_r")
execute_string("walk_left="+name+"_m_l")
execute_string("jump_right="+name+"_j_r")
execute_string("jump_left="+name+"_j_l")
execute_string("squash_right="+name+"_sq_r")
execute_string("squash_left="+name+"_sq_l")

 

In the object information dialog box we set the sprite to the master sprite for that player. The "execute_string" commands fill all the variables with the appropriate sub-sprites. To assign a different player sprite set we only have to change the master sprite to an alternative.

Note that each master sprite must be accompanied by a set of correctly named sub sprites. This is about automatic sprite assignment, not automatic sprite generation (though I may cover this someday).

Assigning a sprite group to an array

Things get more fun with arrays.

In my "curve_platform" demo I had a number of sprites representing the player in different orientations. If the player runs at a curving surface they stay on it and can even run up walls and along the underside of platforms. 

In this demo I use arrays to store the sprites. By using arrays I avoid the need to have lots of "If" or "switch" constructs to determine which sprite to use. The arrays still need to be filled though and the brute force method is not fun:

rn[0,0]=run_l0
rn[0,1]=run_l1
rn[0,2]=run_l2
rn[0,3]=run_l3
rn[0,4]=run_l4
rn[0,5]=run_l5
rn[0,6]=run_l6
rn[0,7]=run_l7
This is only part of the original create code, the full code is nearly three times the length since it includes sprites for running facing left and right in many orientations, and sprites for walking, jumping and standing still.

A better way would be to fill the arrays using loops. To allow this I rename the sprites using numbers to represent the actions and orientations as follows:

Sprites organised in folders For clarity I've only expanded the left hand set of running sprites. The right hand set are in the folder run_r. I have numbered them this time.

The first digit is the facing direction. 0 represents left and 1 represents right.

The second digit is 0 for standing and 1 for walking

In the running sprites the second digit gives the orientation. an orientation of zero means moving to the right so in the left hand set sprite play_run_00 is running on the ceiling.

The code to fill arrays with that lot is as follows:

//auto_sprite code
//takes name of object's sprite and adds suffixes
//to fill a spriteset
var name;
name=sprite_get_name(sprite_index)
for (d=0;d<=1;d+=1)
{
  for (n=0;n<=1;n+=1)
  {
    execute_string("walk_sp[d,n]="+name+"_"+string(d)+string(n))
  }
  for (n=0;n<=7;n+=1)
  {
    execute_string("run_sp[d,n]="+name+"_run_"+string(d)+string(n))
  }
  
}

That's it, it sets up the array walk_sp with the regular sprites and run_sp with all the rotated sprites. I can substitute the sprite-set easily provided the replacement set is suffixed the same way as the original set.

An important footnote: I have not made variables "d" and "n" local. This is because code executed by "execute_string" is considered to be a separate script and cannot see our script's local variables.

GML Purists may dislike using object variables as loop counters. In GM8 it is possible to pass parameters to the string being executed, as though it was a script. 

In GM5 a more "pure" coding using local variables would be:

//auto_sprite code
//takes name of object's sprite and adds suffixes
//to fill a spriteset
var name,d,n;
name=sprite_get_name(sprite_index)
for (d=0;d<=1;d+=1)
{
  for (n=0;n<=1;n+=1)
  {
    execute_string("walk_sp["+string(d)+","+string(n)+"]="+name+"_"+string(d)+string(n))
  }
  for (n=0;n<=7;n+=1)
  {
    execute_string("run_sp["+string(d)+","+string(n)+"]="+name+"_run_"+string(d)+string(n))
  }
  
}

The modified code passes the values of "d" and "n" as numbers, avoiding the problem of the variables being local. Technically its cleaner code though harder to understand.

Footnote:

I am aware that GM version 6 has made it a lot easier to do top down games with rotation, however there is still a strong case for using GM5.3 unless you absolutely must have the new graphics capabilities. Also you may want to use pre-rendered lighting effects on your sprites or a projection that isn't totally top down, in which case rotating the sprite will look wrong.

Further footnote

Often it is possible to think of the create and destroy events as being equivalent to the constructor and destructor in object oriented programming. They serve a similar function, though there are traps to be wary of particularly when changing objects (the create event may be called again) or when leaving a room or ending a game (the destroy event may not be called).