csanyk.com

video games, programming, the internet, and stuff

Category: GameMaker Tutorials

High quality tutorials for GameMaker, GameMaker Studio, and GameMaker Studio 2.

Simple GameMaker performance throttling

Here’s a quick tip for performance throttling in GameMaker.

Say you’ve got some code that you need to execute frequently, but if the game starts slowing down too much, you can live without executing this block of code.

Like, for example, say you want to spawn a new instance of some object very frequently, such as in the Step event, but if performance starts to lag you can skip it. You could try to test out how many instances the game can handle without frame rate dropping to an unacceptable level, and cap the number to something somewhat below the maximum. The problem is that this number will vary depending on the hardware. Someone running your game on an older, slower machine will not be able to sustain the same performance that someone with a brand new, high end machine. There really isn’t a true, one-size-fits-all number that works for every situation.

What you really want to do is base the cap on the current performance of the game as it’s running right now. To do that, wrap it up in an if statement like this:

if room_speed < fps
{
 //keep doing the thing that will eventually cause performance issues
}

The way this code works, as soon as fps drops below room_speed, it will stop doing the thing that contributes to the performance problem. This technique does not guarantee that fps will never drop below room_speed, but it will cause performance to stop degrading by not contributing to the problem once performance has degraded to the point that the conditional check takes the “false” branch.

If you don’t want ANY noticeable performance degradation, you may want to make the conditional check be something more like

if (room_speed + 10) < fps

instead; this will give you a little buffer to keep the fps enough above room_speed that you should not see any noticeable performance problems. Or, substitute the calculation room_speed + n with the literal value that you don’t want fps to drop below. Use this to ensure a safer margin of acceptable GameMaker performance.

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.

GameMaker Tutorial: Password systems 2: encoding/decoding

As we established in the first article in this series, a game save password stores game state data. In this article, we’re going to talk about the low level details of how this is done. We won’t be talking much about the password itself, or the game state data. We’ll be focusing instead rather narrowly on how to encode and decode the data.

Numeric data

As far as numeric data is concerned, we’re only going to worry about encoding/decoding integers, for simplicity’s sake. For the most part, we’ll be concerned with positive integers, but we’ll cover negative values as well.

Actually, treating numeric GML data values as integers is a little bit tricky, since in GameMaker the only data types available are strings and reals (floating point numbers). For the most part you can treat a number as though it were an integer in GameMaker, as long as you don’t do math on the number that causes the digits after the decimal point to become non-zero values.

If you want decimal values, you can store the value as an integer, and then divide it by 10, or 100, or however many decimal places as you need after you decode it. Or, if you want to store a fraction, you can store the numerator and denominator as integers, and divide them after decoding them.

As long as you don’t deal in fractions and decimals, you can pretend that your numbers are integers, and GameMaker for the most part will act as though they are. This will come up later when we hit the limit for the largest integer value that we can encode using our method (16,777,215). I’ll explain why below, but for now it’s enough to know this should be a large enough value that we don’t really need to worry about trying to encode larger values, at least for most games.

The basic idea is that each character in your password alphabet stands for an integer value. Notice that the complete alphabet (26 upper case + 26 lower case) + the 10 numerals + 2 special characters gives you a range of 64 values; 2^6 = 64, so each character in a password can represent a 6-bit binary value.

A B C D E F G H
0 1 2 3 4 5 6 7
I J K L M N O P
8 9 10 11 12 13 14 15
Q R S T U V W X
16 17 18 19 20 21 22 23
Y Z a b c d e f
24 25 26 27 28 29 30 31
g h i j k l m n
32 33 34 35 36 37 38 39
o p q r s t u v
40 41 42 43 44 45 46 47
w x y z 0 1 2 3
48 49 50 51 52 53 54 55
4 5 6 7 8 9 ! ?
56 57 58 59 60 61 62 63

Beyond 63

What can you do if you want to store a value larger than 63? Simple, you just use a second digit in your base-64 number. Just like the next number after 9 is 10, in base-64 the next number after ? is BA. Until you’ve looked at base-64 numbers for a long time, it’s going to be very difficult to recognize the value represented by a base-64 number, but we don’t need to — we’ll tell the computer to do it for us with some gml scripts.

We said before that a 4-digit base-64 number stores 24 bits, or up to 16,777,215 in base-10. But for some numbers that might be overkill. If we wanted to, we could treat each digit as a single base-64 value, and add them together. For a 4-character base-64 string, this would give us a range of 0-252, nearly a byte. It’s a much less compact way of storing the data, but for small values it’s not too bad.

To do the conversion from base-10 to base-64 and back, we’ll need some gml scripts.

b64_to_dec(b64)

/*
Takes a string of a b64-encoded value and converts it to a real number
*/
var b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?";
var value = 0;
var digit = 0;
var neg = 1;

if string_copy(argument0,1,1) == "-"{
 argument0 = string_copy(argument0,2,string_length(argument0)-1);
 neg = -1;
}

for (var i = string_length(argument0); i >= 1; i--){
 value += (string_pos(string_copy(argument0,i,1), b64_alphabet)-1) * power(64,digit); 
 digit++;
}

return neg * value;

/*
Takes a real number and converts it to a base-64 encoded string. Supports integer values from 0-16777215.
*/

var b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?";
var value = abs(argument0);
var r;
var str = ""; 
var done = false;

while !done{
 r = value mod 64;
 str = string_char_at(b64_alphabet, r+1) + str;
 value = (value - r) div 64;
 if (value == 0){done = true;}
}

if (argument0 < 0){
 str = "-" + str;
}

return str;

Lastly, it will be handy to have a function that can pad a B64 encoded string to a specific length:

b64_padded

/*
Takes a base-64 encoded string (argument0) and pads it to padded_length (argument1) with leading "A"'s (0's). 
If the number is negative, the first character in the padded string will be a "-". If the padded_length is 
less than the length of the b64 value, the function returns -1 to signify an error. 

(Note the error code returned is a real number, -1, not to be confused with the correct output of 
b64_padded(-1,2), which would return the *string* "-1") 
*/

//argument0 the b64 string to pad to length
//argument1 the length to pad to

b64 = argument0;
len = argument1;

if len < string_length(b64){return -1;} //too short, return error
if len == string_length(b64){return b64;} //just right; we're already done

var str = "";
if string_copy(b64,1,1) == "-"{
 str = "-";
 len--;
 b64 = string_copy(b64,2,string_length(b64)-1);
}
repeat (len - string_length(b64)){str+="A";}
str+=b64;

return str;

Negative Numbers

The scripts above handle negative values just fine. But the minus sign is not part of the base-64 alphabet. If we need to store negative numbers, we have a few choices.

We can expand our password alphabet to include a minus sign (or an arbitrary symbol that we can use as a substitute).

Or we can designate certain characters in the password to store a boolean which signifies whether a given numeric value stored elsewhere in the password is positive or negative. Then encode the absolute value of the number, and re-combine it later with the boolean that holds the sign.

Or we can sacrifice a bit in our base-64 encoded numbers and treat them as signed integers, such that A-g represent values 0 through 32, and h-? represent negative values -1 through -31.

Beyond 16,777,215?

The encoding/decoding scripts work for values up to 16,777,215, or ???? in b64. This value is (2^24)-1. Beyond that, the numbers do not encode/decode properly. The reason for this has to do with the way GameMaker stores numeric values. All numbers in GameMaker are floating point values. GameMaker uses a 32-bit floating point, of which 24 of those bits are used for the digits to the left of the decimal point. The remaining 8 digits are used for the fractional value to the right of the decimal point. This means that for a number above 16777215, we can’t store the value in a 32-bit floating point variable without losing some precision. This precision prevents us from cleanly encoding a value above 16777215 and decoding it back to the same value. Fortunately, such high values should be rare to encounter in the game state data.

What happens if you try to store a larger value depends on the build target. Some may lose precision, resulting in off-by-one conversions — data corruption. Others may fail to return a value entirely when the conversion script is called, which would result in total loss of the data. Certain build targets (I’m not certain which ones) may use 64-bit floating point values, rather than 32-bit. In this case, we can go even higher, up to (2^52)-1. This is a ridiculously large number, and it’s almost certainly more than enough for any game state value you might want to encode.

Tip for validation

If the password space allows a value larger than the max value for a given game state variable, you can use those larger values as a form of validation check. For example, let’s say that the maximum number of lives in your game is 255. This requires an 8-bit value, but since each character represents 6 bits, and we don’t want to bother splitting characters, we use 2 password characters, which represents 12 bits of data. We can use these additional value space between 256 and 4096 for a validation check.

The simplest method would be to reject any password that contains data in the lives bits that decodes to a value greater than 255. Another way to handle this is to use a math value to obfuscate the lives value. Since 4096/256 = 16, we can take the value of the two characters that we use to encode the lives count as follows: lives x 16 = password substring. Now, when you’re validating the password, you can mod the value of the characters that represent the lives by 16, and if the calculation doesn’t work out to 0, then you know the password isn’t valid.

Or you can make the “right” remainder be dependent upon some other part of the password — for example, when the Level is even, the lives substring should mod16 to 0, but when Level is odd, it should mod16 to 1, unless Level is divisible by 3, in which case… Sneaky/unnecessary complexity like this will make the password harder to understand, and therefore require more effort to crack the password system. Don’t fool yourself into thinking you’re coming up with a super secure uncrackable password, but it will make the password a little less obvious than a simple counter, and for password system crackers, will make the puzzle more fun.

String data

Old NES password games did not typically have any string variables to preserve in a save password. In fact, the few games where you could enter a name were all, to the best of my knowledge, battery backup games that used savefiles.

It’s simple enough to store a string in a password, though, since a password is a string. The only real issue is dealing with variable length strings. For a lot of reasons, it’s probably best to stick with a fixed length for strings and pad shorter strings with spaces, trimming them later when applying the password to game state.

However, it looks weird if your password has string values stored as plain text in the password — it’s an obvious indicator to the player that the password is storing data, which could invite mischief. So it’s probably a good idea to encode the data somehow.

We could encode strings by using the numeric ASCII values of the letters, and then convert them back, character by character. While not encrypting the data, it would be sufficiently obfuscated for our purposes.

I won’t bother implementing this for now, since our demo password only has 4 characters, and in any case it’s not really necessary, but it’s good to have an idea of how we might do it if we decide to later on.

Boolean data

GameMaker doesn’t have a true boolean data type. The boolean constants true and false are equal to 1 and 0 in GML. Boolean expressions in GameMaker are handled by evaluating to a real number, and any number <0.5 evaluates as false, and 0.5 or greater evaluates as true. Therefore, to encode a boolean, all we really need to do is encode a value of 0 or 1. Or any other value that will evaluate to true or false.

But, since a single base-64 character can store up to 6 bits of data, and each bit can store a 0 or a 1, a single character could in theory store up to 6 boolean values, which means a 6x greater density than if we just stored one boolean value in each character.

To achieve this, we need to convert a base-64 value to a binary value, and vice versa. Then we would need to break the binary value into its individual bits, and designate each bit for a specific boolean value.

Note that the next few functions do not really deal with binary values, but rather with strings storing 0 and 1 characters, which we can convert to boolean values by using the real() function.

Edit: I’ve updated these functions with more elegant implementations, and replaced the brute force switch statement lookup table approach that I had here originally. Thanks to Ian Schreiber for the suggestion.

bin_to_dec(bin)

/*
Takes a string of a binary encoded value and converts it to a real value.
*/
var bin_alphabet = "01";
var value = 0;
var digit = 0;

for (var i = string_length(argument0); i >= 1; i--){
 value += ((string_pos(string_copy(argument0,i,1), bin_alphabet)-1) * power(2,digit)); 
 digit++;
}

return value;

dec_to_bin(dec)

/*
Takes a base-10 value and encodes it as a binary string
*/
var bin_alphabet = "01";
var r;
var str = "";
var done = false;
neg = sign(argument0);
value = abs(argument0);

while !done{
 r = value mod 2;
 str = string_char_at(bin_alphabet, r+1) + str;
 value = (value - r) div 2;
 if (value == 0){done = true;}
}

if (neg < 0) {str = "-1" + str;}

return str;

bin_to_b64(bin)

/*
Takes a 6-digit binary encoded string and converts it to a b64-encoded character.
*/

return dec_to_b64(bin_to_dec(argument0));

bin_to_bool(bin)

/*
Extracts the argment1-th bit out of the binary string supplied in argument0 and returns it, converted
to a real number. The real value can be interpreted as a boolean (0=false; 1=true).
Argument0 must be a string consisting only of "0"'s and "1"'s
Argument1 must be a number between 1 and string_length(argument0). 
*/

return real(string_copy(argument0,argument1,1));

bool6_to_bin(bool,bool,bool,bool,bool,bool)

/*
Takes six boolean values and concatenates them to create a 6-bit binary encoded string, 
suitable for converstion to a b64 value with the bin_to_b64() function.
*/

/*
force conversion of arguments to gml boolean constants
*/

if argument0{argument0 = true;}else{argument0 = false;}
if argument1{argument1 = true;}else{argument1 = false;}
if argument2{argument2 = true;}else{argument2 = false;}
if argument3{argument3 = true;}else{argument3 = false;}
if argument4{argument4 = true;}else{argument4 = false;}
if argument5{argument5 = true;}else{argument5 = false;}

/*
concatenate bools to string
*/
return string(argument0) + string(argument1) + string(argument2) +
 string(argument3) + string(argument4) + string(argument5);

bin_padded

//pads a binary encoded string (argument0) to length (argument1)
var bin = argument0;//bin string to pad
var len = argument1;//padded length

if len < string_length(bin){return -1;}
if len == string_length(bin){return bin;}

repeat(len - string_length(bin)){bin = "0" + bin;}

return bin;

These scripts should suffice for encoding/decoding all of our data values.

Advanced password validation with checksums and hashes

Additionally, to keep the player honest, you probably want some kind of tamper-proofing to go with your password encoding. Otherwise, it makes tinkering with the password to cheat too easy. For example, you could play the game, get a password, then get hurt, and get a new password, and by comparing the two, you could infer which characters in the password correspond to your health. Then, you could substitute different values for the password characters that correspond to your health health until you hit upon a value that gave you max health, or, depending on how the game works, even infinite health or invulnerability.

It’s true that deciphering password encoding schemes is a lot of fun, but we don’t want to make the system so easy that it invites cheating. Make the player work at it, and feel like they truly hacked something when they figure out your system.

To do this, we can reserve certain characters in the password for storing checksum values, or even hash values. Understanding hash and checksum math isn’t too difficult, but isn’t terribly necessary, either. We don’t need something impossible to crack, just something functional. There are better checksum functions than I’ll demonstrate here, but this should suffice to make the concept understandable to anyone.

Simple checksum

Using our 4-character example password, we can add an additional two characters for checksum data.

Level Lives Health Ammo Checksum
A-? A-? A-? A-? AA-??

Let’s say the game state data is encoded with the following 4 values from our cipher table, above:

Level Lives Health Ammo
password char B D ? ?
base-10 value 1 3 63 63

A simple checksum for this would be 1+3+63+63 = 130. Of course, this checksum isn’t very strong; any four values for Level, Lives, Health, and Ammo that add up to 130 will have the same checksum. But it does stop someone from changing a single character in the password and getting another valid password.

To add the checksum value to the password, we need to encode 130 as a base-64 value. This would be a 2-digit base-64 number: 22. According to our base-64 encoding table, the value 2 is encoded by the symbol C. So, 22 == CC. So the entire password would be:

Level Lives Health Ammo Checksum
password char B D ? ? CC
base-10 value 1 3 63 63 130

Note that since 63*4 = 252, the highest the checksum value will ever go will be 252, or D8.

If you wanted to get really crazy, you can scramble the order of the characters, shuffling checksum characters in between the data value characters. But we’re not that concerned with obfuscation, so we won’t bother with an example of this.

In the next article, we’ll cover how to design the password specification in greater detail, and write some sample scripts that takes the password, validates and decodes it, and assigns the stored values to re-create the game state.

Part 3

GameMaker Tutorial: Password systems 1: password entry

In old school console games, especially for the NES, it was common to enter a “code” or “password” in order to resume play where you had left off previously. Back in the day, memory was extremely expensive, and very few games implemented a battery backed RAM solution that allowed the player to Save and Restore a game.

Instead, a system of encoding the game state data into a long “password” was often used in lieu of a real save system. In addition to encoding the game state, these password systems often had some kind of validation built into them, so that not just any arbitrary input would be accepted. For fun, sometimes games would have special, secret codes that would enable cheats. For a few players, cracking the encoding system to enable you to configure the gamestate to your exact wishes was a kind of advanced meta-game, excellent for budding young hackers. There is great nostalgia value in these systems if you are into old school retrogaming.

Password systems (general overview)

If you want to build a password save system, at a high level there are a few things you need to do:

  1. Password Entry
  2. Validation
  3. Encoding/Decoding GameState
  4. Password Display

This article will cover Password Entry, while future articles will cover the other topics.

Get the input

For simplicity’s sake, let’s assume a four-character code will encode all the information that we need. In practice, most 8-bit NES games used much more than this, but for a simple input demo this should be sufficient.

The easiest way to enter a string in GameMaker is the get_string_async() function.

save_pw = get_string_async("Enter password", "");

Since get_string_async is an asynchronous function, it does not return a value immediately. We need to add an Async Event to catch the return value when the function calls back to the main program. The correct Async Event to use for this function is the Dialog event. The get_string_async() function doesn’t simply return a string value, though; rather, it returns a data structure called a ds_map, which contains 3 values: an id, a status, and the result. The result is the string that was entered by the player, the password that we are looking for.

We can put the following code in the Dialog Event to handle the return callback:
Dialog Event:

var i_d = ds_map_find_value(async_load, "id");
if i_d == save_pw{
 if ds_map_find_value(async_load, "status"){
 password = ds_map_find_value(async_load, "result");
 }
}

The interface that get_string_async() provides is not very satisfying, aesthetically, but it works well enough for now. (We’ll explore a few other methods later that will more faithfully replicate the “password entry” screens from old NES games, in a future article.)

Right away, we have a few problems with simply getting a string:

  1. Because get_string_async() allows the player to enter any string they want, the player may enter a string of arbitrary length. For our demo, we need them to enter a string that is exactly the right length.
  2. The get_string_async() is not constrained in the characters it will allow the player to enter. Passwords for NES games varied in their alphabets, but many would allow A-Z, a-z, 0-9, and often spaces and special characters. Some games would allow only capital letters, while others would allow lower and upper case. One serious flaw with the old password systems was that the letters were displayed in fonts which often made it difficult to differentiate certain characters, like 1 and l, or 0 and o, etc. Later NES games sometimes corrected for this by using a more distinct font, or by omitting the ambiguous characters from the alphabet entirely.

There are many ways to constrain the allowed characters, but we don’t need to get super fancy with it for our demo.

In the next article, we’ll demonstrate how to decode the password — that is, to translate the password value to game state information. Finally, we’ll demonstrate how to generate and display the password when the game is over (or paused, or at a save point, or whenever it’s appropriate for your game), so that the player can write it down and enter it the next time they play to resume where they left off.

Part 2

GameMaker Tutorial: String handling and Drawing Text

[Editor’s note: This article was written primarily with GameMaker: Studio 1.x in mind. There have been some changes to the way GameMaker Studio 2 handles strings, mainly dealing with escaping codes, and this article has not yet been updated to reflect that. Refer to the official manual chapter on Strings for all the details.]

Drawing text to the screen is a basic part of most videogames. There are a huge number of useful applications for text. Just a few of the more common applications:

  1. Score
  2. HUD/Dashboard
  3. Menus
  4. Special effects
  5. Messages and dialogs
  6. Instructions/Story
  7. Debugging/diagnostics/benchmarking — it can be incredibly useful to draw the current value of variables to the screen when debugging, or performance metrics.

Things to know about drawing stuff in GameMaker

  1. Draw functions only work in Draw Events:  If you try to use them anywhere else, nothing happens. If you’re drawing in the Draw GUI Event, you’ll want to be familiar with the draw_set_gui_size() function so your Draw GUI stuff will be drawn to the proper scale if you’re using Views.
  2. Drawing directly to the screen (especially text) is slow. Draw a lot of text and performance will suffer.
  3. There are ways to improve performance when drawing text. The most important of these is to use Surfaces. Surfaces are not available in the free edition of GameMaker, and not all hardware may support them. Using surfaces properly is not that difficult once you understand them, but is generally considered to be an “advanced” concept in GameMaker, and is less straightforward than drawing directly to the screen in the “normal” way.
  4. But there are challenges. Setting up a Surface for optimizing text performance is tricky because it can be hard to know in advance how large the surface needs to be to contain the text you are drawing. Fortunately, GameMaker provides some useful functions which can enable you to get the dimensions needed for the surface: string_width() and string_height(), which give you the width and height, respectively, in pixels of a string drawn with draw_text() in the current font. If you’re using draw_text_ext() string_width_ext() and string_height_ext() are the functions to use instead. These functions allow you to create a drawing surface of proper dimensions, provided you know the string and font and can decide on a width prior to creating the surface. Keep in mind that the dimensions of a string depend on the font used to display it, so always use draw_set_font() to set the font to the correct one that you intend to draw the string with before using the measurement functions.
  5. Draw settings (for things such as color, alpha transparency, and font alignment) are global in GameMaker. That means that if you have multiple objects which draw functions, and if any of them changes the color, alpha, or font alignment, all objects will be drawn using those same settings. For this reason, if you are using draw functions in your objects, it’s best to set all the draw settings in the object in order to make sure they are what they need to be. If you never change color, or alpha, or font alignment, then you don’t need to set that property before you use draw functions — but if you do need to change them for one object, it’s best to set them to what they need in the Draw event of every object, immediately before calling the drawing routines.
  6. For serious performance optimization, you need to learn how GameMaker “batches” drawing operations, and organize your code to have the least number of drawing batches as possible.

Fonts

Everyone these days knows what fonts are, right? Fonts are like the clothes that text dresses up in when it wants to go out and be seen. In GameMaker, fonts are game resources, just like sprites, or objects, or other resources, and need to be added to the project — you don’t simply have direct access to the same fonts that are installed on the system, you have to explicitly add a font to your project. If your project has no font resources set up, text drawn to the screen will still render, but oddly and probably not consistently across platforms. So, always define a font resource and make sure that it’s used if you’re drawing text.

To save space, you can define a font resource to include only certain character ranges, such as number digits only, or alphabet characters only, or only the upper case or lower case letters in the alphabet. If you know you won’t be needing certain characters, and are concerned about the size of the game when it is built, go ahead and constrain the range. Otherwise, the default range of 37-128, covering A-z, 0-9, and special characters, is good.

For legal reasons, it’s important to note that fonts are copyrighted, and most need to be licensed for commercial use. There are free fonts out there (google for them) with liberal licensing terms that you may be able to use in your project, if the terms of the license allow.

Of course, you can create your own fonts. Creating your own font is outside the scope of this article, but there are tools you can use to produce your own fonts if you’re crazy enough. It’s probably easier to simply purchase a license for a professionally designed font.

Formatting issues

Alignment

Text alignment is set using the and draw_set_valign() functions. Use GameMaker’s built-in font align constants {fa_left, fa_center, fa_right, fa_top, fa_middle, fa_bottom} as arguments to these functions to keep the code readable.

New Lines

To signify a new line in a GML string, use the pound character (#). The GML code

draw_text(x, y, "Hello#World");

would be drawn like so:

Hello
World

You can also use a literal return in your string, but it’ll make your source code look yucky.

draw_text(x, y, "Hello
World");

Would draw to the screen exactly the same as “Hello#World”.

Escape characters

If you’re familiar with strings in programming languages, you know that it gets tricky when using certain characters that are reserved for program syntax or markup. Most languages allow you to “escape” the markup syntax so that you can still use characters normally reserved for markup purposes as literal characters in a string. GML is no exception.

#

What if you want to use a # in a string, and you don’t want it to signify a new line? Use the “#” escape character.

The string "We're \#1!" would be drawn like so:

We’re #1!

Quotes

A matched pair of quotes, single or double, can be used in GML to begin and end a string. If you want quotes to appear as text within a string, you can use the other type of quote to encapsulate them, like so:

my_string = 'This is a single-quoted string.';
my_string = "This is a double-quoted string.";
my_string = 'This is "an example" of a string including double quotes-as-text.';
my_string = "This is 'an example' of a string including single quotes-as-text.";

It gets tricky when you need to have BOTH types of quotes in the same sentence:

my_string = 'Bob said " We shouldn' + "'" + "t."+ '"' ; // Bob said "We shouldn't."

It looks like a mess, but you just have to do a lot of concatenation and quote your quotes with the other type of quote marks.

String concatenation

As with many languages, you can combine two strings together by adding them with the + operator. With number values + adds them; with strings, + concatenates the two strings together, creating a longer string made of the first one and second one stitched together. You can do this with literal string values, or with variables containing strings:

concatenated_string = string1 + string2;
concatenated_string = "Hello " + "World";

But if you try to add a string and a number, you need to tell the program to convert the number into a string. The string() function will convert numeric values to strings, which allows them to be incorporated into a larger string.

health = 100;
draw_string(x, y, "Player1 Health: " + string(health));

GML String functions

We’ve already introduced a few of the more commonly useful ones, but there are many other useful GML string functions. I’m not going to go into each one in depth, but review the official documentation and keep in mind that they’re out there, and can be useful.

One important thing to be aware of with GML strings is that, unlike most other languages, GML strings are 1-indexed, not 0-indexed. This means that when counting the characters that make up the string, the first character is character 1, not character 0.

GML text drawing functions

Mostly I have used draw_text() and draw_text_ext(), but it’s good to know that there are a few more variations on these basic text drawing functions.

  • draw_text
  • draw_text_color
  • draw_text_ext
  • draw_text_ext_color
  • draw_text_ext_transformed
  • draw_text_ext_transformed_color
  • draw_text_transformed
  • draw_text_transformed_color

It might seem like a lot to keep track of, but it’s pretty easy if you remember the following:

draw_text: basic draw text function.

_ext: allows you control over the line spacing and width of the draw area. This means you don’t have to manually handle line breaks by inserting # or return characters in your text.

_transformed: allows you to scale and rotate the drawn text.

_color: allows you to set a color gradient and alpha to the text.

Again, text is always drawn using the current global drawing color, alpha, halign and valign properties. It’s best to set these before drawing to ensure that they are the expected values, using draw_set_color, draw_set_alpha, draw_set_halign, and draw_set_valign functions.

Keep code clean by storing strings in variables

This is perhaps obvious, but it’s often useful to store a string value in a variable, to keep your code neater and easier to read.

draw_string(x, y, "Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.##Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battlefield of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.##But, in a larger sense, we can not dedicate, we can not consecrate, we can not hallow this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us—that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion—that we here highly resolve that these dead shall not have died in vain—that this nation, under God, shall have a new birth of freedom—and that government of the people, by the people, for the people, shall not perish from the earth.");

— is a lot harder to read than:

draw_string(x, y, gettysburg_address);

— and moreover, all that text gets in the way of comprehension of what your code is doing. So use variables to store strings, and keep your code looking clean.

draw_text_ext()

While we’re dealing with a very long string, it’s a good opportunity to talk about a function that makes drawing them much easier.

You could manually set line breaks in a long string by sprinkling #’s every N characters or so, but that is laborious and inflexible. It’s better to use the draw_text_ext() function, which allows you to specify a width for the line, and (optionally) also how many pixels should separate lines.

draw_text_ext(x, y, string, vertical_separation, width);

When drawn, the line will automatically break when it reaches the width provided to the function.

Formatting

GameMaker is rather limited in its typographical capability when drawing text to the screen. GameMaker Font resources, unlike an installed font on the system, are a specific size and style only. There’s no bold or italic or other style options available that you can use to modify the font resource. If you want bold or italic, you have to create a new font resource, and use draw_set_font(font) to that resource in order to use it.

This means that if you want to use bold text in a sentence, you need to create a second font resource for the bold font, draw your normal text, then switch fonts to the bold font, and draw the bold text, somehow positioning the two different drawings so that they look like they’re a single block of text. You have to leave a hole in your normal text where the bold word will appear. This is not easy, nor is it generally recommended. If you really want it, and are masochistic enough to put yourself through the trial and error to do it, go ahead. But before too long you’ll probably realize that it’s not worth the effort.

See this script draw_text_rtf which allows you to draw rich text format, originally written by Miah_84 and improved by me.

Special Effects

Scrolling text

Scrolling text is extremely easy to do. The draw_text function must be called by some object, and includes arguments for the x and y where the text will be drawn. Simply change the x and y over time, add you have moving text. The easiest thing to do is to set the instance that is drawing the text in motion.

Typewriter text

Another easy to implement technique is “typewriter text” — that is, displaying a string one character at a time as though it were being typed out.

First, let’s take a string stored in a variable, my_string.

string_length(my_string) will give you the length of my_string.

draw_text(x, y, my_string) would draw the entire string at once. But we want to draw it one letter at a time.

The GML function string_copy(string, index, length) comes in handy here. We can use this instead of string in our draw_text function:

1
2
//In the Create Event
typed_letters = 0;
1
2
3
//In the Draw Event
draw_text(x, y, string_copy(my_string, 0, typed_letters);
if (typed_letters < string_length(my_string)) {typed_letters++};

Note that this will type at room_speed characters per second, which at 30 fps is extremely fast. You may want to type slower, in which case you can slow down the function in one of several ways. You can use an alarm to increment typed_letters every N steps, rather than increment it in the Draw event. Or you don’t want to bother with an Alarm event, you could do something like this:

1
2
3
//In the Draw Event
if typed_letters < length {typed_letters+=0.1;}
draw_text(x, y, string_copy(my_string, 0, ceil(typed_letters)));

This would give a typing speed of room_speed/10, or 1 character roughly every 0.33 seconds for a 30 fps room, or 3 characters/second, which is a bit more reasonable. You can adjust this rate to taste.

If you want the text to reset and type over again when the message is completed, you can do this:

if typed_letters &lt; length {typed_letters+=0.1;} else {typed_letters = 0;}

Additionally, you can optionally add code to play a sound with each letter, or start a sound when the typing starts and stop the sound with the full length string has been reached.

Marquee text

The Typewriter Text technique can be modified slightly to draw a scrolling marquee:

1
2
3
4
5
6
7
8
//In the Create Event
/*Hint: you may want to pad the end of your marquee string with extra spaces so it 
will scroll all the way off your marquee.*/
my_string = "Some text for your marquee "
start_letter = 0;
marquee_length = 10; // or however many letters in your marquee
type_rate = 3/room_speed; // 3 char per second
marquee_scrolling = true;
1
2
3
4
5
6
//In the Draw Event
if marquee_scrolling{
 draw_text(x, y, string_copy(my_string, start_letter, ceil(start_letter + marquee_length)));
 start_letter += type_rate;
 if (start_letter > string_length(my_string)) start_letter = 0;
}

Blinking text

Blinking is annoying in web pages, but can be a very useful effect in games. Blinking attracts the eye, and can get attention where it’s needed. Of course, blinking can be done with any graphical element, not just text.

Blinking is just turning on the drawing and then turning it off on a cycle, using a timer, such as an Alarm Event.

1
2
3
//In the Create Event
blink = true; //(or false, if you want the initial state to be off)
blink_steps = room_speed/2; //for a 1 second blink cycle. Set this value to suit.
1
2
3
//In the Alarm[0] Event
blink = !blink; //toggles the blink from on to off or vice versa.
alarm[0] = blink_steps; //re-sets the alarm so it keeps blinking
1
2
//In the Draw Event
if blink {/*do the draw stuff*/}

The above code gives a 50% “duty cycle” (the blink is “on” 50% of the time, “off” 50% of the time). It’s possible to vary the duty cycle in a variety of interesting ways…

1
2
3
4
//In the Create Event
blink = true; //(or false, if you want the initial state to be off)
blink_on_steps = room_speed/2;
blink_off_steps = room_speed/4;
1
2
3
//In Alarm[0]
if blink {alarm[0] = on_steps;} else {alarm[0] = off_steps;}
blink = !blink;

This blink code will result in a blink that stays on for 0.5 seconds, and blinks off for 0.25 seconds.

Even more sophisticated blink periods can be achieved using math functions rather than a static value. Setting alarm[0] = irandom(10) would result in a random flicker. Think of creative ways to use other math functions to create interesting effects. If you come up with a good one, share your code by posting a comment to this article.

Yet another way to flicker or blink text is through varying alpha. Or by switching colors. Or even size.

One last way to blink is to toggle the state of the visible boolean in the instance.

visible = !visible;

One thing to keep in mind, though, is that an instance that is not visible will not be checked for collisions. Also visible applies to the entire object’s Draw event, so it is all or nothing. Still it is a simple way to draw or not draw an object.

What next?

If you’re familiar with other programming languages, you may be disappointed at the limits of the built-in functions for manipulating strings. There are a lot of things you can do more easily in other languages than in GML, unfortunately. Since GML only has two data types, strings are extremely important, but because games tend to focus more on graphics, sound, and interface, the average GameMaker developer can get by with the string functions that do exist, for the most part.

There are a number of useful GML scripts for doing more advanced things with strings that have been collected at gmlscripts.com. Many of the functions built in to more mainstream programming languages can be found there.

GameMaker tutorial: Elegant instance_change() in your state machine

In GameMaker, a commonly used technique is to build a system made up of a several objects to represent an entity in your game, such as the player or enemy, in various states, such as idle, dead, shooting, jumping, running, climbing, and so forth. This is what is known as a Finite State Machine pattern.

When the time is right in the game, we change an instance from one state object to another by using the powerful instance_change() function. Instance_change() takes the instance and transforms it into a new type of object. Its Event behaviors will change to those defined by the new object type, but its old properties (object variables) will remain the same as before, allowing the instance to retain its variables with their current values.

The instance_change() function takes two arguments: object, the object the instance will turn into, and perform_events, a boolean which controls whether the new object’s Create event will be performed or not.

Normally, the Create event is where an object initializes its variables and initiates its default behavior. When we’re dealing with a State Machine comprised of a number of objects, this can become problematic, however. Some code in the Create Event is initialization code that we may only want to execute one time, to set up the instance when a brand new instance is created, while other code in the Create event is behavioral and we may need to execute whenever an existing instance reverts back into that state again. Thus, the perform_events argument in the instance_change() function isn’t adequate for this situation — it’s too all or nothing.

For example, let’s say I have a generic object for an enemy, oEnemy. I want some visual variety to this enemy, so I’ve created a few different sprites for it. In the Create Event, I want to randomly choose one of those sprites to be the sprite for this instance. But if the instance changes into another state object, and then reverts back, if I call the Create Event, it will randomly choose a new sprite. I don’t want this, as it ruins the illusion of continuity — I need that instance to retain its sprite. But I do need the Create Event to run, whenever it re-enters this state, because I’m using it to set the instance in motion.

So, how can I elegantly select which lines of code I want to run in the Create Event?

Conditional blocks

This is the least elegant solution, but you could use if to check whether a variable exists or has a value. For example:

if sprite_index == -1 {sprite_index = choose(sprite1, sprite2, sprite3);}

This is inelegant because it adds lots of lines of code that only need to be run one time (when a brand new instance is created) but need to be checked potentially many times (any time that instance changes back into the object state). It also only checks certain, specific things, case by case. As I continue to build the state machine, I may end up introducing more features which require initialization, which would necessitate more checks, further bloating the code. I always want to write the least amount of code needed, both for reasons of performance and maintainability.

Move one-time code to an init state object.

The more elegant solution is to recognize that initialization is its own state, and we need to separate it out from the other states in the state machine. We can create an oEnemy_init object, put our one-time initialization code into it, and then the final step in the Create Event for the init object would be to change the object into the default state.

None of the other states in the state machine should put the instance back into the init state, thereby guaranteeing that the init code only executes once. Now your code is neatly separated, your states objects in your state machine are as simple as can be.

Game Maker Wave Motion Tutorial

Following up on my motion and position tutorial, I present a tutorial on wave-motion. This was something I wanted to include in the original article, but I realized that there’s enough complexity to this concept that it merited its own separate article.

Wave Motion

Wavelike motion is any motion that involves periodic oscillation, not just linear undulating motion. (Other types of wavelike motion include pulsing and concentric ripples, for example.) But we’ll talk mostly if not exclusively about linear undulation, since it is easiest to understand, simplest to implement, and the basis for many others.

(more…)

GameMaker Position and Motion Tutorial

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

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

Random number generation in game programming

Random number generators are extremely useful in game programming. I have found a lot of uses for randomness in my projects.

What can you do with randomness?

Man, all kinds of stuff. Nearly any value that you want to initialize in a game object is a candidate for possible randomization. Randomness fuzzes up your game, making less deterministic and therefore harder to defeat with simple patterns and more replayable.

Pretty much any time I would normally use a literal or a constant number in my code, anymore I step back and ask myself what range of values might work in that place, and then create a random function that will provide me with a number in that range. The only time I don’t do this is when I really do need a precise value, or when performance is too important to sacrifice the computation time needed for the random function to return its result.

Here are just a few ideas for how you can use random numbers to improve your games:

Unit stats

Nothing makes a video game feel more like a video game than when every enemy you encounter is an exact clone of all the other enemies that look like it. You can use randomness to give your enemies some personality by giving them randomized stats. Instead of fixed values for Attack, Defense, Speed, Damage, etc., use a random range of values to generate stronger and weaker versions of your enemies. It takes a little more time to compute these values on the fly, but modern processors can handle this load easily, unless you’re generating a huge number of units.

Sprite generation

Why spend a lot of time hand-drawing every sprite in your game? Create a generator system that randomly puts pieces together, and create random sprites on the fly. If you’ve played around with an avatar generator such as eightbit.me or the Mii generator on Wii or the XBox Live Arcade avatar generator, imagine that kind of model system, but with a random selector in charge of picking the hair, eyes, etc. You can do this to randomly generate other things, such as buildings, procedurally, as well.

Colors

If you’re calling drawing functions, randomizing colors can give your game a lot better visual appeal. If you’re clever in how you pick your random colors, you can come up with color schemes that work nicely, yet are always slightly different each time you play. You can either pre-define a palette of colors and randomly select one, or you can randomly select R, G, B or H, S, V numbers and create a color at runtime. You can experiment with different mathematical tweaks to shape and constrain the randomness.

Map generation

If you can write a good random map generator, you can save yourself from having to hand-design all your maps. GOOD random generation may be very difficult to accomplish, however — especially for more complex games. But even if you can’t guarantee a good random map at runtime, an almost-good random map generator can save you tons of time or spur your creativity by doing most of the work for you, leaving you with something almost good enough, that just needs a little hand-polishing to make shine.

Procedurally generated content in general is a good use for random functions. You can use a random number function to create a deterministic sequence of generated values that is always the same. This is because computer hardware actually does not have a means of creating a truly random number — it fakes it, approximating randomness with a pseudo-random algorithm.

This is used to good effect in one of my favorite Atari 2600 games, Pitfall. A pseudo-random function, using a fixed seed, is used to generate each screen in the game. This achieves a very high information density, since the data that was needed to represent each screen could not be stored on a 4kb ROM, but a generator function that creates that data easily could. This technique is not used very much in modern game development since storage isn’t much of an issue any more, but it is still a very interesting technique and one which merits study.

AI

There are many potential applications of randomness to AI. Whenever your AI needs to make a decision, you potentially can use randomness to make that decision less predictable. Weighted probability is important here, as completely random AI behavior is erratic and seems crazy, while an AI that occasionally does something unexpected will seem tricky or deceptive or clever. Dynamically weighting the probability according to context at runtime will make your AI seem smarter.

GameMaker/GML random functions

These are built in to the Game Maker Language (GML):

  1. random(N): returns a random floating point value between 0 and N, not including N.
  2. random_range(A, B): returns a random a floating point value between A and B, not including B.
  3. irandom(N): returns a random integer value between 0 and N.
  4. irandom_range(A, B): returns a random integer value between A and B.
  5. choose(a,b,c,d,e,f,g,h,i,j,k…): Randomly returns one of the arguments, up to 16 arguments may be passed into a GML function. You can weight the likelihood of one result by repeating it in the arguments list. (e.g., choose(dog, dog, dog, cat) would be 3/4 likely to return dog, 1/4 likely to return cat.)
  6. random_get_seed(): Gets the current seed value for the randomizer.
  7. random_set_seed(): If you need a randomized, but deterministic function, you can set the seed for your random function. (This approach of setting a known seed is how the levels in Pitfall for Atari 2600 were always the same even though they were generated by the Atari’s random number generator.)
  8. randomize(): Sets the seed of the randomizer to a random value.

These are scripts you may import into your project:

  1. gauss(median, deviation): Returns a random value with a gaussian (“normal”) distribution around a median value. From GMLScripts.com.

All programming languages have similar random functions or classes built into them. Whatever tool you happen to be using, it pays to learn about how it can produce random numbers, and how you can use them to do useful and interesting things.

If you have any favorite ways of using random numbers in your programs, post a comment below and share it!

Keeping randomness under control

One of the mistakes most game developers will make when using random functions is to use too wide a range of random values, or failing to control the range of values returned by the random function.

Know your math

Randomness feels most random when the probability distribution is flat. However, this often does not make for the most interesting gameplay mechanics. It’s often more useful to have a weighted function that has a greater probability of returning a value in one part of the range than in another. Understanding probability math is key to getting your randomized functions under control. The other key is to develop your intuition to know what range of values will work best for a given situation.

If you’ve ever played tabletop role playing games, then you know about dice. Dice are good analog randomizers, and can help us understand probability and randomness in a computer program. In classic Dungeons & Dragons, character ability scores are randomly determined by adding the values of three six-sided dice. This results in a bell curve, meaning that the results of a 3d6 roll are distributed in such a way that an “average” score between 9-12 is far more likely than an extreme score of 3 or 18. So one way to directly simulate this type of dice rolling in a computer program would be :

N = 0;
repeat(3){N+=irandom_range(1,6)}; // generates a value between 3-18, distributed around 10.5

In computer programming, there are more efficient ways to achieve a bell curve distribution than this. Calling random() multiple times, and writing loops will make your code slow, so if there’s ways to avoid doing that, it’s a good idea. The gauss() function from gmlscrips.com creates a “normal” distribution around the agrument passed into it, and is fast and efficient.

round(gauss(10.5, 3.5)); //simulates rolling 3d6, approximately

Note that this will not return exactly the same distribution of values as a true 3d6 roll will. But this is because 3d6 is actually an approximation of a gaussian distribution — the gauss() distribution is more accurate to a “standard normal” statistical distribution. If you compared graphs of the bell curves of 3d6 vs. the gauss() function, the gauss curve would be smoother, and would include values outside the 3-18 range (2 and 19 would show up a tiny percent of the time).

There are other types of distibutions that you might want to achieve with your randomized functions, for some purpose. Knowing your math is important here. Learn the graphs of common functions, and understand the relationship between the shape of the graph and the probability distribution of a randomized function modified by each function. For example, random(x), ln(random(x)), and random(x)^2 have very different looking distributions. Knowing this, you can tailor an equation to fit your needs.

Once you get comfortable with the math, it’s actually fun. Play around with a graphic calculator and see what different graphs you can come up with. Each time you discover an interesting or useful shape, make note and file it away for a time when it might be useful.

Testing

Because adding randomness to your functions make the game non-deterministic, it can make things more difficult to test. Certain conditions become hard to duplicate, because you don’t directly control them, and this can make repeatable testing of your game seem impossible.

There are approaches you can take to ensure that your code works, still. First, when you are building up your functions, ensure that the non-random parts of the code work well before you introduce randomness. If necessary, temporarily remove the randomization and replace it with a literal value, a constant, or a variable. Once you are have tested thoroughly and are sure the code is working correctly with a range of controlled values, you may safely replace the controlled values with random values that are constrained to the ranges you tested.

In some cases, you may need to go back and re-test code. It would be a pain to have to find/replace every random() call in your code. Not only would it be time consuming, it would increase the opportunity for errors to creep in. A better approach may be to comment out the equivalent non-random code next to the random code, and leave it in your code file. That way you just have to comment out the randomized function and un-comment the deterministic version.

Even this is time consuming and error prone, however. You may want to create randomized and non-random versions of your functions, and introduce a configuration variable that you can toggle to enable/disable randomness. Then you can pepper your code with things like:

if settings.random {randomized_function()} else {deterministic_function()};

All this extra branching in your code can get ugly and unmangeabeable too, so try to limit this to keep it to a minimum.

You could also use polymorphism to create sibling classes, one which inherits randomized functions, and one which doesn’t, and just spawn the appropriate class instances according to the game configuration at runtime.

The great thing about being able to turn randomness on or off at runtime is that it allows you to very quickly see the difference, reducing the lag time between test runs. This could even turn into a feature of the game, rather than a debug exercise.

With proper care taken during development, randomized functions can be just as reliable as deterministic functions. It just takes a little extra forethought and planning.

Making a Configuration System in Game Maker, part 2: Requirements

If you haven’t yet, go back and read Part 1

Design choices

Since we’re starting from (basically) nothing, we have a lot of decisions to make. Therefore, thinking about the design of your configuration system first before you start building things probably is a good idea.

Requirements

First, let’s think of the features that we need. When I brainstorm features, I tend to go crazy. I think about everything I might possibly need. I think about all the things that would be OMG SO AWESOME to have. I find it helpful to do this, but I have learned that while having all these ideas is great and exciting, in the end you have to build everything, so every idea you come up with represents a lot of work and a lot of testing.

I’m only one person, working on these projects in my spare time — not a design house, or even a full-time lone developer, so if I want to ever have a hope of finishing my work, I have to scale back to the essentials. So why think about everything I can imagine?

  1. I like my imagination. It’s awesome, and using it is fun.
  2. The more I think about things, the better my ideas get.
  3. When I think complex, even if I don’t ever build the whole thing, I can at least create a design that will better accommodate further development later if I want to extend the basic implementation. I might do the extending, or someone else might do it later; it doesn’t matter. Building code as a foundation for future code is a good thing if you can manage to do it. Doing so correctly means avoiding having to repeat yourself in future projects.
  4. Even if I don’t have all the resources or talent that I might need in order to implement a design, having a good design documents makes it that much more likely to inspire others to contribute something to the project.

A very simple Options system might consist only of a single screen. But as we’ll soon see, there may be need to break things up into multiple screens, especially if we have many different options or categories of options.

If we have multiple screens, we’re going to need a means of navigating between them. This can be as simple as a group of rooms with room_goto commands linking them up, or it can be something else.

To design our Configuration Options system, we need to address a few things:

  1. Features: What options do we want the user to be able to configure? What choices do we want each configuration option to have?
  2. Interface/Controls: How do we want to present these options to the user? How will the user interact with the interface to set it?
  3. Implementation/Integration: How do these configuration choices get applied, technically? How will these configuration options interface with the game itself?

Features

Some of these will be fairly standard, common to many games, while some will be highly specific to the specifics of this game. I’ll address the standard ones, but don’t worry — once you see how we implement the standard features, it will be easy to set up config options for the features that are unique to your game.

You don’t need to support all of these options, but the following list is a good start for what you might want to consider:

Graphics

PC hardware very commonly has different graphical capabilities, due to differences in hardware, particularly the video card and monitor. While just about any video card is going to be capable of playing most Game Maker games at full quality, there is still the monitor to contend with.

Display settings

It might be easiest to force a specific display mode, but that’s not a flexible approach and may not work for all players. By far, it’s better to assume that the display mode the game starts up in is the player’s preferred (or only) graphics mode, and leave it as is.

If you want to enable everyone to play your game, it’s a good idea to give them some control over how the graphics of your game will be displayed on their screen.

The easier approach is to allow the player to set the display settings through the computer’s control panel, and just run in whatever mode the display is set to when the game runs.

More professional looking games usually offer the play an in-game configuration menu that allows them to change the same settings without having to leave the game program. It’s a convenience, to be sure, but it does keep the user in your game.

Keep in mind, too, that in GameMaker, there’s a distinction drawn between the Display (the physical hardware), the Window, the Room, and the View. Most of what you might think could be accomplished by forcing a specific display configuration can be better accomplished through Widow, Room, and View settings.

  • Fullscreen or Windowed mode?
  • Display resolution
  • Aspect ratio
  • Refresh rate
  • Color depth

Fullscreen or windowed mode?

Most games play best in fullscreen mode, but sometimes players like the option of playing inside a window, as it allows them to switch between other applications more easily. The downside of this is that it becomes all too easy to mouse outside of the game window, and lose focus. You can set the game to pause if the window loses focus, but this is still annoying disruption and can mess the player up even with pausing the game.

When to run in a window?  As a general rule, I like to develop and debug my game in windowed mode, since it’s easier for me to get at other windows that I’m working in. But for finished games, I usually like the game to run fullscreen. I want the game experience to be distraction-free.

That’s not always the case, though. Casual style games, pausable games, puzzle games, and turn-based games that wait on you to act are good candidates to have a Windowed mode as an option.

Resolution

These days, it’s probably not necessary to change the display resolution. Just about everyone uses LCD displays with fixed resolution. While these screens are capable of emulating other resolutions, they do not look as good when they do. Games for mobile devices of course will play on a device with a specific resolution that cannot be changed.

In any case, the game should never force a specific resolution on the player; you may want to offer the player controls to allow them to change the resolution for themselves within your game interface, though.

If a player wants to set a specific resolution, they can always just use the display settings control panel on their computer. If you want to provide an interface for this to them in your game, you can, but it’s a convenience or luxury feature, not a necessity. Supporting multiple resolutions means a lot of extra work and testing for a developer, so unless you’re a professional studio with the resources for this, it’s probably better to focus on supporting one resolution well.

Don’t worry about supporting every possible display size right away, the amount of work it takes to do it well will kill your project. Instead, focus on making the game as good as it can possibly be in one default resolution, and if your game ends up being popular enough to warrant it, you can build resources (primarily different sized rooms) to support other display resolutions better.

If you do change display resolution in the game, keep in mind a few things:

  • Always change it back when you’re done. Use display_reset() for this. Keep in mind if the game crashes, this doesn’t get called, though, and may leave the computer in a resolution the player doesn’t want. This can panic a non-technical user.
  • Don’t change display settings without first testing them. Use display_test_all() with the settings you’re about to set, before you actually set them. Be sure to have some fallback code that gracefully handles what takes place if the new settings don’t test OK.

You probably do want to know what resolution display the game is playing on, though. There are a lot of reasons to need to know this. Use display_get_width() and display_get_height() to detect the display resolution. Note this will return the current settings for the display, not what the display’s maximum or native resolution is.

You should decide the minimum display resolution you’ll support. GameMaker’s default room resolution is 640×480, which is the old VGA standard resolution. This is a very safe resolution to use, because just about any display will support it, but is also quite tiny these days. It’s still not a bad resolution to start out with, though. The smaller the minimum resolution you support, the more devices your game will run on.

It’s good to support larger resolutions, too, of course. Most people do have larger displays these days, and it’s desirable to utilize all that space effectively. Very large display resolutions can introduce performance issues, though, so test your framerates when running at maximum resolution, and make sure they’re acceptable.

If you’re targeting a specific mobile device, learn what its native resolution is, and use that. Read up on guidelines for Android and iOS development to learn the recommendations other developers follow.

To accomodate other resolutions, there are a variety of approaches. You can create a series of rooms and HUD graphics to provide a tailor-fit screen for every resolution you support. This is a lot of extra work, though. Scaling the game to fill the display can be an OK approach to take, and requires a lot less effort, but will result in a less attractive game with blurry edges due to the way the Game Maker runner handles scaling graphics. Another approach is to letterbox — draw the game in its standard resolution at a fixed 1:1 scale, and leave a black border around the edge of the screen, framing the game window. This can be good, too, but if you have too much black border it can be annoying.

Aspect Ratio

These days, you also have to consider aspect ratios, the ratio of the width and height of the screen. In the old days, computer monitors and TV sets in the United States all used 4:3.

Today, it’s a different story. On the desktop alone, people may have 4:3, 16:9, or 8:5 (16:10) displays. 16:9 is pretty quickly becoming the most common, particularly in 1920×1080 (1080p), and is also the ratio of HDTV, so if you have any desire to port your game to a game console, you may want to start out at 16:9.

And there are still others, albeit less common ones. If you’re planning on targeting a mobile platform, you’ve got even more possibilities.

If you’re building an HTML5 game, keep in mind that the browser window “chrome” (menus, toolbars, etc.) all take up space as well, which should be subtracted from the available display you have to run your game in, and this can change the effective “aspect ratio” of the web page unpredictably.

Enable/disable special effects which may affect performance (such as particles).

There are two main reasons for making these configurable: performance on slower machines, and user preference. Some players don’t like effects-heavy games, and prefer a sparse, cleaner visual experience without all the bells and whistles. Sometimes the screen can become so cluttered with particles that you can’t see the action, and it hurts your game rather than enhances it. So it’s nice to allow the player the option to not have these things in their game.

Refresh Rate and Color Depth

These settings are controllable in GameMaker, but there’s almost no reason for it. Most games shouldn’t have any need to mess with the color depth of the display.

In the 1990’s, it was more common to see variety here, but these days it’s pretty safe to assume that the computer will be running in 32-bit color mode. Oddball machines might be running in 16-bit or 24-bit color, and even more rarely you may encounter a display configured to run in 16-color or 256-color mode, but these are rare, and probably won’t have the necessary hardware to run a GameMaker game adequately anyway.

Refresh rate is probably also safe to leave alone. This setting is more pertinent to CRT displays, which are rapidly disappearing from the desktop computer landscape. Most LCD monitors use a 60Hz refresh rate, although there are LCD HDTVs that use 120Hz or 240Hz refresh rates. Older TVs used 30Hz.

Some people can notice a difference between refresh rates, and can tell you readily just by looking what refresh rate a monitor is using, especially if they are familiar with the display in question, but most people can’t, and don’t even think about such things if they’re even aware of them.

Some game developers will say that it’s a good idea to sync the room_speed of your game to the refresh rate. Keeping FPS and refresh in sync, or at least in a whole-number ratio, is not a bad idea. But the better way to do this is to set your room_speed to the display_get_frequency() or just assume a refresh rate of 60 and use a room_speed of 30 or 60. Keep in mind that regardless of what the room speed is set to, it’s fps that is the actual frame rate, and this usually fluctuates a bit.

Sound

  • Master volume
  • Music volume
  • Effects volume
  • Mute

Again, for the most part, these configuration options could be set by the user outside of the program, by using the volume knob on the speaker, or through the Sound and Volume control panel. But it’s a nice convenience to provide an interface to the user so they don’t have to leave your game to make adjustments. The nicest one is the separated music and effects volume. This will allow the player to adjust the mix to their taste.

One important thing to do is to remember the user’s preferred volume settings and automatically set them when the game runs, and set them back when the game exits.

The harder task will be to separate the volumes for the Master, Music, and Effects volume controls. Mute is actually very simple, and there are a few techniques that can be used. One way is to have a global variable called “mute”, and to set up a conditional before each and every sound function call. This is an inferior approach because it means you have to make sure you catch every single sound function call in all your code, whichis a pain to program. The other problem with it is that all those extra if (mute){} checks take processing power at runtime, albeit a tiny amount, it still adds up and could conceivably hurt performance.

The better way to handle mute is to simply setting the volume to 0. This is done with the sound_global_volume() function, which we also use for setting the master volume. The sounds still play, but at 0 volume, you don’t hear them. You don’t have to add code for every sound_play and sound_loop function in your code. And since the computer doesn’t have to ask every single time whether the game is muted or not, it’s a lot less processing. sound_global_volume(0) mutes your game, and sound_global_volume(1) restores the master volume to full. Use a global variable to store the setting for the master volume as well, so instead of restoring the volume to full blast on unmute, you set it back to the master volume value.
globalvar master_volume;
//master_volume is set in the config screen.
mute() {sound_global_volume = 0;}
unmute() {sound_global_volume = master_volume;}
You can put the Mute function into your game configuration menu, or you can make it more readily accessible to the player by creating a control for it that they can access while the game is playing.

Separate volume controls for bgm and sound effects will take a little more work, using the sound_volume() function to control the volume for each individual sound in your game, so we’ll cover that in detail later.

Note: GameMaker Studio 1.1 introduces an entirely new audio system. The above code samples work with the old system.

Controls

  • Provide the player with a screen showing the current settings, and allow them to set up their own custom settings.
  • Allow the player to save their custom settings as a profile, load from a profile, delete profiles.
  • Allow the player to reset the controls back to their default settings, or to select a custom profile (such as for different keyboard layouts, etc.).
  • Provide the player with options to use various input devices (keyboard? mouse? joystick/gamepad?)

Difficulty/Game Options

  • Difficulty (Easy/Normal/Hard)
  • Starting level
  • Enable/disable (or throttle) specific features
  • Number of lives
  • Text size/speed (if you’re displaying lots of dialogs)
  • etc.

This part is highly dependent upon your game. You can set up an interface to allow the user to set these things, but integrating them into your game will be highly dependent upon your game. Some things (number of lives, starting level) will be trivial to implement and integrate; others will take a great deal of design sense and playtesting.

High Scores/Achievements

An Achievements system, again, will be highly dependent on your game. But we can probably provide some abstractions that make it easier to implement your achievement system in a consistent way, such that the specifics may be different from game to game, but they way they are handled will be the same.

Some features we might like to see:

  • Record more than 10 high scores
  • Record other types of achievements
  • Record achievements per player account
  • Clear achievements
  • Upload scores/achievements to an online “Hall of Fame” server

Localization options

  • Language
  • Keyboard layout – keyboard layout could tie in well with the Controls. A user with a non-QWERTY keyboard could set that here, or have it be auto-detected from a system variable, and automatically update the keyboard controls with default keys appropriate to the layout map of the local keyboard. But then, the user should still be able to override these with their own preferences.

Save States/User Profiles

If your game stores user profiles or save state data, provide an interface to the user to do things with them. Common activities include:

  • Create new
  • Delete
  • Copy
  • Rename
  • Edit info (for user profile data, such as user name, password, and other profile data).

What’s in the save state file will be a bit beyond the scope of this series, and in any case should be highly dependent on your game. While I won’t tell you what to put in your savefile, I can tell you how to set up some file i/o functions that will enable you to read and write your savefile, and maybe some suggestions for how to protect this information, validate it, and format it.

It’s also a good idea to save the configuration settings themselves. Configuration settings (graphics, sounds, etc.) should be separate from game savestate data (My character’s name is XYZ, He is level N, his inventory consists of…, he has visited the following locations… he has achieved the following goals… etc.)

We have a few design choices for how we want to do the config save. The simplest would be to simply revert to defaults every time the game is launched (ie, not save anything, but remember a basic set of options that will definitely work on any system the game is run on.) From a user’s perspective, however, this would become annoying, as they will need to re-configure settings to their taste every time they quit the game. The next simplest approach would be to remember what the settings were the last time the user set them, and to remember the defaults in case the Last config profile gets corrupted.

This is probably as far as you really need to go; but once you are saving profiles, you’re not too far from allowing the player to save multiple configuration profiles, or per-user profiles. We’ll probably implement this later on as an advanced feature.

  • Other stuff

  • Network: These days, you may also want to have configurations options for network (TCP/IP settings, firewall/proxy server settings, etc.)
  • Social: Or you might want to have some kind of social networking features, such as sharing your game progress with your Facebook and Twitter friends, inviting friends to try out your game, or even send friends in-game items to help them, and so on.
  • Hall of Fame/Achievements: Or a “submit high score to server” feature. Or you might have a registration and payment screen.
  • Update Checker: Or a “check for updates/download/install” feature.

These things are much more complex to design and implement properly, and as such will be outside the scope of this tutorial for now, but it’s good to think about them!

Personally, I would like to see these type of features built in to Game Maker, and I hope that YoYoGames will incorporate features like this in time. When I say “built into Game Maker, I don’t  just mean having a library of available GML functions that one can use to build a configuration system out of. That is, after all, what we are going to do with this project. What I mean is, it would be nice if such a system existed as a ready-made component that you could just drop in to any project, and set up with just a few clicks or lines of code.

These features, and the interface the user will interact with to manage them, will be challenging and time-consuming to implement, and are not really “the game”. A good configuration system and interface is excellent polish for a professional-quality project. Game Maker’s purpose is to make game development easy by doing the hard technical stuff for you. So far, they’ve done that by focusing on the in-game building blocks that a designer would use to produce a play experience. Now that they’re turning Game Maker into a more professional tool, I hope that they’ll start thinking about including these kind of features, too.

Until then, we have to fend for ourselves. The above list of features represents a significant amount of work that we need to do. Setting up a system that is flexible enough to allow us to do this easily is no small task. If I’m lucky, by procrastinating long enough, I may find that they end up doing the work for me:) If I am going to do all this work, then I want to get the most return for that work that I possibly can by making a re-usable system that I can apply easily in any game. This means a de-coupled, generic system that can be adapted easily to a wide variety of projects. This is a good situation to create a Game Maker Extension (.gex). However, an extension will not give us a complete system — an Extension allows us to package a library of useful new GML functions that we write, but our built system will also need room, object, sprite, and sound resources, and a .gex cannot include those resources. Ultimately, this means that we may not be able to realize a dream of a drop-in system. But even providing better building blocks to create such a system would be better than nothing.

To begin, we’ll start small, and implement some basic things, and then iterate and refine our solution until we have something that hopefully works really well for a wide variety of games.

In our next article, we’ll discuss the code needed to make these configuration settings, as well as how to store and retrieve them.

csanyk.com © 2016
%d bloggers like this: