Skip to content

Seifer Tim's Tutorial for Flixel v1.25 Part 2

Haledire edited this page Sep 13, 2010 · 2 revisions

Flixel Basic Platformer Game Tutorial

by SeiferTim & DarthLupi

Contents:

PART 1
I. Introduction II. Getting Started III. Creating the Menu
i. MenuState
PART 2
IV. Getting Ready for the Playstate
i. Player
ii. Tilemaps – Creating the Game World
iii. Setting up the Playstate
V. Adding the Enemy to the Game VI. Fighting the Enemies
i. Make the Weapon
ii. Equip the Player
iii. Update the PlayState
PART 3
VII. The Enemies Fight Back! VIII. Keeping Score
i. Health
IX. Spawning Enemies

Note: This tutorial is not for the current version of Flixel . The purpose of this older version is to familiarize you with the concepts of Flixel. If you want to follow this tutorial exactly, please make sure you have the 1.25 version of Flixel extracted. For a more recent version of this tutorial, please go here .

IV: Getting Ready for the PlayState

As you now know, the MenusState will call the PlayState class when you press ‘X’, so we’re going to need to build our PlayState class. The Playstate class is where all of the game objects are added, and to some degree, controlled. This State is going to load, hold, and control all of the assets we need for the game – sound, scores, sprites, blocks objects etc, as well as maintain their position and behaviors.

Before we create this wonderful class file, we should create some content to add to it.

The first two pieces we will need to create to add to our PlayState are a Tilemap for the floors and walls, and a Player object to jump around on them.

Back to Contents

The Player

For our Tutorial, and probably for most games made with Flixel, you’re going to be dealing with “Sprites”. Essentially, a sprite is simply an object that will be on the screen for the player to interact with in some way. The little guy that the player moves and jumps and collects stuff is the Player sprite. Enemies that the Player can collide with are also sprites. For the most part, Sprites behave independently of one another (except when colliding), but we have to define how each type of sprite will move, look, and interact with the game.

To create our Player Class, we want to first create the graphics for our Player. Open up your “Image Editor”(Photoshop, Gimp, etc.), and create a new document that is 128 × 16 pixels. Our Player is not going to be a 128 pixel wide graphic on the screen – he’s only really going to be 16 × 16, however, Flixel makes it very easy to create animations by splitting up images into equal-size squares. We’re going to create 8 frames of our player’s animation.

You can learn and practice making sprites, but its more of an art than a science. Here’s the graphic Darthlupi created for this tutorial, feel free to use it or copy it:

!Lets break down this image so we can see what we actually have:

When this graphic is going to be loaded into Flixel, it will be assigned a number for each 16×16 block, starting at 0 – which you can see above.

  • Frame 0 is going to be our standard, "_idle_ " graphic for our Player.
  • When the player is running, we will play frames 0, 1, 2, and 3 in succession.
  • When the player is jumping, we’ll show frame 2.
  • When the player is attacking, we’ll play frames 4, 5, and 6 in succession.
  • When the player is getting hurt, we’ll play frame 2 then 7.
  • Finally, when the player dies, we’ll show frame 7.

If you want to, you can make your own graphic for your player that has more or less frames, just make sure you have frames to use for all of the above animations.

If you look in your project’s directory, under "**data** ", you’ll probably see a lot of files there. Those are all from Mode , the demo game that comes with Flixel . You can safely remove all of those files, since we’ll be making all of our own files for this Tutorial.

Save your player’s sprite graphic to this folder ("**data** ") as "**Player.png** "

Creating the Player Class File

Now that we have our Player Graphic , we need to build an object to contain all of our Player’s information. This will be the Player Class.

The Player Class is going to serve a couple of purposes. We’re going to create a Class that contains within it all of the stuff that makes up the player: Health, animations, movement, etc. This will give us lots of control over how the player’s sprite behaves and is controlled.

1. Start by creating a new Class file in the com\Tutorial directory, name it “Player.as

2. Import Flixel ’s stuff:

import com.adamatomic.flixel.*;

3. Tell this Class to extend the FlxSprite class:

extends FlxSprite

4. We’re only going to embed one file right now:

[Embed(source='../../data/Player.png')] private var ImgPlayer:Class;

5. Next we need to define some variables. We want to set some constants to be used later on in the code:

private var _move_speed:int = 400;
private var _jump_power:int = 800;   
private var _max_health:int=10;

And we also want to set a counter which we will use to ‘flash’ the player for a few seconds after they get hurt:

private var _hurt_counter:Number = 0;

6. Next, we need to setup the constructor for this class. When we create a new Player object we want to be able to pass the X and Y coordinates where the player should start. So, change

public function Player()

to say

public function Player(X:Number,Y:Number):void

7. On the first line inside the constructor, we need to call the constructor of the FlxSprite class this class is extended from:

super(ImgPlayer, X, Y, true, true);

If you look up the definition of FlxSprite in the documentation, you’ll see that we’re passing the image to use for this sprite, the x and y coordinates for this sprite, and we’re setting “animated” and “reverse” to True.

8. Next, we’re going to initialize our player with a bunch of information. The FlxSprite class is already setup with a lot of variables that we can use for various things:

//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;

Now, we’re adding bunch of stuff at once. Basically, what we’re saying is this: when we create a Player object in our game, we’re going to send it the X, Y coordinates where we want the Player to start on the screen, and then we’re going to se all of these variables to these specific values instantaneously and before the Player can do anything else.
We’re setting the speed of the player first, then the starting health of the player. Next the strength of gravity on the player, and the amount of friction. The Bounding box tweaks sort of shrink the boundaries of the sprite to better fit the size of the graphic so that if it collides with a wall, it will look better.

9. Next we want to setup our sprite’s animations_. We’re going to use a Flixel Function called "_addAnimation" to do this.

Remember when we were creating our player’s graphic, and we made a layout for which frame would be used for what? Well, here’s where we define them:

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: Finally, we’ll set “facing” to True, which says that the player is facing to the right:

facing = true;

and that’s it for the constructor.

11. Now we need to override the FlxSprite “update” function. After the constructor, we’ll add a new function with this code:

override public function update():void
{
}

Inside this function, we’re going to do a couple of things in order:

  • Check to see if the player is dead or not dead.
  • Figure out if any buttons are being pressed by the player (left,right,jump), and act on them.
  • Change animation based on player’s current status .

12. So, to check if the player is dead or not is simple:

            if(dead)
            {
                if(finished) exists = false;
                else
                    super.update();
                return;
            }

FlxSprite has a “dead” variable which is true when the sprite’s health is brought to 0. All this check is saying is:
If this sprite (the player) is marked as “dead”, check to see if it’s finished animating. If it is done animating, set this sprite’s ‘exists’ property to false, which will remove it from the game. If the sprite is still animating, simply call the FlxSprite update function, and in either case, exit this function without going through the rest of the code.

13:.Next, we know that if the player got past the first if statement that the player is still alive. When the player gets hurt by an enemy, we want a few things to happen: we want him to lose a health point, and then we want him to be invincible for a few seconds so that the player can get their bearing.

Our _hurt_counter variable is going to be used to find out if the player should be ‘invincible’ or not. Later on, when the player gets hurt, the _hurt_counter is going to be set to 1. Here we want to simply reduce the amount of time left on the counter:

            if (_hurt_counter > 0)
            {
                _hurt_counter -= FlxG.elapsed*3;
            }
Important:
Notice instead of simply subtracting from *_hurtcounter* we are using some funky FlxG.elapsed3* thing. Why is that you ask? FlxG.elapsed is a variable that represents REAL time in seconds ( or fractions of a second ) since the last FULL GAME FRAME. We do this to keep everything that needs to remain accurate for every computer, no matter how fast, since it is time based.

If we simply subtracted 0.1 from hurt_counter that would happen much faster for a very fast computer than a slower one because the faster computer. We multiple FlxG.elapsed * 3 because we want the timer to go down a bit faster. It is still accurate since still using the amount of time since the last frame!

14. Getting keyboard input is a very important part of the Player object. The Flixel class FlxG handles this for you by either setting the following boolean variables to true or false for pressed or not pressed:

  • FlxG.kLeft (for left arrow key)
  • FlxG.kRight (for right arrow key)
  • FlxG.kUp (for up arrow key)
  • FlxG.kDown (for down arrow key)
  • FlxG.A (for the X key)
  • FlxG.B (for the C key)

There is also a function that checks to see if a key has just been pressed this once:

FlxG.justPressed(FlxG.B)
Programming Tip:
If you want to dig in deeper or change the keys you will need to look at the FlxGame Class, and look at it’s onKeyUp and onKeyDown functions to see how they assignment of keys works in Flixel .

Now that you know which variables to check for to see if you are pressing a key, let’s use it to move the player about.

15. First we check to see if the left arrow or right arrow is pressed and we set the direction the sprite is facing using facing. Then we set the movement on the X axis using velocity.x .

The variable velocity.x represents how much to move along the x axis when the FlxSprite is updated. Notice that we use _move_speed * FlxG.elapsed again when set the velocity.x . Since we are again doing a calculation that adds over time, we are using the FlxG.elapsed variable make sure we are using the time since the last frame was drawn to keep it accurate for all computers.

            if(FlxG.kLeft)
            {
                facing = false;
                velocity.x -= _move_speed * FlxG.elapsed;
            }
            else if (FlxG.kRight)
            {
                facing = true;
                velocity.x += _move_speed * FlxG.elapsed;                
            }

16. What is a platform game without jumping?

            if(FlxG.justPressed(FlxG.A) && velocity.y == 0)
            {
                velocity.y = -_jump_power;
            }

17. Next, we want to handle Animations. In Flixel , all we need to do is tell the sprite to “play” the animation we named in the constructor depending on what’s going on:

            if (_hurt_counter > 0)
            {
                play("hurt");
            }
            else            
            {
                if (velocity.y != 0)
                {
                    play("jump");
                }
                else
                {
                    if (velocity.x == 0)
                    {
                        play("stopped");
                    }
                    else
                    {
                       play("normal");
                    }
                }
            }

18: Next we need to call update() on the FlxSprite Class that Player extends to update position and animation.

            super.update();
        }

19. The hurt function is a public function in the FlxSprite class that we want to change a little, so here we override it and add our changes. We are simply adding the _hurt_counter = 1 in the event the player’s hurt function called. Also, we call the Flixel Sprite’s hurt function that we are overriding so we don’t exlude anything that is in there.

        override public function hurt(Damage:Number):void
        {
            _hurt_counter = 1;
            return super.hurt(Damage);
        }       
    }

That’s it! That is the player object, for now!

Your code should look like this:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;

    public class Player extends FlxSprite
    {
        [Embed(source='../../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;
        private var _hurt_counter:Number = 0;

        public function Player(X:Number,Y:Number):void
        {
            super(ImgPlayer, X, Y, true, true);//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;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);
        }

        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.kLeft)
            {
                facing = false;
                velocity.x -= _move_speed * FlxG.elapsed;
            }
            else if (FlxG.kRight)
            {
                facing = true;
                velocity.x += _move_speed * FlxG.elapsed;                
            }
            if(FlxG.justPressed(FlxG.A) && 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);
        }       
    }
}

Back to Contents

Tilemaps – Creating the Game World

Now we need something for the player to stand on. Make a new image document that’s 48×16 Pixels. We’re going to keep it simple and just make 2 different 16 × 16 block sprites_:
!http://timsworld.nfshost.com/images/tut
pics/Tiles.png!

You might not realize it, but the first tile we want to keep ‘blank’.
The first tile is not going to collide with sprites, but the rest are. In the future, you can make some neat effects by having more than 1 non-colliding tile .

Tiles work similarly to the Player’s graphics: every square block will be assigned a number, starting at 0. You can make as many tiles as you want. Save this file as “Tiles.png” in the data directory.

Next, we’re going to create our Tilemap. There’s already a great tutorial on how to do this here . Using a tilemap is a simple way to create a map that looks the way you want. When you setup your map, we’re going to want to make it twice as wide and tall as the game screen_, so make sure you define the tile size as 16 × 16 and that you want your map to be 40 × 30 tiles in size.
!http://timsworld.nfshost.com/images/tut_pics/mappy_new
map.png!

Once you start your new map, Import your tiles.png file, and go wild! Just make sure that there are no gaps along the edge of the map – we don’t want anywhere where the player can leave the map.

Once you finish making your map, save it, and then you can choose “Custom→Export Flixel Tilemap”. Name it “map.txt” and save it to your src\data directory. When it asks for a number to adjust the tiles by, leave it at “-1”.

And there you go! You now have everything ready to start setting up the PlayState!

Back to Contents

Setting up the PlayState

The PlayState is going to be our State that says: “The player is currently playing the game”, ie, they’re not on the Menu Screen, etc.

1. You should already have the PlayState.as file in your com\Tutorial directory, open it up now.

2. First, import the Flixel library right after the initial package declaration:

import com.adamatomic.flixel.*;

3. Extend PlayState by adding

extends FlxState

to the end of the public class declaration.

4. Next, we need to Embed the assets we want to use in the state. In our case we want to embed 2 files: the Tiles.png and the Map.txt . In a new row after the Class declaration add:

[Embed(source = '../../data/Tiles.png')] private var ImgTiles:Class;
[Embed(source = '../../data/map.txt', mimeType = "application/octet-stream")] private var DataMap:Class;

What this does is actually include a file into the finished SWF file that’s created, while assigning it a name so it can be accessed programmatically. We can do this for images, sounds, and much more later on.

5. We also need to define 2 of the objects that are going to live in our PlayState : the Player and the map . Flixel has a class called FlxTilemap which lets you pass it a map (like the one we created with Mappy), and some other parameters and then draws them on the screen. When we define a variable to hold the FlxTilemap, we can also do some other things (like test collision) with that Object . The Player object is going to be a Player class , which we created earlier. In a new row right beneath the Embeds, add:

private var _p:Player;
private var _map:FlxTilemap;

6. Finally, we’re going to define 3 FlxLayers to display our items in. FlxLayers let you specify the order that you want to display your graphics in. Anything placed in the topmost layer will be shown above everything in the other layers.

        public static var lyrStage:FlxLayer;
        public static var lyrSprites:FlxLayer;
        public static var lyrHUD:FlxLayer;

7. Now, within the PlayState constuctor, the first thing we want to do is call the FlxState constructor by saying:

super();

8. Next, we need to initialize some of our variables that we defined earlier:
First, the layers:

            lyrStage = new FlxLayer;
            lyrSprites = new FlxLayer;
            lyrHUD = new FlxLayer;

This is telling the system to make a new FlxLayer object for each of our layers, and assign them to the variables we specified.

9. Next, we’re going to set up the Player object:

            _p = new Player(48, 448);

this is just saying that our _p variable is going to be a player object, and it’s going to start at 28,29 on the map.

10. Now we’re going to attach the player to the Sprites layer:

lyrSprites.add(_p);

11. Next, we want the game’s camera to follow the p_layer_. We do that by using the FlxG function : “follow”. We will also want to set some options related to the camera to give the player a smoother experience:

            FlxG.follow(_player,2.5);
            FlxG.followAdjust(0.5, 0.5);
            FlxG.followBounds(1,1,640-1,480-1);

12. Next, we’re going to create our Tilemap object, and place it into lyrStage:

            _map = new FlxTilemap(new DataMap, ImgTiles, 1);
            lyrStage.add(_map);

13. Finally, we attach our layers (in order) to the State:

            this.add(lyrStage);
            this.add(lyrSprites);
            this.add(lyrHUD);[/code]

You may notice that we did not put anything into lyrHUD – we’ll add score and health to that layer later on.

14. Now we need to override the update function of FlxState, so add this function to PlayState:

override public function update():void
        {
            super.update();
            _map.collide(_p);
        }

This should make some sense to you by now: we’re calling the FlxState’s update function first, and then we’re telling Flixel to collide our Player object with our tilemap object.

Once this is done, you can test your project, and you should be able to run around the map you created!

At this point, your PlayState.as file should look like this:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;

    public class PlayState extends FlxState
    {
        [Embed(source = '../../data/Tiles.png')] private var ImgTiles:Class;
        [Embed(source = '../../data/map.txt', mimeType = "application/octet-stream")] private var DataMap:Class;

        private var _p:Player;
        private var _map:FlxTilemap;

        public static var lyrStage:FlxLayer;
        public static var lyrSprites:FlxLayer;
        public static var lyrHUD:FlxLayer;

        public function PlayState():void
        {
            super();

            lyrStage = new FlxLayer;
            lyrSprites = new FlxLayer;
            lyrHUD = new FlxLayer;

            _p = new Player(48, 448);
            lyrSprites.add(_p);

            FlxG.follow(_p,2.5);
            FlxG.followAdjust(0.5, 0.5);
            FlxG.followBounds(1,1,640-1,480-1);

            _map = new FlxTilemap(new DataMap, ImgTiles, 1);
            lyrStage.add(_map);

            this.add(lyrStage);
            this.add(lyrSprites);
            this.add(lyrHUD);
        }

        override public function update():void
        {
            super.update();
            _map.collide(_p);
        }
    }    
} 

Back to Contents

V. Adding the Enemy to the Game

At this point you should start to see a game coming together. What we are lacking is any sort of challenge or interaction, and a simple way to add challenge is with enemies.

In this section we will be setting up a very simple enemy using a new Class named Enemy that extends FlxSprite. This Enemy class is going to be a near copy of the Player class that we already created so some portions of the class will not be covered in detail since we have already done so.

The first thing you are going to want to do is create an image file for new Enemy object with the same basic frame setup that we used for the Player. We are doing this because for this tutorial we are going to be using the animation layout we used for the Player.

Notice that this image is exactly the same as the player with little recoloring job applied. Simple stuff right?

Alright, let get down to coding!

1. Start by creating a new Class file in the com\Tutorial directory, name it “Enemy.as

2. Next we define the package structure, import all of Fixel , and set this new public class Enemy to extend Flxsprite.

package com.Tutorial
{
    import com.adamatomic.flixel.*;
    public class Enemy extends FlxSprite
    {

3. We will want to also embed the new image we created for this Enemy class

    [Embed(source='../../data/Enemy.png')] private var ImgEnemy:Class;

4. Next we declare our variables and define our constructor .

        private var _move_speed:int = 400;
        private var _jump_power:int = 800;   
        private var _max_health:int = 10;
        private var _hurt_counter:Number = 0;
        private var _can_jump:Boolean = true;
        private var _last_jump:Number = 0;
        private var _p:FlxSprite;        
        public function Enemy(X:Number,Y:Number,ThePlayer:FlxSprite):void
        {
            super(ImgEnemy, X, Y, true, true);//Max speeds
             _p = ThePlayer;
            maxVelocity.x = 200;
            maxVelocity.y = 200;

There are four differences here from how we setup our Player Class :

  • The _can_jump and _last_jump variables that will be used to tell the Enemy if it is ok to jump now or not.
  • The _p variable which is set to the type of FlxSprite. This allows the variable to hold the id, index or value of a FlxSprite Class. This one in particular is going to hold the value of the Player class that is added in PlayState.
  • In the constructor we are passing a variable ThePlayer defined as a FlxSprite that will allow us to pass the id of the Player object to this Class from the PlayState class .
  • Below the call of super, FlxSprite’s constructor, we see where we assign ThePlayer to private variable _p we initialized earlier.

We can now use it throughout the class to do checks against the player!

5. Now let’s finish the Enemy’s constructor. Again, this section is exactly like the Player Class. We setup health, gravity, friction, bounding boxes for collisions, and the animations.

            health = 1;
            acceleration.y = 420;            
            drag.x = 300;
            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);
        }

6: Now again the first section of the update function is a carbon copy our Player Class. First we override FlxSprite’s update function , verify that this object is not “dead” and that it “exists”, and setup the counter being hurt.

        override public function update():void
        {
            if(dead)
            {
                if(finished) exists = false;
                else
                    super.update();
                return;
            }
            if (_hurt_counter > 0)
            {
                _hurt_counter -= FlxG.elapsed*3;
            }

7: In this next section we are going to add horizontal movement for the Enemy_. Notice that we are using the **p variable** that holds the Player’s id that we added to the PlayState.

What we are doing here is saying that if the Player’s X coordinate is less than the Enemy’s X coordinate then we need to move the Enemy to the left and change the facing for animations.

            if(_p.x < x)
            {
                facing = false;
                velocity.x -= _move_speed * FlxG.elapsed;
            }

If it is not, then we need to move the Enemy to the right and change the facing for animations.

            else
            {
                facing = true;
                velocity.x += _move_speed * FlxG.elapsed;                
            }

8. Jumping is pretty simple to pull off.
First, we don’t want the Enemy jumping all the time – we’re going to give him a little bit of a delay between jumps:

            if (_last_jump > 0)
            {
                _last_jump -= FlxG.elapsed;
            }

Plus, we want to make sure the enemy is on the ground_. If he’s moving up or down, or if the _last_jump variable is greater than 0, we set his "**_canjump**" variable to false :

    if(velocity.y != 0 || _last_jump > 0)
    {
        _can_jump = false;
    }

Next, we check to see if the Enemy wants to jump. We do this by checking to see if the Player is above the Enemy, and if the Enemy’s _can_jump variable is set to true.

            if(_p.y < y && _can_jump)
            {

Now we want to make that sucker jump! We do this by setting the velocity on the up and down or Y axis to a negative number. Notice we do not use FlxG.elapsed here because we are not adding to the jump speed over time. Just one burst.

velocity.y = -_jump_power;

Set the variable _can_jump to false to avoid the enemy trying to jump mid air, and set the variable _last_jump to 2 so that he can’t jump again right away and close the if statement.

                _can_jump = false;
                _last_jump = 2;
            }

9. Set up the animations, call FlxSprite’s update function, and close the update function just like you did for the Player Class .

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();
        }

10. Next we will want to override the hitFloor function that is part of the FlxCore Class, which FlxSprite extends. The function is called when ever there is a collision against a block on the top side. We are overriding to set the _can_jump variable to true . Now the Enemy can jump again! We also want to return the value of FlxSprite’s hitFloor so we call the super.hitFloor() .

override public function hitFloor():Boolean
        {
            _can_jump = true;
            return super.hitFloor();
        }

11. Finish up the Enemy Class by overriding the hurt function just like the Player Class does, and closing the Class and Package definitions.

override public function hurt(Damage:Number):void
        {
            _hurt_counter = 1;
            return super.hurt(Damage);
        }       
    }
}

This is what your final Enemy.as class file will look like:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;
    public class Enemy extends FlxSprite
    {
        [Embed(source='../../data/Enemy.png')] private var ImgEnemy:Class;
        private var _p:Player;
        private var _move_speed:int = 400;
        private var _jump_power:int = 800;   
        private var _max_health:int = 10;
        private var _hurt_counter:Number = 0;
        private var _can_jump:Boolean = true;
        private var _last_jump:Number = 0;
        public function Enemy(X:Number,Y:Number,ThePlayer:Player):void
        {
            super(ImgEnemy, X, Y, true, true);//Max speeds
            _p = ThePlayer;
            maxVelocity.x = 200;
            maxVelocity.y = 200;
            health = 1;
            acceleration.y = 420;
            drag.x = 300;
            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);
        }

        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 (_last_jump > 0)
            {
                _last_jump -= FlxG.elapsed;
            }
            if (velocity.y != 0 || _last_jump > 0)
            {
                _can_jump = false;
            }
            if(_p.x < x)
            {
                facing = false;
                velocity.x -= _move_speed * FlxG.elapsed;
            }
            else
            {
                facing = true;
                velocity.x += _move_speed * FlxG.elapsed;                
            }
            if(_p.y < y && _can_jump)
            {
                velocity.y = -_jump_power;
                _last_jump = 2;
                _can_jump = false;
            }
            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 hitFloor():Boolean
        {
            _can_jump = true;
            return super.hitFloor();
        }

        override public function hurt(Damage:Number):void
        {
            _hurt_counter = 1;
            return super.hurt(Damage);
        }       
    }
} 

Back to Contents

VI: Fighting the Enemies

Now that you have the player and the enemy on the screen, you need to give the player a way to fight back! We’re going to give him some Ninja Stars too throw.

Making the Weapon

1. First, draw a simple sprite for your Ninja Star. This doesn’t have to be complicated.
- this image is really transparent, just put it on a black background to see better.

Name this file “NinjaStar.png”, and place it in your data directory.

2. We’re also going to create a “spark” graphic for when the Ninja Star hits something.

Link to the tiny sprite above << Right click this link and save as

Name this file “Spark.png” and place it in the data directory as well.

3. We’re going to create a new Class for our stars. Name it “NinjaStar.as”, then import Flixel and make it extend FlxSprite .

4. Right before the constructor, you’re going to Embed the Ninja Star and Spark graphics:

        [Embed(source = "../../data/NinjaStar.png")] private var ImgStar:Class;
        [Embed(source = "../../data/Spark.png")] private var ImgSpark:Class;

5. We’re also going to setup a variable to be a FlxEmitter to give a little special effect to our star.

private var _sparks:FlxEmitter;

6. Change the constructor’s heading to look like this:

function NinjaStar(X:Number, Y:Number, XVelocity:Number, YVelocity:Number ):void

7. Then within the constructor, we’re going to call super , and then setup a few different things:

             super(ImgStar, X, Y, true, true);

            //Basic movement speeds
            maxVelocity.x = 200;  //How fast left and right it can travel
            maxVelocity.y = 200;  //How fast up and down it can travel
            angularVelocity = 100; //How many degrees the object rotates            
            //bounding box tweaks
            width = 5;  //Width of the bounding box
            height = 5;  //Height of the bounding box
            offset.x = 6;  //Where in the sprite the bounding box starts on the X axis
            offset.y = 6;  //Where in the sprite the bounding box starts on the Y axis
            addAnimation("normal", [0]);  //Create and name and animation "normal"
            _sparks = FlxG.state.add(new FlxEmitter(0,0,0,0,null,-0.1,-150,150,-200,0,-720,720,400,0,ImgSpark,10,true,PlayState.lyrSprites)) as FlxEmitter;   
            facing = true;
            //The object now is removed from the render and update functions.  It returns only when reset is called.
            //We do this so we can precreate several instances of this object to help speed things up a little
            exists = false; 

Most of this should be familiar to you from our other FlxSprites . However, the FlxEmitter is a little new. This object is going to actually create a bunch of sprites at once when we call it – using the ImgSpark graphic – to look like a little explosion of sparks. There’s a lot of neat effects you can do with FlxEmitters .

8. Next, we’re going to override some of the FlxSprites functions so that whenever our Ninja Star hits a wall or something, it will call the “Kill” function, and basically kill itself.

        override public function hitFloor():Boolean
        {
            kill();
            return super.hitFloor();
        }
        override public function hitWall():Boolean
        {
            kill();
            return super.hitWall();
        }        
        override public function hitCeiling():Boolean
        {
            kill();
            return super.hitCeiling();
        }

9. Next, our kill function . FlxSprite has a Kill function which instantly kills the sprite , regardless of any health they might have or whatever. We’re going to override this function to let our Ninja Star die in glory (using our Spark FlxEmitter):

        override public function kill():void
        {
            if(dead)
                return;            
            _sparks.x = x + 5;
            _sparks.y = y + 5;
            _sparks.reset();
            super.kill();
        }

What this says is:

If this object is already dead, don’t do anything – get out. Otherwise, place our FlxEmitter object at the location of this sprite (+5 in x and y), and then “reset” our Emitter – which tells it to explode all of it’s particles right then. Then we call the actual kill function of FlxSprite which will remove this sprite.

10. Finally, in order to save resources, we’re going to let the system ‘reuse ’ dead Ninja Stars and place them back in the game, so we’re going to create a ‘reset’ function :

        public function reset(X:Number,Y:Number,XVelocity:Number,YVelocity:Number):void
        {
            x = X;
            y = Y;
            dead = false;
            exists = true;
            visible = true;
            velocity.x = XVelocity; //Set the left and right speed
            play("normal"); //Play the animation
        }

This will just ‘undo’ all the stuff that happens when you Kill a sprite so that the game can use this Ninja Star again.

That’s it for this Class! Your NinjaStar.as file should look like this:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;

    public class NinjaStar extends FlxSprite
    {
        [Embed(source = "../../data/NinjaStar.png")] private var ImgStar:Class;
        [Embed(source = "../../data/Spark.png")] private var ImgSpark:Class;

        private var _sparks:FlxEmitter;
        public function NinjaStar(X:Number, Y:Number, XVelocity:Number, YVelocity:Number ):void
        {
            super(ImgStar, X, Y, true, true);
            maxVelocity.x = 200;
            maxVelocity.y = 200;
            angularVelocity = 100;
            width = 5;
            height = 5;
            offset.x = 6;
            offset.y = 6;
            addAnimation("normal", [0]);
            _sparks = FlxG.state.add(new FlxEmitter(0,0,0,0,null,-0.1,-150,150,-200,0,-720,720,400,0,ImgSpark,10,true,PlayState.lyrSprites)) as FlxEmitter;            
            facing = true;
            exists = false;
        }
        override public function hitFloor():Boolean
        {
            kill();
            return super.hitFloor();
        }
        override public function hitWall():Boolean
        {
            kill();
            return super.hitWall();
        }        
        override public function hitCeiling():Boolean
        {
            kill();
            return super.hitCeiling();
        }

        override public function kill():void
        {
            if(dead)
                return;            
            _sparks.x = x + 5;
            _sparks.y = y + 5;
            _sparks.reset();
            super.kill();
        }

        public function reset(X:Number,Y:Number,XVelocity:Number,YVelocity:Number):void
        {
            x = X;
            y = Y;
            dead = false;
            exists = true;
            visible = true;
            velocity.x = XVelocity;
            play("normal");
        }

    }

} 

Back to Contents

Equipping the Player

1. We now need to change the Player.as file to allow the player to actually throw these stars. Open this file now.

2. We need to add 2 more variables to the top of the class . First, a FlxArray which will connect to the Array we will later setup in PlayState to hold all of the stars the player throws. Then we’ll also need a counter to track when the player can throw another star – we want to create a pause.

        private var _stars:FlxArray;
        private var _attack_counter:Number = 0;

3: Next, we’re going to need to pass the Array of stars to the Player object when it initializes . So, change the top of the constructor class to this:

private function  Player(X:Number,Y:Number,Stars:FlxArray):void

4: Somewhere within the constructor, after "**super** ", we need to actually connect the Stars array we pass into the constructor to the _stars array we created.

_stars = Stars;

5. Next, in the update function, we want to see if our attack counter is > 0,and if so, subtract from it to make it closer to 0. The player will only be able to throw a Star if the counter is at 0. You can place this code right around the hurt counter code:

if (_attack_counter > 0)
            {
                _attack_counter -= FlxG.elapsed*3;
            }

6. Now we want to respond to the player pressing the attack key . Still in update , and below the other keypress checks, add this:

            if(FlxG.justPressed(FlxG.B) && _attack_counter <= 0)
            {
                _attack_counter = 1;
                play("attack");
                throwStar(facing);
            }   

So what this says is: If the player presses the B Key ( C ), and the attack counter is 0 or less, then set the counter to 1, play the “attack” animation , and then call a function called "**throwStar** ", passing data based on which direction the player is facing . We’ll write this function later.

7. Next, we’re going to make sure the attack animation plays while the attack counter is going down. This helps show the player that they can’t attack again just yet. In the update function, right beneath the _hurt_counter check to play(“hurt”), we’re going to add this clause to the if statement :

            else if (_attack_counter > 0)
            {
                play("attack");
            }

8. For the throwStar function , we’re going to do a couple of things. First, we need it to receive the direction we want to face to first determine which way to throw the star . “facing” is a variable built into FlxSprite . If an object is ‘facing ’ to the right, then it’s true , otherwise, it’s false . We pass facing to this function, and use it to determine which direction the star will move.
Then we’re going to see if we have any stars that we can reuse: stars that are in our array of stars , but aren’t currently on the screen . If we do, we’re going to use one of those stars , or we’ll make a new star if we have to.
Then we set the star’s velocity , and add our star to the lyrSprites layer , so it starts moving in the direction specified.

private function throwStar( dir:Boolean ):void
        {
            var XVelocity:Number;
            if (dir) XVelocity = 200;
            else XVelocity = -200;
            for(var i:uint = 0; i < _stars.length; i++)
                if(!_stars[i].exists)
                {
                    _stars[i].reset(x, y + 2,XVelocity, 0);
                    return;
                }
            var star:NinjaStar = new NinjaStar(x, y + 2, XVelocity, 0);
            star.reset(x, y,XVelocity, 0)
            _stars.add(PlayState.lyrSprites.add(star) );    
        }

That’s it for the Player.as file for now! It should looks like this:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;

    public class Player extends FlxSprite
    {
        [Embed(source = "../../data/Player.png")] private var ImgPlayer:Class;
        private var _move_speed:int = 400;
        private var _jump_power:int = 800;   
        public var _max_health:int = 10;
        public var _hurt_counter:Number = 0;
        private var _stars:FlxArray;
        private var _attack_counter:Number = 0;

        public function  Player(X:Number,Y:Number,Stars:FlxArray):void
        {
            super(ImgPlayer, X, Y, true, true);

            _stars = Stars;
            maxVelocity.x = 200;
            maxVelocity.y = 200;
            health = 10;
            acceleration.y = 420;            
            drag.x = 300;
            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 = true;
        }
        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 (_attack_counter > 0)
            {
                _attack_counter -= FlxG.elapsed*3;
            }
            if(FlxG.kLeft)
            {
                facing = false;
                velocity.x -= _move_speed * FlxG.elapsed;
            }
            else if (FlxG.kRight)
            {
                facing = true;
                velocity.x += _move_speed * FlxG.elapsed;                
            }
            if(FlxG.justPressed(FlxG.A) && velocity.y == 0)
            {
                velocity.y = -_jump_power;
            }

            if(FlxG.justPressed(FlxG.B) && _attack_counter <= 0)
            {
                _attack_counter = 1;
                play("attack");
                throwStar(facing);

            }            

            if (_hurt_counter > 0)
            {
                play("hurt");

            }
            else if (_attack_counter > 0)
            {
                play("attack");
            }
            else            
            {
                if (velocity.y != 0)
                {
                    play("jump");
                }
                else
                {
                    if (velocity.x == 0)
                    {
                        play("stopped");
                    }
                    else
                    {
                        play("normal");
                    }
                }
            }
            super.update();

        }

        override public function hitFloor():Boolean
        {
            return super.hitFloor();
        }

        override public function hurt(Damage:Number):void
        {
            _hurt_counter = 1;
            return super.hurt(Damage);
        }        

        private function throwStar( dir:Boolean ):void
        {
            var XVelocity:Number;
            if (dir) XVelocity = 200;
            else XVelocity = -200;
            for(var i:uint = 0; i < _stars.length; i++)
                if(!_stars[i].exists)
                {
                    _stars[i].reset(x, y + 2,XVelocity, 0);
                    return;
                }
            var star:NinjaStar = new NinjaStar(x, y + 2, XVelocity, 0);
            star.reset(x, y,XVelocity, 0)
            _stars.add(PlayState.lyrSprites.add(star) );    
        }

    }

} 

Back to Contents

Updating the Playstate

1. We need to make a few changes to PlayState.as , so open that file. Somewhere in your variable declarations we want to add a new FlxArray to hold all the stars our player is going to throw.

private var _pStars:FlxArray;

2. Somewhere within the constructor, before you initialize the Player object, we want to setup this FlxArray :

_pStars = new FlxArray;
            for (var n:int = 0; n < 40; n += 1)
            {
                _pStars.add(lyrSprites.add(new NinjaStar(0,0,0,0)));
            }

We’re going to set _pStars to be a new (empty) FlxArray , and then we’re going to add 40 new stars to this array so that the game won’t have to create them on the fly. This is the array which we checked in the Player class to see if we can reuse a star or if we need to make a new one.

3. Next, we need to make the stars collide with our Tilemap so that they don’t fly through walls. Inside the update function, after super.update() , add:

FlxG.collideArray2(_map,_pStars);

This works the same as the collision with the enemies.

4. Now, we also want our stars to damage the enemies when they get hit. So we need to add an overlapArray check.

FlxG.overlapArrays(_pStars, _e, StarHitsEnemy);

This works the same way that collision between the player and the enemy works – we’re just going to call a different function if any star overlaps any enemy.

5. The StarHitsEnemy function is going to be pretty straightforward – if a star collides with an enemy we want to Kill that star , and hurt that enemy . Since our enemies (right now) only have 1 health each, this will kill them, too. In the future, we could add stronger enemies that take more hits, and/or stronger weapons that deal more damage.

private function StarHitsEnemy(colStar:FlxSprite, colEnemy:FlxSprite):void
        {
            colStar.kill();
            colEnemy.hurt(1);
        }

That’s it! Test out your code and you should be able to press C to kill the enemy!
Your PlayState.as should look like this:

 package com.Tutorial
{
    import com.adamatomic.flixel.*;

    public class PlayState extends FlxState
    {
        [Embed(source = '../../data/Tiles.png')] private var ImgTiles:Class;
        [Embed(source = '../../data/map.txt', mimeType = "application/octet-stream")] private var DataMap:Class;

        private var _p:Player;
        private var _map:FlxTilemap;
        private var _pStars:FlxArray;

        private var _e:FlxArray;

        public static var lyrStage:FlxLayer;
        public static var lyrSprites:FlxLayer;
        public static var lyrHUD:FlxLayer;

        public function PlayState():void
        {
            super();

            lyrStage = new FlxLayer;
            lyrSprites = new FlxLayer;
            lyrHUD = new FlxLayer;

            _pStars = new FlxArray;
            for (var n:int = 0; n < 40; n += 1)
            {
                _pStars.add(lyrSprites.add(new NinjaStar(0,0,0,0)));
            }

            _p = new Player(48, 448, _pStars);
            lyrSprites.add(_p);

            FlxG.follow(_p,2.5);
            FlxG.followAdjust(0.5, 0.5);
            FlxG.followBounds(1,1,640-1,480-1);

            _map = new FlxTilemap(new DataMap, ImgTiles, 1);
            lyrStage.add(_map);

            this.add(lyrStage);
            this.add(lyrSprites);
            this.add(lyrHUD);

            _e = new FlxArray;
            _e.add(lyrSprites.add(new Enemy(432, 320, _p)));
        }

        override public function update():void
        {
            super.update();
            _map.collide(_p);
            FlxG.collideArray2(_map, _e);
            FlxG.overlapArray(_e, _p, EnemyHit);
            FlxG.collideArray2(_map, _pStars);
            FlxG.overlapArrays(_pStars, _e, StarHitsEnemy);
        }

        private function EnemyHit(E:Enemy, P:Player):void
        {
            FlxG.log(P._hurt_counter.toString());
            if (P._hurt_counter <= 0)
            {
                if (E.x > P.x)
                {
                    P.velocity.x = -100;
                    E.velocity.x = 100;
                }
                else
                {
                    P.velocity.x = 100;
                    E.velocity.x = -100;
                }
                P.hurt(1);
            }
        }

        private function StarHitsEnemy(colStar:FlxSprite, colEnemy:FlxSprite):void
        {
            colStar.kill();
            colEnemy.hurt(1);
        }

    }    
} 

At this point, you can try to add some more enemies to the game to see how it works. We’re next going to cover how to give the enemies stars to throw as well.

Back to Contents