csanyk.com

video games, programming, the internet, and stuff

Tag: programming

Other People’s Code

A GameMaker user recently posted an interesting question in one of the communities that I watch:

So, I’ve known for a long time now how helpful it is to go through source code you haven’t written, just to pick up good habits, and generally understand how things work.

I got the source code for several games including Home and Ink from a Humble Bundle deal a while ago, and am just starting to take a look at them now. I’m finding it really difficult to get a grasp of what’s going on/which objects and scripts relate to what though – mainly because I’m having to switch between several windows of code.

Does anyone have tips about how to learn to understand GM projects? I usually have no problem understanding singular sections of code, it’s more getting to grips with how everything fits together, especially in full-length games. And generally, any tips about how you work through other people’s code.

This happened to be a very timely question, as I’ve recently been going through this myself.

I find it very enjoyable to code my own projects from scratch. I know what I’m trying to do, and I know why everything I put into the project is there. Especially when the project is something I’ve recently worked on.

It’s quite a different thing for me to try to look at a project that someone else has coded. I don’t know what the author was trying to do, or why they did anything they way they did it. Oftentimes the code is not well organized, and very frequently the code is done in a style very different from how I code, or the programmer’s approach is very different from my own, or they use math that I don’t understand well without explaining what they’re doing or why or how it works, and so on. This can make it very difficult to get anywhere.

It’s of course vitally important to be able to work with code that other people have developed unless you have no aspirations to be anything more than a solo coder for your entire life. But even if you never work with other people collaboratively, it can be very valuable to look at other people’s code and learn from it.

If the code is well-documented, this shouldn’t be too hard. I like to say it’s the original author’s  responsibility to ensure that the code they write is easy to understand.  But well-documented code is unfortunately rare. Documentation (ideally) tells you what you need to know in order to understand the code. How does the author know what you need to know to understand their code? No project can afford to be a 101-level tutorial for new programmers. People will come to the project code with all levels of knowledge and from many different backgrounds, and you can’t anticipate everyone’s needs. Nor should you have to — it should be expected that someone reading your code comes prepared. Working code is rarely intended to be a textbook example for neophytes. Most often, the programmer, if they document their code at all, documents it for themselves, and doesn’t care about anyone else’s needs if they should ever read the code.

So it’s quite understandable, it’s still all too common for code to not be well documented, or just not documented in a way that is useful to you.

Getting into big project that you didn’t author can be overwhelming.

What to do:

  1. Before doing anything else, make sure the project builds and runs.

    Play it for a while, and try to catch any runtime errors that may already be in the code. Don’t make any changes to the project until you’re satisfied that it is working. But recognize that there may still be bugs that are hidden more deeply than your playtesting was able to reveal. These may not be the fault of the programmer, either; it’s always possible that some update in GameMaker caused a bug.

    It can be really frustrating to open an (unknown to you) already-broken project and make changes to it, then try to build it and find that it’s broken. The assumption will be that it’s your changes that broke the project, but if the bug was already there before hand, you’ll waste a lot of time focusing on your changes, not where the problem really is. So to avoid that, test the project first before you do anything else to it.

    While  you’re running the project, observe it and take note of any objects that seem important.  Guess at what these might be called in the project, and look for them when you start exploring the project in the IDE.

  2. Start with the first Room in the project.

    Open it, and see what’s in there. In GameMaker, the project always starts with the first room in the resource tree. Start taking notes about what you find in this room. What objects are present in the room editor, or being created in code? These should be the first objects to examine.

  3. Look at how resource groups are used.

    The project may use folders to group things together, especially if it’s a large project. If the author used folders to organize their projects, this will give you some insight as to how the author thought about the project and its parts, and how they relate to each other.

  4. Look for parent objects and see how they are organized.

    The inheritance structure of objects will reveal a lot about how the game works. The game may have a lot of objects, but if the author organized them effectively, it’s likely that many of them are related to each other through inheritance, so you may have a comparatively smaller number of object families that you’ll need to familiarize yourself with, and understanding the family broadly will convey much of what is needed to be understood to understand the project as a whole.

  5. Look for dead code and other resources that aren’t used.

    It’s very common for developers to leave stuff in a project that never gets used at runtime. This is the “cutting room floor” of the project; it represents stuff the developer worked on for a little bit, but for whatever reason never finished and didn’t end up using. Sometimes there can be a lot of it. Identifying the dead code can be helpful because then you can ignore it, and focus on the live code.  Once you’re sure the code isn’t used by the project, you may want to move it to a folder of dead code where it is out of the way and can be ignored, or just delete it from the project entirely.

    There isn’t really an easy to way to identify dead code.  But a few things to look for are code that is commented out, scripts that are never called anywhere in the project (use GMS’s “search in scripts” feature to find everywhere a script is called in the project.) Objects that are not present in any of the rooms, and are not referenced in code anywhere. Logical conditions that can never be satisfied can lead to dead code branches in if or switch statements, also, but these are a little harder to spot.

  6. Look at scripts.

    Scripts (often) support objects. Scripts are also (usually) well isolated and self-contained, so as to be reusable. Depending on the project’s size and the programming style, scripts may call other scripts.  This can become difficult to follow if you  have to jump between a bunch of different codefiles, but at least on your first pass this shouldn’t matter as much — just recognize that a script call is abstracting some detail, and you don’t necessarily need to get into the detail yet.

  7. Look at objects.

    First, scan the resource tree for objects that have sprites that you recognized from playing the game. Those are probably a good place to start.

    Look at the player object(s), then enemies, and other things like terrain objects, weapons, pickups, or whatever. There may be some important objects that are invisible during gameplay and don’t have sprites. Look at the name for a clue about what the object is for.

    As you get into each object in detail, probably the most important Events to understand are the Create, Step, Collision, and Draw events. Start there if the object has these Events defined, and take note of any other events it may use, and what it is doing in them.

    You may find GML code, or you may find Drag-and-Drop actions, or a little of both.

    As you start looking through the code, it’s going to be very tempting to make small changes, perhaps to conform to your ideas about code style and good practices. But try to resist the temptation to do too much of this at first. Even seemingly simple fixes can have consequences that you don’t realize. And you might find after “cleaning up” a bunch of code that it’s not needed at all, and can just be discarded.

    The only thing you should really do on your very first pass through the code is leave comments. I like to prefix my comments with my initials, and what the comment is about: Question, reminder to do something later, explanation, bug, possible bug, etc. This helps me as I go through the whole project and find answers to the questions I initially had when I first started digging into the project. And my initials help me to keep straight whether the comment is mine or the original author’s.

  8. Take notes.

    As you review the project code, add comments with your notes, explaining where needed. Comments can be questions, eg “What’s this for?” or notes for things to do later (“This looks interesting, come back later and understand this fully.”
    You can (and should) also take notes outside of the project. It depends a bit on the purpose — are you just looking at this project to learn from it, or are you working on the project to maintain or modify it? If you’re conducting a learning exercise, the notes should be the takeaway of what you learned by exploring the project files. If you’re actively working with the project, then the notes can be a guide to accumulate the understanding that you gained as you explored the project, a list of questions you still have, problems you intend to work on, bugs that need to be fixed, or other work to be done.

  9. Focus on the interesting parts.

    (The stuff you don’t yet know, but are interested to know/figure out.) Don’t worry about stuff you don’t understand, but aren’t interested in. Don’t worry about the stuff that’s simple/obvious to you.

  10. Don’t assume perfection.

    The programmer knew what they were doing. Well, maybe. Programmers make mistakes. Or do experiments. Or half-formed thoughts.  Or just don’t know a better way to do something.

    Code often has bugs. Code is usually sub-optimal.

    As you take ownership of the project, you will need to start being the decision maker who knows what they’re doing.  It’s easiest and best to carry forward with a good understanding of the previous author’s thinking, but it’s almost never available to us directly, and we have to infer it from what we do have.

    If you’re new to programming and lack experience and are looking at advanced, complex code in a big project, it’s hard to do this.  But start with small changes, test constantly, and build confidence as you learn and grow familiar.

  11. Ask questions.

    Ask questions about the stuff you can’t figure out. Contact the author directly if you can, be respectful and appreciative if they’re open to talking about it, and understand if they’re not interested. If they’re not responsive, or you still have questions, take them to the GMC Forums. Lots of people are there who will take an interest and try to help. Find a friend who is willing and able to mentor you, or even work with you on the project.

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…)

Fun enum ideas for game development

Most video games have a counting, ranking, or level system of some sort. It’s often good to have a thematic flavor to your counting systems. This way, rather than calling your level progression by boring old regular numbers, you can give each level number a meaningful, or flavorful, name. This can add character or meaning to your game world, or it can be used to help disguise the underlying math, resulting in a game where the underlying mechanics are masked away from the player, leading to a more mysterious experience where they need to experiment and discover. What’s cooler: a “level 3 sword?” Or “the sword of autumn?” What’s more powerful: the knight sword or the rook sword?

To that end, I thought I’d demonstrate a few example enums that you can use to spice up your game design.

ENUMerate all the things!

Alphabet {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z}

Greek Alphabet {Alpha, Beta, Gamma, Delta, Epsilon, Zeta, Eta, Theta, Iota, Kappa, Iota, Kappa, Lambda, Mu, Nu, Xi, Omicron, Pi, Rho, Sigma, Tau, Upsilon, Phi, Chi, Psi, Omega}

Military Phonetic Alphabet {Alpha, Beta, Charlie, Delta, Echo, Foxtrot, Golf, Hotel, India, Juliett, Kilo, Lima, Mike, November, Oscar, Papa, Quebec, Romeo, Sierra, Tango, Uniform, Victor, Whiskey, Xray, Yankee, Zulu}

Army (simplified) {Private, Specialist, Corporal, Sergeant, Officer, Lieutenant, Captain, Major, Colonel, General}

Navy (simplified) {Seaman, Petty Officer, Ensign, Lieutenant, Commander, Captain, Admiral}

Chess {Pawn, Knight, Bishop, Rook, Queen, King}

Rainbow {Red, Orange, Yellow, Green, Blue, Indigo, Violet}

Elements {Earth, Air, Fire, Water}

CardRank {Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace, Joker}

CardSuit {Clubs, Diamonds, Hearts, Spades}

Zodiac {Aquarius, Pisces, Aries, Taurus, Gemini, Cancer, Leo, Virgo, Libra, Scorpio, Sagittarius, Capricorn}

Seasons {Spring, Summer, Autumn, Winter}

Months {January, February, March, April, May, June, July, August, September, October, November, December}

Weekdays {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}

Lunar Cycle {New, WaxingCrescent, FirstQuarter, WaxingGibbous, Full, WaningGibbous, ThirdQuarter, WaningCresent}

Some of these may not necessarily have a natural order to them, but might just be a set of categories which you could use to organize something in your game. That something could be worlds, or items, or creature types, or powers, or anything else you can think of.

What else?

What else is there? Probably a whole lot! You could probably make a good basis for a game system out of gemstones, or metals, or barnyard animals, or anything, really!

If you know of a set of things that would make a good enum that I’ve left out, leave a comment below.

Using enums in GameMaker Studio

Enums are covered in the manual in the article on Data Types.

The basic syntax is as follows:

First, you have to declare the enum.

enum <variable>{<constant> [= <value>]}

You create the enum using the enum keyword, naming the enum <variable>. Then, in brackets, you list all the constants that make up the values in the enum. You can explicitly declare values for the enum constants, or you can leave them implied, in which case GameMaker will assign them integer values starting at 0, and increasing in order through the collection. You can even use expressions rather than a static value.

To access the enum constants, you use the syntax enum_variable.constant.

In your code, there are all kinds of situations where using an enum is potentially useful. Here’s a few things to look for in your code that might signal a good opportunity to use an enum:

Conditionals for checking state

Consider the following ways of checking the state of some object:

//check state using string matching
if state == "state_idle" {//do state actions}
//check state using literal numbers
if state == 0 {//do idle actions}
//check state using variable
state_idle = 0;
[...]
if state == state_idle {//do state0 actions}
//check state using enums
if state == enum.value {//do state actions}

Out of these, using enums is the best approach. Why are enums better?

Conditional checking by string matching is much slower than number matching. Your string value provides semantic meaning to the reader, making the code easier to understand when a human reads it. But when the program goes to check the conditional, it has to check every letter in both strings to see if they match.

As well, this approach is fairly error prone. It’s very easy to type the strings in slightly wrong. If you’re not careful, it’s easy to type one string where the state is assigned, and another string value where you’re checking state in order to do something state-specific. If you don’t catch the error, you’ll have a hard to diagnose bug because the conditional check won’t match what you’ve set the state variable to. One extra space, or misspelled word, or inconsistent use of capital letters, and the string match check will fail. The compiler won’t help you catch this sort of bug.

It’s a lot faster to do conditional checking via numbers. But naming your states with literal numbers: 0, 1, 2, etc. provides no meaning. Is 0 the idle state? is 1 running? is 2 jumping? Keeping track of this stuff in your head makes your work as a programmer harder, and makes the code hard to read, and brittle. You can do slightly better by creating a named variable and assigning the number value to it. The named variable can be expressive, e.g. state_idle = 0, state_running = 1, etc. But variables values can change, and storing each value takes a little bit of memory. By contrast, an enum value is constant — it cannot be changed once it is declared. And enums are global, so they are only declared and set in memory once, and thus require fewer resources. Even if you have ten thousand instances that use your enum, the memory used by them is the same as if there is only once instance.

Create an enum that provides expressive labels on these values, and you have the best of both worlds: expressive code that checks conditions fast, and uses computer resources efficiently.

Iteration over a set

Conventionally, programmers often rely on conventional looping variable names such as i or j to iterate over a collection of things, such as the members of an array, or other data structure. While this is OK, you can make your code more expressive by using enums to denote important numbers.

For example, it’s common for programmers to use a nested for loop iterating over the variables i and j to create a grid structure. Instead of using i and j, we can use variables named row and column instead. As we iterate over the range of rows and columns, we can use enums as flags to do something special at row (or column) == enum.constant.

Another example:

Say we want to implement an inventory equipping system for the player. The character has inventory slots for head, body, gloves, right hand, left hand, shoes. We decide to create an array, called inventory[], and assign the equipped item in each slot.

We could simply index the array with numbers:

inventory[0] = hat;
inventory[1] = armored_breastplate;
inventory[2] = iron_gauntlets;
inventory[3] = sword;
inventory[4] = empty;
inventory[5] = double_jump_boots;

Now, it’s not too hard to tell what each inventory slot is for. But it’s not as easy as it could be, either. Is the sword in the left hand or the right hand?

Let’s use an enum, and make the code easier understand.

enum inv_slots{head, body, gloves, right_hand, left_hand, shoes};
 
inventory[inv_slots.head] = hat;
inventory[inv_slots.body] = armored_breastplate;
inventory[inv_slots.gloves] = iron_gauntlets;
inventory[inv_slots.right_hand] = sword;
inventory[inv_slots.left_hand] = empty;
inventory[inv_slots.shoes] = double_jump_boots;

Now, it’s quite easy to see that the double_jump_boots are being worn on the player’s feet, rather than being carried in the player’s hands. It’s simple to keep track of which slot is which.

Creating a set of related, named values

Here’s a useful enum for direction angles for a top-down game:

enum compass {e = 0, ne = 45, n = 90, nw = 135, w = 180, sw = 225, s = 270, se = 315};

It’s much easier to remember and understand direction = compass.sw instead of direction = 225. You can use this enum for 4-direction and 8-direction movement or aiming, and you could even expand upon it if you wanted by adding constants for ene, nne, nnw, wnw, wsw, ssw, sse, and ese, like so:

enum compass {
 e = 0, 
 ene = 22.5, 
 ne = 45, 
 nne = 67.5, 
 n = 90, 
 nnw = 112.5
 nw = 135, 
 wnw = 157.5
 w = 180, 
 wsw = 202.5
 sw = 225, 
 ssw = 252.5
 s = 270, 
 sse = 292.5
 se = 315,
 ese = 337.5
 };

(I like to format my code like this; lining up the variable names, equals signs, and values makes it easy to scan down the file, and makes it clear that these values are all related.)

If I wanted, I could go a step further and use radians to make it clearer that these values are fractions of a circle:

enum compass {
 e = 0, 
 ene = radtodeg(pi * 0.125), 
 ne = radtodeg(pi * 0.25),
 nne = radtodeg(pi * 0.375), 
 n = radtodeg(pi * 0.5), 
 nnw = radtodeg(pi * 0.625),
 nw = radtodeg(pi * 0.75),
 wnw = radtodeg(pi * 0.875),
 w = radtodeg(pi), 
 wsw = radtodeg(pi * 1.125),
 sw = radtodeg(pi * 1.25),
 ssw = radtodeg(pi * 1.375),
 s = radtodeg(pi * 1.5),
 sse = radtodeg(pi * 1.625,
 se = radtodeg(pi * 1.75),
 ese = radtodeg(pi * 1.875)
 };

You can group together, and name other values that go together in a system using enums in this way, too.

Parting thoughts

Hopefully, these examples will help you see how enums can be useful to make your code more expressive, easier to read, and easier to maintain. Look for opportunities to use them in your code. They can really help!

Tutorial: GameMaker Object with multiple collision zones

GM Version: GameMaker:Studio 1.4 (concept should work in earlier versions)
Target Platform: ALL
Example Download: https://csanyk.com/cs/releases/games/LD48-36_AncientTechnologies/LD48_36_Ancient_Technologies_csanyk.gmz
Links: http://ludumdare.com/compo/ludum-dare-36/?action=preview&uid=10473

Summary:

How to make an object that handles multiple collision zones by using multiple sub-objects

Tutorial:

I used this technique in my recent Ludum Dare game, Ancient Technologies. The project has a TON of extraneous code in it, some of which is pretty messy, so I’ll replace the download link with a cleaner example project of just this technique in coming days.

If you look at the Atari 2600 console in this game, it is actually multiple objects, made to look like a single object. This enables each switch to have its own collision detection and mouse events.

I accomplished this with a few simple techniques that work really well:

The console itself is the main object. Then there are 6 objects for the 6 switches on the console.

I then took the image I wanted to use for the sprite, and cut out all the different clickable regions, putting each into its own subimage by itself. Then once all the subimages were created, I removed each subimage into its own sprite, and set the collision mask to cover just the pixels in the image

Atari 2600 clickmap sheet

As you can see, this approach results in a lot of “wasted” space in the sprite sheet in the form of transparent pixels, but if you’re concerned about this, you could always achieve the same effect by using sprite origin and sprite offset to position the images without all the whitespace in the sprite sheet. I skipped using sprite origin because I didn’t feel like bothering with counting pixels and setting each sprite origin differently. That would have been tedious, and if I had ever needed to change the sprites, could have thrown everything off and required extensive rework to recalculate again. If your sprites are tiny, it probably doesn’t matter too much, but if they’re quite large, then you should probably use sprite origin/offset.

(Note: With the small pieces, you’d need to use origin numbers that are larger than the dimensions of the sprite itself, or possibly negative values for the origin. You’d also need to carefully calculate the distance to set the origin so that the images all line up precisely. The goal is to make all the images align when each instance’s x and y are identical. This makes them easy to move around together as a group, without having to deal with offsets and so on. To make everything align easy, I created sprites that have the same height and width, so that getting them to align takes no extra effort or calculation at all.)

Next, use the “main” object to generate the collision zone objects, in the create event:

Note the example code below isn’t exactly the code in my project, but is cleaned up a bit for easier comprehension.

///oConsole Create Event:
power_switch = instance_create(x, y, oPowerSwitch);
tv_type_switch = instance_create(x, y, oTVTypeSwitch);
left_difficulty_switch = instance_create(x, y, oLeftDifficultySwitch);
right_difficulty_switch = instance_create(x, y, oRightDifficultySwitch);
select_switch = instance_create(x, y, oSelectSwitch);
reset_switch = instance_create(x, y, oResetSwitch);

Since these objects all use sprites that are the same size, they all line up together when their x,y position is the same. Now you have an easy way to place just the main object and have it create all of its collision zone objects, perfectly aligned.

If the main object needs to be able to move, you just need to update the x,y of all the objects whenever you need to.

Another great thing is that each of these instances can have its own animation which is completely separate from the rest of the collection of objects. This means you can do really sophisticated animation rigging.

Also, you can access the instance variables of the collision instances by using the main instance, like so:

oConsole.power_switch.position = UP;

is (essentially) equivalent to:

oPowerSwitch.position = UP;

This allows you to use the collision zone instances’ instance variables like you would use properties of a helper class in another language, which may make accessing these variables easier if you’re used to other programming languages.

As well, if each of these switch instances were of the same object, using the id returned by instance_create() would allow you to differentiate between them. This could be useful for, say, a battleship with multiple turrets that are all of the same object type.

Finally, you’ll want to be mindful that if you destroy the main object, you’ll likely want to destroy all the sub-objects. Perhaps not — maybe you’re doing a spaceship with an escape pod or a rocket with multiple stages that break away. But in that case you’ll wan to be sure that once the main object is gone, you are handling whatever remains somehow.

Managing complexity when implementing complex conditionals

Programming is in large part about managing complexity and imposing structure onto a problem domain in order to make it simple (simple enough for a machine to be able to do the solution, as well as simple enough for human programmers to be able to build it).

Something struck me about a technique that I used when I was developing my game last weekend.

The code that handles the setup of the simulated atari 2600 console is full of conditionals like this:

if PowerPlug.is_plugged_in && (console.switch_power.position == POWER_ON) 
 && (TV.switch_power.position == POWER_ON) && Cartridge.is_plugged_in && 
 Joystick.is_plugged_in 
{
 //simulate the game 
}

So one thing that I had to deal with as I was building this was all the preliminary stuff’ you know, to create all the objects that needed to be checked in the conditional statement above. Rather than do that, all at once, and have a lot of conditions that could have bugs in them, any one of which could make the thing not work, and be hard to figure out since multiple bugs could end up canceling each other out, or mask one another in some way, I opted to simplify things at first:

if true
{
 //simulate the game
}

The advantage here is obvious. It’s simple, and you KNOW that conditional is always going to evaluate to true! So you can get right into working out the code inside the branch, without worrying for now about the conditional logic that would cause it to execute.

Once that was working, I added in stuff:

if PowerPlug.is_plugged_in
{
 //simulate the game
}
else
{
 if PowerPlug.was_plugged_in_previously
 {
 kill_game();
 }
}

Then from there it became easier to elaborate on the conditional:

if PowerPlug.is_plugged_in
{
 if Console.switch_power.position == POWER_ON
 {
 //simulate the game
 }
}
else
{
 if PowerPlug.was_plugged_in_previously
 {
 kill_game();
 }
}
 
//etc.

(Note, the above isn’t the exact code from my project, which is a bit messy at the moment as I have some refactoring to do, but it illustrates the point.)

I found that by coding it in this way, I could iterate and test each little piece of the logic easily, and make sure it was working before trying the next piece.

I found that doing it this way made it much, much easier to handle the complex nested if business, and as well I found it easier to know which branches of the complex conditional tree the next bit of code needed to belong in.

Now that I did it this way, it seems obvious, nothing of great insight… but 6 years ago I would have tried to code the whole thing out, and then pulled my hair out swearing at myself when it didn’t work as expected, and I had to untangle all the logic to figure out wtf it wasn’t working.

Back then I would have thought that a really superior version of me who I aspired to be would be able to code out all the conditions in one go without making a mistake.

Today a really superior version of me knows better. Start simple, add elaboration through iteration.

This is a good lesson to hang onto. And to pass on.

Of course, there’s also often ways to avoid having to code conditionals at all, but that’s a topic for another post.

Archeology vs. Engineering: contrasting approaches to long-term maintenance of IT Systems

Thinking about programming and maintaining a system in a team environment, where the system has a long life and the team supporting it experiences turnover.

When I program I know exactly wtf I mean by . I understand its purpose and design, and this makes me fairly confident that I know what is and what it does or is supposed to do. My ability to solve problems when I code from scratch is limited by my ability to understand the problem and to conceive of a solution. Often what I can come up with is inferior to what the greatest minds can come up with, but for many problems I can come up with something acceptable, and code it in such a way that it is very easy to understand the code, because I like to write code that is written in a way that lends itself to understanding.

When I look at someone else’s , I don’t know wtf they meant when they coded . I have all kinds of questions about what was in the programmer’s mind, how they understood the problem, what they handled in and what they elected not to handle in it, and so on. It helps immensely if I know the language and any frameworks or libraries well, but often when you’re inexperienced that’s not the case. And, especially with larger, older things that have been built up over time, that becomes a very steep learning curve. Until I’ve had enough exposure and experience to , I feel very uncomfortable, uncertain, and unconfident that I understand any part of . And, if is sufficiently large, I never get over this, and it ends up limiting and paralyzing me in my efforts to become a better programmer.

If I built it, then I know why, and I can be in a position to know better later when I’ve learned more and maybe decide to change my mind based on some revelation. 

But if someone else coded it, unless they coded it in such a way that the code clearly expresses its intent, and/or they’ve commented it extensively, or documented it somewhere, explaining their rationale in detail, I can only speculate, and depending on whether I feel like I am smarter and more knowledgeable than they are, I might or might not feel comfortable making a change. I would at the very least feel uncomfortable making a change that I could not roll back quickly/easily if something broke, ideally in a test environment where there would not be serious consequences. But I might not feel confident that a change would be instantly and obviously noticeable; often things break in very subtle ways. Having a suite of unit tests is very useful here, but it’s often the case that there are no tests written.

Even when a system has extensive documentation, there’s no guarantee that it is up to date, or accurate, or correct. Other people often have very different ideas about what and how to document, and how much detail to include, and where to put what information. All systems of documentation seem to involve significant tradeoffs, and there is no silver bullet solution to documenting adequately.

I call such situations IT Archeology, rather than IT Engineering. It’s very much like discovering a lost culture’s artifacts and trying to figure out how their civilization must have been structured and how daily life must have been lived, and then trying to adopt those ways and live by them yourself in order to understand them better. By contrast, IT Engineering is what you do when you have a solid foundation of understanding of the problem domain that provides the context that it works within, and knowledge of the system and the technology stack that it is built upon.

At the moment, I am wrestling with how one moves from an Archeology paradigm to an Engineering paradigm. But it’s an observation I’ve noted many times in my experience, and it seems quite common. I am interested in advice from developers who have to deal with this sort of thing about what approaches actually work.

GameMaker Position and Motion Tutorial

Motion is critical to just about any video game. Nearly every game has moving things in it, and how they move is a vital part of the game. Learning how to program motion and control it effectively is one of the most important parts of a successful game. There are a number of possible approaches to handling position and movement. Learning how these work will help you make better games.

This isn’t absolutely everything there is to know about motion, but it’s a great overview to start with, and covers everything I’ve learned with respect to motion in GameMaker Studio.  (more…)

2D Targeting for AI in Game Maker 8

After several weeks of effort, I have finally nailed an effective set of 2D targeting scripts for AI in Game Maker 8.

The story for this is worth telling sometime, but for now I’ll just be posting a video demo:

Source .gmk is available on Releases.

I’ll be refactoring this into a Game Maker Extension (.gex) soon as well, which will also be available along with full source.

csanyk.com © 2016
%d bloggers like this: