Platformers are probably going to be one of the big things made with Flash Punk + Flash Develop, so I thought it might be handy to create a small tutorial on how to go about building a basic one.
This tutorial uses Flash Punk V0.87If you are using an outdated version you will need to download V0.87. If you're using a new version (such as FP1.00) check out
this topic.
INTRODUCTIONThis tutorial will show you how to create a platform game in Flash Develop, with AS3 and Flash Punk. This tutorial assumes that you have already set up Flash Develop, with the Flex compiler and properly downloaded the Flash Punk library. If you have not done so,
go here.
This tutorial also assumes that you have a basic understanding of how to code with AS3. If you no absolutely nothing about how AS3 in Flash Develop works, this may not be the best tutorial for you.
The first two "parts" sort of go through what's explained in the basic tutorial, but I thought I should add this in anyway just to make sure things are clear when you're starting up the project.
The final product will look something similar to
thisAlrighty, onto the tutorial
PART I: SETUPStep 1:Create a new project and title it whatever you want. I'd just go with something like "platform or "platformer". Once you've done this put the Flash Punk code into the project folder, and set main.as to
"Always Compile" (by right clicking main.as).
Now, go to Project>>Properties and set it to run in Flash Player 10, and let's also resize the room to 320x240 (800x600 is too big).
Once you've done all this, open up main.as and replace any code in there with the following:
package
{
import punk.core.*;
[SWF(width = "640", height = "480")]
public class Main extends Engine
{
public function Main()
{
showSplash(0x202020, 0xFF3366, .5, false);
super(320, 240, 60, 2, World);
}
}
}
If you went through the basic tutorial you'll already know this part, and the next little bit.
Step 2:We now want to create the level.as, which will load up our main level in the game. First, lets create a new folder in our project and call it the "game" folder, which will hold all of our main game scripts. Once you've done that (by right clicking the project and Add>>New Folder) we'll want to add a new class called "Level.as" into our newly made game folder.
In Level.as we want the package (at the very top) to say "package game", so we know that it's within the game folder. Once you've done that (if it already isn't there), you need to import a few things from Flash Punk:
import punk.core.Entity;
import punk.core.World;
import game.*;
We also need the level to extend world, so it's a level/room/world.
you're final level.as should now look like...
package game
{
import punk.core.Entity;
import punk.core.World;
import game.*;
public class Level extends World
{
public function Level()
{
}
}
}
Save level.as.
Step 3:Now we're going to go back to main.as and we're going to replace "World" in the super function that is executed to Level, so that the game loads up Level at the start. The new line should look like:
super(320, 240, 60, 2, Level, false, true);
Well we're here, we also need to import game.Level, so we can actually create it from here. So, under the line
"import punk.core.*" put
import game.Level;
Save main.as, and you've should have successfully created the set up stuff for your game. Test the game, and if everything runs smoothly, continue to the next step.
PART II: WALLSStep 1:Before we create the player, we're actually going to make the walls that the player walks on. To start, we're going to need to create a sprite for the wall, and then create the Wall.as. First, create a wall sprite, or use the one below, and put it in the data folder in your project, and name it something like "wall.png".
The Wall SpriteNow that the wall is in the correct place, we're going to create a Wall.as in the game folder. Do this now, and make sure the package, at the top is "package game".
After that, we're going to import flash punk's actor class, which we will be using for the wall. at the very start, right after the package line, put
import punk.Actor;
and after that, make sure the class extends Actor (
"public class Wall extends Actor")
Step 2:Next, we need to load in the sprite of the Wall. To do so, we're going to use the following code, which should go under the Wall class (or rather within it)
[Embed(source = '../data/Wall.png')]
private var imgWall:class;
Step 3:Now we need to set the sprite of the Wall to the sprite we just loaded in, and also set the collision area and type of the Wall, so that the player can have collisions with it. To do this, we need to add the following code to the
public function Wall():
sprite = FP.getSprite(imgWall,16,16);
setHitbox(16, 16, 0, 0);
type = "Wall";
The first line here sets the sprite, and sets its width and height to 16 pixels. Then, we set the collision rectangle with a width and height of 16 pixels, starting at 0,0 on the sprite. Finally, we set the type of collision to "Wall", so we know what we're going to be running into from the player.
Finally, your Wall.as should look something like this:
package game
{
import punk.Actor;
public class Wall extends Actor
{
[Embed(source = '../data/Wall.png')]
private var imgWall:Class;
public function Wall()
{
sprite = FP.getSprite(imgWall, 16, 16);
setHitbox(16, 16, 0, 0);
type = "Wall";
}
}
}
And yay, you have the floor object working properly, though if you play you'll still see nothing as you haven't added it to the level yet.
PART III: THE PLAYER (finally..)OK, most of the stuff you did back there you've already done in
A Simple game in Flash Develop, so it's nothing new. Now, however, we're going to make the player object which should be a little interesting. The player is going to contain things like moving, jumping, variable jumping, friction, gravity, and pixel perfect collisions. So, without further talking...
Step 1: Player setupFirst, we need to make the Player.as in the game folder. Do this, and make sure the package is
package game. Next, import
import punk.Actor;
import punk.core.Spritemap;
import punk.util.*;
which we will need to do some things (such as using the keyboard) in the player. After you've done this, make sure that the player extends Actor.
next, we want to create two sprites: playerRight.png and playerLeft.png, that go into the data folder with Wall.png. You can either create your own sprites, or use the ones below.

playerRight.png and playerLeft.pngOnce you have done this, import them just like we did with the wall, except we're going to add them as a sprite before we start executing other stuff, since we'll be changing the sprites relatively often.
So, once you've imported both your sprites and created a sprite variable for both, your code should look like so:
package game
{
import punk.Actor;
import punk.core.Spritemap;
import punk.util.*;
public class Player extends Actor
{
//load sprite and create the sprite variable
[Embed(source = '../data/playerRight.png')]
private var imgPlayerRight:Class;
private var sprPlayerRight:Spritemap = FP.getSprite(imgPlayerRight, 16, 16);
[Embed(source = '../data/playerLeft.png')]
private var imgPlayerLeft:Class;
private var sprPlayerLeft:Spritemap = FP.getSprite(imgPlayerLeft, 16, 16);
public function Player()
{
}
}
}
Step 2: The variablesNow that we have the basics of our player set up, we're going to need to create some main variables for the movement of the player. The two obvious ones are the horizontal and vertical speeds of the player. I'm going to call these variables
xspeed and
yspeed. The other variables you will need is an acceleration speed for the player, so he doesn't start moving at max speed as soon as you hit left or right. I'm going to call this variable
aspeed, to represent acceleration speed.
We're also going to need some form of gravity, to pull the player down after he jumps, or walks off an edge. I'm going to call this
gspeed to represent gravity speed. There are three more variables we now need, maximum speed, friction speed, and jump speed. They're pretty self explanatory, and I'm going to call them mspeed, fspeed, and jspeed, respectively.
You can set each of these values to whatever you want, or use the ones I set below. It's totally up to you. Either way, you should end up with something like this:
private var xspeed:Number = 0;
private var yspeed:Number = 0;
private var aspeed:Number = 0.2;
private var gspeed:Number = 0.2;
private var fspeed:Number = 0.4;
private var mspeed:Number = 1;
private var jspeed:Number = 3.6;
Alrighty, that's a lot of variables, but each one of them will be needed when we start to actually move the player around the world.
Step 3: Keyboards and setting the spriteNow we need to set the current sprite of the player. To do this, we're simply going to set the variable sprite to sprPlayerRight, in the public Player function. We're also going to set the delay time between each frame of our image. I set mine so it ends up being about 2 loops of the animation per second, by doing delay = (30/4), since I have 4 frames, and there are 60 frames per second.
For the player, we're also going to need to set a collision rectangle, so that it can have proper collisions with the Wall. The player may not take up the whole 16x16 size of the sprite, so you may not want the collision rectangle to take up the whole area. Personally, I felt that doing something around setHitbox (12, 12, -2, -4); was relatively good. The rectangle is 12x12 pixels, leaving 2 pixels on either side empty and 4 at the top.
Next, we need to set up the keyboard input for the player. This is really simple, we're just going to make 3 inputs, left, right, and jump, which ends up looking like this:
Input.define("right", Key.RIGHT);
Input.define("left", Key.LEFT);
Input.define("jump", Key.X);
I set my jumping key to X, as I prefer that to the up arrow key, but you can set it to whatever you like.
Once you've done all this, your player function should look similar to the following:
public function player()
{
//set the sprite
sprite = sprPlayerRight;
//set the delay
delay = (30 / 4);
//collision mask....
setHitbox (12, 12, -2, -4);
//keyboard controls.
Input.define("right", Key.RIGHT);
Input.define("left", Key.LEFT);
Input.define("jump", Key.X);
}
Step 4: Finally making the player do stuffOK, now that we have set the sprites, made the variables, set the keyboard controls, and made our collision mask, we're FINALLY ready to make the player move around.
To start, we're going to need to override a function called update. Update is called every frame of the game, meaning that if you haven't changed the frame rate, it should be executed 60 times a second. So, add the following code below the player function:
override public function update():void
{
}
Everything we do with moving the player is going to be happening in here.
Now that we have our update function, let's check if the player is holding the left or right arrow keys, and if they are, we're going to increase, or decrease (depending on the key held) the xspeed.
To check if the right key is being held, we simply add an if statement that checks if the left key is being held, like so:
if(Input.check("right")) {
}
Now, of course, we want the xspeed to increase when we hold the right key, so we're going to put xspeed += aspeed within the brackets, so we end up with
if(Input.check("right")) {
xspeed += aspeed;
}
Now do the same for the left key, except instead of adding aspeed, we want to subtract aspeed, since you're going in the opposite direction.
After you've completed that, you'll want check if the player is pressing the jump key. To do this, we do the same if statement as with left and right, but this time with "jump" instead. However, we don't want you to be able to press jump at any time and be able to magically jump. We only want you to jump if you're touching a wall below you. So, we want check if a wall is below us in the if statement as well, by adding an && and putting collide("Wall", x, y + 1) after it.
collide basically checks if you have a collision with a certain type at a certain position. In this case, we want to check for the wall, at our x position, and 1 pixel below us.
After we've done that, we want to actually make the player jump, so within the brackets add yspeed = - jspeed;
What this does is set the yspeed to negative jump speed (since we're going up, not down).
In the end, your jump check should look like this:
if(Input.check("jump") && collide("Wall",x,y+1)) {
yspeed = - jspeed;
}
Bravo, we're doing well.
OK, now we're increasing the xspeed and yspeed, but we're not actually moving the player yet. Different people have different methods of doing so, but I've found it easiest, and best, to actually check every pixel before moving, creating a relatively flawless movement engine.
Now, I'm going to start with the x movement first. To check every pixel before moving it, I'm going to do this:
for (var i:int = 0; i < Math.abs(xspeed); i += 1) {
if (!collide("Wall", x + FP.sign(xspeed), y)) { x += FP.sign(xspeed); } else { xspeed = 0; break; }
}
This may look a bit complicated, so I'm going to cut it down a bit.
The first line uses a for statement. For statements basically repeat the code within the brackets until something is no longer true. In this case, we're saying repeat the following code until i is no longer smaller then the absolute value of xspeed. Incase you're unfamiliar with absolute/abs, it basically turns any negative number into a positive one. For example, abs(-7) equals 7, and abs(4.6) equals 4.6.
In this for statement, we also increase i by one every time it executes the code below, so that we don't create an infinite loop (which sucks, trust me).
The next line checks if we have a collision with a wall, at our x position plus the sign of xspeed. sign basically converts things to -1, 0, or +1. If anything is positive, it becomes 1, and anything negative becomes -1. This is useful for us since we only want to check one pixel to our right or left. If we don't collide with a wall (notice the !), then we move x left or right 1, depending on the current xpeed. If we DO hit a wall, we stop all the movement, and end the for statement.
This may sound a little confusing... Lets pretend xspeed is equal to 4. Therefore, we're going to run the code within the brackets 4 times, and each time it runs the code we're going to check 1 pixel before we move. That way, we check if each pixel is free before moving there, creating perfect collisions.
Now, moving up/down is going to work almost exactly the same way. The only real difference is we're using yspeed and moving the players y position.
for (i = 0; i < Math.abs(yspeed); i += 1) {
if (!collide("Wall", x, y + FP.sign(yspeed))) { y += FP.sign(yspeed); } else { yspeed = 0; break; }
}
Lastly, we need to make the player fall back down.. so under all this code we added, lets simply put
yspeed += gspeed;
After all of this, your Player.as should look something very close to this:
package game
{
import punk.Actor;
import punk.core.Spritemap;
import punk.util.*;
public class Player extends Actor
{
//load sprite and create the sprite variable
[Embed(source = '../data/playerRight.png')]
private var imgPlayerRight:Class;
private var sprPlayerRight:Spritemap = FP.getSprite(imgPlayerRight, 16, 16);
[Embed(source = '../data/playerLeft.png')]
private var imgPlayerLeft:Class;
private var sprPlayerLeft:Spritemap = FP.getSprite(imgPlayerLeft, 16, 16);
private var xspeed:Number = 0;
private var yspeed:Number = 0;
private var aspeed:Number = 0.2;
private var gspeed:Number = 0.2;
private var fspeed:Number = 0.4;
private var mspeed:Number = 1;
private var jspeed:Number = -3.6;
public function Player()
{
//set the sprite
sprite = sprPlayerRight;
//set the delay
delay = (30 / 4);
//collision mask....
setHitbox (12, 12, -2, -4);
//keyboard controls.
Input.define("right", Key.RIGHT);
Input.define("left", Key.LEFT);
Input.define("jump", Key.X);
}
override public function update():void
{
//move/jump
if (Input.check("right")) { xspeed += aspeed; }
if (Input.check("left")) { xspeed -= aspeed; }
if (Input.check("jump") && collide("Wall", x, y + 1)) { yspeed = - jspeed; }
//actually move the player now
for (var i:int = 0; i < Math.abs(xspeed); i += 1) {
if (!collide("Wall", x + FP.sign(xspeed), y)) { x += FP.sign(xspeed); } else { xspeed = 0; break; }
}
for (i = 0; i < Math.abs(yspeed); i += 1) {
if (!collide("Wall", x, y + FP.sign(yspeed))) { y += FP.sign(yspeed); } else { yspeed = 0; break; }
}
yspeed += gspeed;
}
}
}
You're probably anxious to test your game now, so let's take a small break from the player, and work on creating the world.