csanyk.com

video games, programming, the internet, and stuff

Category: programming

GameMaker tutorial: high speed collisions

high speed collision skipping

If you have a small object, such as a bullet, collisions can be problematic for GameMaker if the bullet’s speed is faster than the length of the bullet. Any time an object moves faster than its width, it is possible for the object to skip over object in its way that it should have collided with.

The illustration below shows why:

high speed collision skipping

Here’s a simple, elegant solution for your high speed object to avoid this problem, regardless of its top speed.

Make the bullet sprite’s length as long as the bullet’s speed. If your bullet moves 16px per step, then its sprite should be 16px long.

If the bullet’s speed is variable, then the thing to do is vary the length of the bullet as the speed of the bullet varies. You can use this using the image_xscale property to scale the bullet.

Say the base bullet speed is 16px. If the bullet can somehow accelerate to 32px per step, then it should lengthen to 32px, by setting the image_xscale to 2. We can see from this that a general expression that solves the problem for any sprite width and speed would be:

image_xscale = speed/sprite_width

But it’s probably a good idea to use image_xscale = max(1, speed/sprite_width), to keep the bullet from shrinking if it is moving slowly.

A nice thing about this effect is that it makes high speed bullets look like elongated streaks, very similar to a blurry action photo where the subject of a photo is moving too fast for the shutter speed. In effect, that’s exactly what’s happening in GameMaker, if we consider the game speed to be like the shutter speed of a camera. So this creates a very natural looking visual effect, and solves a problem with high speed collision skipping.

This technique may be applicable to other objects besides bullets, but it works best with smaller objects because the stretching isn’t as noticeable. As the instance is moving at a high speed while being stretched, it can trick the eye into thinking it’s just a motion blur effect. With larger objects, this illusion is not as convincing, and the apparent distortion is more obvious.

Pinpointing the point of impact

Let’s say you need to know the exact point of impact from your high speed bullet. Since the bullet is stretched out, the x, y coordinates are no longer approximate enough.

high speed bullet point of impact

We an use point_direction to find the angle at which the impact occurred.

point_of_impact = point_direction(other.x, other.y, bullet.x, bullet.y);

If the target object is a circle, then calculating the point of impact is simple from whatever angle:
impact_x = other.x + lengthdir_x(other.radius, collision_direction);
impact_y = other.y + lengthdir_y(other.radius, collision_direction);

The above will work as long as the high-speed bullet isn’t positioned such that the x,y origin of the sprite is on the far side of the target object. If it is, the point of impact will be on the opposite side of the target object. If this is a problem, you can do additional calculations using the direction of the bullet’s travel as a clue to determining where the true point of impact was. You can also reduce the problem by making the bullet sprite’s x-origin 0, so the bullet’s [x,y] position will always be at the rear of the bullet.

If the target object is non-circular, or non-centered, you’ll need to do a bit more work to determine the point of impact, and exactly how to do that is outside the scope of this article. But using built-in variables image_xoffset, image_yoffset, bbox_top, bbox_bottom, bbox_left, bbox_right, you should be able to figure it out, at least approximately enough to be useful.

Writing files in GameMaker Studio

The levels in TARJECTORIES are procedurally generated, using a lot of calls to the Random Number Generator. Since the output of the RNG is stable for a given seed, the only variability from one runtime to another is the number of calls made to the RNG. Since the number of calls to the RNG in TARJECTORIES is deterministic, this gives the game the same random procedurally generated levels each time the game is played. Fortunately, those levels aren’t too bad to play.

The RNG sequence will vary if you die and play a new game without quitting/relaunching the game, but if you do quit/relaunch it will be the same sequence of levels every time.

I became worried that the game was too sensitive to the RNG, and wanted to capture the level data so that I could re-generate any of the levels in the procedurally generated sequence if I wanted to, even if the game ends up getting modified in such a way that the number of RNG calls changes, which would otherwise screw up the level generation.

To that end, I just wrote up a little script that writes the level data to a .json file, and played through the game, going through two cycles of the 18 procedurally generated levels, so I could get the original sequence plus the level sequence from GE Mode. This was pretty straightforward, except the part about where GameMaker writes the file.

It’s complicated and not easy to understand. GameMaker runs sandboxed, so you can’t just write files anywhere you want. There’s documentation that explains this in the manual, but even after reading it several times, it’s still not entirely clear where you should look to find a file.

My intent in this post isn’t to write a tutorial that guides you to a complete understanding of where GMS writes its files, but I will explain how I found the location.

GML has a built-in variable, working_directory, which is a path on the filesystem where the game.exe is running. The confusing thing about working_directory is that it can return two different values. In certain contexts, the working directory is literally the location where the game.exe is located. You can read files from here, so if you have any “included files” in your project that you need to read from, this is where they’ll be. The thing is, this location is read-only (to GameMaker) so you can’t have your application write any data here, not even to update existing included files. You have a separate location where GameMaker (and all well-behaved Windows applications) can write files, in %LocalAppData%.

This is where it gets weird for me, and I still don’t entirely understand what’s going on.

I run the project out of GMS, and to help me find the file I’m writing, I draw the working_directory to the screen, and I get this:

C:\Users\XXXXX\AppData\Local\gm_ttt_20698\gm_ttt_1803\

So I go there, and I do not see the file that I wrote!

Eventually, I found the file I was looking for in C:\Users\XXXXX\AppData\Local\[project_ name]

I was able to find it because I went to %LocalAppData%, sorted by date last modified, and looked for the most recently updated directory until I found it.

What’s the first directory, then? That’s where GMS builds your project when you run it through the IDE! GMS also uses %LocalAppData% to store the temporary build that it creates when you click Run Project. But this is the read-only working_directory location that is where the game.exe resides. The game.exe then creates its own directory in %LOCALAPPDATA% which is named whatever you named your project (eg., [project_name]) and this is the directory that GMS will allow itself to write to. And of course you can also read files from here, too.

The confusing bit is when you call working_directory in the context of, say, draw_text(x, y, working_directory) and working_directory returns the read-only path, but when you call working_directory in the context of fileID = file_text_open(working_directory+"file.txt");
file_text_write_string(fileID, "text")
, working_directory returns the writeable path.

The behavior of working_directory is what’s confusing. It would be a good idea, I think, if YYG were to create a GML variable called “working_directory_writeable” which was an alias of the writeable working_directory that always returns the writeable path. There should then also be a companion variable called working_directory_readonly They could leave the behavior of working_directory as it is, to avoid breaking anything already written. It can return the readonly directory or the write-able directory, just as it does currently. But if you need to be sure the path to the writeable directory, then you could use “writing_directory”.

So, moral of the story, if your your GameMaker Studio project wrote a file to working_directory, you will find it in C:\Users\XXXXX\AppData\Local\[project_ name] — not the location given by working_directory if you draw it to the screen.

Debugging TARJECTORIES: a Ludum Dare 38 postmortem epilogue

If you haven’t played TARJECTORIES yet, I encourage you to download and give it a try first.

Next, I encourage you to read the project postmortem article I wrote about the experience I had creating TARJECTORIES over a 48 hour period.

Back? OK.

In the wee hours of Sunday morning, in the midst of my debugging hell hours, I posted this on Facebook:

Debugging a program sometimes is like converting one system of thinking into an equivalent system of thinking, only one that works where the other one fails.

Since you can do this only one keystroke at a time, it’s pretty rough when you’re looking at transitional code and are looking for signposts to remind you of where you are. It’s common when you get distracted briefly to come back to looking at your code, desperate for these signposts.

This can end up making you very confused and waste a lot of time.

How to avoid this?

To put it in more familiar terms, pretend you’re writing a novel, and you want to change some of your characters around, so you need to update the text so that everything Smith says is now said by Roberts. Halfway into this, you’ve forgotten who’s who anymore, and there’s nothing in the manuscript that can help you tell what’s been changed already and what still needs to be changed.

If you keep versions of files, you can run a diff of the current manuscript against the most recently checked in version. If you check in versions at the right time, when things are self-consistent, not in a transitional state, this can be very useful.

If you don’t have, or don’t make use of, these tools, you’re putting a massive cognitive burden on yourself that prevents you from higher order thinking about the problem you’re trying to solve. If you get distracted in the midst of your transitional revision work, it’s like a juggler dropping their balls. One hiccup in the routine and the entire thing falls apart, and the only thing left to do is try to pick it all back up and start over.

The hardest thing is when you keep having ideas and thoughts about what else you’re going to need to do next. Often times a change will have a cascade of changes. Not necessarily code changes, but feature changes, or new features. Each one of these is like someone throwing another ball at the juggler. Even the best juggler can only handle so many things before they slip up and have to drop it. And while a person is juggling, there’s very little else they can do.

If you want to get real work done, don’t be a juggler. They spend most of their time repeating a cycle of familiar tasks that just maintain a status quo, and hardly have time to make any progress with anything else. It looks impressive, but it’s monumentally wasteful.

Learning not to juggle is even harder than learning to juggle, though.

The thing is, when you’re doing game development, design and development are tightly interwoven. It’s not even iterative, it’s sub-iterative. You’re figuring out what’s possible, what’s fun, what you can make work, and figuring out how to make it work, making it, and then seeing what else it needs — all at the same time. It’s inevitable that you will have ideas at every step in that process. And each idea is a ball to juggle. I found it works best to leave myself “TODO” comments throughout the code whenever I have an idea come to me while I’m still in the middle of working on something else, and keeping a notepad handy for things that I observe at runtime. Otherwise, you need to be able to split your conscience like a fractal if you want to have any hope of being able to do all the things that occur to you while you’re in the middle of development.

Holy s—. I’ve been at it since 10pm, and I think I finally just fixed a bug that I’ve been puzzled by for several hours. I can’t even begin to explain all the stupid details that I’d need to in order to convey what I just did.

But here goes anyway.

In my project, I have a planet which has a player on it. I had this working just fine with a single planet.

I wanted to create mutli-planet levels, but when I added a second planet to the game all kinds of things started going wrong because of all these hidden assumptions my code was making about there always only ever being one planet.

So I started disentangling the planet, which was really doing a lot of extra stuff that it didn’t really have any business doing. This stuff was managing other game state, and when you had multiple planets, they ended up screwing with each other as they both were updating the game state. Sort of like a race condition, I guess.

It caused some REALLY bizarre bugs, though.

The level counter went up smoothly until it hit 11, and then it started repeating itself a few times, then skipping a head several numbers, like 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 14, 16.

It also had a strange tendency sometimes to not spawn any player at all, on the second level 11.

I fiiiinally got the problem solved, without realizing that was what I was doing. I just started to give up and work on other problems, when I decided that the planet object should not create its own player. I’d done that early on because it made sense when there is only one planet. With two planets, each would create its own player instance, and then I’d have to go back after and delete one of them at random. This SEEMED to work, but somehow became responsible for the weird level number glitch, and the missing player object on the second level 11.

I STILL don’t get how that exactly worked. It still doesn’t make any sense to me. But when I decided to de-couple the player-spawning duties from the planet object, and instead of two planets each creating a player on it, and then randomly selecting one of those two to delete, I re-did the code to spawn two planets, then randomly choose one of the planets to spawn a single player on.

That fixed it. Like, whoa.

Update: A day later, it finally occured to me what happened, and why. This may not interest many, but for posterity, here’s what was going on:

When I made the game, I started out with a 1-planet room. I had this working so well that I didn’t want to screw it up, so when I was ready to start work on a 2-planet room, I created a new room so I could mess around in it. I had my level counter set up in such a way that after level 8, it would switch rooms to the 2-planet room for planet 9.

I had some code in the LevelUp object that handles this. But the thing about the Level Up object is, it doesn’t do those things when the game room loads. So on the transition into the 2-planet room, there were certain things that weren’t happening. To compensate, I (this was a bad move on my part) copied some code out of the LevelUp object and put it into the Creation code for the 2-planet room, and then failed to maintain this code adequately as I continued to make further changes on the LevelUp object.

So what happened was, when Level 8 is cleared, the LevelUp object does its end=of-level thing, and increments the global level variable, and creates new planets for the next level. It then checks to see if the level is 9, and since it is, it moves to the 2-planet room. The 2-planet room starts, and there’s 2 planets there from the room editor, not from the LevelUp object resetting the level. So nothing weird happens until after Level 9 is cleared, because that’s the first time the LevelUp object levels up in a 2-planet room.

I’m still unclear why the level incrementer goes from 9 to 11 — it seems obvious that it has something to do with there being two planets, but I didn’t have the level incrementer tied in any way to the number of planets. And it’s more mysterious to me still that it gets stuck on level 11, and so displays level 9, 11, 11, 11, 14. If GameMaker Studio had a better debugger, I might have bothered to trace through the script and watch the variables change to understand what’s going on, but I figured out enough to fix the problem without completely understanding it (well let’s hope so).

As to why there was no player at all on level 12, I do understand that. The planet’s Create event creates a player instance, and stores its ID in an instance variable, so it can update the player’s x,y position as the planet rotates. In the two-planet world, of course, I ended up with a player instance on each world. I didn’t need or want both of them, so I figured I’d just randomly destroy one of them, rather than fix it so that there would only be one. So I used a for loop and iterated over an array that should have been as long as the number of planets (2), and picked 1 array index to keep, and destroy all others. I figured I might want to make a 3-planet room at some point, and was trying to make the code work for N-planet rooms, rather than solve the 2-planet problem and leave a 3+ planet problem still unresolved. But sometimes the random chooser picked an array index that was out of bounds for the “keeper”, and so the for loop would go through and destroy all of the player instances. This happened to happen on level 12. It happened consistently on level 12 because the RNG seed value was always the same, and the same number of calls to random() always happen between the start of the game and getting to level 12.

I fixed the code by removing the player create from the Planet object, and put it after the planets are created, using choose() to select which planet if there’s more than 1.

The code works and appears to be bug free, but from a design standpoint it’s still messy. When I get around to doing it over, I will clean things up by removing the 2-planet room, which was only ever supposed to be an experimental room, and re-writing the code so that rather than move to that room when the level incrementer hits 9, it will spawn two planets, instead. This will enable move to remove the duplicate code by getting rid of the now-obsolete experiment room entirely. In retrospect that’s what I should have done in the first place, but because I was so concerned about not screwing up my working 1-planet room, I didn’t want to risk it. But really, all I needed to do was check-in to source control and create a fork to experiment in.

As easy and fast as the progress I made Friday night was, Saturday night was an absolute nightmare of a debugging session, just to figure this thing out and get it working. It’s SO weird how it was sortof almost working. Sometimes it’s a lot better to have something completely not working and obviously wrong. Those kind of bugs are much easier to fix.

The good news, though, is that I kept my Friday momentum through Saturday until I took my long break. I implemented a lot of features and had the game 95% complete, or so I thought. Just that last little bit of debugging took about 7 hours, and now the game is basically done.

So I made excellent progress Friday, and most of Saturday, although I unwittingly introduced this nasty puzzling bug during that progress, which stalled me for the entire night. But I still got so much done so quickly that I’m basically done now.

I have until 9pm Sunday to work on it, but I think it’s pretty good right now.

A tale of two GML scripts: code optimization through iterative development

Today I wanted to share two versions of a function that I wrote, in order to show how my iterative approach to software development works when I am doing code optimization to improve performance.

This example comes from my iMprOVE_WRAP asset. It’s a function that returns the shortest distance (taking into account the wrap capabilities of the calling object) between the calling instance and a target object.

The first implementation works, in that it correctly does what it’s supposed to do, but I never released it, because I wasn’t satisfied that it was good enough code to ship.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
///iw_distance_to_object(target_obj, x1, y1, x2, y2, do_wrap_h, do_wrap_v,)
 
///@description Returns the distance_to_object from an improve_wrap object calling this function to another instance. 
///Compares all relevant points for the iw_object and returns the nearest distance, taking the wrap range into account.
///@param target_obj id of the target object to determine the distance to.
///@param x1 left x boundary of wrap range
///@param y1 top y boundary of wrap range
///@param x2 right x boundary of wrap range
///@param y2 bottom y boundary of wrap range
///@param do_wrap_h set whether the horizontal wrap is on (true) or off (false)
///@param do_wrap_v set whether the vertical wrap is on (true) or off (false)
 
 
//get the distance from the nine virtual positions
//return the shortest distance
var obj = argument[0];
var iw_distance, iw_distance_up, iw_distance_down, iw_distance_left, iw_distance_right, 
    iw_distance_up_left, iw_distance_up_right, iw_distance_down_left, iw_distance_down_right;
var tempx, tempy, shortest;
var x1, y1, x2, y2, range_width, range_height, do_wrap_h, do_wrap_v;
 
//keep track of original location of target object
tempx = x;
tempy = y;
 
//set up wrap range
x1 = min(argument[1], argument[3]);
y1 = min(argument[2], argument[4]);
x2 = max(argument[1], argument[3]);
y2 = max(argument[2], argument[4]);
range_width = x2 - x1;
range_height = y2 - y1;
 
do_wrap_h = argument[5];
do_wrap_v = argument[6];
 
//check distances
//check center
iw_distance = distance_to_object(obj);
 
if do_wrap_h && do_wrap_v //wrap vertical and horizontal
{
  //check corners
  x = tempx - range_width;
  y = tempx - range_height;
  iw_distance_up_left = distance_to_object(obj);
 
  y = tempx + range_height;
  iw_distance_down_left = distance_to_object(obj);
 
  x = tempx + range_width;
  iw_distance_down_right = distance_to_object(obj);
 
  y = tempy - range_height;
  iw_distance_up_right = distance_to_object(obj);
 
  //check left and right
  y = tempy;
  x = tempx - range_width;
  iw_distance_left = distance_to_object(obj);
  x = tempx + range_width;
  iw_distance_right = distance_to_object(obj);
 
  //check up and down
  x = tempx;
  y = tempy - range_height;
  iw_distance_up = distance_to_object(obj);
  y = tempy + range_height;
  iw_distance_down = distance_to_object(obj);
 
  shortest = min(iw_distance, iw_distance_up, iw_distance_down, iw_distance_left, iw_distance_right, 
                iw_distance_up_left, iw_distance_up_right, iw_distance_down_left, iw_distance_down_right);
}
if do_wrap_h && !do_wrap_v //do_wrap_h
{
  //check left and right
  x = tempx - range_width;
  iw_distance_left = distance_to_object(obj);
  x = tempx + range_width;
  iw_distance_right = distance_to_object(obj);
 
  shortest = min(iw_distance, iw_distance_left, iw_distance_right);
}
 
if do_wrap_v && !do_wrap_h //do_wrap_v
{
  //check up and down
  y = tempy - range_height;
  iw_distance_up = distance_to_object(obj);
  y = tempy + range_height;
  iw_distance_down = distance_to_object(obj);
 
  shortest = min(iw_distance, iw_distance_up, iw_distance_down);
}
if !do_wrap_h && !do_wrap_v
{
  shortest = iw_distance;
}
 
//return calling instance to original location
x = tempx;
y = tempy;
 
return shortest;

Let’s take a moment to appreciate this function as it’s written. It’s well-structured, documented, and expressive. First we declare a bunch of variables, then we do stuff with the variables, then we get our answer and return it. And this gives a correct result…

So what’s wrong with the above? It’s an inefficient approach, which checks each virtual position of the wrapping object. If the calling instance wraps vertically and horizontally, it has to temporarily move the calling instance 9 times and check the distance from each of 9 virtual positions, then return it back to its original position, only to return the shortest of those 9 virtual positions.

There’s also a lot of code duplication.

Still, it’s not horrible code. But it’s up to 9x slower than the distance_to_object() function it’s based on, if you’re wrapping in both directions, which will probably be common. I didn’t think that was good enough.

Rather than check each virtual location to see which is the shortest distance, we just need to know whether the horizontal and vertical distances are more than half of the width and height of the wrap region. If they are, then it’s shorter to go around the wrap. To know this, you simply take the x and y values of the two positions, subtract one from the other, and compare to the size of the wrap range. Once you know which virtual position is the closest one, you can temporarily place the calling instance there, and use distance_to_object() to get that distance. Put the calling instance back where it was, and then return the distance.

I realized as well that depending on whether the calling object wraps in both directions, you may not need to check for a wrap shortcut in the horizontal or vertical. So we can potentially avoid doing some or all of the checks depending on whether the do_wrap_h and do_wrap_v arguments are true or false. As well, this means we can avoid declaring certain variables if they’re not needed, which conserves both execution time as well as RAM.

I usually create local script variables in a var declaration, and assign the arguments to them so the code will be more readable, but I wanted to avoid doing that so that this function could be as lean and fast as possible. This might be an unnecessary optimization, but that’s hard to predict since I have no way of knowing ahead of time how this function might be used in a future project. In a project with many wrapping instances, it could very well be called many times per step, and every optimization could be critical. Since the script is intended to be included as a function in an extension, once I have it working properly it shouldn’t be opened for future maintenance, so making the script readable is not as important. So I opted to remove the local variable declarations as much as possible and just use the argument[] variables directly.

Also, to ensure that the wrap range is defined properly, in the non-optimized version of this function, I declare x1, y1, x2, y2 and assign their values using min() and max() so that (x1, y1) is always the top left corner, and (x2, y2) is always the bottom right corner of the wrap range. Technically for this function, we don’t care precisely where the wrap range is, only what the width and height of the wrap range are. That being the case, I can further optimize what I have here, and rather than use min and max, I can just take the absolute value of the difference of these two values.

It turns out that the process I went through to optimize this function is pretty interesting, if you care about optimizing. So I’ll go into greater detail at the end of this article about the approach I took to get there. But for now, let’s skip ahead and look at the finished, optimized function. Here it is, re-implemented, this time doing only the minimum amount of work needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
///iw_distance_to_object(obj, x1, y1, x2, y2, do_wrap_h, do_wrap_v)
 
///@description iw_distance_to_object returns the shortest distance in room pixels between two objects in the wrap range, 
///taking into account the horizontal and/or vertical wrap properites of the calling object.
///@param obj the id of the target object
///@param x1 left x boundary of wrap range
///@param y1 top y boundary of wrap range
///@param x2 right x boundary of wrap range
///@param y2 bottom y boundary of wrap range
///@param do_wrap_h set whether the horizontal wrap is on (true) or off (false)
///@param do_wrap_v set whether the vertical wrap is on (true) or off (false)
 
 
if !(argument[5] || argument[6]) //not wrapping actually
{
 return distance_to_object(argument[0]);
}
else
{
 //We're going to figure out which virtual position is the nearest to measure from
 //To do that, we have to compare the h-distance and v-distance of the calling instance and the target position
 //If this distance is <half the range size, then the original position of the calling instance is closest
 //Otherwise we have to use one of the virtual positions
 //Then we're going to temporarily put the calling instance in that location, get the distance, and put it back 
 
 //arguments
 var tempx = x, tempy = y;
 
 if argument[5] //do_wrap_h
 {
   var range_width = abs(argument[3] - argument[1]);
   if abs(x - argument[0].x) > (range_width * 0.5)
   {
     x -= sign(x - argument[0].x) * range_width; 
   }
 }
 
 if argument[6] //do_wrap_v
 {
   var range_height = abs(argument[4] - argument[2]);
   if abs(y - argument[0].y) > (range_height * 0.5)
   {
     y -= sign(y - argument[0].y) * range_height;
   }
 }
 
 var d = distance_to_object(argument[0]);
 
 //return calling instance to where it was
 x = tempx;
 y = tempy;
 
 return d;
}

We don’t need to measure all nine distances to know which is the shortest; we can tell by comparing the direct distance to the size of the wrap zone — if it’s less than half as big as the wrap zone, the direct distance is the shortest. If not, then we need to wrap. We can check the x and y axes separately, and if both are called for then we can just combine them.

The second function should be much faster to execute, and uses less RAM. How much faster? Well, let’s do a test project using my Simple Performance Test and compare.

Download the iMprOVE_WRAP distance_to_object test project

It turns out that the improved code runs about 50% faster than the old code! That’s a measurable and worthwhile improvement. Although, that said, the old function ran well enough that I could have released it, and likely it would not have been a problem for many uses, particularly in Windows YYC builds.

Appendix: Optimization through Iteration

(more…)

iMprOVE_WRAP 2.2 released

iMprOVE_WRAP 2.2 has been released.

I’ve added two new GML functions to the asset: iw_point_distance() and iw_point_direction(). These functions work much like the built-in GML functions point_distance() and point_direction(), except they take into account the iMprOVE_WRAP wrap region.

Release Notes

Version Notes
2.2 New functions:

  • iw_point_distance(): returns the shortest distance between two points, taking into account the wrap zone.
  • iw_point_direction(): returns the direction of the shortest distance between two points, taking into account the wrap zone.

Get iMprOVE_WRAP

GameMaker Marketplace

itch.io

Full Documentation

iMprOVE_WRAP 2.1 released

iMprOVE_WRAP 2.1 has been released. Get it at GameMaker Marketplace or itch.io.

Full Documentation.

Release Notes:

1.0 Initial release
1.0.1 Updated iw_draw_self_wrap() to use image_blend rather than c_white for the color argument.
2.0.0 Added new functions:

  • iw_draw_sprite_wrap(): an iMprOVE_WRAP version of draw_sprite()
  • iw_draw_sprite_ext_wrap(): an iMprOVE_WRAP version of draw_sprite_ext()

Improvements:

  • Boundary drawing now occurs at wrap corners as well.
  • Phantom collison checking also occurs at wrap corners.
  • iw_collision_wrap() and iw_collision_wrap_map() functions now incorporate do_wrap_h and do_wrap_v arguments, and only perform collision checks where they are needed. They still return a value for all locations, but where no check is needed, they return noone.
2.0.1 Improvements:

  • iMprOVE_WRAP demo resources have been placed in folders to keep them tidy when importing the asset into a project.
  • oIMprOVE_WRAP_demo sprite has been updated to allow for more precise positioning. Sprite is semi-transparent, with a yellow pixel at the origin
  • oIMprOVE_WRAP_demo object now draws guide lines indicating the height and width of the wrap range. This is useful in confirming that clone drawings and wrapping is occuring where it should.
  • iMprOVE_WRAP demo dashboard text has been updated to be a bit more clear
2.1 New functions:

  • iw_distance_to_object(): returns the shortest distance to the target object from the wrapping object, taking into account all directions available.
  • iw_distance_to_point(): returns the shortest distance to the target point from the wrapping object, taking into account all directions available.

New demo room for the iw_distance_to_object() and iw_distance_to_point() functions

 

GameMaker Studio 2 impressions: Object editor

The most notable change in the Object Editor is that sub-windows are “chained” to the main form, in what YoYoGames is calling “Chain view”.

GMS2 Object Editor

The idea is that different parts of the Object editor should all be visible, not overlap each other, connected visually.

The main Object window shows the object’s basic properties: the Name, Sprite, Collision mask, and Visible/Solid/Persistent/Physics properties, as you can see. Chained to it are the object’s Events, and the Code Editor (or DnD Editor) will be chained off of the Events sub-panel. If your object happens to be a Physics object, or has Parents or is a Parent, then the Parent and Physics sub-panels will also chain themselves to the main Object editor form.

GMS2 Object Editor chain

This takes some getting used to, and occupies quite a lot of space on screen, which for users with smaller displays can make it a problem to work with Objects inside of a Workspace.

Fortunately, Object Editor windows, like any other window, can be broken out of the main GMS2 window and maximized, to fill up the entire screen if desired. Users will either love or hate Workspaces and Chain View windows, and if you’re one of the ones who hates them, you’ll need to get used to breaking the editor out into its own window and maximizing it, as this seems to be your only recourse for now. There’s a few Preferences in the Text Editors section that will make this easier for you, should you want to configure them:

GMS2 Text Editor Preferences

The GameMaker Community Forums have been very active in discussing the UX issues created by the new UI, though, so don’t be surprised if YYG do make a few changes in future updates.

DnD or GML?

The Object Editor comes in two flavors: Drag-n-Drop (DnD) and Code Editor (GML). Which variant you get is currently determined when you create a new Project, but you can switch at any time. Most users will probably prefer to create GML projects and work in the code editor, but beginners, younger users, and non-programmers may prefer the DnD option.

Probably the most important feature of either variant is its interface for defining actions in your Object’s events.

I’ll be focusing mainly on the GML version, since that’s what advanced users will use. But briefly, Drag-n-Drop has been completely overhauled in GMS2.

The new Drag-n-Drop system

Vastly expanded in GMS2, there are now DnD equivalents to just about every function in GML. Unfortunately, this means that there are vastly more icons needed to represent all of these new DnD actions, making them harder to learn. Similar to Chinese or Japanese, where every written word has its own symbol, there’s a DnD icon for every GML function. While it’s reasonably easy to pick up a DnD library with a small number of actions, this quickly becomes unwieldy as the number of actions grows. Unfortunately I expect this will have the undesired effect of making DnD too complex to use for beginners and non-programmers, making it questionable how valuable the DnD system will be in the future. Learning to code by typing out instructions isn’t that hard, and is arguably the better way to learn in the first place. But it’s nevertheless true that for certain people, they feel intimidated by programming or typing, and an intermediary step of using DnD like “training wheels” until the new user has an understanding of GameMaker’s fundamentals and is ready to move on to GML, has been one of GameMaker’s defining features.

In GMS1.x and earlier, DnD Actions were iconographic representations of special GML functions that started with action_ for example, action_set_hspeed(number). These functions were mostly redundant, being equivalent to other GML functions and expressions, for example hspeed = number;

The action_ GML functions are obsolete in GMS2, and are no longer needed. DnD Actions can convert directly into GML with a single menu command. This is a one-way conversion, and should help users who want to “graduate” from DnD programming to GML programming. Formerly, in previous versions of GameMaker, there was no way to convert DnD to GML code, other than to manually re-write everything. If you try to convert GML into DnD, rather than a sequence of DnD actions, you’ll get your GML code wrapped up in an Execute Code DnD Action, and the Object Editor will switch to DnD mode, allowing you to continue programming with DnD actions. While not particularly useful for advanced GMS users who are already familiar with programming in GML, it’s a nice improvement to the way the DnD system works.

GML Code Editor

The new GML code editor is still somewhat rough, but shows promise of numerous improvements. Indenting is standardized, to 4 spaces per tab by default, although this is configurable, and there are subtle guidelines showing where tabs will align to in the background. Row lines are numbered, again configurable if you don’t want to see them.

GMS2 Code Editor

The most obvious difference is the new color coding for syntax. This may take a bit of getting used to, but at first I found that my code looked very rainbow-y, and I found this to be somewhat of a distraction at first, but after a few days I found that I had adjusted. Every color is customizable, if you want to bother with that.

Auto-completion and hinting is improved in the new editor. All project variables, macros, etc. are included, not just the built-in GML keywords.

GMS2 Code Editor AutoSuggest

The completion hints at the bottom of the Code Editor window are very helpful to remember all the arguments that must be provided to a function, in the right order. And for any scripts which you author, if you use JSDoc commenting, you can provide hints for your own functions as well.

GMS2 Code Editor Completion Hint

Rough Edges

Cursor navigation keys are either different from standard Windows text editors, or else not yet fully implemented. I’m accustomed to, and very reliant upon, using Home|End|Page Up|Page Down|Shift|Control|Arrows to move the cursor about the window, to select text, and for copy/pasting. In the GMS2 code editor, these keyboard shortcuts do not all work as expected, which can be pretty annoying.

In most text editors, Home and End keys will make the cursor jump to the 0th or last position in a row, or if Ctrl+Home|End is pressed, the 0th or last position in the file. Presently, Home and End do not appear to be supported at all in GMS2.

The Arrow keys move the cursor around the document one character at a time, and if Shift is held down, the characters that the cursor passes over are then selected. Holding Ctrl down will speed the cursor up, moving it a word or a paragraph at a time.

For some reason when selecting text using Ctrl+Shift+Arrow, with the horizontal arrows, the selection gets “stuck” at the beginning/end of a row, and will not advance beyond that unless Ctrl is briefly released. This is a relatively minor annoyance, but should nonetheless be corrected. Normally, Ctrl+Shift and the Left or Right Arrow key will select to the next word, and will wrap lines if it reaches the end of a line.

Up or Down Arrow will move the cursor up or down a row, and with Ctrl+Shift held down, should move up/down to the next blank line. This is standard behavior in pretty much every text editor I’ve used in Windows, or Mac OS for that matter, but it is not the behavior in GMS2 at the time of this writing. I am hopeful that this will be addressed before the end of the Beta.

But by far the biggest thing that users are complaining about in the Community Forums has been the way the IDE wastes space in its default configuration, due to the way Workspaces and the Chain View UI work. Fortunately, breaking out the Code Editor into its own, maximized window is an easy workaround to this problem, and largely addresses it to my satisfaction.

Apart from these issues, I like the new UI for the Object Editor, and the Code Editor very much.

GameMaker data structures: a cautionary tale

I learned recently that when you are working with data structures in GameMaker, you have to be very careful.

When you create a data structure in GMS, the ds_*_create() function returns an integer value which is the id of the data structure. You then use this id whenever working with that data structure.

When you destroy the data structure, this frees up the memory that the data structure used, and the data structure is now longer accessed.

If you take that id and test to see if the data structure exists, using the ds_exists() function, it should return false.

The operative word being “should”. It doesn’t always.

Why not? Because, the GameMaker runtime reuses the ids, and if you create a new data structure, it will hand out that id that belonged to the data structure that was just destroyed.

To illustrate the example, we’ll use a ds_stack:

var a = ds_stack_create();
ds_stack_destroy(a);
 
var b = ds_stack_create();
show_message(string( ds_exists(a, ds_type_stack)));


You would think that ds_exists(a, ds_type_stack) would return false. But it doesn’t, because ds_stack b just moved into ds_stack a‘s old address.

Apparently the correct practice, which is not mentioned in the manual, is to immediately clear the value of a after destroying the ds_stack that it points to, like so:

ds_stack_destroy(a);
a = undefined;


If you do that, then a no longer points to an address that can potentially be used by another data structure, and you’re safe, even if b has the same address as a once did.

Of course there’s still a problem if multiple variables all point to that same destroyed data structure. This is probably a somewhat rare circumstance, but could possibly by the case in some circumstances, for example if you have a global variable, or controller object, holding a data structure that is accessed by multiple instances of some common object. Keeping track of every variable that references the destroyed data structure and clearing all of them when the data structure is destroyed is not an easy thing to do.

Update: Thanks to @YellowAfterLife for the very clever tip!

In other words, do this:

//original declaration:

a[0] = ds_stack_create();
//aliases
b = a[0];
c = a[0];

//destroy the data structure AND clear the variable
ds_stack_destroy(a[0]);
a = -1; // this destroys the array a[], and therefore kills b and c's references to it

d[0] = ds_stack_create();

ds_exists(a[0], ds_type_stack); //returns false
ds_exists(b, ds_type_stack); //returns false, despite not knowing to clear b
ds_exists(c, ds_type_stack); //returns false, despite not knowing to clear c
ds_exists(d[0], ds_type_stack); //returns true

Actually, don’t do that. I just tested it, and it doesn’t work. I must have misunderstood @YellowAfterlife or maybe he didn’t think through his advice carefully enough. I’m currently waiting for an answer on this, and will update it with something that does work, or a full retraction, once I have it.

It’s debatable whether the re-use of handles like this is in fact a good practice, but at the moment it’s the way GameMaker works, and has worked, for years. To some extent, re-use is probably impossible to avoid entirely, but the discovery that they are re-used so soon after being freed is a bit of a shocker.

More information.

The worst code I’ve ever written

Over at the GameMaker Community Forums, there’s an amusing thread where people confess to the worst code they’ve ever written. Since GameMaker is often used by newbies, it’s pretty common to see some hilarious WTF moments in code.

My favorite has been:

if fps < 60 { fps = 60; } //Fixes any performance problem!

If you’re not familiar with GameMaker, fps is a read-only variable that the engine updates to indicate the number of frames per second the engine is making. If it’s below room_speed (often 30 or 60), the game appears to stutter or lag. This happens when the hardware can’t keep up with the demands of the software…. so naturally the way to fix this to simply assert that fps is meeting target! ROFL!

I think that this code would actually compile and run without throwing errors in old GM. Despite being a read-only variable, GM would allow you to overwrite the value, not caring because the engine would simply re-calculate fps and update it again on the next step, and nothing adverse would happen. But if you were drawing fps to the screen, you would see “60” — even though the game might be lagging.

Here’s my own story:

When I was working on my game Alamogordo for Ludum Dare 29, I wanted to replicate the font used by the Atari 2600 in the game E.T. 
 
I had not heard of sprite fonts before, and had never used them. So I improvised my own system. First, I fired up E.T. in an emulator and took screen captures of the score, taking care to get all 10 digits that I would need. I then cut out the numbers and put them into a sprite resource. As I recall, I had problems with the selection size not always being the same, or being exactly centered, so it took quite a bit of time just to get the sprite set up so that it was the right size for all digits, and all the digits were positioned the same relative to the sprite origin.

Next, I wrote a script that took a number that I passed into it, converted that number into a string, then took each digit in the string and ran it through a switch statement, matching the value in the substring against a case that drew the right sub-image of my sprite that held the images of all the digits, and correctly offset them from one another, to draw the score on the screen. Only… no, I didn’t even do it like that! I actually used div to get the ones, tens, hundreds, and thousands digit, and then used that value as the subimage index to draw the sprite.

It worked beautifully, and at the time I thought it was brilliantly clever, but that there had to be a better way if only I had the time to RTFM.

I was using the variable name “finds” for storing the score because the player’s score goes up whenever they uncover something as they dig through the Alamogordo landfill, finding it, and you got a certain amount of points for each find you discovered.

///Create Event:
finds = 0;
image_index = 0;
image_speed = 0;

ones_digit = 0;
tens_digit = 0;
hundreds_digit = 0;
thousands_digit = 0;

///Draw Event:
ones_digit = finds mod 10;
tens_digit = finds div 10;
hundreds_digit = finds div 100;
thousands_digit = finds div 1000;

draw_sprite(sprite_index, ones_digit, x, y);
draw_sprite(sprite_index, tens_digit, x-sprite_width, y);
draw_sprite(sprite_index, hundreds_digit, x-(2*sprite_width), y);
draw_sprite(sprite_index, thousands_digit, x-(3*sprite_width), y);

At the time, my inspiration for the game was very last minute, I hadn’t even intended to produce a project that weekend, but the idea came to me Sunday and I ended up putting the game together in literally about 10 hours altogether. So I was just pleased that it worked on the first try. I had literally no time to experiment or read through the manual in the hopes that I would find a proper way to do it!

Lol! I recall writing that code, running it, seeing that it worked, and saying “Right, then! Good enough!” and moving on to the next thing I had to do. It’s fantastic sometimes what you can crank out when you’re only concerned with finishing on time, and don’t care about things like correctness or maintainability.

Truth be told, this is probably not my worst code I’ve ever written, but it might be the worst code that I’ve written that actually did what it was supposed to do.

Epilogue: I later learned about and used a sprite font in my Ludum Dare 36 game, Ancient Technologies. It was a much nicer way to draw the score to the screen!

GameMaker Tutorial: Instance Pooling for performance optimization

Instance pooling is a design pattern which can potentially help performance in games where you are creating and destroying a lot of instances.

If your program is spawning and destroying objects very frequently, calling the Create and Destroy Events many times per step, it’s potentially a lot of extra processing. Creating instances and destroying others constantly seems wasteful. Why destroy the instances that need to be destroyed when instead you can re-use them, and avoid having to call the Create event on a New instance every time you need one?

The Code

You can download my demo code here:

The GMS2 demo is an import of the 1.4 demo, cleaned up to remove the Compatibility scripts.

Looking at the project code, you will see that I have used inheritance to make the Control and Instance Pool test implementations as close to identical as possible. I have included telemetry code, which tracks the FPS of the game while it runs, so that performance is quantifiable. In the GMS1.4 version of the project, all of the telemetry code is neatly separated from the demo code into its own Execute Code action. This makes it easy to modify the project for your own use, and get rid of the telemetry code entirely if you don’t need to quantify runtime performance, and only want the code that actually implements the Instance Pool pattern.

How it works

The basic concept of the Instance Pool pattern is simple. The goal is to minimize the amount of times we create new instances of some object in large numbers during the game. A good candidate for objects to use Instance Pooling with would be bullets, bonuses, and enemies. Instance Pooling is particularly beneficial when the object in the pool is somewhat “heavy” — that is when the Create or Destroy events cause a lot of processing to happen.

The way we achieve this goal is by re-using already-existing instances of the object. This is done by deactivating the instance rather than destroying it. We add the id of all deactivated instances to our Pool. Then, when a new instance is needed, we first check the pool to see if an already-created but inactive instance exists, and if so, we remove it from the pool, activate it, and re-use it.

The most efficient way to create and manage our pool is with a stack. Stacks are elementary data structures in computer science. Imagine a stack of pancakes. The stack gets bigger as you add more pancakes to it. If someone orders a pancake, you take the top one off of the stack, and give it to them.

This is what’s known as Last In, First Out, or LIFO data access. This happens to be just what we need. The stack gives us an easy way to keep track of the id of all of our deactivated instances, and when we need to activate one, we just pop it off the top of the stack. This is very simple to do, and therefore very fast. In GameMaker, stacks are created by ds_stack functions. If you’ve never used them before, take a moment to read up on them. It will help you understand how the demo works.

The code at the heart of this is quite simple. It looks like this:

//First check the pool to see if it has any deactivated instances in it that we can use
 if ds_stack_empty(pool)
 {
 //If not, we need to create a new instance. No performance gain.
 instance_create(x,y,oInstancePoolBullet);
 }
 else
 {
 //If the pool has an instance we can use, take it out and activate it.
 var bullet = ds_stack_pop(pool);
 instance_activate_object(bullet);
 
 //Reset the state of the instance. 
 //**If** this is cheaper than a new instance, we gain performance.
 bullet.x = x;
 bullet.y = y;
 bullet.direction = direction + random_range(-20,20);
 bullet.alarm[0] = bullet.TTL;
 }

There’s only a little bit of other code that you need. You need to create the ds_stack, most likely this is best done in the Create Event, using ds_stack_create(). And when the pool is no longer needed, you need to destroy the ds_stack using ds_stack_destroy(), most likely in the Destroy Event.

Of course, we also need to push instances to the stack when they are no longer needed. To do that, we do this whenever we would ordinarily destroy the instance:

ds_stack_push(oInstancePoolPlayer.pool, id);
instance_deactivate_object(id);

So, don’t destroy instances that you want to pool; deactivate them instead. With our bullet demo, I do this by giving bullets a “time to live” and then setting an timer. When the alarm goes off, the instance’s id is added to the stack, and the instance is deactivated. Then it just waits for the game to need it. In a full game, you would need to look at other occasions where you need to deactivate the bullet, such as when it goes outside the room, off the screen, or when it hits an object.

That’s basically it. But you may need to think about some additional concerns, which I will cover in the Findings section of this tutorial.

Running The Demo

Open the project with GameMaker Studio, compile, and run.

The Demo consists of an Instance Pool demo and a Create-Destroy demo for comparison.

Press the Space bar to spawn bullets. Press Tab to switch from one demo to the other.

The FPS is displayed at the top-left of the screen. The first number is the minimum fps, the middle is the average fps over the last 30 steps, and the last number is the maximum fps. The minimum and maximum fps reset once per second. The average is a rolling average over the last second.

In the Instance Pool demo room, hold the space bar down until the first bullets to spawn reach the end of their life. At this point, the instance pool is filled, and no more instances will spawn; instead, deactivated instances will be re-used. At this point, you should notice the FPS dramatically shoots up and stays consistent.

Findings

To my surprise, I did not find that there was much benefit to creating an Instance Pool for bullets. Create and Destroy is about as fast as Instance Pooling with simple bullet objects in GameMaker. I expect the reason for this is that the GameMaker runtime engine is not particularly fast, but even when I did a YYC build, I still saw no real advantage to using an Instance Pool, at least in terms of fps.

Over a long game session, it may be that there is a reduction in memory fragmentation since we are updating values in RAM that has already been allocated, rather than rapidly allocating/de-allocating memory when we create new instances and destroy them constantly. But I haven’t tested that, so it’s merely speculation for now. Update: This benefit of reducing memory fragmentation is confirmed by Mike Dailly of YoYoGames.

In order to show a benefit to Instance Pooling, I had to weigh down the Create Event for my bullets. To do this, I ran a simple repeat loop to increment a variable. This is obviously inefficient and pretty pointless, but it serves the purpose of simulating a complicated object with an init script that takes some time to run. If that init script needs to be run only once, and not every time the instance is re-used, then there is a benefit and using the Instance Pool pattern will help.

When does it make sense to use the Instance Pool pattern? When there is a performance issue, and the Create or Destroy Events are more expensive than instance_activate_object() and instance_deactivate_object().

Setting up and managing the instance pool is pretty simple, but it does take some work, and incurs some overhead. It’s not worth it to incur this overhead unless there’s a tangible benefit in the form of noticeably improved performance.

Run the code profiler, and see where your code is spending the most time. If it’s heavy on Create and Destroy events, an instance pool might be just what the doctor ordered. Otherwise, you may want to look elsewhere for your low framerate fix.

Additional Considerations

As I mentioned, there are a few other considerations that you should think about when implementing an Instance Pool, which I will discuss here.

Should we Prime the pool?

In my demo, we only create new instances when there are none in the stack. This means that until we’ve deactivated our first instance, the stack is empty, and there is no performance gained by having it.

In fact, since we’re checking the stack before we create each new instance, there’s actually an infinitesimal hit to performance. When you run the Instance Pool room, we see pretty much the same fps performance as we did in the Create-Destroy room, until the first bullets start to be recycled. Only then do we see fps shoot up.

Well, what if we want the performance to be high from the very first bullet? We can do that, by priming the stack, by creating a bunch of bullets, and then immediately deactivating them and pop their id’s onto the stack.

We could create a special object that we might call oPoolPrimer, which lives for several steps, creates the instances we need and deactivates them, adds them to the Instance Pool stack, and then destroys itself when its work is done.

The best time to do this is when the performance hit won’t be noticed, such as when the game is in a menu screen. Alternately, we can create the instances gradually, over a longer time than would introduce a noticeable performance penalty.

To be effective, you will need to have a good way of calculating maximum the number of instances you need to have active in your game at any given time. Prime your pool with that many instances. That way, even when you have all of them active, you aren’t creating new ones, and your fps will stay high.

Trim the pool?

One of the downsides of the Instance Pool is that all those deactivated instances take up resources. Even though the CPU may not be spending as much time with those instances, they do incur some processing overhead to manage even when disabled, and as well they use some RAM.

Depending on your game, you might have a moment when you need a huge number of instances active, but in other parts of the game the action isn’t as intense, and you won’t need so many. For example, in my demo, I spawn 100 bullets per step, for 20 steps, resulting in some 2000 instances max if you hold the space bar down the entire time. But if you don’t need to shoot all the time, all those disabled bullets end up in the stack. Do you really need 2000 disabled instances? If there isn’t going to be as much shooting going on, you might not. We can free up resources by trimming the stack every so often.

One way to do this would be to set a recurring alarm so that every few seconds, the stack is trimmed if it goes over a certain size.

///Alarm0 Event:
while ds_stack_size(pool) > trim_limit
{
 //remove the top instance from the stack and destroy it
 with ds_stack_pop(pool) {instance_destroy();}
}
alarm[0] = reset;

This will keep the stack small when it doesn’t need to be so big.

Delete the pool?

If you delete the stack, you should realize that any disabled instances that we were tracking in the stack still exist. They will continue to do so until the game ends, or you change rooms. If you don’t change rooms right away for some reason, you may want to run through the stack before you destroy it, popping all the instances out of them so that they can be destroyed as well. Otherwise, those inactive instances will continue to take up resources.

while !ds_stack_empty(pool)
{
 //remove the top instance from the stack and destroy it
 with ds_stack_pop(pool) {instance_destroy();}
}
ds_stack_destroy(pool);

 

Benefits in network multiplayer games

This comes from GMC Forums user JuJu: There’s a nice implicit benefit of instance pooling: It makes handling multiplayer netcode for complicated scenes easier if you can refer to instances by a common index across all machines.

csanyk.com © 2016
%d bloggers like this: