Thursday, June 26, 2014

Creating A Simple RPG Part 1: Player Control And NPC

    It seems like it is not easy for me to post during school time... Anyway...

    During last few weeks I've been thinking about making an RPG-like game with Game Maker. I've started working on it and decided to put my progress here as I move forward. Here in this article I will tell you about how I made my character and NPC characters move around without overlapping walls. By the way, I know I can use tileset feature of Game Maker, but I was actually trying to make a random dungeon generator which requires tiles exist as individual objects, therefore no, I won't use that feature.

    First of all I would like to say that the tileset I'm using is pretty awesome and it should get its credit here. The set is created by DragonDePlatino from opengameart.org and is called DAWNLIKE 16x16 Universal Rogue-like Tileset V1.8. And as DragonDePlatino wished, I also credit DawnBringer for awesome color palette.

    Now I'm relieved... Let's start!

Floors (Things you can walk on) And Walls (Thou shall not pass! things)

    You might have seen it many times before if you have played indie RPG's like Ahriman's Prophecy before: sometimes there are some buggy-like tiles that player should not be able to pass over but somehow it does. For example there is a high cliff but player can go up like there is an invisible ladder. To avoid this first thing that comes on mind is putting an object that player cannot pass through. But this time player will not be able to walk on little things like carpets on the floor or tall grasses in the field etc. The main thing about making tiles passable and not making it look weird is creating some kind of layer structure. By layer structure I mean sorting everything by their vertical position and volume. There should be floor in the lowest layer, and a carpet above it, and the player that can freely move above it. There also should be a wall in the same layer as player so that player should not able to pass through it, and all above them there should be light effects and HUD's so that they won't get overlapped by tilesets as we move our character. To summarize:

0: Floor.
1: Thin things on the floor.
2: Player, walls, player-size objects, NPC's etc.
3: Lights and other effects.
4: HUD's (like lifebars, score, inventory etc.)
5: Message windows, menus...

    We have this kind of a layer feature in GM. It is called "Depth" and things get "upper" as depth "decreases". To begin with, we will create a character, give it a sprite and make it solid. Then as we said, give it a depth of "-2". This will make it 2 level higher than any other object in depth 0. Then create another object for floor, do not make it solid and give it a depth of 0. And another object for walls, make it solid and give a depth of -2. Then create a small room with your newly created objects, like this:

Animated Sprite?
    Yes! We can make an animated sprite. From the DAWNLIKE set, I chose a sprite, which has only 2 frames. To animate it, I simply used an alarm function.

    Inside the Create Event of character I put in a code:
image_speed = 0;
kicker = 0;
facing = 0;

    After I add an Alarm 1, and put this if command inside:
if (step=0) {step=1} else {step=0}
alarm[1] = 30;

    And inside Step Event of character:
image_index = step;

    This is just a simple clock system that in every 30 frames It changes step value, and changes image index as step value changes.

Let's Get Moving!
    There are several ways to make a character move, I will show you one of the shortest ways. But what I actually trying to do is making player stay in grid all the time. Like if I pressed UP and released, player should move upwards until it comes to another grid cell instead of stopping immediately. This way we can get rid of un-wanted situations, like "DAMN IT I CAN'T GET INTO THAT DAMN DOOR!". And also I want the character actually "move", not just jump to the desited position like in most rogue-like games.

    To begin with, we will need two local variables for our character. I named first one as facing, it will be used for testing which way the player is facing. And the second one as kicker, which will keep player moving until we release the button. Inside the Create event, put these variables with zero value:
facing = 0; kicker = 0;

Now add an event from Keyboard, and choose "<Any key>" from the list. Inside that, we will add a code with if command:

if (kicker = 0)
{

}

Inside the brackets, we will put the following code:

if (keyboard_check(vk_down)) and (place_free(x,y+16)) {vspeed = 1; alarm[0] = 16; facing=0;kicker=1;}
if (keyboard_check(vk_right)) and (place_free(x+16,y)) {hspeed = 1; alarm[0] = 16;facing=1;kicker=1;}
if (keyboard_check(vk_up)) and (place_free(x,y-16)) {vspeed = -1; alarm[0] = 16;  facing=2;kicker=1;}
if (keyboard_check(vk_left)) and (place_free(x-16,y)) {hspeed = -1; alarm[0] = 16;facing=3;kicker=1;}
if (keyboard_check(vk_down)) {alarm[0] = 16;facing=0;kicker=1;}
if (keyboard_check(vk_right))  {alarm[0] = 16;facing=1;kicker=1;}
if (keyboard_check(vk_up)) {alarm[0] = 16;facing=2;kicker=1;}
if (keyboard_check(vk_left))  {alarm[0] = 16;facing=3;kicker=1;}

    It looks a little complex but I will explain. As you see there are 2 different If structures within the code above. The first line directly checks if the pressed button was DOWN arrow. And not only that, It also checks if the place that is 16 pixel down the player is free. If these two conditions met, then it gives player a vertical speed of 1 for 16 frames, which will move player down for 16 pixels. Then sets an alarm for 16 frames, just when the player would be moved 16 pixels. After that it defines direction by giving it zero value. As you can see if player moves right, facing becomes 1, moving up makes 2 and left makes 3. And lastly there is a setting for kicker that makes it 1. When kicker is not zero, then none of the movement functions work, this way the player always moves 16 pixels each time.

    In the second type of if command, it again checks for the button pressed, but not for the empty places. This is just for making player change its facing if it cannot move. Turn back to the picture of my room and take a look at the dead-end at the top right corner. If we won't add another if command like this, then it would be impossible for the player to turn left or right in that corridor, because left and right side are not empty. This command will not give any speed to the player, but change it's facing direction.

   And lastly add an Alarm 0 event, and put these little code inside:
speed=0;
kicker = 0;

    This way each time we call the Alarm 0, player will stop moving and immediately be able to move again.

    Doing this, we also made another good trick too. The trick is that player will continuously move until we release the button, and it will always stop at the right place of the grid. Go ahead and try it!

What About Non-Player Character's?
    For an NPC, we will do the exact same thing as we did for character, except, we will make them move in every 160 frames instead of pressing buttons. Don't forget to make your NPC sprite a two-step one just like the player sprite! Now copy the player object and name the copy as NPC, then delete the Keyboard event for <Any key>. Inside the Create Event, put another alarm: Alarm[2] = 160;. Create an Alarm 2 event, and put the following code:

if (kicker = 0)
{
 to = choose(0,1,2,3);
 switch (to)
 {
  case 0: if (place_free(x,y+16))
  {vspeed = 1; alarm[0] = 16; facing=0;kicker=1;}
  else
  {alarm[0] = 16;facing=0;kicker=1;}
   break;
 case 1: if (place_free(x+16,y))
  {hspeed = 1; alarm[0] = 16;facing=1;kicker=1;}
  else
  {alarm[0] = 16;facing=1;kicker=1;}
  break;
 case 2: if (place_free(x,y-16))
  {vspeed = -1; alarm[0] = 16;  facing=2;kicker=1;}
  else
  {alarm[0] = 16;facing=2;kicker=1;}
  break;
 case 3: if (place_free(x-16,y))
  {hspeed = -1; alarm[0] = 16;facing=3;kicker=1;}
  else
  {alarm[0] = 16;facing=3;kicker=1;}
 break;
 }
}
alarm[2] = 160;

    This one again works with the same kicker stuff. To make it choose a direction randomly, I just put a choose function and assigned it to a variable called "to". Then added a switch command for to, and defined what to do for each to values with case statements. Each if statement inside the cases checks for if the desired to direction is empty, if empty it moves the NPC and changes its direction, if not it does not move the NPC but still changes the direction. And in the end it recalls Alarm 2 after 160 frames to choose another direction and move NPC again.

This way your NPC should be moving itself. And this is the end of this tutorial.



    I hope someone can make use of these series I am going to write during summer. Next time I will show you how to make simple message windows that can change it's place depending on players position.