csanyk.com

video games, programming, the internet, and stuff

Tag: tutorial

GameMaker Tutorial: Configuration system

Many games have options or settings that are configurable.  The specifics can vary widely, from graphics options to music and sound effects volume to to input configuration to in-game options such as difficulty level and which rules are enabled/disabled.

This article will cover how to create a basic configuration system, using external file i/o, ds_map, and json.

Define your configuration requirements

First, before you develop it, design the system.  Not every variable in your project need be subject to customization. Decide what configuration options you want, and define the variables that will be needed to control them, and decide on your default values. 

The default configuration should be safe to run on all devices the game is intended to run on, and should have the “standard” or “recommended” settings for the best or recommended game experience. 

Having defaults is important in case the external config file is missing or corrupted, and needs to be regenerated.  Create a ds_map called defaults, and use it to store all the default values.

Coding the config system

Coding a simple config system is actually very easy.

First, define variables to store the default configuration and the actual configuration, and create ds_maps for them.  The best time to create these ds_maps is immediately on launch, as soon as the program starts.  You may want to make these global variables, so that they will be accessible anywhere and will persist for the entire runtime.

global.defaults = ds_map_create();
global.config  = ds_map_create();

Of course, when we are about to exit the game, we will need to remember to destroy these data structures, to properly free up the RAM:

ds_map_destroy(global.defaults);
ds_map_destroy(global.config);

Next, initialize the defaults ds_map with all the variables that are part of the configuration, and their default values. It’s easiest to learn the shortcode for accessing ds_map values:

defaults[? "key"] = value;

In the code above, “key” is the name of your variable, in quotes, as a string. The value is the literal value of the variable.

So you can do this:

global.defaults[? "starting_lives"] = 3;

or

global.defaults[? "starting_lives"] = default_starting_lives;

As you can see, the ds_map’s key indexing allows you to choose meaningful names for the keys, which makes it easy to recall them later.

When you apply the configuration to the variables in your project, assign the value from the ds_map, like this:

variable = config[? "key"];
starting_lives = config[? "starting_lives"];

Once you have your ds_map populated with your configuration data, it’s time to write it to disk.  This is will save the file so that the configuration state can be read and applied to the game state the next time the program runs.

The gml function json_encode() makes it very easy to write the ds_map to a file, by converting the ds_map into json, or JavaScript Object Notation, which is just a commonly used data format, and can be thought of as a specially formatted string. You don’t need to know anything about the syntax of json in order to use the encoding/decoding functions.

Create a json-encoded string version of the config ds_map:

config_string = json_encode(global.config);

Check to see if an external config file exists already, and if not, create it and fill it with the default configuration:

if !file_exists(working_directory + "config.json")
{
config_file = file_text_open_write(working_directory + "config.json");
file_text_write(config_file, defaults_string);
file_text_close(config_file);
}

If the config file already exists, read it into a string variable, decode the json string to convert it back into a ds_map, validate the data in the ds_map, and, if valid, apply the configuration data:

//First read the json data out of the config file
config_file = file_text_open_read(working_directory + "config.json");
config_json = file_text_read_string(config_file);
file_text_close(config_file);

//Next, decode the json, converting it to a ds_map
global.config = json_decode(config_json);

//Now, validate the configuration data to verify the settings are good
if !config_validate(global.config) //config_validate is a script that you wrote to validate your custom configuration data.
{
   //if we failed validation, destroy the config and create a good copy using defaults.
   ds_map_destroy(global.config);
   config = ds_map_copy(global.default);
}

[...]

//apply data stored in global.config to the variables in your project as needed, where and when appropriate.

The exact details will vary depending on your project, but the above is a good example to follow to get  you started.

Implementation: Data and File I/O

It might help to explain the above, in plain English, without the code examples getting in the way.

When the game runs, the very first thing the program should do is check to see if a configuration file exists.   If it exists, we read the data out of the file using file_text_string_read(fileid) and json_decode(string).  This returns a ds_map of key:value pairs that contain all the configuration data. If the config file does not exist, then we create it, populating it with our default data values.

Next, if we’ve successfully read data out of our config file, we need to validate the configuration data that we read from the file. If the configuration data is invalid, depending on the setting and the value, the game will not work properly, and may crash or may perform unexpectedly or break.  Your program may have written the data incorrectly, or it might have a bug that results in a corrupted file, or the user may find the file and manually edit it, and introduce errors.  So check each value in the configuration ds_map, and verify that it is valid by checking to see if the value is of the correct data type (string, number, or boolean), and that it is within the range of acceptable values for that variable.  Write a script that does this, according to the particular needs of your game.

If validation fails, whether because the file is missing, or because one of the values found within it is incorrect, we can handle this in several ways.  One way is to reset all values back to their default values. Another way is per-setting, restoring the invalid value back to the default value.  Or we can simply display an error message to the player and exit the program. Decide what is best for your project, and implement it that way.

Usually, I recommend checking whether the file exists, and if not, regenerate it using defaults, and if it exists, restore individual settings to default one by one if there is an invalid value, rather than resetting the entire configuration back to the defaults if any of the values is invalid.  This way, a small error in the config file doesn’t blow out all the settings, and program will leave any valid customized settings alone, so that the user will only need to correct the values that were reset to default.

If we have a valid configuration, the next step is to apply those values to the game variables. 

Once the game variables have been set, the game is now ready to run.

Editing the configuration

One great thing about storing the configuration data in an external file is, we no longer need to re-compile the game every time we wish to tweak the settings.  This can greatly speed up testing, as compilation can take a minute or more each time, and this rapidly adds up when  you’re quitting, coding, re-launching again and again as you develop. So use the configuration system as  you test your game design.

We can edit the configuration in various ways.

The simplest to develop is to develop nothing; just rely on the user to find the configuration file on disk, open it up with a text editor, and change values, save the file, and run the game again.  You’ll find the writeable working directory for your game somewhere inside of %appdata% in a Windows build, but if you’re building the project for other platforms, you’ll need to find it.

While this is the easiest approach, this isn’t the most user friendly solution. A better user experience would be to put a GUI in the game to change the settings, and let the game program edit the config file.  This will be safer for the user, as you can constrain the input and check it for valid values before saving the config file.

The downside is that this can take a lot of extra work to build the user interface. Sadly, GameMaker does not provide a library of user input controls, such as buttons, checkboxes, text boxes, sliders, and so on. So to build the UI, we first have to build these controls.

This is a great opportunity for beginner programmers to get some experience designing and programming their own controls.  But it’s also a time-consuming task, and can be a frustrating challenge for a newbie programmer to figure out how to make a collection of robust, bug-free UI controls that are easy to use, work together, and behave in a way that the user expects based on their experience using other software. And doing this takes time away from developing the game part of the project.

It’s a lot of work, so having an easy way out is a good thing. There are assets available through GameMaker marketplace, which can be purchased for a few dollars, and provide the needed functionality. Either way, it’s good to have a set of reusable controls that you can put into any project, so whether you buy some, or you decide to make your own, you can get a lot of value out of them.

Advanced concerns

Storing arrays, ds_structures inside json

Up until now, the examples I’ve given have all been simple data types.  If you want to store something else, like an array, or a data structure, or graphics data, it gets more complicated.  This article is just an intro level tutorial, so it won’t cover these advanced topics.  But I expect I may cover them in the future, at some point.  Many games will not need the advanced techniques to store the basic configuration data.

Applying config to application state

Certain configuration changes, such as display size settings, will require the room or game to be restarted before they take effect.  In such case, you may need to use game_restart() or room_restart(). If you are confident that the data is being applied correctly to update the game variables, but you’re not seeing the change, try a room restart and see if the changes take effect.

But any room or game restart will restart the game in progress, and that may not be what you want to happen at all!  If you have a game where you can change the configuration from the Pause screen, for example, you will not want to disrupt the running game.  In that case, you’ll need to go further and handle this in some way, such as:

  1. Display a message to the user saying that the changes will take effect after they quit and relaunch the game.
  2. Give the play the option save the present game-state and then restore it with the new configuration in effect.

Details on exactly how to do this will vary considerably depending on your game’s design, but if you need to do this, you’ll essentially be building a save state feature for your game, and then calling it to save and then restore the game after the restart.

The basic logical flow of this process is as follows:

If <config change will require restart> and <game is in progress>{ save game-state to external file; restart;}

On re-start, check for existence of a game-state file, and if it exists, clear the room of default starting instances; restore room from game-state; then delete the game-state file.

This sounds fairly straightforward and simple, and, in concept at least, it is.  The difficulty is with storing all of the instances and their state data. Depending on the game, you may have hundreds of instances, and each instance may have a lot of data, and all of it needs to be written to file, then read back out of file, in order to reconstruct the game in the state where it left off.

Making it tamper resistant

You may welcome the user tinkering with the config file, or you may want to protect the config file against unwanted tampering. Usually this isn’t critical for a config file, but for a save file or a high scores file, it might be important to prevent cheating. If your game uses password-protected user accounts, or stores any kind of financial data or purchase records, you should be storing that data securely.

This should be all you need. I won’t get into technical detail here, but will outline a few tools you can make use of.

  1. Use ds_map_secure_save() and ds_map_secure_load() to securely store the configuration data in an encrypted file. The encrypted file will not be editable by a curious user. The manual doesn’t give detail about what encryption is used.  Depending on how serious you are about protecting the data, you will want to research encryption and use an algorithm that hasn’t been broken. Don’t attempt to invent your own encryption.
  2. Create a cryptographic hash of the configuration data string, and store the hash with the file.  When you read the file, re-hash the data you read out of the file, and verify that the hashes match.  If they don’t, something has changed, and you know the file has been tampered with or corrupted.  In this case, you should re-generate the entire config file from defaults. 

    Look at GML’s md5 functions to get started.  GML also provides sha1 hashing for this purpose. MD5 hashes are no longer considered secure, and sha1 is also no longer considered secure, but may be “good enough” for non-critical needs. While not “hacker proof” they will prevent casual tinkerers from being able to modify the data.

Saving external data for other purposes

Now that we know how to store data in an external file, retrieve it, validate it, and use it at runtime, there are other applications we can use these techniques for. 

The most obvious one that comes to mind is save states.  I’ve already touched on this above, in brief.  Other ideas include:

  1. High score, leaderboard, and achievement data.
  2. Telemetry data for debugging or analytics.
  3. User profiles (to allow multiple users of the same machine to each have their own configuration and save file preference).
  4. Level Editors.
  5. Mod packs, to allow a community of players to make custom modifications to your game, such as level data, or other external resources like sprites and sound effects, etc.

As these features become more and more advanced and complicated, they’re really best left to professionals working on commercial projects.  But by following the approach described in this article to do a simple configuration system, you’ll have taken the first steps toward getting your skills up to that level.

GameMaker Tutorial: Audio speedup with sync

In so many games, music speedup is a great way to get the message to the player that they need to hurry up and get things done.

It’d be great if you could simply set a new tempo with a simple GML function, and have the current background music adjust on the fly. Something like audio_sound_set_speed(sound, speed) would be lovely. But it’s not as simple as that in GameMaker, as I found out recently.

Here’s how I implemented a speedup for my GMLTetris project:

First, I created two music tracks, one at normal speed, and one at double speed, and added them to the game project as sound assets.

Everything else is just programming:

if <condition> {stop slow_music; start fast_music;}

This is easy enough, the trickiest part is probably getting the condition right to switch tracks, but depending on the game, that condition could be very simple, too.  The only real complication is that if you’re checking the condition repeatedly, as you normally would every Step, you only want to trigger the changeover once.  So to do that, set up a variable to track whether the switch has happened already, and check it, too, even if the condition that triggers the changeover continues to remain true on successive steps.

if <condition> && !music_switched {stop slow_music; start fast_music; music_switched = true;}

The music speedup that happens in a game like Super Mario Bros., where the music speedup occurs when the level timer hits 100, is a typical example of such a technique. If you only need to do a single, one-way switch, this is all you need.

If your game needs to switch back and forth between slow and fast music, your conditional needs to be more sophisticated.

if <condition>
{
   if !<fast_music_already_playing>
   {stop slow_music; start fast_music;}
}
else
{
   if !<slow_music_already_playing>
{stop fast_music; start slow_music;}
}

Here, because the game can switch multiple times, when the condition check happens, we can’t get away with a music_switched variable that changes one time. What we need to do is check to see if the music we need to switch to is already playing, and if not, stop the current music and switch to the other music.

One thing to keep in mind, this basic technique will start the fast music from the beginning of the track. This might be what you want, but it would also be good if you could start the fast music at the position where the slow music was, for a seamless transition.

GML has the functions to do this: audio_sound_get_track_position() and audio_sound_set_track_position(). But in order to make use of them, we need to do a bit more work.

First, since the gml functions return the absolute time position of the track, and since two tracks play at different speeds, we need to adjust the position proportionately when we switch tracks, so that the position is at the same position percentage-wise. This is actually easy, as long as we know the tempo change, which we do. Since the fast track is double speed, we can easily calculate the equivalent position in the other track by multiplying or dividing by 2.

Slow to fast: position *= 0.5;

Fast to slow: position *= 2;

Where I ran into trouble was, I needed to be able to switch both ways. It seemed like it should be simple — just check whether the desired track is already playing, and if not, get the position of the current track, adjust it proportionately, start the desired track, set the position. Easy, right?

Let’s look at it in pseudocode first:

if <condition to switch to fast music>
{
   if audio_is_playing(slow_music)
   {get position; stop slow music; start fast music; set position;}
}
else
{
   if audio_is_playing(fast_music)
   {get position; stop fast music; start slow music; set position;}
}

This was when I discovered that audio_play_sound() returns a handle for identifying the specific instance of the sound that is playing. This is necessary to use to set the track position of the playing sound. You can’t just set the position for the sound_index; you have to set it for the specific handle of the currently playing instance of the sound. If you set the track position for the sound_index, any time that sound resource is played in the future, it will start from that position.

///Create Event:
bgm = audio_play_sound(slow_music, 10, true);
///Step Event:
if <condition>
{
  if audio_is_playing(slow_music)
  {
  var pos = audio_sound_get_track_position(bgm);
  audio_stop_sound(bgm);
  bgm = audio_play_sound(fast_music, 10, true);
  audio_sound_set_track_position(bgm, pos * 0.5);
  }
}
else
{
  if audio_is_playing(fast_music)
  {
  var pos = audio_sound_get_track_position(bgm);
  audio_stop_sound(bgm);
  bgm = audio_play_sound(slow_music, 10, true);
  audio_sound_set_track_position(bgm, pos * 2);
  }
}

I also discovered that detecting which track is playing with audio_is_playing() does not work for this purpose. I still don’t have a clear understanding of what was happening in my code, but some debugging showed that my track position calculations were being distorted by being called multiple times. This doesn’t make sense to me because the song should no longer be playing after the first step when the switch condition is met. But my theory is that since the audio is played in another process from the main program, there’s some messaging going between the two processes asynchronously, and as a result audio_sound_is_playing() can return true a step later after the message is sent to stop the track playing.

By trying to set the track point multiple times in quick succession, weird and unexpected results happened, and the tracks switched but did not set to the correct position.

So I had to come up with a surer method of knowing which music is playing.

Debugging was tricky, since I couldn’t tell from listening where the audio position was. To aid debugging, I drew the position of the playing track to the screen, and then I was able to see that the position was not being set correctly as expected. Somehow, switching from slow to fast would drop the track back from about 10 seconds to 2 seconds, and then switching from fast to slow would jump from 2 seconds to 38 seconds.

I couldn’t figure out why that was happening, so I tried using show_debug_message() and watched the output console, and saw that the track position would update 2 or 3 times when the tracks switched; I was expecting it to only update once.

This is what clued me in to what I believe was happening due to the multiple processes communicating synchronously.

The solution I used in the end was easy and simple: instead of checking which track was currently playing, and switching from slow to fast or vice versa based on the currently playing audio asset, I just added a new variable, condition_previous, and compared condition to condition_previous, and made the switch happen only when the current condition didn’t match condition_previous. This only happens in one step, when the condition changes from false to true, or vice versa, and so the track position is set once, when the bgm switches tracks and syncs up the new track to where the old track left off.

switch_previous = switch;

switch = <condition to check for switching to the fast music>;

if switch && !switch_previous
{
var pos = audio_sound_get_track_position(bgm);
audio_stop_sound(bgm);
bgm = audio_play_sound(fast_music, 10, true);
audio_sound_set_track_position(bgm, pos * 0.5)
}
else
{
if !switch && switch_previous
{
var pos = audio_sound_get_track_position(bgm);
audio_stop_sound(bgm);
bgm = audio_play_sound(slow_music, 10, true);
audio_sound_set_track_position(bgm, pos * 2);
}
}

This works, because it guarantees that the condition checks will be accurate, as they do not depend on checking the status of the audio playing in another thread.

GameMaker Tutorial: Delta Time

What is “delta”?

Delta is a term often used in math and science to indicate change. For example, in physics calculations, the term ΔV (pronounced “delta-vee”) is used to quantify a change in velocity as measured at two different moments in time. If you didn’t have an introduction to this concept in your education, it can seem mysterious why we talk about “delta” like this, but it is a common convention.

Background

In videogames, the game program incorporates a routine called an update loop, which repeatedly applies the rules of the game to the game’s present state in order to create the next game state. Normally, it is desirable that the program control its frame rate, the number of times this update loop executes per second, so that a consistent frame rate is achieved, in order to have the game run smoothly and consistently. Ideally, this frame rate would be constant and unvarying.

Achieving this in practice can be quite complicated. Back in the old days, when games were programmed in Assembly for specific hardware platforms, and the game program ran on the bare metal, the programmer relied on the CPU clock and the timing it took to perform various operations to ensure that everything worked in sync. This required great effort and intimate understanding of the hardware and program code.

For the most part, GameMaker makes things simple enough for basic game development, and you don’t have to worry about regulating the frame rate yourself.

In older versions of GameMaker, there’s a room property, room_speed, which sets a target number of steps the update loop will attempt to execute per second.  Typically, this is set to a value that aligns nicely to the refresh rate of the display — often 30 or 60.

In GMS2, room_speed is replaced by a global option in the project’s properties.  (Cog symbol) -> Main options -> General and set “Game frames per second”.

This simple approach, simply setting a frame rate through the Game FPS or room_speed properties, works well enough for many projects.  Just set the desired frame rate and let GameMaker manage itself to try to achieve this target frame rate.  As long as the program is able to do so, everything will run as desired.

We can run into problems, though, because of the limitations of the computer hardware to perform all the calculations needed during the update loop to generate the next game state.  As long as the computer has plenty of resources, it should have plenty of time to generate the next game state.  But if those resources are not available, or if the game demands too much work be done to allow the available system resources to create the next game state in time, the game will not run smoothly.  The program will not crash, but it may slow down or stutter, or perform in a jerky manner.

It’s not really avoidable; to avoid the problem completely, you would need to write the game in such a way that it was guaranteed that the program always requires the same amount of calculations to be performed per step, and always has the same system resources available to perform those calculations.  The first is very unlikely, and would impose constraints that would make the program’s design very rigid. The second is well beyond your control, unless you’re writing software that has direct control over the hardware it’s running on, rather than writing an application that runs on top of an operating system that also supports numerous other multi-tasking programs and services.

The solution is to write efficient code that has an extremely good chance of running all the calculations for the next game state before that game state is needed for drawing the game to the display, so that there will never be a delay that results in a dropped frame.  Doing this requires that you mostly write small, simple games, and run them on hardware robust enough that it will easily be able to guarantee that sufficient resources are available in order to complete the computation required, on time, every step. Those are both significant downsides, but even so you can create a great many games within those constraints these days, without too much of a problem.

But there’s still things that are out of your control, even if you write a small program with very efficient code. You can’t control what hardware the user will run the game on.  You can’t control what other processes might also be running on that system, in the background, while the game program is running. These uncontrolled factors can defeat all your efficient programming and give a poor experience at runtime on a system with insufficient resources to perform all the instructions that it needs to, on time.

Enter delta time

When the simple approach isn’t good enough, it’s time to turn to a concept involving delta time.  This is simply measuring, each step, the amount of time that has passed since the previous step.  This amount will vary from step to step, depending on how much work the game had to do to generate that step, and how much hardware resources were available to perform that work, in addition to whatever else the operating system may have been doing.

Once you know how much time has passed, you can use this to make adjustments in various things that depend on time, such as movement speed, timers, sprite animation speed, and so on, to keep everything looking smooth.

In a simple game, you would normally just set an instances speed to some value, V, and every step the game would update by moving the instance V pixels in direction theta.

In a delta timed game, you would first measure the amount of time that has passed since the previous step, calculate a ratio between this time and the number of game state updates desired per second (frames per second), and then use that ratio to modify the speed of everything in the game, so that even if the game runs a little behind in calculating everything, the end result is that the instance looks like it moves V pixels in direction theta each step.

Make sense?  It can be a lot to grasp at first, but it’s actually fairly simple.

Let’s look at it now in detail and walk through an implementation.

Implementing delta time

GameMaker provides a built-in global variable, delta_time, which is the time in microseconds that has passed since the previous step.  Since the engine performs this measurement for you, it’s very reliable, and all you need to do is use it.

A microsecond (μs) is one millionth of a second, or 1/1000000, or 0.000001. Typical GameMaker projects target a framerate of 30 or 60 steps per second, so each step is 1/30th or 1/60th of a second, or about 33333.333…μs per step for a 30fps game, and 16666.666…μs for a 60fps game.

So, if your game were running ideally, updating the game state every step exactly on time, every step’s delta_time should be exactly 33333.3… or 16666.6…  In the real world, you won’t hit those numbers exactly, but you’d expect that they should be very close, most of the time. (And in GameMaker, the value of delta_time drops the decimal, which introduces a problem down the road that we’ll have to deal with somehow… [[Hmm… maybe it’d be better to use a room speed that yields a whole number ideal delta_time?]] We’ll get to that in a bit.)  To the extent that delta_time differs from this ideal value, you can adjust all of the time-based variables that influence the game state, and if you do it all correctly, thereby smooth everything out.

It would be nice if you could just have a “use delta time” checkbox in your project, and all the built-in time-dependent variables — speed, hspeed, vspeed, image_speed, alarm[], etc. would just receive these adjustments by virtue of the engine, automagically.  Wouldn’t that be nice?

But for whatever reason, they don’t give us that feature, but at least they give us the built-in delta_time variable to work with.  So we’ll have to do that.  And of course, every other user-defined variable that we add to our project that depends on time will also need to be adjusted with delta_time.

So, if the game is running too slow, delta_time will be higher than the ideal value, because the amount of time that passed was longer than the ideal time.  In the game, instead of moving N pixels per step for 60 steps in a second, the instance would move N pixels per step, but only hit some number less than 60 steps per second, resulting in slowdown and jerky movement and animation.

To fix this, we should be able to see how:

ideal_delta_time = 1/room_speed * 1000000; //typically 33333 or 16666

This simplifies to 1000000 / room_speed. Next, we need to compare the measured delta between the previous step and this step (delta_time) to the ideal delta time:

dt_ratio = delta_time / ideal_delta_time

Now that we have the ratio for this step, we must use this ratio for all time-dependent calculations 

speed = ideal_speed * dt_ratio;

or, since computers multiply with fewer cpu instructions than they can divide:

speed = ideal_speed * delta_time * room_speed * 0.000001;

Or in other words, to move at an average speed of N pixels per step, you adjust the actual speed every step by multiplying it by delta_time/ideal_time:

speed = ideal_speed * (delta_time/ideal_time)

(Note, many games that use delta_time eschew the built-in speed system, instead using variables that are not managed by the engine, to give the programmer complete control over the motion. This is a very common approach taken with GameMaker projects, because the way the built-in variables are managed by the runner don’t offer the flexibility and control needed.)

Once we have the dt_ratio defined for this step, we don’t want to have to re-calculate this delta/ideal ratio again and again for every calculation performed in this step, so it’s best to do it once per step as a global variable, and have every instance that needs to use it reference this global value:

global.step_delta = delta_time/ideal_time;

That’s the basics of it.

Difficulties

The main problem that people have is that they have to adjust every variable in their game that is time-dependent.  Not just speed, but alarms, image animation, and possibly even such things as the pitch and duration of sound effects.  This can get tricky and drive you nuts.  Especially when you have things going on in your game like non-linear acceleration.

Another tricky problem with delta_time is the accumulation of rounding errors.

Let’s look at what happens when we want to use delta_time to achieve an average speed of 6 pixels/step:

speed = ideal_speed * delta_time * room_speed * 0.000001;

speed = 6 * delta_time * 30 * 0.000001

Let’s say that delta time is always exactly the ideal delta_time, 33333 microseconds.  That would give us:

speed = 6 * 33333 * 30 * 0.000001

speed = 5.99994 //shit. um...

Close enough, right?  Well, it adds up over time.  And keep in mind, if delta_time varies from the ideal_delta_time, the error can be larger.

Can’t we just round it?

Well… no.  We can’t.  Because it’s entirely possible, and even likely, that you can have a legitimate fractional speed as a result of delta_time adjustments, especially in the event that your game is lagging frames!  And rounding the value in that case would actually result in even more error.

Well, so what? Can’t we just live with it?

Maybe? I’m not sure what else you can do, honestly.  But this does leave us with problems. Even in the simple demo project that I’ve created, after just a minute or so, I can observe my control object deviate out of sync with the various delta-based objects.  Even the different delta-based objects will fall out of sync with each other, due to tiny differences in the way they calculate their deltas.

Most distressing of all, I’ve seen the objects glitch at their reversal point, getting stuck in the edge of the screen.  This isn’t consistent, and seems to depend on fluctuations in delta_time at just the right time to adjust the instance far enough outside of the room that the Intersect Boundary event will be triggers on two successive steps, resulting in the instance oscillating, becoming stuck on the boundary.

I’m still looking for a solution for how to fix this issue.

Manual errata

The manual gives us the following incorrect example:

speed = spd * (ot - delta_time);

Where ot is the value of delta_time from one step ago, e.g. a “delta_time_previous”.

This is wrong. The manual is in error, this is not how delta_time works.  It seems whoever wrote the manual was under a mistaken impression that delta_time is basically a point-in-time value of System.DateTime(Now) that is updated every Step, and that you would then need to calculate the actual delta by subtracting the current value for Now by the value of Now from the previous step.

The way delta_time really works, it is the number of microseconds since the previous step, essentially it is already the result of the expression “ot - delta_time” used in the example.  The correct implementation for the example should be:

speed = spd * delta_time/ideal_delta_time;

Alarms and animations

If you want to use delta_time to smooth out your movement, you should also consider whether you need to also consider delta_time with other time-based variables, such as image_speed.

Sprite animation is controlled by image_speed, and is normally set to a value of 1 by default, resulting in the sprite cycling through its sub-images at a speed of 1 per step.

An instance’s image_speed can be adjusted to speed up or slow down the animation speed.  At speeds slower than 1, the same sub-image will be drawn for more than 1 step in a row.  At speeds higher than 1, the animation will skip over sub-images.  At speeds less than 0, the animation will cycle in reverse.

If you’re using delta_time, and want your animation speed to also be delta_time-based, then the best thing to do is set image_speed to 0, and control animation manually.  Each step, increment image_index by 1 * ideal_time/delta_time:

image_index += ideal_time/delta_time;

Sprites can be set up and used in too many different ways to achieve customized ends, so if you’re storing sub-images in a sprite resource and selecting them in some other way than cycling through them as an animation, this is not necessarily going to apply.

For Alarms, you’ll want to stop using the Alarm Events that are built into GameMaker, and implement your own timer system. This is simple to do.

Step Event:

if timer >= -1
{
    timer -= ideal_time/delta_time;
    if timer <= 0
    {
          //do stuff
    }
}

You can have as many timer variables as  you need, and name them however you want, store them in an array if you want, or another data structure if you want.

Once you get used to it, you’ll probably like this better than the built-in Alarm system. YellowAfterlife’s article on Custom GameMaker Alarms is a great read for more information.

Capping the delta

Delta_time is intended to smooth out minor variations in fps, but if you have a longer interruption, delta_time will break your game. It’s useful to understand why and how, so you can prevent problems.

You’ll need to cap the amount of delta that you want the game to react to.  For example, if your game loses application focus, it will stop processing, essentially pausing the action.  The timer that delta_time is based on still keeps track of time however. This means that when you return application focus to the game, so much time will have passed that the ratio between ideal_time and delta_time will be enormous.  If your game uses this enormous ratio to adjust your game speed, it will almost certainly break the game.

Say you have switched application focus for a second to respond to an instant message you receive in another app. The instant you return to the game, the delta_time between the next step and the step your game paused at is going to be several seconds, instead of a value close to 1/30th or 1/60th of a second.  This means that in that step, everything in the game is going to move the equivalent of several seconds worth of distance, missing collisions that would have happened during those “missing” seconds”, and skip several seconds worth of animation frames, and count down several seconds worth of alarms, in just one step.  Things will go haywire — unless you cap the ideal:delta ratio.

You’ll want to experiment with your game and see where this limit should be set. It should be a value that is well short of any game-breaking bugs that a too-large ratio would cause. The exact value will depend on the specifics of your game.

To implement the cap is simple, though. Revising our earlier dt_ratio expression, we just use clamp() to cap it:

dt_ratio = clamp(delta_time / ideal_delta_time, min_cap, max_cap); 

The value of max_cap is your maximum allowed delta_time/ideal_time; min_cap is your minimum allowed delta_time/ideal_time.

Controlling the tempo

Delta_time can be used for more than just smoothing out your frame rate.  You can use these same ideas in slightly different ways to “control time” — that is to create a slow motion effect, or to speed things up like a “fast forward” effect.  To do this, just add in an extra factor, which I’ll call tempo:

speed_this_step = ideal_speed * (delta_time / ideal time) * tempo;

  • If tempo is equal to 1, the game will run at normal speed.
  • If tempo is >1, the game will speed up, moving faster than normal.  You can do this within some limits, but if you speed up the game too much, you’ll start having to deal with “high speed collisions” — situations where an instance moves so far that it can skip over objects that it should have collided with — which will require more work to handle correctly.
  • If tempo <1, the game will slow down.
  • if tempo == 0, the game will freeze.
  • If tempo < 0, the game will run backwards! (Not perfectly, though; any use of randomization will cause a loss of determinism which means that things won’t run backwards exactly as they ran forwards, only in reverse — and negative tempo won’t cause events like Collision, Create, and Destroy to “undo” or run in reverse.  But for certain uses, especially if used briefly and sparingly, this may not matter.)

How cool is that!  You can do a lot of things with this.

You can have a global.tempo, for controlling the overall speed of your game, and an instance.tempo factor that is unique to each instance.  Change the value of global.tempo, and the entire game will speed up or slow down; change the value of instance.tempo for selected instances, and only that instance will be affected.

speed_this_step = ideal_speed * (delta_time / ideal time) * global.tempo * instance_tempo;

A good range for tempo, for most games, is probably anywhere from 0.10 to 2.0 or 3.0 or so. But it will depend a lot on the specifics of your project, so you’ll need to experiment and test.

You use tempo to create a pause system easily, by applying delta_time to objects that are used in the game, setting global.tempo to 0, and controlling the global.tempo using objects that are not bound by delta_time so that they will remain responsive when the rest of the game is paused.

You could also create a script that performs this function:

///delta(ideal_value, global.tempo, instance_tempo) 
///@description returns value_this_step computed from ideal_value based on delta time, global and instance tempo values.

return argument[0] * (delta_time / ideal_time) * argument[1] * argument[2]; // We assume here that ideal_time is defined elsewhere in the project, probably as a #macro, or is represented inline with the direct calculation here.

Then when you use this script, you avoid code duplication, and it will make the code more readable:

speed = delta(10, 1, 1);

Beware bogus approaches

There are a few common approaches to implementing delta_time-like solutions that you may run into if you look at other people’s code.  I’ll touch on them briefly here, in order to explain why they’re not ideal.

Changing room_speed.  In older versions of GameMaker, before Studio, the program could change the room_speed and the GameMaker engine would adjust its speed on the fly.  This was very easy to do, but wrong.  For example, if you set the room_speed to 0, or a negative number, that would break the game.  A room_speed of 0 would freeze the game, and make everything stop, even the runner, so that the game would never update again.  And negative room_speed values are not allowed.

Later on, GameMaker was updated so that the room_speed variable could be changed during runtime (so it wasn’t a read-only variable) but that changes did not take effect at runtime for the current room, meaning you could not change room_speed in the current room and see immediate results; only by leaving and returning to the room, or restarting the room would the new room_speed be in effect.  This effectively ended adjusting room_speed as a viable approach.

Programmers who have the idea of how to do a technique like delta_time, but haven’t heard of it before, will sometimes “re-invent the wheel”, usually by using fps_real and room_speed to create a ratio similar to delta_time/ideal time.  This approach can seem at first to work, but it is always off because fps_real is the actual, achieved fps for the previous frame.  For the current frame, however, fps_real could actually be a very different value from what it was one step ago, meaning that if your game just did a huge amount of work in the previous frame, say to initialize new instances that were just created, and it doesn’t need to do that work in the current frame, the calculations could be way off, giving a poor result.  As long as fps_real is not changing dramatically from one step to the next, and is reasonably close to the target fps for the game, it will appear to work well, but when fps is very inconsistent, which is when you really need it, it will not work well.

If you see these techniques in use in a project, it can be a pain to convert from the bogus delta time technique to the good technique.  You’ll need to meticulously review the code for the entire project and find every place that uses the bad technique, and refactor it to use the good technique. Depending on how well documented the code is, and how expressive the code style is, this can easily get out of hand.  The best thing to do is to use version control and create a branch, and work on the conversion in the branch, and just focus on converting the delta time system before you go about making any other changes.  Take notes and put comments in the code to keep track of your progress, remind you of things that you need to do, need to test, or need to figure out.

Demo

In writing this article, I created a demo project to help me understand the concept of delta_time, and test various approaches. You can download it for free and play with it yourself.

Summary

  • Delta_time is simply the number of microseconds that have elapsed since the previous step. This value can be used to smooth out unevenness in frame rate if the program drops below the ideal frame rate.
  • It’s easier to implement delta_time if you decide to use it from the very beginning.
  • The basic delta time expression is: 
    N * delta_time * room_speed * 0.000001
  • You can adjust the tempo of the game even more by using global and instance tempo variables, and multiplying by those as well:
    N * delta_time * room_speed * 0.000001 * global.tempo * tempo
  • You can create a simple pause system (in part) by setting the global.tempo (or a custom pause factor varable) to 0.
  • Remember to consider things like animation speed and timers as well as movement.
  • Avoid using the built-in Alarms Events, movement functions and speed variables; it’s easier to manage everything if you take complete control over the position, speed, etc. by using your own system.
  • Cap the amount that delta_time can adjust your game, to keep things from going out of control after a long pause between frames.
  • Delta_time will result in values that are slightly off the ideal target value due to decimal imprecision/rounding errors.
  • Test thoroughly!

GameMaker Studio Tutorial: Getting Into Shaders

Shaders have been a part of GameMaker Studio for a while now, having been introduced in 2014. Since their inclusion, I have mostly remained mystified by them, with a vague and cloudy understand of what they are and what they can do, and haven’t used them at all. That will [hopefully] start to change today.

As always, when try I learn something new in programming, I find that writing up what I’ve learned helps me to remember and keep my learning organized. I like to publish my notes so that they can help others, and so that others can find errors and make suggestions for better ways to do things. I’m still very new to working with shaders, so I’m not trying to put myself out there like I’m some kind of expert, but here’s what I’ve been able to learn about using shaders with GameMaker Studio so far:

Shader basics

First, we need to understand what a shader is. A shader is a specialized program that processes graphics. Shaders are executed on the Graphics Processing Unit, or GPU, which is specialized hardware for accelerated graphics processing. Thus, shaders are very fast. As well, since they work on the GPU, using shaders will free up the CPU to do other tasks, which can further help to improve the frame rate of your games.

This sounds like enough of a reason to want to use shaders, doesn’t it? Well, it gets better. The main thing about shaders is that they can do amazing visual effects, which will can make your game look better, but can also play an active role in how the game plays. For example, you could use a shader to handle the graphical processing of a special view mode in the game, such as night vision or x-ray vision. One of my favorite shader-based gameplay mechanics that was centered on the use of shaders was Daniel Linssen’s Birdsong, winner of the “Overall” and “Theme” categories of the Ludum Dare 31 compo held in 2014. The theme of LD31 was “Entire Game in One Screen”, and Linssen’s approach to this was to create a giant one-room game, that was crammed into a single screen(no scrolling), and, using a fish-eye lens effect done with a shader, magnify the area where the player is so that it was large enough and detailed enough to be playable.

There’s virtually no limit to what graphical effects you can come up with using shaders, other than the limits of your imagination and of course your programming and math skills. It also helps to understand how computers work with graphical concepts such as color, pixels, binary math, and so forth. Additionally, scientific knowledge in disciplines like optics can be very useful. Shaders have their own specialized programming language that they are coded in — actually there are several related languages to choose from. Because of this, shaders are considered an advanced topic in programming, and there are numerous hurdles to surmount in order to be able to write them yourself.

That said, shaders are re-usable bits of code, and so one of the first things you can do when you start getting into shaders is to simply use pre-existing shaders that have been written by other people.

Getting Started with Shaders

Before you can use shaders, you’ll want to familiarize yourself with a few concepts.

Shader references

Here’s links to the relevant pages in the GMS manual on using shaders in the context of GameMaker Studio:

GMS1:

Shaders Overview

Shaders GML reference

Shader Constants

Tech Blog Shaders tutorial: 1 2 3 4

GMC Forums shader tutorial.

GMS2:

Shaders Overview

Shader Constants

 

Other shader resources (general)

Language References

The four shader languages that GMS supports are: GLSL ES, HLSL9, HLSL11, and GLSL. Which one you need to learn and use will depend on your target platform, but for this article we’ll focus on GLSL ES, since it supports the most target platforms (all of them, except Windows 8).

GLSL ES language specification

HLSL language specification

I haven’t gotten into the shader languages enough yet to know why you’d ever pick HLSL over GLSL, but presumably there must be some advantage to using HLSL when targeting Windows platforms, either in terms of correctness or performance. Otherwise, I would think you’d be better off just sticking with GLSL ES and be compatible with the most targets.

Tools

Shadertoy Shadertoy is a wonderful website that allows you to play with shader programming, running them in your web browser. Then you can share your shader creations with the community of users of the website, and in turn you can demonstrate and use shaders written by others.

Other graphical concepts in GameMaker, and how they relate to shaders

It’s not a bad idea to review and understand the entire chapter on Drawing in the GameMaker documentation. There are many concepts that you will need a working knowledge of in order to understand how to use drawing to its fullest capacity, and to get things working together smoothly.

But the manual isn’t the end of the story. Often I find that the manual doesn’t go far enough to explain how different concepts work. The sections on blend modes and alpha testing are particularly inadequate by themselves. The manual also doesn’t go very far to demonstrate or suggest how different features and functions can be connected to one another. That’s for the user to infer, and verify through experimentation. This is great if you are creative and love to experiment and discover. On the other hand, there’s so much that has already been figured out and discovered by others, and it would be nice if that was all documented in an easy to search reference somewhere.

Read the entire manual, cover to cover, if you can. Create little demo projects to test your understanding of what you’ve read, and figure out how to do things. Read it again. And refer to it whenever you need to. There’s no substitute for reading and understanding the manual. I’ll still touch briefly on the major concepts here, for summary:

Surfaces

All drawing happens somewhere, and in GameMaker that somewhere is called a surface. Behind the scenes, a surface is simply a chunk of memory that is used to store graphics data. You can think of it as a 2D grid of pixels, stored in the program’s memory, and drawn to the screen only when called for. You can also think of it as virtual “scratch paper” where you do some work “backstage” and then bring it out to use in the game when needed.

The application has an Application Surface, and by default everything is drawn here. But you can create other surfaces, which you can work on, composing a drawing off-screen, until you are ready to draw it to the screen. As you might imagine, there are countless reasons why this is useful, and endless ways to make use of surfaces.

Surfaces are relatively easy to use, but are considered an intermediate level programmer’s tool in GameMaker, for a couple of reasons:

  1. Surfaces consume memory, and need to be disposed of when no longer needed.
  2. Surfaces are volatile, and can be destroyed without warning, so should not be assumed to exist. For example, if the player switches focus to a different application, or if the computer enters sleep or hibernation mode, or if the game is saved and resumed, surfaces that were in existence at the time the application was last running may have been cleaned up by the operating system, and will need to be re-created if they don’t exist.
  3. All drawing must happen in one of the Draw events. If you try to use draw functions in other events, it may or may not work correctly, and this will vary from computer to computer. I once made a game where I did some set-up in the Create Event of an object, where I created a surface, drew to it, and then created a sprite from the surface, and assigned the newly created sprite to the object. It worked fine on my computer, but when other players downloaded my game to try it out, it did unexpected things and the graphics were glitched. Fortunately, I figured out what the problem was, and fixed it by moving this sprite creation into the Draw Event. Once I did this, the game ran correctly on everyone’s computer.

Drawings done to surfaces can be run through a shader, as input, and thereby be processed by the shader. In short, a surface can be the input image data for a shader, and the output of the shader will be the processed version of that surface, transformed by the shader.

Blend Modes

For a long time, long before GMS introduced shaders, GameMaker has provided blend modes. Blend modes affect what happens when GameMaker draws graphics over existing graphics that were drawn previously. Normally, when you draw something, it covers the pixels that were there before. But, by changing blend modes, you can do other things than simply replacing the previous pixels with new pixels, blending the old and the new in different ways according to the mathematical rules of whatever blend mode you had selected.

To be honest, I’m not sure what useful purpose there is for every blend mode. It would be great if there were more tutorials showing useful applications for them, especially the obscure ones that I don’t see used much, if ever.

The most commonly useful blend modes, in my experience, are bm_normal and bm_add. Normal blending is the default drawing mode, and is what you use 99% of the time in most games. Additive blending creates vivid glowing effects, and is particularly lovely when used in conjunction with particle systems to create glowing systems of overlapping particles, especially when you are drawing translucent pixels (using alpha < 1).

Blend modes are also useful for creating clipping masks. For more info on that, there are some good tutorials already written on how to create a clipping mask using surfaces and blend modes.

Some of the first questions I had when Shaders were introduced were: What do we do with blend modes now that we have shaders? Do we still need them? Can we combine them with shaders, somehow? Or do shaders make blend modes obsolete?

Basically, as I understand it, the answer seems to be that blend modes were kind of a limited predecessor to shaders, and enabled GM users to achieve some basic drawing effects simply, without exposing GM users to all that highly technical stuff that shaders involve, that I mentioned above.

Anything you could do with blend modes, can be done with shaders instead, if you wanted to. That said, if all you need is the blend mode, they’re still there, still supported like always, and you can go ahead and use them. They’re still simpler to use, so why not.

One thing to be aware of, though, when using blend modes, every time you change blend mode in GameMaker, you create a new “batch” of drawing. The more batches, the longer GM will take to draw the game each step. Thus, many batches can slow drawing down tremendously. This is an area where you may need to focus on optimization. And if you’re that focused on performance, then it might be worth looking into a shader-based approach instead.

Once you’ve become sufficiently comfortable with shaders, you may not have as much need for using GameMaker’s drawing blend modes.

D3D functions

I have not used GML’s d3d functions, much, either, so my understanding is very limited. Basically, as I understand it, the d3d functions in GameMaker wrap Microsoft’s Direct3D drawing functions, and enable drawing with more sophistication than is possible with the basic GML draw functions such as draw_rectangle, draw_line, draw_ellipse, etc.

Despite the name, the Direct3D functions are useful for 2D drawing as well as for 3D.

This article will not cover using GML’s d3d functions, as we’re focusing on shaders. But as any graphics in your game can be used as input into a shader program, anything you draw using d3d functions can become input for a shader to process.

Particles

Particles are “cheap” (efficient) graphical effects that can be created without having to instantiate an object. They are efficient because they do not incur all the processing overhead that comes with a full-blown object. Huge numbers of particles can be generated at very little cost. These can be used for all sorts of effects, so long as the particles do not need to interact with instances in the game, such as triggering collisions. Typically, particles are used for things like dust clouds, smoke, fire, glowing “energy plasma”, haze, rain, snow, and so on to create additional atmosphere.

To use particles, you have to create a particle system. As with Surfaces, particle systems take memory, and need to be disposed of when no longer needed, in order to free up that memory. Full detail on how to set up and use particle systems is beyond the scope of this article.

Several external utilities have been developed by GameMaker users over the years to make designing, building, previewing, and testing particle systems easier, and these are highly recommended.

In conjunction with shaders, I don’t know that there is any direct interplay between particle graphic effects and shaders, but certainly a shader may be used to further process a region of the room where particles exist, to create more sophisticated effects.

Using Shaders in GameMaker Studio

Right, now that we’ve introduced the concept of what a shader is, and reviewed the other main graphics concepts in GMS, here’s where we get to the heart of how to use shaders in GameMaker.

A shader is a pair of shader-language programs, consisting of: a vertex shader, and a fragment shader. Vertex shaders deal with the edges of the drawn area, while fragment shaders deal with the insides.

Let’s say you want to use a shader program that has already been written, perhaps by someone else. All you need to do is use this code in your draw event:

shader_set(my_shader);
 //draw stuff
shader_reset();

So, it’s a lot like drawing to a Surface. With surfaces, first you set which surface you want to draw to, then you draw, then you reset so that drawing resumes to the application surface. With shaders, you set the shader you want to use, draw the stuff that you want to be transformed by the shader, then reset to normal drawing.

Everything drawn between setting the shader and re-setting back to non-shader drawing will be drawn through the shader program.

Easy enough, right? Well, there’s slightly more to it than that.

Uniforms

“Uniforms” is a strange term at first, and was where shaders started to seem strange to me. This is a term that comes from the shader language itself. The GameMaker manual talks about them in a way that assumes the reader is already familiar with the concept, and doesn’t go into a lot of detail explaining it to newbies.

In essence, “uniforms” are input variables that can optionally be passed into a shader that is designed to use input values. Once you understand what a uniform is, it’s not that difficult a concept. You can read more about them at these pages:

The gist of it is, when writing a shader program, when you declare a variable, you can declare it to be a uniform variable, which means that the variable can be accessed from outside the shader, thereby giving the program that calls the shader a way to change the shader’s behavior during execution. To do this, you can’t just refer to the uniform variable by name; you have to get the uniform variable’s memory handle, using shader_get_uniform(nameOfUniformVariable), and then change the value of the variable using shader_set_uniform_f(nameOfUniformVariable, value). Uniforms are actually constants within the shader’s execution scope, so once a value is passed into the shader from the outside and it is set as a uniform, it cannot be changed (the value could be copied to another variable, and that variable could then be modified, though.)

If you’re using a shader that has uniforms that you need to set, it’s done like this:

u_color1 = shader_get_uniform(my_shader, "f_Colour1");
u_color2 = shader_get_uniform(my_shader, "f_Colour2");

shader_set(my_shader);
shader_set_uniform_f(u_color1, 1, 1, 1);
shader_set_uniform_f(u_color2, 1, 0, 0);
//draw stuff
shader_reset();

There are actually a few uniform functions in GML:

Conclusion

That’s about all I know about shaders, for now.

As I get more familiar with using shaders, I’ll update this with more complicated examples, such as (possibly):

  • How to use multiple shaders on the same drawing (eg chaining the results of one shader’s transformation of some drawing into the input for a series of shaders).
  • Other stuff…

How to write shaders in GLSL, if I ever do it, will be a topic for its own article (or series of articles).

GameMaker tutorial: high speed collisions

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.

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

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.

Recipe: really good very cute boyfriend stir fry

When I’m cooking at home, I like to do stir fry dishes in my wok. Recently I have been doing this, and have been finding that I have been feeling extra-well, and more energized on days after I have the following for dinner.

So maybe it’ll help you too. It is easy to make, reheats well, and you end up with an amount of food that you can turn into about four or five good meals.

really good very cute boyfriend stir fry

Ingredients:

  • rice
  • tofu
  • broccoli crowns
  • white onion
  • green onion
  • canned water chestnuts
  • canned baby corn
  • snow peas
  • crimini mushrooms
  • baby bean sprouts
  • sesame oil
  • teriyaki sauce
  • hoisin sauce
  • soy sauce

Equipment:

  • Rice cooker
  • Wok
  • wood spoon
  • large pot with lid

Preparation:

Rice.

Start the rice first, because it takes the longest. Depending on the type of rice you use, it will take a varying amount of water and cook time, so I can’t get too specific on this. Just watch carefully the first few times you’re cooking the rice and learn what works with a specific variety.

Some people say you should soak and rinse the rice before cooking. I don’t really bother with that; just a couple minutes of soaking, while I’m chopping the vegetables, and no rinsing.

Press tofu.

Take the tofu out of the container, and press it between two plates with a weight on top. This will squeeze out excess water, resulting in firmer tofu. After pressing for about 20-30 minutes, you can cut it into cubes. You can get away with less press time if you want, but less than 10-15 minutes is not recommended.

Chop vegetables.

Chop the different vegetables up into bite-size pieces. If you want you can save some time by buying pre-sliced vegetables. I usually get pre-sliced mushrooms, water chestnuts, and baby corn.

For the green onions, you just want the pieces to be about a quarter of an inch long.

The water chestnuts and baby corn come pre-sliced in cans, but you’ll need to strain them.

Cooking

Note: With a few exceptions, I don’t really bother with measuring ingredients. Learning to cook isn’t about following precise steps exactly every single time. It’s about exploring and experimenting and being observant and understanding.

Measuring makes it easier to repeat a result, but only if the ingredients are constant. The thing with vegetables and other ingredients is, they’re not very constant. They vary depending on season and freshness.

So the amount of oil or sauce or temperature or cook time that might be good for one session might not work for another, or according to your taste.

You develop a feel for this over time. Vary and experiment until you feel like you know what you’re doing and know what works for you. Use your eyeballs and your head. Use your tongue. This recipe will tell you what I look for when I’m cooking and how I estimate, not how to be scientific and rigid with your cooking method.

  1. Start the rice cooker about 10 minutes before you start cooking the rest of the vegetables.

    I’ve found the type of rice I usually cook takes about 20-25 min to be done, and the wok items take about 10-12 minutes or so to do.I use a rice cooker that I bought at a drug store for $10 like 17 years ago, nothing special (but it’s a really well made and well designed appliance and I’m happy with it).

    Some people use super fancy rice cookers made in Japan, that cost $150+ and use sophisticated sensors and computer programming with AI and fuzzy logic to do perfect rice every time with no fuss. There’s nothing wrong with that. At all. The Japanese know WTF they’re doing with their rice. Someday maybe I’ll buy one myself.

    With my rice cooker, it just has a setting for “warm” and a setting for “cook”. It’s supposed to switch automatically to “warm” after the water boils off. I’m not sure how it knows to do this, I expect maybe it’s by weight but who knows.

    But usually it’s wrong, so I have learned to watch it and flip the switch manually. I just need to keep an eye on it, checking on it after about 15 minutes to see how much water has evaporated.There’s a narrow window (maybe a minute or two, tops) between the water boiling away and the rice at the bottom of the pan starting to burn. That’s what you watch for. When the rice is done, the water should be boiled away leaving moist steamy grains of rice that may stick together or not depending on the variety and whether you washed and rinsed it.
    Sometimes, I see little holes in the rice, that look almost like someone took a bunch of chopsticks and stuck them in the rice, then pulled them out when the rice firmed up enough to stick that way. These holes are created by the streams of steam bubbles coming up through the boiling water. You don’t see them until toward the end when the rice is ready, and they don’t always form. But if you do see them it’s a good sign that the rice is ready.

    My rice cooker has a second stage to it, a basket that goes on the top of the cooking bowl, which you can steam vegetables, dumplings, or other food in. I put the broccoli in here. If your cooker has this feature, you can do that too, if not just stir fry it in the wok.

    Broccoli is done cooking when it is still bright green and stiff and crunchy. It is overdone when it starts to wilt, turns a darker green with a brownish tinge, or gets mushy. Basically, you just want the broccoli to be hot, not to break down the cells of the plant that make it crunchy and crisp.

    Fortunately it takes about the same amount of time to steam the broccoli as it does to cook the rice. But you may need to pull the broccoli a little early. Just lift the lid and check on it after about 10 minutes and see if it looks good.

    I don’t put anything on the broccoli while it’s steaming, just let it steam on its own. After it’s done steaming, I’ll throw it in the wok for a few seconds to a minute, taking care not to overcook it, to get some flavor from the sauces and oil.

    As soon as the rice cooker is set up and going, I turn my attention to the wok and the vegetables.

  2. Throw some sesame oil into your wok, and ignite the burner under it. Gas stove top is the only way to fly here. A “splash” of oil, enough to coat the cooking surface and leave a slight puddle is sufficient. You’re not deep frying, so you don’t need to cover the ingredients in oil, you just need enough to keep them from burning to the cooking surface of the wok. As you run through adding the different ingredients, you may need to add a little more oil at times.

    Tilt the wok so the oil flows over the entire inner surface, as close to the edge as you can get without spilling the oil. Then put the wok on the burner and let the oil flow back down to the center and get good and hot. If it starts smoking, it’s ready, but it’s also ready before then. It only takes a minute or so to heat up good and hot. Woks are made from thin metal that heats up quickly, and don’t retain the heat very long. It should be hot enough to start cooking very quickly.

  3. Now, cook the vegetables and tofu in the wok. Wait, tofu’s a vegetable too, right? Whatever, just cook the vegetables.

    Starting with the thickest, sturdiest vegetables, working my way down to the flimsiest, I cook each ingredient with a little bit of teriyaki and hoisin sauce, and throw on a little soy sauce as well.

    The sauces help flavor the food as well as keep it from drying out while it’s cooking in the oil. Oil heats up much hotter than the boiling point of water, so it can really dry out food from the outside in and turn it crispy. That’s what deep frying is all about — drying out the surface of the food, making it crispy. But with stir frying, you want to preserve the moisture in the food, and just give it a light coating of oil so it won’t stick to the wok. The oil helps transfer the heat into the food. But you don’t want it to dry out or it will burn. So you add a little sauce to help balance moisture and add flavor.

    It’s easy to use too much sauce, but there’s no strict guideline on how much to use. When I first started my wok experiments, I used too much, and my food didn’t taste like the food it was, all I could taste was the sauce. Now, I use less, and it flavors the food, the sauce doesn’t mask the food and become the entire flavor.

    You don’t want a deep pool of sauce at the bottom of the wok that the food is boiling in; you want a coating of sauce that you can stir the food in.

    Soy sauce is the most watery of the three, and adds moisture to the food while also adding a salty flavor. Teriyaki is sweet. Hoisin is sweet and a little hot. I find these blend well together, but you can experiment with other types of sauces. Fish sauce, oyster sauce, sate sauce are all worth looking at, as well as others.

    If you want to you can also toss in other spices, such as ginger or dried chili peppers, or whatever else you want. These can add even more flavor. But resist the temptation to overwhelm the dish with these flavors. Strong spicy dishes can taste great, but if you dial back and let the spice accent the food flavor rather than smother it, it’s even better.

    I know lots of people like to flip and toss and catch their food with the wok, and this is considered the correct way to use a wok, but I don’t find this to be all that necessary. I just stir it with a large flat wooden spoon/spatula thing, and it works just as well, without risk of spilling the food or straining my wrist. If you enjoy flipping the food around and being a showboat, knock yourself out.

    The purpose of stirring the food is to move it around so it doesn’t stick to the wok and burn. Also it helps even out hot spots so that everything cooks to an even temperature. Also it helps distribute the spice and sauce over the whole surface of each piece of food. Also, it helps the food flip over so it gets cooked from all sides, not just the side it happened to land on when it landed in the wok. And if you’re cooking more than one ingredient together, it helps them to mix. That’s it.

    As each ingredient is cooking, I sample a piece every now and then to see how it’s doing. Once it’s done how I like it, I transfer the food from the wok to the pot, and put the lid on it to keep things warm. I work my way through the ingredients, doing the thicker, sturdier foods first, and cooking them longer, and the lighter ingredients last, cooking them briefer. Items like baby corn, water chestnuts, onions, mushrooms, and tofu all take longer, several minutes, and can be done together if they fit in the wok, or done separately if there’s too much. Broccoli takes long too, unless it’s already steamed, in which case it just needs a short bath in the sauces. Items like spinach leaves, green onion, and baby bean sprouts don’t take long at all, and can be thrown in toward the end for a minute or less.

    Tofu is done when it starts to brown on the edges and develop a bit of a skin. If it’s not pressed enough to remove enough moisture, it may not brown on the edges or develop a skin, so keep that in mind. If you want to, you can do meat instead of tofu: chicken, beef, shrimp, or something else. Be sure to cook chicken thoroughly.

    Once all the ingredients are done, mix them up in the pot and then scoop it out and serve with the rice.

Making a Configuration System in Game Maker, part 3: Where’s Part 3?

A while ago, I started a series of articles that I never finished, on creating a configuration system for GameMaker games. I posted Part 2 almost 2 years ago, and had intended to follow up in just a couple weeks with Part 3, but it stalled and never came out.

A few readers have asked, so I figure an explanation is owed.

The series I had started on creating a configuration system has been on hold since the second article. As I got into designing the system, I kept running into problems with making my system platform-independent and universal.

I had intended to write a universal configuration management engine for handling things like monitor resolution switching, master volume, music volume, and sound fx volume, and a customizable set of configuration widgets for whatever your game might need (essentially, a set of generic, reusable buttons, sliders, checkboxes, and other UI controls). Ideally, I had hoped to write a set of scripts that would use GML functions to define rooms and objects that would constitute the configuration system. Ultimately, I wanted to create a GML script config_system_create() that would set up the entire system — rooms, objects, and functionality, so that you could simply import a GEX extension, call one function, and have a full-featured system generated at runtime, without having to spend any time designing and implementing all the resources yourself. But in GM:S, YYG obsoleted the functions that allowed me to define objects at runtime, execute strings as GML code, and a number of other functions relating to how Windows handles video and audio.

My goal was to enable a developer to drop in my finished configuration system as a GameMaker extension, and invoke its default implementation with a single function call, and use other function calls in the extension to build custom features, but use most of the features without modification, beyond turning features on or off, and to also have the capability of adding customized settings specific to their game. I wanted to write my system one time, and have it be re-usable so that I could save myself from having to spend a lot of time re-creating the system in each new project.

Most difficult of all, it requires that I design a system that anticipates every need a game developer might possibly have, for any type of game they might dream up. Doing it one time and never having to touch it again would be great, but it’s probably not realistic.

This was an overly-ambitious goal. I’m laughing at myself a bit, now, thinking about it.

Still, there were a lot of achievable ideas that I had for the tutorial, and a demonstration of how to implement them would be a worthwhile exercise.

It won’t be drop-in, single-function system, and it’ll be specific to the Windows build target, but the design should be adaptable to your project with a little work.

I don’t want to make a promise for when I expect to deliver this, but I will complete the series and a reference implementation before I publish the first article.

GameMaker Tutorial: Password systems 3: on-screen keyboard

Yeah, I know. Last article, I said in this article we’d cover parsing and validation and converting the password into gamestate data. We’ll get there eventually. But I want to take a detour and show you how to build an on-screen keyboard.

In the first article in this series, I handled the user input simply, by using the get_string_async() function. This is good enough to be functional, but has two main problems:

First, because we’re allowing input directly from the keyboard, the player can enter any characters at all, which means that we’d need to handle characters that are not part of our password’s alphabet. This means extra programming. Not necessarily a bad thing, if that’s what’s right for the user, but it is more work to write up validation scripts that properly handle our Base64 password alphabet.

Second, the user experience of typing a password into a textbox is fine, as far as it goes, but it isn’t at all like an authentic old school keyboard system. This is a somewhat debatable point — old school consoles used on-screen keyboards not because they were better than keyboard entry, but because there was no keyboard. Entering passwords this way is slower and more tedious. But, if we are going to re-create the experience authentically, we need to heed the conventions of the time.

Every onscreen keyboard in the old days was a custom implementation. No two were exactly the same, although there were a lot of commonalities, and most of the differences were cosmetic. The most common implementation was a grid of characters, which the player selected one at a time using the D-pad, pressing a button to enter the character, and another button to go back. When the entire password was entered, typically the player submitted it by pressing the Start button, or sometimes there was an on-screen button that the player would select with the D-pad. We’ll build something like that in this tutorial, although we’ll use the keyboard rather than a gamepad control. Implementing the gamepad controller input capability isn’t too difficult, though, so I’ll probably come back and add that eventually.

The Alphabet and the Font

One of the problems people have with writing down (and later reading) passwords is that certain characters look very similar (such as 0, o O, l, l, 5, S, 9, g, etc.) You can’t help the player’s handwriting, but at least when the game is displaying the characters on the screen, you should be sure to pick a font that makes these characters unambiguous. These symbols are merely labels which stand for a number value, so it doesn’t really matter what they are.

It’s not a bad idea to omit the similar looking characters, although for this tutorial I’m going to stick with the full alphabet. We’ll at least want a font that has very distinct characters to aid the player in recognizing them correctly. Google for “programmer” or “console” fonts, for suggestions on good ones to use, and pick something out that looks right for the style of game that you’re making. Make sure the capital “I” has serifs and the zero has a slash or dot in it, and so on.

The Onscreen Keyboard Object

oOnScreenKeyboard

Create

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?";
font = fConsole;
var k = 1;
for (var i = 0; i < 8; i++){
 for (var j = 0; j < 8; j++){
 keyboard[i,j] = string_copy(alphabet,k,1);
 k++;
 }
}
row_selected = 0;
column_selected = 0;
instructions_text = "Press A to select letter. Press B to erase letter. Press Enter to submit password.";

input_string = "";
max_input_string_length = 4;

Draw

draw_set_font(font);
draw_set_halign(fa_left);
draw_set_valign(fa_top);
draw_text(10,10, "PASSWORD: " + input_string);

draw_set_halign(fa_center);
draw_set_valign(fa_middle);

for (var i = 0; i < 8; i++){
 for (var j = 0; j < 8; j++){
 draw_text(x+(32*i), y+(32*j), keyboard[j,i]);
 }
}
draw_rectangle((column_selected * 32) - 16 + x,
 (row_selected * 32) - 16 + y, 
 (column_selected * 32) + 16 + x, 
 (row_selected * 32) + 16 + y, 
 true);

Press Button1

(Pick whatever keyboard key you want for this.)

if string_length(input_string) < max_input_string_length {
 input_string += string_copy(alphabet, (row_selected*8) + column_selected + 1, 1);
}else{
 audio_play_sound(snBadPassword,1,false); 
}

Press Button2

(Again pick whatever keyboard key you want for this.)

input_string = string_copy(input_string,1,string_length(input_string)-1);

Press Enter

if validate_password(input_string){
 apply_password(input_string);
 room_goto(rmGameStart);
} else {
 bad_password();
}

Press Up

row_selected = (row_selected + 7) mod 8;

Press Down

row_selected = (row_selected + 1) mod 8;

Press Left

column_selected = (column_selected + 7) mod 8;

Press Right

column_selected = (column_selected + 1) mod 8;

That's it for the Events, but we need to write three scripts for the Press Enter event. These passwords are where most of the implementation-specific code is going to go.

The details of these scripts will vary from project to project, but this is where we'll handle the validation, parsing, and conversion to gamestate data -- what I thought we'd be covering in this article. We'll cover our example versions of them in the next article.

csanyk.com © 2016
%d bloggers like this: