How To Make A Simple Platform Game In Flash Part 3: Creating The Player

Go back to How To Make A Simple Platform Game In Flash

The Playstate

As you may have realized already, MenusState will call the class PlayState when you press "X", so we will need to build our class PlayState. The Playstate class is where all game objects are added, and to some extent, controlled. This state will load, hold and control all the assets we need for the game - sound, notes, sprites, objects, blocks, etc., and to maintain their position and behavior.

Before creating this wonderful class file we should create content to add.

The first two parts that we need to create to add to our PlayState: 1) Tilemap are for floors and walls, and 2) an object Player to jump on them.


The Player

For our tutorial, and probably for most games created with flixel, you'll be dealing with "sprites". Essentially, a sprite is simply an object that will be on display for the player to interact with in some way. The little guy as the player moves and jumps and collects things is the sprite Player. The enemies that the player can collide with the sprites are too. For most, Sprites act independently of each other (except in case of collision), but we need to define how each type of sprite move, look, and interact with the game

To create our class player, we first want to create graphics for our players. Open your "Image Editor (Photoshop, Gimp, etc.) and create a new document that is 128 × 16 pixels. Our player is not going to be a graphic 128 pixels wide screen - it will really be 16 × 16, however, flixel it is very easy to create animations by splitting pictures into squares equal in size. We will create 8 frames of animation of the players.

Lets break that image so we can see what we have in fact:

When this image is loaded into flixel, it will be assigned a number for each block of 16 × 16, from 0 - you can see below:

* Frame 0 will be our standard, "_idle_" graphic for our player.
* When the player is running, we'll play frames 0, 1, 2 and 3 in succession.
* When the player is jumping, we'll show you the image 2.
* When the player is to attack, we will play the frames 4, 5 and 6 in succession.
* When the player is getting hurt, we will play part 2 and 7.
* Finally, when the player dies, we'll see box 7.

If you wish, you can create your own graphics for your player who has more or less, just make sure you have frames to use for all the animations above.

If you look in your project directory, under "data", you will probably see a lot of files. These are all fashion, the game demo that comes with flixel. You can delete all these files, since we do our own files for this tutorial.

Add the sprite graphics in this folder on your drive ("data") as "Player.png"


Creation Of The Class File Player.as

Now that we have our player sprite, we need to construct an object to contain all our information on players. This will be the class Player.

The class player will serve many purposes. We will create a class that contains in itself all the things that makes the reader: health, animations, movement, etc. This will give us much control over how the player sprite behaves and is controlled.

1. Start by creating a new class file in the com\Tutorial, name it "Player.as"

2. Import flixel's stuff:
import org.flixel.*;

3. Extend this class as FlxSprite class:
public class Player extends FlxSprite

4. Embed the player's sprite:
[Embed(source = '../../org/flixel/data/Player.png')] private var ImgPlayer:Class;

5. Define the variables and constants for later use in the code:
private var _move_speed:int = 400;
private var _jump_power:int = 800;   
private var _max_health:int = 10;

This counter is used as delay to ‘flash’ the player for a few seconds after they get hurt:
private var _hurt_counter:Number = 0;

6.Then, let's setup the constructor of this class so that we should be able to pass the X and Y coordinates where the player should spawn initially. So,modify it to:
public function Player(X:Number,Y:Number):void

7. Then immediately in the first line inside the constructor, let's call the constructor of the FlxSprite class that this class is extended from:
super(X, Y);
loadGraphic(ImgPlayer, true, true, 16, 16);

If you look FlxSprite in the flixel documentation, we pass the x and y coordinates for this sprite. Then we use loadGraphic to load the image for this sprite, and the "animated” and “reverse” set to True.

8.Then, let's initialize our player with the necessary information. The FlxSprite class already have predefined variables to be used:
//Max speeds
maxVelocity.x = 200;
maxVelocity.y = 200;
//Set the player health
health = 10;
//Gravity
acceleration.y = 420;            
//Friction
drag.x = 300;
//bounding box tweaks
width = 8;
height = 14;
offset.x = 4;
offset.y = 2;

During the creation of Player object in the game, we’re passing the X, Y coordinates to where we want the Player to spawn on the screen, and then setting all of these variables to these initial values before the Player can start doing anything else.

The following were set: the speed of the player, the initial health points of the player, the amount of gravity and the amount of friction on the player. The bounding box tweaks is to shrink the limits of the sprite to better fit the size of the graphic so that it looks better when it collides with other objects.

9. Then, let's setup the sprite’s animations. We will use the flixel function called "addAnimation" for this.

Going back to our player’s graphic, we created a layout so that each box has it's own image for specific movements of the player:
addAnimation("normal", [0, 1, 2, 3], 10);
addAnimation("jump", [2]);
addAnimation("attack", [4,5,6],10);
addAnimation("stopped", [0]);
addAnimation("hurt", [2,7],10);
addAnimation("dead", [7,7,7],5);

10: Now lastly to be added on the constructor, set “facing” to True, which sets the player to be facing right initially:
facing = true;

11. Next is to override the FlxSprite “update” function. Let's add a new function update():
override public function update():void
{
}

We will do the ff. inside the update() function:

* Test to see if the player is dead or still alive.
* Check if any keys are pressed by the player (left,right,jump), and do the necessary action on the corresponding keys.
* Update the animation based on the player’s present status.


12. Therefore, to test if the player is dead or still alive:
if(dead)
{
    if(finished) exists = false;
else
    super.update();
    return;
}

FlxSprite contains “dead” variable that is set to true whenever the sprite’s health becomes 0. This check is simply saying:
If this sprite (the player) is “dead” or true, it test to see if it’s done animating. If so then, it set this sprite’s "exists" variable to false, and remove it from the game. If this sprite is still animating, it proceeds to call the FlxSprite update function and will exit this function without going through the rest of the code.


13:.Then, if the player past through the if statement then the player is still alive. If the player gets hits by the enemy, here are the few things to occur: player decrements or loses a health point, and it will be invincible for a few moment so that the player can't get hurt simultaneously.

The _hurt_counter variable is used to figure out if the player could be ‘invincible’ or not. Then, when the player gets hits from enemy, the _hurt_counter will be set to 1. It just simply reduce the time left of the counter:
if (_hurt_counter > 0)
{
    _hurt_counter -= FlxG.elapsed*3;
}

Note:

Notice the use of FlxG.elapsed * 3 instead of subtracting directly to _hurt_counter.
This is because FlxG.elapsed is a variable that provides REAL time in seconds (or fractions of seconds) since the last FULL GAME FRAME.
This is used to keep the delay accurate for almost every computer no matter the speed since the calculation is time based rather than a constant.


14. Obtaining the keyboard input is an important part of the Player object. The Flixel class FlxG handles this setting the following boolean variables to true or false which represents pressed or not pressed:

* FlxG.keys.LEFT (for left arrow key)
* FlxG.keys.RIGHT (for right arrow key)
* FlxG.keys.UP (for up arrow key)
* FlxG.keys.DOWN (for down arrow key)

There is another function that tests if a key is just pressed only once:

* FlxG.keys.justPressed("X")

Note:
If you want to learn more or modify the keys you will need to check the FlxGame Class, and observe it’s onKeyUp and onKeyDown functions to check how the assignment of keys in Flixel.

Now, let's use these functions to control the player's movement.

15. Test if the left arrow or right arrow is pressed and then set the direction where the sprite is facing using facing. Then apply the movement on the X axis by setting velocity.x.

The velocity.x value will set how much displacement along the x axis when the FlxSprite is updated. The use of _move_speed * FlxG.elapsed to set the velocity.x is to make sure that it is accurate for any computers.
if(FlxG.keys.LEFT)
{
    facing = LEFT;
    velocity.x -= _move_speed * FlxG.elapsed;
}
else if (FlxG.keys.RIGHT)
{
    facing = RIGHT;
    velocity.x += _move_speed * FlxG.elapsed;                
}

16. Now this is how we jump the player:
if (FlxG.keys.justPressed("X") && velocity.y == 0)
{
    velocity.y = -_jump_power;
}

17. This code will handle the animations. In flixel, we just need to tell which sprite to “play” the animation.
if (_hurt_counter > 0)
{
    play("hurt");
}
else            
{
    if (velocity.y != 0)
    {
        play("jump");
    }
    else
    {
        if (velocity.x == 0)
        {
            play("stopped");
        }
        else
        {
            play("normal");
        }
    }
}

18: Then, call update() function of the FlxSprite Class that the Player class extends to update position and animation by using super.update() :
super.update();

19. The hurt() function is a public function in the FlxSprite class and we want to change it so we need to override it and add our new changes. Just simply add the _hurt_counter = 1 if the player’s hurt function is called. Also, just call the Flixel Sprite’s hurt() function that we are overriding so we still include any processing from there.
override public function hurt(Damage:Number):void
{
_hurt_counter = 1;
return super.hurt(Damage);
}


That's all for Player.as for now.

Here's the complete code of Player.as:
package com.Tutorial 
{
    /**
     * ...
     * @author pisces_eyes
     */
    
    import org.flixel.*;
    
    public class Player extends FlxSprite
    {
        [Embed(source = '../../org/flixel/data/Player.png')] private var ImgPlayer:Class;
        
        private var _move_speed:int = 400;
        private var _jump_power:int = 800;   
        private var _max_health:int = 10;
        
        public var _hurt_counter:Number = 0;
        
        public function Player(X:Number,Y:Number):void
        {
            super(X, Y);
            
            loadGraphic(ImgPlayer, true, true, 16, 16); 
            
            //Max speeds
            maxVelocity.x = 100;
            maxVelocity.y = 200;
            
            //Set the player health
            health = 10;
            
            //Gravity
            acceleration.y = 420;
            
            //Friction
            drag.x = 300;
            
            //bounding box tweaks
            width = 8;
            height = 14;
            offset.x = 4;
            offset.y = 2;
            
            addAnimation("normal", [0, 1, 2, 3], 10);
            addAnimation("jump", [2]);
            addAnimation("attack", [4, 5, 6],10);
            addAnimation("stopped", [0]);
            addAnimation("hurt", [2, 7], 10);
            addAnimation("dead", [7, 7, 7], 5);
            
            facing = RIGHT;
            
        }
        
        override public function update():void
        {
            if(dead)
            {
                if(finished) exists = false;
                else
                    super.update();
                return;
            }
            
            if (_hurt_counter > 0)
            {
                _hurt_counter -= FlxG.elapsed*3;
            }
            
            if(FlxG.keys.LEFT)
            {
                facing = LEFT;
                velocity.x -= _move_speed * FlxG.elapsed;
            }
            else if (FlxG.keys.RIGHT)
            {
                facing = RIGHT;
                velocity.x += _move_speed * FlxG.elapsed;                
            }
            
            if (FlxG.keys.justPressed("X") && velocity.y == 0)
            {
                velocity.y = -_jump_power;
            }
            
            if (_hurt_counter > 0)
            {
                play("hurt");
            }
            else            
            {
                if (velocity.y != 0)
                {
                    play("jump");
                }
                else
                {
                    if (velocity.x == 0)
                    {
                        play("stopped");
                    }
                    else
                    {
                       play("normal");
                    }
                }
            }
            
            super.update();            
            
        }
        
        override public function hurt(Damage:Number):void
        {
            _hurt_counter = 1;
            return super.hurt(Damage);
        }       
        
    }

}

On the next part we will add the floors and walls by using Tilemap.

Go back to How To Make A Simple Platform Game In Flash

No comments: