/ Articles / Game Maker: Adventure Games (Zelda Example)

About This Blog

Yes, we are still very much alive!

This blog is a placeholder Gaming World's upcoming main site, GW6. The release date is still unknown even to us and this site is designed to introduce and keep you updated on what's happening in our community while the main site is being worked on.

Enjoy your stay at GW and register on the forums if you haven't done so already!

The Editors

ramirez (webmaster)

DragonSlayer (manager)

Sarevok

Raz

Wash Cycle

dicko

HL

crumply

Marcus

thecatamites

Murex Brandaris

Bakafura

PTizzle

Community | Games
Game Maker: Adventure Games (Zelda Example)
Game Maker: Adventure Games (Zelda Example)



Zelda Example

This example will illustrate basic adventure actions using zelda resources.

Contains multiple actions and other adventure aspects such as drawing life bars. Be sure to press F1 for advanced game information.
Source + Executable

Before you start, you should probably download the example above first. Now, adventure games combine aspects of several different genres into one. They can contain RPG elements, action elements, and even puzzle elements. Because of this, it's hard to explain the entire genre, but instead we will focus on the core adventure aspects.

Character Movement

In your game, typically the main character will have many actions they can perform. The most basic being movement. There are an incredible amount of ways to code such a thing, however I highly recommend you do this through the step event so that you can centralize your actions instead of having them in several key press / release events. To start off, let's look at the most basic kind of movement code:

Code:
1
2
3
4
if (keyboard_check(vk_left)) x-=2;
if (keyboard_check(vk_right)) x+=2;
if (keyboard_check(vk_up)) y-=2;
if (keyboard_check(vk_down)) y+=2;

If you were to put the above code in the step event of any object you would be able to move it using the arrow keys. However, there's a lot of problems with this code. While it does work, it lacks collision checking (this can be done using collision events, but many times you will want to use collision functions instead), and it won't allow things like running, terrain speeds, and more. So obviously you will need more advanced code to do this. The code in the example for movement is broken into two parts, but the main mechanism behind the movement is move_contact_solid(direction,maxdistance). As mentioned, there are many ways you can do this and choosing a suitable method is very important.

Animating

Your character can move, but he needs to animate. This can be hard or easy depending on how you decide to do it, which is why it's important to think up a good method from the start. For this example, I use a naming scheme for my sprites so that I can easily do this by simply assigning a few variables and it will automatically assign the sprite for me. Let's take a look at one of the names:

spr_0_b1

It may look strange, but all of this means something. Broken down, this is how it works:

spr_0_ = spr tells me it's a sprite, and 0 is not used in this example but allows for future modification. As is, if I wanted to have multiple main characters I could have a prefix of spr_1_ for a new character, and all I would need to do is change one line of code in my animation script.

b = This stands for an action, that is, whenever you do an action in the game it assigns a variable to a letter. b happens to stand for boomerang.

1 = The direction. In GM, you will often use commands which involve degrees. As we all know, degrees range from 0 to 360. The default angle (0) is right, and goes counter-clockwise (270 would be down). So what does one mean? Instead of writing the angles, I decide to divide them by 90. So 0 is right, 1 is up, 2 is left, and 3 is down. There's a lot of reasons for this, but this is how the naming system works.

So to use this same naming system again, spr_0_j2 (J means jump in this game) would be of the character jumping while facing up. Given this, once I have my variables assigned all I need to do is call this line of code:

Code:
1
execute_string("sprite_index=spr_0_" + string(action) + string(dir/90));

And I get the correct sprite when I do any action. The variable action will be the single letter I mentioned earlier, and as you can see I divide my degrees by 90 to produce smaller numbers (a reason I won't explain so as not to confuse you even further!).

Items / Attacks / Actions

There are many attacks and actions in the example, but let's look at something farily simple: jumping. When you press the jump key, the character sprite is changed to the jumping sprite and plays the jumping animation. Once the jumping animation is over, it resets back to normal movement. However, we need to make jumping do something in game code. If you look at the example game map, you will see hole in the ground. obj_player has a collision event with these holes (obj_hole) with the following code:

Code:
1
2
3
if (action='j) exit;
x=xstart;
y=ystart;

Like mentioned, J stands for jump, which means as long as my action is J I am in the air according to the game. As you can see, it's as simple as exiting the event if I want my character to be able to "jump" over holes instead of being reset to the start. Some actions are even simpler, and just require you to create an object at the correct location and to give the newly created object simple properties. The arrow and bomb both work like this.

Collisions


The red boxes above illustrate what the collision boxes are set to by default. It's the maximum size of the sprite on all sides. You can manually set this, but if your character has multiple sprites, you would need to assign the same boundary values across all sprite to replicate what a collision mask does.

Since I am dealing with a lot of sprites, it's best to have something called a collision mask for my main character. This is because by default your sprites will use collision boundaries the maximum size of the sprite, which leads to different boundary sizes across all sprites. This can cause problems, because if this changes when you are near something like a wall, you'll get stuck inside if it switched to a sprite with a bigger collision area. This way, it uses the same collision box across all sprites for that one object. In the example, this is spr_0_mask.

Things To Do

It would take a very long time to talk about all the elements in this example, but these topics should get you started. The example contains only core adventure aspects, so there are many things for you to do yourself and learn. You can try making the weapons effect your own objects, such as bombs destroying walls, or making enemies that can only be damaged by particular weapons. Keep building onto the game with additions and modifications and not only will you see actual progress but you will better understand how GM works.

SCRIPT OF THE WEEK:

Script: sView(viewx,viewy,easing);
Code:
Code:
1
2
3
4
5
6
7
8
//Syntax: Arg0=View X center, Arg1=View Y center, Arg2=Easing (optional, higher number means longer delay)
if (argument2=0) easing=1; else easing=argument2;
view_xview[0]+=(round(argument0-(view_wview[0]/2)) - view_xview[0]) / easing;
view_yview[0]+=(round(argument1-(view_hview[0]/2)) - view_yview[0]) / easing;
if (view_xview[0]<0) view_xview[0]=0;
if (view_xview[0]+view_wview[0]>room_width) view_xview[0]=room_width-view_wview[0];
if (view_yview[0]<0) view_yview[0]=0;
if (view_yview[0]+view_hview[0]>room_height) view_yview[0]=room_height-view_hview[0];
Example: sView(x,y);
Description: In the room editor you can specify a bounding for an objects that the view to follow, but you need to tailor these settings exactly to make it so that the view is always on center with your object. Instead what you can do is use sView() in the end step event of an object, and supply the x and y to center on the screen. You can also supply easing which delays the camera following the player (higher is a greater delay). To add this as a script, simply create a new script called sView, then copy and paste the code into it.
Posted on June 29, 2008