RPG Character Movement – Part 2

June 21st, 2008

This tutorial is continuing from last one posted here: RPG Character Movement – Part 1

In the last tutorial we talked about basic movement and key detection. This time we are going to cover Hero walking animation and collision detection.

First, lets begin opening our Hero movie clip set in the Library. Highlight both the left and right circled arms and convert it to a movie clip. Give the movie clip the name “arms” since this movie clip are the arms of our Hero. In the “arms” movie clip, create a tween for each of the circled arms, moving them back and forth of each other to represent the moving arms of our Hero when walking.

Next, lets create a new movie clip called “Level”. When creating the movie clip, make sure to click on “Export for Actionscript” because this will be called by Actionscript to be placed on the stage. For now, just place a large transparent rectangle with borders inside the movie clip. Also, make sure you have set the large rectangle to be in the top left. The border will represent our drawing space and boundaries of the level. The rectangle I made is 920×680. You may also draw several small blue spots in the middle of the rectangle to serve as the water of in our level.

Lastly, lets create one more movie clip called “gold”, but don’t have it export for Actionscript. With this movie clip, just create a simple golden circle (about 15 x 15) within the movie clip, then place the “gold” movie clip inside the movie clip “Level” we just created and give it an instance name of “gold”. Here is where I placed my movie clip gold inside the movie clip level:

Finally, lets start typing some code. First, lets quickly add an object level which will hold our “Level” movie clip when we export it from the Library. Right have we created the hero object, add these two little lines:

[as]
var level:Object = new Object();
level.mc = new Level();
[/as]

With these two lines we are declaring a new object called level, and with that object we are adding a property mc (short for movie clip) and adding the movie clip “Level” to it from the Library.

Then in the function game_begin, lets add our level to the game’s movie clip. Right after we added the game.mc to the stage, and before we added hero.mc to the game’s movie clip, lets add the level movie clip. Here is what you should have in the function game_begin (note, line 8 is all we added from the original):

[as]
// begins the game by setting up the mc’s
function game_begin():void {

// add game mc to stage
addChild(game.mc);

// add level to game.mc
game.mc.addChild(level.mc);

// add hero to game.mc, place in center of game screen
game.mc.addChild(hero.mc);
hero.mc.x = game.sw / 2;
hero.mc.y = game.sh / 2;

// add event listeners
stage.addEventListener(Event.ENTER_FRAME, game_loop);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keys_down);
stage.addEventListener(KeyboardEvent.KEY_UP, keys_up);

}
[/as]

If we test the movie out, we will see again our Hero in the middle of stage with a brand new level! If you walk around the stage/level, you will notice two things: 1) The stage isn’t centering our Hero as he walks around the level, and 2) Our Hero is walking on water. So lets create two more functions that will handle these 2 little problems. First, lets create the center function. This function will keep our Hero centered in the middle of stage as we walk around our level. Add this function before the hero_move function:

[as]
// center in middle of screen
function center(mc:MovieClip):void {

// movie clip we want to move, the root
var root_mc:MovieClip = MovieClip(root);

// fix for x
if(mc.x > (game.sw / 2)) {
if(mc.x < (level.mc.width - (game.sw / 2))) { root_mc.x = (game.sw / 2) - mc.x; } else { root_mc.x = -(level.mc.width - game.sw); } } else { root_mc.x = 0; } // fix for y if(mc.y > (game.sh / 2)) {
if(mc.y < (level.mc.height - (game.sh / 2))) { root_mc.y = (game.sh / 2) - mc.y; } else { root_mc.y = -(level.mc.height - game.sh); } } else { root_mc.y = 0; } } [/as] 2. Create the function center that requires a movie clip so the function knows what to center in the middle of the stage
5. Declare a local variable root_mc which will be the movie clip we are going to be move around and about to center around the passed movie clip (in which case, will be our Hero)
8. This will check if the mc.x position/value is greater than half the game.sw (if you recall, this is holding the stage width value). So if you have the stage width as 600, and the mc.x value is greater than 300 (the stage width divided by 2), it will continue to lines 9 – 13; else, it will set the root_mc.x value to 0. In another words, we don’t want to too center our Hero if its going to show the “out of bounds” area of our level to the far left.
9. This is the same as above but applies if our mc.x position/value is too far right. Again, our stage width is 600 and the level’s width is 1200, and our Hero is at 900 or less, it will continue to line 10; else it will set the root_mc.x to be at the far right hand side of the stage, not showing the “out of bounds” area.
19 – 27. The same rule applies but for vertical movement along the y position/value
28. Close the function center

The last function we are going to create is hit_test, which will detect if we have hit any of the levels colored area. Place this function right after the center function:

[as]
// hit test between two movie clips. if flag is true, return true or false if we hit the movie clip. if flag is false, just move mc1 out of mc2’s way
function hit_test(mc1:MovieClip, mc2:MovieClip, flag:Boolean = true) {

// make a new point
mc1.point = new Point(mc1.x, mc1.y);

// left
while(mc2.hitTestPoint((mc1.point.x + (mc1.width / 2)), mc1.point.y, true)) {
if(flag) {
return true;
} else {
mc1.x–;
mc1.point.x–;
}
}
// right
while(mc2.hitTestPoint((mc1.point.x – (mc1.width / 2)), mc1.point.y, true)) {
if(flag) {
return true;
} else {
mc1.x++;
mc1.point.x++;
}
}
// top
while(mc2.hitTestPoint(mc1.point.x, (mc1.point.y + (mc1.height / 2)), true)) {
if(flag) {
return true;
} else {
mc1.y–;
mc1.point.y–;
}
}
// bottom
while(mc2.hitTestPoint(mc1.point.x, (mc1.point.y – (mc1.height / 2)), true)) {
if(flag) {
return true;
} else {
mc1.y++;
mc1.point.y++;
}
}

// if we made it down here, then its false
if(flag) {
return false;
}

}
[/as]

Looks like a lot of code for a function, buts its very repetitive.

2. Create the function hit_test that accepts three parameters. The first two parameters are the two movie clips which we are doing the hit test. The last parameter is a return parameter if set to true. If this parameter is set to true, then it will return either true or false when we test a collision between two movie clips. If this parameter is set to false, it will force the mc1 to stay out of the bounds of mc2. For example, our hero can not step in the water. This parameter is set to true by default.
5. We create a property point to our mc1 by creating a new Point setting it its x and y values to that of the mc1 position.
8. This while loop will check a hitTestPoint detection between the two movie clips we are hit testing. If the statement returns true, AND we have our third parameter set to true (as explained above), it will break the while loop and return true; else it will update the mc1.x and mc1.point values minus 1 each time (moving it to the far left hand side of the mc2) until it isn’t colliding with mc2.
17 – 42. We are doing the same testing as mentioned above but for the other three corners.
45 – 47. If the script made it all the way to the bottom, and the third parameter was set to true, we return false so we know that we didn’t hit any other movie clip.
49. Close the function hit_test

Next we have to update our function hero_move to do the walking animation and collision detection with the level. So at the end of the function hero_move, add these lines of code:

[as]
// arm animation when walking
if(left || right || up || down) {
hero.mc.arms.play();
} else {
hero.mc.arms.gotoAndStop(1);
}

// pickup any gold
if(hit_test(hero.mc, level.mc.gold)) {
level.mc.removeChild(level.mc.gold);
}

// collision detection with level mc, we set the third parameter to false so when don’t get a return Boolean value
hit_test(hero.mc, level.mc, false);
[/as]

2 – 3. If either the left, or right, or up, or down values declared earlier in the function are true, we have in the Hero movie clip play the movie clip “arms”. This will look like our Hero is walking
4 – 5. Else, have the arms stop on frame 1, which should be our standing still pose
9. We are doing using our hit_test function to check if we collided with the movie clip “gold” in our level movie clip.
10. If our Hero hit the movie clip gold, then we remove it from the level.mc. We will of course create an inventory in the future, but for now I just wanted to demonstrate the functionality of our hit_test function.
14. We use the hit_test function again but this time setting the third parameter as false. This way, when our Hero hits the water or the border in our level movie clip, it will not walk on top it. Instead it will be walking along the side of it.

Finally (what a long tut!), add this one line inside of the function game_loop, after we execute the hero_move function:

[as]
center(hero.mc);
[/as]

Here is what you should have at the end:

Download the file here.

AS3 > Games > RPG

7 Responses to “RPG Character Movement – Part 2”

  1. Tyler Says:

    Firstly, thank you so much for this tutorial. It provides the exact irregular collision detection I need without the need for some complicated trigonometry method I find everywhere else. I don’t know if you can help me, but I’m trying to use your function on my character. He is seen from more of a 3d angle, so instead of the collision detection on his whole body, I need to isolate it just to his feet? Any ideas how I could do that?

  2. Gregory Says:

    Hmmm…. to be honest, off hand I don’t have the exact answer. That requires a bit more calculation (but not too much). But here is a possible quick solution (I haven’t tested this however).

    You can try having a MovieClip placed at the bottom of heros feet and do it that way. For example, you have a Hero MovieClip, then inside that you have a “real_hit” MovieClip. Do you hit_test function with the “real_hit” MovieClip instead of the Hero MovieClip.

    Since we are on the topic of Isometric, I would like to mention and recommend as3isolib. This framework is fairly new, easy, and going to be very “great solution” for isometric games done in flash in the near future. It doesn’t provide any collision detection at the moment, but its on the to-do-list. So watch for it.

  3. Tyler Says:

    Awesome resource: as3isolib. Thank you. I’ve needed something to help me accomplish 3D but I don’t need to get as in depth as using papervision. Just something simple. Thanks. I also tried what you said. It’s strange. I didn’t return an error, it just didn’t work. There was no longer any collision. I took your function and tried testing a movie clip in a movie clip against another movie clip on the stage. —- hit_test(character.characterFeet, level.mc, false); But for some reason it doesn’t do anything. I’m going to recreate the code. Maybe there is a flaw somewhere.

  4. Ricky Says:

    Just wondering.. why do you keep your FPS in your source code at 60? Is that standard for RPGs? The reason I noticed is because when I made my code, it ran a bit slower than yours.. and I scratched my head.. till I saw the FPS. Please respond.. thanks!

  5. Gregory Says:

    @Ricky
    It depends on the kind of RPG/game you are making. Is it turn based or real time, etc. I prefer to keep my games 30+ FPS, or in this case 60 FPS. The slower the FPS the slower it runs… simple as that.

  6. Marin Petkov Says:

    What do you do if you want the level to start with the character being at a specific position, lets say by the gold nugget?

  7. chrisvvolf Says:

    awesome , thanks mate. This is awesome just what a newbie like me needs:)^^
    Keep up the good work :)!!!!