Game Maker Courses02 Mar 2008 06:45 pm

Making a text / keyboard menu

This tutorial allows you to create a text / keyboard menu. This is the technique used in swords and sorcery to have the user select options using the keyboard.

I didn’t want players to use the mouse in that game to add to the « oldie » effect.

  1. Drawing text on the screen.

GM requires that you draw text and images on the screen using the draw event. That means all scripts that post text or images on the screen have to be called by an object’s draw event.

Game Maker object properties form.

In the example, the script write_monsters draws the text in the combat menu.

The combat feature being text driven, when combat starts the player is directed to a new room dedicated to fighting. This way everything in the room originates from the controler object (read more about those in the founding principles tutorial).

This is important because text is only drawn for one step, so there are only two ways to make text stay on the screen as long as you like :

  1. Preventing the step from finishing by interrupting it (more on specific commands below).
  2. Having the text re-drawn at each step, for as long as you want it there. Associating it to a controler object’s draw event works for that because those are redrawn at each step. Then all you need is a variable to determine whether or not that same text is to be written again or not.


Commands for drawing text and images are the following :

- draw_text (x position, y position, ‘your text here’)

- draw_sprite (x position, y position, sprite_name)

o The sprite has to be uploaded in the sprite folder.

Adding a sprite to the game.

Making the text stay on the screen.

As mentionned above, one of the ways to prevent your text from disappearing as the game moves on to the next step is to prevent it from doing that. Use the following command :

keyboard_wait()

This freezes the game until the player strikes a key.

But that’s not enough. For it to make sense, your game has to react different ways according to what key is pressed. That means first recording what key the player did press.

keyboard_lastkey() returns exactly that.

Using an if statement and variables you can determine what happens next, depending on what key was pressed.


Here is an extract from the weapons store script in Swords and Sorcery :

// buying weapons

if (global.buying =11)

{

draw_text (350,400,’(1)’);

draw_text (350,435,’(2)’);

draw_text (350,470,’(3)’);

draw_text (350,505,’(4)’);

draw_text (350,540,’(5)’);

draw_text (400,400,global.itemname[1]);

draw_text (400,435,global.itemname[2]);

draw_text (400,470,global.itemname[3]);

draw_text (400,505,global.itemname[4]);

draw_text (400,540,global.itemname[5]);

draw_text (600,400,global.itembuyfor[1]);

draw_text (600,435,global.itembuyfor[2]);

draw_text (600,470,global.itembuyfor[3]);

draw_text (600,505,global.itembuyfor[4]);

draw_text (600,540,global.itembuyfor[5]);

draw_text (700,400,’gold’);

draw_text (700,435,’gold’);

draw_text (700,470,’gold’);

draw_text (700,505,’gold’);

draw_text (700,540,’gold’);

draw_text (100,710,’Available gold:’);

draw_text (300,710,global.gold);

draw_text (100,745,’(X)Back.’);

io_clear ();

screen_refresh();

keyboard_wait();

if (keyboard_lastkey = ord (’1′)) global.itembought = 1 else

if (keyboard_lastkey = ord (’2′)) global.itembought = 2 else

if (keyboard_lastkey = ord (’3′)) global.itembought = 3 else

if (keyboard_lastkey = ord (’4′)) global.itembought = 4 else

if (keyboard_lastkey = ord (’5′)) global.itembought = 5 else

if (keyboard_lastkey = ord (’6′)) global.itembought = 12 else

if (keyboard_lastkey = vk_numpad1) global.itembought = 1 else

if (keyboard_lastkey = vk_numpad2) global.itembought = 2 else

if (keyboard_lastkey = vk_numpad3) global.itembought = 3 else

if (keyboard_lastkey = vk_numpad4) global.itembought = 4 else

if (keyboard_lastkey = vk_numpad5) global.itembought = 5 else

if (keyboard_lastkey = vk_numpad6) global.itembought = 12 else

if (keyboard_lastkey = ord (’X'))

{

global.buying = 1;

exit;

}


There are several variables involved here.

- global.buying determines what the player is currently doing in the store, which depends on previously picked options. In this case the choice he made previously led him to the buying weapons menu which lists the items available, their price (global.intembuyfor[item id), and how much gold the player has (global.available gold).

- global.itembought records what item the player bought according to the choice he made. If player hit ‘3’ when asked what he wishes to buy, then he has chosen item 3, which corresponds to a large club according to the item list script.

o He is then asked to confirm and if he does, the item is added to his inventory by calling a script dedicated to doing that (add_to_inv()). The price is subtracted from his available gold.

Here is the end of that script’s section for information :

if (global.itembought = 0) exit;

if (global.itembought >0 && global.gold >= global.itembuyfor[global.itembought])

{

draw_text (350,635,’Confirm y/n’);

io_clear ();

screen_refresh();

keyboard_wait();

if (keyboard_lastkey = ord (’Y'))

{

global.newitem = global.itembought;

add_to_inv();

if global.packfull = 0

{

draw_text (350,670,’OK’);

global.gold -= global.itembuyfor [global.itembought];

global.newitempacked=0;

} else

{

draw_text (350,670,’No space left to pack!’);

global.packfull=0;

}

screen_refresh();

sleep (1000);

exit;

} else exit;

} else

{

if (global.itembought =0) exit;

if (global.gold < global.itembuyfor [global.itembought])

draw_text (350,670,’Cant afford it!’);

screen_refresh ();

sleep (1000);

exit;

}

}

If the player hits the wrong key, then itembought is still at 0. That causes nothing, the step finishes, and all text would disappear if it weren’t for the following way to keep text on the screen from one step to the next.

NOTE : screen_refresh is necessary each time new text is to be added to the screen (like « can’t afford it ! » or the menu items written at the beginning.)

Using controller objects to keep text on the screen.

When a step ends, the room starts over, and objects that were added to the room are drawn again.

Using a controler object that doesn’t show on the screen because it has no sprite associated to it ensures that your store script is read again at the next step, during the controler’s draw event.

If the player hits the wrong key, then the variables haven’t changed and your store text is re-drawn just like during the previous step.

To make this easier, my store and combat screens are in separate rooms. When the player enters a store or is engaged in combat, I use room_goto (room #) to transfer to a different room.

This isn’t necessary for in-game options such as « There is a cave opening. Enter ? ». Those are triggered by the player being in a specific place so it will appear again if the player returns to that specific place.

Those are done by creating instances when the conditions for the question appearing are met.

Example :

if (global.posx=7 && global.posy=15) instance_create(100,100,inn_obj);

If the player is at the coordinates (7,15) on the map then the inn_obj is created. That launches a new draw event, to which I will have associated a script asking if the player wants to enter the inn (the sript works for all stores and special events so it checks the coordinates again to act accordingly).

From there if he says yes, he is sent to the adequate room.

I systematically destroy objects I no longer need to prevent objects to pile on each other, severely slowing down the game, using the position_destroy (i,j)command. All script generating objects are created at the same place to make it simple.

This should enable you to add text menus to your games, even if they are nothing like swords and sorcery.

Game Maker Courses24 Feb 2008 10:01 am

Loops and Arrays

When I read here and there it seems that loops freak people out. They shouldn’t. Running a loop in your game is actually quite simple.

Actually they make things a lot simpler and can save you lots of time.

Let’s take for example an automapping feature (yes, like the one in Swords and Sorcery)

Automapping means that when a player visits an area, the game automatically uncovers that area in the game map. To do this, a variable (actually, an array) is used to say whether each point on the map has been seen or not.

Each time the player moves, a command in a script says that at the current location, the area has been seen by the player.

if (global.currentmap = 1) global.seenmap1 [global.posx, global.posy] = 1;

There is a line like the one above for each map in the game (there are 30) that is run each time the player moves forward (and changes location).

The array global.seenmap1 can be read as follows : Seenmap1 = 1 (is true) for the current value of global.posx and global.posy (the current location).

For map 2, I simply replace seenmap1 by seenmap2 and currentmap =1 by =2.

When the player types (M) to see the map, a loop is used to check for each combination of global.posx and global.posx that exists in the map and if it is seen, it draws the walls and terrain that exist on that spot.

The loop uses « for » and goes as follows :

if (global.currentmap = 1)

{

for (i=5; i<=21; i+=1)

{

for (j=5; j<=21; j+=1)

{

if (global.seenmap1 [i,j] = 1) overmapread();

}

}

}

i checks the pos.x coordinates. For i=5 it will check each value of j (being placed where it is in i,j it corresponds to the posy coordinate) between 5 and 21 by applying :

if (global.seenmap1 [i,j] = 1) overmapread().

If the if () condition is met, then the spot was seen and can be drawn. overmapread() checks what elements are on that spot to draw on the map (grass, forest, water, …) and draws them.

Then it will repeat for i=6, 7… and so on so as to check and accordingly draw each point that was seen on the map.

You’ve now seen how () work. Let’s look at how it’s structured.

for (i=5; i<=21; i+=1)

{

do this ;

do that ;

also do this while you’re at it.

}

It works a lot like the if statement does, only it needs a bit more information to work. It needs :

- a loop start value (i=5),

- when to end the loop (when i reaches 21)

- and how it gets from 5 to 21 (i+=1, meaning by adding 1 each time). If that was 5, it would apply the instructions in the brackets for i=5, then i=10, then i=15, then i=20.

You also should understand the workings of arrays. A two dimensional array :

global.seenmap1 [global.posx, global.posy] = 1

can be read as follows : global.seenmap1 for global.posx and global.posy = 1. A different way to say it then it was above but it says the same thing.

One dimensional arrays are even simpler. They are the same with just one argument.

Example : global.monsterstrength [1]=20 ;

This one is read as follows : the monster strength for monster #1 is equal to 20. Then you can use global.monsterstrength [1] in a function to help calculate the damage done to your character by monster #1 in a fight.

Other details :

I use posx, not x, because x is a built-in variable that always corresponds to the associated object’s location on a map. This doesn’t apply to Swords and Sorcery but I didn’t want to risk confusion so I used that instead (same goes for y).

In global.monsterstrength global means that the variable or array will be made available from any script or function in the game. The variable name without global means it will only be stored for the use of the current script.


Game Maker Courses21 Feb 2008 08:27 am

Game Maker basic commands (if statement)

Once you’ve understood :

- GM is event based,

- How to launch the first event,

- Associating actions to events

You can launch your scripts (select an object and the control tab to the right, scripts are in the code section under questions and others) and not end up asking yourself why there’s just a blank screen. You’re ready to try some basic coding.

The point of this tutorial is to teach you how the most basic work. Later tutorials will apply basics to concrete examples.

If :

This command’s basic articulation is as follows :

If something is true

Do this

An if command can litterally contain pages of code. Something is true can actually be a combination of many things.

When two or more things need to all be true for the condition to be true. In this case they are associated by &&, which means and. Sometimes if just one of the listed conditions is true then they are associated by ||, which means or.

There can even be a combination of both.

Do this is typically even longer.

So as to contain the condition that is checked and what needs to be done if it is true, the if command can be written as follows :

If ((something is true && somethat is true) || someotherthing is true)

{

Do this ;

Do that ;

Do this as well while you’re at it ;

}

each command line finishes with a semi-colon ( ;).

Sometimes you’ll want the program to do things if the condition is true, and other things if it is false. That’s where else comes in.


If ((something is true && somethat is true) || someotherthing is true)

{

Do this ;

Do that ;

Do this as well while you’re at it ;

} else

{

do this other thing ;

do that other thing ;

}

Here is an example from the Swords & Sorcery code (this runs the search function and determines if something is found when the player searches the area Peregrine is in):

if (global.options_disabled=0)

{

// essential loots

if (global.posx=16 && global.posy=17 && global.questlog[170] = 1 && global.currentmap=17)

{

checkitems();

if global.itemcheck=0 global.loot_for_quest = 1050;

}

if (global.posx=11 && global.posy=13 && global.questlog[70] = 1 && global.currentmap=7)

{

checkitems();

if global.itemcheck=0 global.loot_for_quest = 1100;

}

if (global.posx=12 && global.posy=13 && global.questlog[80] = 7 && global.currentmap=22)

{

checkitems();

if global.itemcheck=0 global.loot_for_quest = 1010;

}

if (global.posx=17 && global.posy=12 && global.questlog[300] = 2 && global.currentmap=30)

{

checkitems();

if global.itemcheck=0 global.loot_for_quest = 1040;

}

// **********************************

if (global.newgold > 0 || global.loot1>0 || global.loot2>0 || global.loot_for_quest <> 0)

{

screen_redraw();

if (global.newgold > 0)

// manage gold found

{

draw_text (350,585 + findspot*35, global.newgold);

draw_text (450,585,’GOLD!’);

screen_refresh ();

sleep (1000);

findspot+=1;

}

global.gold = global.gold + global.newgold;

global.newgold = 0;

// ****

if (global.loot1=0 && global.loot2=0 && global.loot_for_quest = 0)

{

global.lootfoundroll = random (1000);

if (global.lootfoundroll <= global.lootprob1)

{

global.lootpos1 = random (1000);

if (global.loot1=0 && global.loot2=0 && global.loot_for_quest = 0) for (i=1; i<=40; i+=1)

{

if (global.lootpos1 >= global.itemlootfloor [i] && global.lootpos1 < global.itemlootfloor [i+1])

global.loot1 = i;

}

if global.loot1 = 0 global.loot1 = 1;

}

}

if (global.loot_for_quest <> 0) global.loot1= global.loot_for_quest;

if (global.characterpackedspot6[1] <> 0 && global.loot1<>0)

{

draw_text (350,585 + findspot*35, ‘No space left for:’);

draw_text (650,585 + findspot*35, global.itemname [global.loot1]);

screen_refresh();

sleep(500);

findspot+=1;

} else if (global.loot1>0)

{

draw_text (350,585 + findspot*35, global.itemname [global.loot1]);

screen_refresh();

sleep(500);

findspot +=1;

global.newitem=global.loot1;

add_to_inv();

global.loot1=0;

global.loot_for_quest = 0;

}

if (global.loot1=0 && global.loot2=0 && global.canloot=1)

{

global.lootfoundroll2 = random (1000);

if (global.lootfoundroll2 <= global.lootprob2)

{

global.lootpos2 = random (1000);

if (global.loot1=0 && global.loot2=0) for (i=1; i<=40; i+=1)

{

if (global.lootpos2 >= global.itemlootfloor [i] && global.lootpos2 < global.itemlootfloor [i+1])

global.loot2 = i;

}

if global.loot2 = 0 global.loot2 = 1;

}

}

if (global.characterpackedspot6[1] <> 0 && global.loot2<>0)

{

draw_text (350,585 + findspot*35, ‘No space left for:’);

draw_text (650,585 + findspot*35, global.itemname [global.loot2]);

screen_refresh();

sleep(500);

findspot+=1;

} else if global.loot2<>0

{

draw_text (350,585 + findspot*35, global.itemname [global.loot2]);

screen_refresh();

sleep(500);

findspot+=1;

global.newitem=global.loot2;

add_to_inv();

global.loot2=0;

}

} else if (global.newgold = 0 && global.loot1=0 && global.loot2=0)

{

draw_text (350,585,’Nothing!’);

screen_refresh();

sleep(1500);

}

global.lootfoundroll=0;

global.lootfoundroll2=0;

findspot=0;

}

The entire script will only be done if global.options_disabled=0. Nothing happens if that’s not true. I added that because I didn’t want people to search while a game character was talking to them, in which case I assign global.options_disabled=1 when it starts and put it back to 0 when it ends.

You see that whether something is true is often determined by a simple math function like global.variable > 0. Then Do this is a list of commands like drawing text, assigning values to other variables…

Now you should fully understand if an dits full potential.

You also understand the limits of GM’s drag and drop. Scripts like this are actually much more easily built by writing them out.

You’re ready to go on to another command, or try to build your first GM game further.

Have fun !

P.S. Don’t be discouraged if you can’t make everything out in the example. There is good reason for this. It’s complicated and it took me quite a while to debug it. Also, it is linked to other scripts.

The game has 163 scripts ! Some are simple and lots aren’t

Game Maker Courses20 Feb 2008 08:08 am

Introduction to Game Maker – Events and actions.

The idea of this post is to give you a quick introduction to Game Maker’s founding principle: how the overall “game loop” runs. Details on components such as sprites will be easier to understand once this is. (You’ll find the latest version of GM on www.yoyogames.com BTW).

Games always follow a game loop. In game maker, each time the created game completes a loop, this is referred to as a “step” in the documentation. When a step is done, the loop has been completely run, and another step is about to begin.
You can set how long a step lasts in the room settings (the default value is 30 steps per seconds, which means that if uninterrupted, the step lasts 1/30th of a second).

During each step, one or several “events” may occur. An object may be created and drawn, or a previously created object may be drawn again. Game Maker being “event driven”, this is extremely important to understand.

You’ll see in the interface that objects have associated events, which in turn have associated actions. The first event that is necessarily associated to an object is its creation. Once it is created, until it is destroyed or disabled, the game will check it for further events at each step.

illustration of the GM object properties interface.

If it is visible, it will systematically be redrawn at each step. This is the draw event.

The draw event’s action by default is to draw the associated sprite at the object’s current coordinate. This means that unless you add a draw event and associate it to your own list of actions, the game will just draw the sprite you assigned to the object.

Let’s take an example. The object is a monster, and at some point, if the player does well, that monster is going to die some way or other. As it dies, the sprite on the screen should change in some way. That means that just drawing one same sprite no matter what isn’t going to cut it. That object will require that you associate actions to the draw event. Actions in this case will be:

  1. Draw the normal sprite if it has not collided with a bullet.
  2. Draw the dying sprite if it has collided with a bullet, then destroy the object.
  3. If the same object has multiple instances (several copies of the same monster pop in the room, like the ghosts in Pacman), then that object’s specific instance will be destroyed and not the others.
  4. Other events can occur after the object is created. The player can press a key (CTRL or SPACE), a pressed key can be released… Those are just the examples in the screenshot.
  5. The list of events that can be included to trigger actions appear when you click on the Add event button.
  6. The list of actions that can be associated to each of them is spread accross the move, main1, main2, control, score, extra and draw tabs to the right.

This is just an example that illustrates the founding principle of Game Maker: Events and actions. Everything starts there. Knowing this should save you the first couple hours I wasted trying to learn how to get my first image to appear on the screen.

Weblog19 Feb 2008 07:27 am

Makingvideogames.com has been around for a while now, and in a continuing effort to share game creation and learn more myself each day, I am starting my very own weblog!

First I’ll be playing around with the features, then you’ll start seeing new articles on past and upcoming games, and on how you can easily learn to make your own hit titles!