Wednesday, 15 August 2012

Thanks

That was it guys!

I have truly learned a lot while making this game in Game Maker, I hope you have as well! Keep in mind this was my first venture into programming and Game Maker in general. If I have done things inefficiently please forgive me. If you find any bugs I haven't come across, please let me know in the comments and I will take a look at them.

Now that you have programmed all these things don't forget to make a nice challenging level for you and your friends to play through!

Thanks for taking this educational programming journey with me! Good luck on your future programming and/or Game Maker endeavors!

Post 07 - Collect & Score

No platformer is complete without collectibles. In this post we will create a collectible object to be placed around the level, a HUD object which will count how many collectibles you have collected and a level exit which only becomes available after a set amount of collectibles have been collected.

THE COLLECTIBLE
We'll start by creating a new sprite and object voor the collectible. I've named mine obj_coin. This collectible will work slightly different then you might be used to. For it to be 'picked up' both players must have collision with it at the same time. Collision Events only allow collision with one object, so we make a Step Event in the collectible object and check for collision with code. If both players have collision with it, it will disappear. We use the following bit of code for this.

if collision_rectangle(x-8,y-8,x+8,y+8,obj_char,0,0) && collision_rectangle(x-8,y-8,x+8,y+8,obj_char2,0,0)
{
    instance_destroy();
}

The above collision code works like this. First you give the four corners of the collision rectangle, then the object you want to check collision with, and then if precise is true and if notme is true. We draw a collision rectangle as big as the coin (in relation to the center of the coin it  draws the collision rectangle from x-8 to y-8 to x+8 to y+8) and check for the player1 object. Precise and notme are false. We do the exact same for player2 object. Place some collectibles in your test level.

THE HUD
Next we want our amount of collected objects to be displayed somewhere. For this we make a new object which will serve as a hud. I made it 32x32 and made it look like the image below. It's basically my collectible object with an equal sign next to it. I called it obj_hud. Place the hud object in your test level. Preferably somewhere in one of the upper corners.


We are going to count the collected objects in the hud object. So let's start off by defining collect_counter in the Create Event of the hud object and settings it's value to 0.

collect_counter = 0;

Next we want the counter to go up by 1 every time both players collide with a collectible. To do this we edit the above piece of code like this.

if collision_rectangle(x-8,y-8,x+8,y+8,obj_char,0,0) && collision_rectangle(x-8,y-8,x+8,y+8,obj_char2,0,0)
{
    obj_hud.collect_counter += 1;
    instance_destroy();
}

Now it will add 1 to the collect_counter of the hud object before being destroyed.

Our next step is to be able to see how many collectibles we have collected. To do this we will use a draw function. Draw functions can only be used in Draw Events, so we'll make a draw event in the hud object. Once an object has a Draw Event, the object itself must be drawn within this event, or else it won't be visible in the game. So in the Draw Event we type the following code.

draw_sprite_ext(spr_hud,0,x,y,1,1,0,c_white,1);

draw_text(x+20,y-10,collect_counter);

The first bit of code draws the hud object. It is the same piece of code we have used before. The second piece draws how many collectibles we have collected at position x+20 and y-10 from the hud object position. This position worked best for the hud object I draw, it might work out differently with yours. Play around with it a bit until you have a nice position.

THE EXIT
Next up we'll create an exit object. Mine looks like a gate and is called obj_exit. Place the exit somewhere in the test level. We want this exit to appear whenever a set amount of collectibles have been gathered. In my case 5. To do this we make a Draw Event in the exit object and use the next piece of code.

if obj_hud.collect_counter >= 5
{
    draw_sprite_ext(spr_exit,0,x,y,1,1,0,c_white,1);
}

This code checks if the collect_counter has reached 5. If so, it draws the exit in the same way we have drawn objects before.

Lastly we want the exit to show a message and how many collectibles you have collected. We do this by creating a Step Event in the exit object and using the same collision check code as we used before with the collectibles. Except now the collision rectangles are bigger because my exit object is bigger.

if collision_rectangle(x-16,y-16,x+16,y+16,obj_char,0,0) && collision_rectangle(x-16,y-16,x+16,y+16,obj_char2,0,0)
{
    show_message("GOOD JOB! You have collected "+string(obj_hud.collect_counter)+" coins!");
    game_end(); 
}

So if there is collision with both players it will show the above message. What is typed in between quotes will be shown as text in the message. In the string we show the collectible counter. Afterwards the game will close.

Great! You now have the basics of a sneak/platform game!

ASSIGNMENT: COUNT PLAYER DEATHS
Find a way to count the death of both players and show this amount at the end message.

Take a look at how I did it (including assignment answers) here.

Post 6 - Hiding

The game we are building wouldn't be a sneaking game (which it will be) if you wouldn't be able to hide. And this is exactly what we are going to do in this post. We are giving the player a way to hide behind certain objects so he can pass by enemies undetected. To start off let's create an object the players can hide behind.

CREATING THE OBJECT
Create a new sprite and the same way we made the ducking sprite but give it another color. Then make an object and link the sprite to it. Don't forget your naming conventions! My objects to hide behind is a crate, so I've called it obj_crate.

SNAPPING BEHIND THE OBJECT
We want the player to be able to hide behind this object. We'll do this by writing a piece of code which ensures that whenever the player ducks while in collision with a crate he will snap to the precise position of the crate and will not be killed by the enemy or his sightcone.

Let's start with the position snapping. For this we write the following bit of code in the crates Collision Event with player1.

if (obj_char.sprite_index = spr_char_low)
{
    obj_char.x = x;
    obj_char.y = y;
}

If there is collision between player 1 and the crate the code checks if the player sprite is the ducking sprite, meaning the player is ducking. If this is true the players x and y position are made exactly the same as the crate's x and y position. 

NOT DYING WHILE HIDING
Now that we've got the snapping down let's code that the players don't die by the sightcone or enemy when hiding behind a crate. We do this by first creating hide_status in the player's Create Event.

hide_status = false;

Then we set hide_status to true whenever the player is hiding behind a crate by editing the above code.

if (obj_char.sprite_index = spr_char_low)
{
    obj_char.x = x;
    obj_char.y = y;

    obj_char.hide_status = true;  
}
else
{
    obj_char.hide_status = false;
}

Next we edit the code in the enemy Step Event which kills the player whenever he touches the sightcone and include a check to see if the player is hiding or not. So now it checks the collision line, player collision with scone and hiding status before deciding whether to kill the player or not.

if (collision_line(x,y,obj_char.x,obj_char.y, obj_tile,1,0) < 1 && scone.char_collision = true && obj_char.hide_status = false)
{
    obj_char.destroy = true;
    scone.char_collision = false;
}
scone.char_collision = false;

And finally we edit the enemy Collision Event with player 1 (the piece of code you had to write on the last post's assignment) to include a check if the player is hiding or not. 

if obj_char.hide_status = false
{
    obj_char.destroy = true;
}
else
{
    obj_char.destroy = false;
}

Whenever there is collision between the enemy and the player this code checks if the player is in hiding. If not the player is destroyed.

We now have a fully functioning hiding mechanic!

ASSIGNMENT: CAN'T HIDE BEHIND SAME CRATE
Let's make our hiding mechanic a tad fancier. By editing the code in the crate's collision event with the players, make it impossible for both players to hide behind the same crate. Don't forget to add all the functions you've written above to player2 as well!

Take a look at how I did it (including assignment answers) here.

Post 5 - Enemy Sightcone

Because we are creating a sneaking game one of the main goals will be to stay undetected. To indicate when you are in or out of an enemy's sight we create a sightcone which we will attach to the enemy.

CREATING THE CONE
Let's start by actually creating the sightcone the same way we created other sprites and objects. Create a new sprite and make it 120x48. Make it look like the image below. Give it precise collision checking and set it's origin at x = 0 and y = 24 (left/middle). Give it an appropriate name.


ATTACHING THE CONE
Once the cone is created we can attach it to the enemy. We do this in the enemy's Create Event with the following code.

scone =  instance_create(x,y,obj_scone);

We have created scone (sightcone) at the x and y of the enemy and used the sightcone object. The sightcone is now attached to the enemy but will not turn with the enemy yet. For this we need the following piece of code in the enemy's Step Event.

scone.image_xscale = enemy_direction;

scone.x = x;
scone.y = y-8;

Because we are referring to scone from the enemy object, we write scone. infront of our code. So when we write scone.image_xscale we are talking about the xscale of scone and not the xscale of the enemy itself.

The image_xscale is linked to enemy_direction. Meaning if the enemy walks to the right (direction = 1) the scale of the scone will be 1 (original image, facing right). If the enemy walks to the left (direction = -1) the scale will be -1 and the image will be mirrored, facing left.

The scone.x = x and scone.y = y-8 position the scone according to the enemy x and y. So now the scone will be drawn 8 pixels above the enemy center.

Next create an object for it with an appropriate name and link the sprite to the object. This sightcone is quite large and it can be quite annoying if objects in the level are hidden behind it. This is why we will give it an alpha of 0.5 so we can see partly through it. To do this we must add a Draw Event to the object. In this Draw Event we write the following piece of code which draws the cone from scratch and gives the ability to edit it in various ways.

draw_sprite_ext(spr_scone,0,x,y,image_xscale,1,0,c_white,0.5);

In the brackets the following aspects of the image must be given: which sprite must be drawn, which subimage of the sprite must be drawn, x position, y position, x scale, y scale, rotation, color and alpha. If the color is set to white it means it will be shown as its original color as given in the sprite editor.

MAKING IT KILL
To make it more interesting we'll have the sightcone kill players straight away once they touch it. To do this we must first add a Create Event to the sightcone object and type in the following code.

char_collision = false;

We have now created char_collision and defined it as false when the cone is created. Next we create a
Collision Event in the sightcone object with player1 as the collision target. In here we type the following code.

char_collision = true;

Now we go to the enemy object's Step Event. Here we will decide what will happen if collision between player and sightcone is true. We do this with the following code.

if scone.char_collision = true;
{
    obj_char.destroy = true;
    scone. char_collision  = false;
}

This code is quite simpel. If scone collides with player1, set player1's destroy value to true and set collision to false. This last bit is important, if you don't set it back to false after the player has died, he will keep on dying because the collision value will still be true. Player1 doesn't have a destroy value yet though. In the assignment below you will set that value and actually make the player die.

We now have a working collision code (if you take in account the assignment below) except for one problem. What if the player has collision with the sightcone but there is a tile in between the player and the enemy? This should mean the player should not be seen by the enemy shouldn't it? To do this we use the following code.

collision_line(x,y,obj_char.x,obj_char.y, obj_tile,1,0);

This piece of code draws an imaginary line between a given x and y to another given x and y and checks if it collides with a certain object. It also needs to know prec and notme. Prec means precise and refers to if you want to check the precise sprite or the bounding box. Notme refers to if you want to check the for the item in which you have set the code as well. In our case we use the enemy's x and y and player1's x and y, check for the tile object, check the precise sprite (1 = true) and do not check for the enemy object in which the code is made (0 = false).

Now we'll use this code in combination with our previous destroying code.

if collision_line(x,y,obj_char.x,obj_char.y, obj_tile,1,0) < 1 && scone.char_collision = true
{
    obj_char.destroy = true;
    scone.char_collision = false;
}
scone.char_collision = false;

This is the exact same as our code before except now it checks if the collision_line check is false. So if there is no collision with a tile and the collision line AND the player has collision with the sightcone, the player will be destroyed and the sightcone's player collision will be set to false. We have also added a second line which sets collision with player1 to false. This one is to prevent the following scenario: Player 1 is behind a tile. So there is no line of sight but there is collision. Because there is collision scone.char_collision is set to true. If the player would later move away when there is no longer collision but there is line of sight, the player will still be destroyed.

ASSIGNMENT: ACTUALLY DESTROYING PLAYERS
So we see the above code changes the destroy value in obj_char (player1) to true but obj_char does not actually have a destroy value yet. With the things you have learned create a destroy value for the player and make sure when it is true, the player respawns at his spawn location using the following code.

x = xstart;
y = ystart;

After that create a piece of code which destroys he player when he is touched by the enemy. Then Do everything you've done for player1 for player2 as well.

Take a look at how I did it (including assignment answers) here.

Post 4 - Enemy Behaviour

Next up we'll begin working on our enemy. To start off we will make him patrol between walls, meaning we want to our enemy to do the following:

  • Walk until there is collision with a tile in front of him
  • Short pause 
  • Turn around
  • Walk in opposite direction until there is collision with a tile in front of him
  • etc
MOVEMENT
First let's define our enemie's direction. Add a Create Event to the enemy object and write the following.

enemy_direction = 1;

We have now created enemy_direction and have given it the value 1. Now lets use it to make our enemy walk. Add a Step Event to the enemy object and use the enemy_direction as it's walking speed.

x += enemy_direction;

PATROLLING BEHAVIOUR
Next let's make the enemy stop, wait and turn around. For the wait we need a timer. We can create one in the Create Event.

wait_counter = 0;

We have now created wait_counter and set it to 0. We continue on the patrolling code by typing the following in the enemy's Step Event.

if enemy_direction = 1
{
    if !(place_free(x+1,y))
    {
        wait_counter += 1;
        x = xprevious;
        
        if wait_counter >= 30
        {
            enemy_direction = -1;
            wait_counter = 0;
        }        
    }
}

That's quite a bit of code in one go, and we're not even done yet. Let's go through what we just coded. First we check if the enemy_direction is 1 (to the right). As we have created our enemy_direction with value 1 in the Create Event, in the beginning this will always be true.

Next we check if there isn't any place free right in front of the enemy. If this is true (meaning there is a solid object in front of him) we add 1 to the wait_counter every frame and the enemies x position is changed to his last known x position. This means he has stopped moving.

Afterwards we check if the wait_counter has reached 30. If so, the enemy_direction changes to -1 and the wait_counter is reset to 0. (I run my game on 60 frames per second, meaning 30 frames = 0.5 seconds. You can change your framerate under the Settings tab in your Room)

So to recap: The enemy walks to the right, stops when he encounters a solid object in front of him, waits for 0.5 seconds and turns around. But we aren't finished yet. We haven't told the enemy what to do once he's turned around. Luckily it's not that hard anymore as we want him to do the exact same thing but in the opposite direction. So straight underneath the above code we write the following.

else
{
    if !(place_free(x-1,y))
    {
        wait_counter +=1;
        x = xprevious ;
        
        if wait_counter >= 30
        {
            enemy_direction = 1;
            wait_counter = 0;
        }
    }
}

This does the exact same as the previous piece of code except it checks whenever the direction is not 1 (else) and changes the direction to 1 after the counter has reached 0.5 seconds.

We now have patrolling enemies!

ASSIGNMENT: CHECK FOR GAPS, FASTER MOVEMENT, INCREASED WAIT TIME
Make sure that whenever the enemy encounters a gap in the ground, he also stops moving, waits and turns around. Also increase his movement speed by 3(without changing enemy_direction in the Create Event) and his wait time to 1.0 seconds.

Take a look at how I did it (including assignment answers) here.

Thursday, 2 August 2012

Post 3 - Sprite Index

In post 2 we gave our players basic movement options, going left, right and jumping. This post we'll add ducking to this list. I'm putting ducking in a separate post because making the players duck is a tad different compared to the other basic movements. Mind you, its quite easy.

SPRITE INDEX
When player1 ducks, we want him to be half the size of his original sprite. To do this we create a new sprite exactly the way we created the other sprites (See Post 1) except this time, when we are in the image editor, we make sure that we fill only the bottom half of the sprite (the sprite is 16x32, fill the bottom 16x16). Be sure you use the same color as before.

Now you have a second sprite which is half the size of the original sprite. Give the sprite a easy to remember name in line with the naming convention you used for the other sprites. My original sprite is called spr_char. My ducking sprite is called spr_char_low.

What we want is that player1 switches to the new sprite whenever he ducks. We do this with sprite_index. But first we'll use the following code learned in our previous post, assigning a key and checking if player1 is standing on a tile. Choose whichever key you want to use. I'll use the down arrow. We follow this with sprite_index which we assign to our newly made sprite. We do this in our player1's Step Event, in the same place where we wrote our previous movement code.

if keyboard_check(vk_down) && !(place_free(x,y+1))
{
    sprite_index = spr_char_low;
}
else
{
   sprite_index = spr_char;
}

Now you can duck and the place_free check ensures we can only duck when standing on a tile (not while jumping/falling etc). If we try this code out we see that we can still move around while ducking. To fix this we edit our movement code.

if (keyboard_check(vk_right) && place_free(x+walk_speed,y)) && sprite_index = spr_char
{
    x += walk_speed;
} 
 

Now we have included a check in the movement code which checks if the sprite of the player is the normal sprite meaning he can't move around if he's ducking because then he uses another sprite.

ASSIGNMENT: MAKE PLAYER2 DUCK
Do the same you did for player1 to player2 to get a hang of it. Also build in the sprite check for movement to the left and jumping.

Take a look at how I did it (including assignment answers) here.