EZPlatformer (Flixel)

From Flash Game Dojo

Jump to: navigation, search

This tutorial will introduce you to Flash, ActionScript and Flixel from the programmer's perspective, and walk you through the creation of a simple platformer. This tutorial was originally written for and published in Game Developer Magazine.


Contents

Getting Started

Building a game, like building an Ikea desk, requires a few simple tools. Fortunately, they're free, fun to use, and very powerful! Programmers call these tools IDEs, or Integrated Development Environments. If it helps, you can think of them like Microsoft Word for programmers. You type your code, hit save, and then run your game. We're particularly interested in IDEs that are friendly toward or compatible with ActionScript 3 (or AS3), the programming language that powers Flash games.

Windows: For Windows users, we recommend downloading an awesome piece of free software called FlashDevelop from their official website - just click on the yellow box in the upper right that says 'Latest Release'. FlashDevelop users need to download something called the Flex SDK from Adobe, which is free as well, from the official Adobe site (all the URLs for these resources are provided at the end of the article). Unzip that big pile of files into your directory of choice; we recommend C:\Flex. When FlashDevelop asks where you installed Flex, point it at this directory. That's it!

Mac: For Mac OSX users, we recommend Flash Builder, which is free for 30 days, and includes the Flex SDK (the collection of files and utilities that allow us to program Flash games), so there's just one download and one installer. You can download Flash Builder from the official Adobe site.

Experts: This section is meant exclusively for the console cowboys of cyberspace. If you've never written a computer program before, we highly recommend using FlashDevelop, Flash Builder (listed in the Mac and Windows sections above) instead of attempting any of the methods listed here. If you're still with us, great! All you need to do is download the Flex SDK from Adobe, and you can program flash games in your text editor of choice. Flash community mainstay Senocular has an excellent tutorial on how to get started with MXMLC, the free compiler included in the Flex SDK. Alternatively, you can follow this flixel specific MXMLC tutorial. Resourceful and/or picky experts can also retrofit Visual Studio or XCode to use for AS3 dev as well.

Making a Game

For anyone new to creating games, using an established game library can help speed things up by taking care of a lot of the basics of game code. Both of the libraries we recommend are free and cover the fundamentals of 2D games, including sprites, tilemaps, collisions, and scrolling.

Flixel: Flixel was created by myself over roughly 2 years of spare time development. Features include:

  • Quad Trees
  • Debug Console
  • Baked Sprite Rotation
  • Style: C++/Java

FlashPunk: FlashPunk was created by ChevyRay, a surprisingly young veteran of the GameMaker scene. Features include:

  • Path-Based Movement
  • Pathfinding Tools
  • Intuitive Movement + Collision
  • Style: GameMaker


A Short Tour of Flixel

For the sake of simplicity, this tutorial will use the Flixel library only. If upon completing (or failing to complete) the tutorial you feel like Flixel just isn't for you, I heartily encourage you to try FlashPunk. Even though there is no tutorial for FlashPunk in this issue, there are a lot of great online resources for it. However, a lot of new designers have had good luck with Flixel, so we're going to put together a short tour or explanation of how a Flixel game is put together so things aren't too overwhelming when we get to the nuts & bolts of the tutorial later.

Games have a lot of different stuff in them, but we're going to take this first game pretty slow. Flixel is an "object-oriented" library or API, which means it is a collection of source code files, and each file represents a different kind of basic game element, including characters (or "sprites"), sound effects, and a helper or "container" for the game itself. Let's start with that one!

The FlxGame Object: FlxGame is a special object that is only created once, and you only really use it right at the start of the game, to set up some important basic properties. Other than that, it just acts like a container and manager all in one. FlxGame is in charge of keeping track of your game objects and making sure they get updated and drawn, so it does most of its work behind the scenes.

The FlxState Object: To help keep things organized, Flixel uses a simple "state engine". What this means is different parts of your game that have different behavior can be written or scripted in different files. For example, most Flixel games have a "menu" state, and a "play" state, so you don't have to muck up your gameplay code with menu display or list controls. All your real game design work and game objects will be done as part of a "state", not part of FlxGame.

The FlxSprite Object: A FlxSprite, or "sprite", is a graphical representation of a game object. It might be the player's avatar, or a collectible powerup, or a giant boss vehicle, or some atmospheric particle effects. By adding sprites to the game's state, you can change their position and draw them to the screen and make a game. The only visual objects that aren't sprites in Flixel are big level-objects, like:

The FlxTilemap Object: Since levels are so big, it can be helpful to have a special object to store them and draw them more efficiently. FlxTilemap takes a array of numbers, and uses those numbers to draw tiles (little squares of level graphics) to the screen, like Super Mario Bros. or Legend of Zelda. It can also use those numbers to determine what objects you can run into, or collide with, and which tiles should be treated as non-existent. Just like a FlxSprite, FlxTilemaps are first created and then added to the game state.

FlxG And FlxU: These oddly named objects, or classes, are short for "Global" functions, and "Utility" functions respectively. They contain a bunch of helpful things that we use a lot in game development, like checking for keyboard presses, or bumping objects into each other.

That's all we're going to use to make our Mario-esque platforming game. To recap, our ingredients include a game object, which contains a state object, which in turn contains sprites and a tilemap. If the game was a Russian nesting doll, FlxGame would be the biggest doll, FlxState would be the medium doll inside, and inside FlxState there would be a whole pile of tiny dolls. So let's make our biggest doll first!


Step 1: Adding Flixel

In your development environment of choice, create a New AS3 (that's our programming language, remember?) Project, and add the flixel source folder to the project. Depending on what IDE you are using, you might have to just copy the whole flixel folder right into your new project's source folder on your hard drive. In Flash Builder you can simply look up the folder where you downloaded Flixel and add it to your project that way.


Step 2: Creating The Game Object

This is going to be a long step, even though it's a short file we won't use again for this game, just a heads up! In the new project's main or application file (usually it has the same name as your project), we need to add the following code:

package
{
	import org.flixel.*;
	[SWF(width="640", height="480", backgroundColor="#000000")]
 
	public class EZPlatformer extends FlxGame
	{
		public function EZPlatformer()
		{
			super(320,240,PlayState,2);
		}
	}
}

Let's go through this line by line real quick, since this is the start of the project, we don't want to miss any details quite yet.

package
{

This is simply telling your Flash IDE that you're creating a file in the default source directory. This may not be ideal if you are sharing source code with another project or something, but for quick Flixel sketches its no problem.

import org.flixel.*;

This tells the Flash IDE that you want to include all the Flixel source files. If you wanted to include a specific file, you would just us that filename (e.g. "FlxGame.as") instead of "*". Including all the Flixel files is fast and easy and doesn't really have any penalties, so it's usually simplest to just use the asterisk wildcard.

[SWF(width="640", height="480", backgroundColor="#000000")]

This is a special "pre-processor" command to your Flash IDE, that tells it what size to make your Flash game project. We want our sample game to run at a full resolution of 640x480 (more on this later in this file).

public class EZPlatformer extends FlxGame
	{

Whoa! What is this? There are a few different things going on here. "public" refers to the "visibility" of the class. For now, let's not worry about it, suffice to say that all our stuff for this project will just be public. "class" refers to what FlxGame is - it's programmer-speak for object, basically. Classes can have variables, and functions, and even store other classes. "EZPlatformer" refers to what I named our project. "extends" means we're making a new class, called EZPlatformer, that is based on FlxGame, our "largest Russian doll" from before.

public function EZPlatformer()
		{

This is what we call a "function declaration", and since it has the same name as the object, it's called a "constructor declaration". That means this function, or set of instructions, is automatically called when the object is created. Since this is our main object, Flash creates it automatically, we don't even have to do anything. You'll notice yet another weird bracket there - each "open" bracket indicates that we're opening a different kind of Russian nesting doll. In this case, what we're telling Flash is that our constructor ("EZPlatformer()") belongs inside our EZPlatformer object, which belongs inside our default source tree package from the first line.

super(320,240,PlayState,2);
		}
	}
}

This is the most important line of code in this file. "super" refers to the object you're extending, in this case FlxGame. Since there is no function attached to it (e.g. "super.update()"), you know that what we're calling is FlxGame's constructor. Then we give it some important parameters. In order, these parameters indicate the width, height, starting game state, and "zoom" level of your entire game. Notice something funny? The width and height of the game are exactly half the size of the width and height we passed to Flash earlier in this same file. However, "width" (320) times "zoom" (2) exactly equals the number we put up there: 640. Make sense? We're going to display the game at 640x480, but the game itself is only 320x240. We're going to blow it up, or zoom it in, so that each pixel takes up twice as much space. This leads to a kind of chunky, retro aesthetic, but it's a good way to get extra performance out of your Flash game. Finally, we close our function, then our game object, then our package with closed brackets.


Step 3: Creating A Game State

Ok - save that file and close it, we're DONE with it. On to more exciting things! We probably won't dive as deep into this code, but I'll try and break it up into more manageable steps as well. First, we want to make a new AS3 file called "PlayState", which will store all of our gameplay code including creation, gameplay, controls and drawing.

package
{
	import org.flixel.*;
 
	public class PlayState extends FlxState
	{	
		override public function create():void
		{
			
		}
	}
}

Look familiar? It's pretty close to our other class, but this time we're extending "FlxState" instead (the medium Russian doll). This is technically a functional game state, but it won't do a thing, because we haven't asked it to! So next up we're going to declare a bunch of variables to hold our objects.


Step 4: Adding Some Variables

Not all game objects need to be stored in variables, but if you want to refer to that object later, then you need a way to look that object up. So let's add all the variables for our game:

public class PlayState extends FlxState
{
	public var level:FlxTilemap;
	public var player:FlxSprite;
	
	override public function create():void
	{

Here we have variables to store the game's level or environment, and then one variable for the player. Usually when you're designing a game or a prototype, you won't know ahead of time all the variables you need, and you'll add one or two at a time as you think of new features or options. This is a great way to start small and add complexity in a sensible, organic way.


Step 5: Creating The Background

It's nice to put something behind your level, whether it is a blue sky or a creepy cave, or even something abstract like this:

override public function create():void
{
	FlxG.bgColor = 0xffaaaaaa;

Here we are assigning a variable called "FlxG.bgColor" a weird series of numbers. "FlxG.bgColor" is a built-in variable that controls the background color of the game. The default value is black, and we want something brighter, like light gray. In Flash, colors are stored using kind of a weird format. It's similar to the hex values that you might use on your web page or something, just a little different:

WEB:   0xRRGGBB
FLASH: 0xAARRGGBB

Where "A" refers to the alpha of the color. "ff", like we used above, means fully opaque. The other values of course add up to light gray.


Step 6: Creating A Level

So now we have a blank gray background, we see how Flash handles colors, and we need to create a level for the player to explore. Add this code RIGHT after that bgColor code:

{
	FlxG.bgColor = 0xffaaaaaa;
 
	var data:Array = new Array(
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
		1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
		1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
		1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 );
	level = new FlxTilemap();
	level.loadMap(FlxTilemap.arrayToCSV(data,40), FlxTilemap.ImgAuto, 0, 0, FlxTilemap.AUTO);
	add(level);

Haha, holy crap! Look at all that code! Most of that is an actual 1:1 representation of the game level. It is 40 tiles wide and 30 tiles tall. 1s indicate where a solid wall or floor tile will be placed, while 0s represent blank or open space where the background will show through. Once we create the data or layout for our map, all we do is create a new map, and add it to the game state. That crazy line in the middle ("level.loadMap()") load the level data up in the tilemap object, and tell it we want Flixel to place the tiles for us automatically. Now we can run the game and see our level in action:

EZPlatformer Level.png

Step 7: Adding The Player

Now that we have level we can add a player character to run and jump around in there.

add(level);
 
//Create player (a red box)
player = new FlxSprite(FlxG.width/2 - 5);
player.makeGraphic(10,12,0xffaa1111);
player.maxVelocity.x = 80;
player.maxVelocity.y = 200;
player.acceleration.y = 200;
player.drag.x = player.maxVelocity.x*4;
add(player);

This code creates a new sprite and stores it in "player". It positions the player at the top center of the screen, and then creates a new graphic to represent the player: a 10x12 red box (using that weird color format from before). To get smooth, Mario-like movement, we are going to use flixel's acceleration variables, instead of just changing the position or velocity (i.e. speed) manually. To use the acceleration system, we want to set some barriers or boundaries to the acceleration behavior. So we set the "maxVelocity" on each axis (x is horizontal, y is vertical). Then we permanently set the player's vertical acceleration to 200; this simulates the force of gravity on the player object. To make sure the player slows down when you let go of a direction key, we're going to set the "drag" of the player object to 4 times the maxVelocity. This means if the player is moving left and suddenly stops, they will slide to a halt in about a quarter of a second. This feels smooth but not too slippy in our game!


Step 8: Updating The Player

So now a red box appears, and falls and falls and falls. That's not very useful, is it? It's time to assign some logic or instructions that will take a key press and translate it into a meaningful action, as well as check for overlaps or collisions between the player and the game level.

override public function update():void
{
	player.acceleration.x = 0;
	if(FlxG.keys.LEFT)
		player.acceleration.x = -player.maxVelocity.x*4;
	if(FlxG.keys.RIGHT)
		player.acceleration.x = player.maxVelocity.x*4;
	if(FlxG.keys.SPACE && player.isTouching(FlxObject.FLOOR))
		player.velocity.y = -player.maxVelocity.y/2;
	
	super.update();
	
	FlxG.collide(level,player);
}

So there are basically 3 chunks of code there, a big one and 2 small ones. Let's tackle the big one first; this represents the process of taking keyboard input and converting it into an action for the player character. In this case, we are telling the player to move left if the left arrow is pressed, right if the right arrow is pressed, and to jump when space bar is pressed. It looks a little complex, but that's because we want that smooth Mario movement, so we're setting acceleration to 4 times the maxVelocity (yeap, the same factor we used when we created the player). A lower acceleration means it would take longer for the player to reach his top speed, making the player feel heavy or clumsy. A high acceleration makes the character feel more twitchy and possibly over-responsive. This can be a tough thing to balance! Note, however, that when the player jumps, we only jump if the player is touching the floor, and then we set the velocity, not the acceleration.

super.update();

This line of code is tiny but very important. Like the FlxGame constructor, it is talking to the object you extended, in this case, FlxState. Calling FlxState.update() tells the game state to go through all the objects you added and call update() on each one. This is the call that actually moves them around the screen. update() checks for things like acceleration to be set, and then moves the object around for you.

FlxG.collide(level,player);

Finally, this critical line of code checks to see if the player overlaps the level anywhere, and if they do, pushes them out of the tile they're touching. This is what makes the level feel solid!

EZPlatformer Level and player.png


Conclusion

With some free tools and a few lines of code, it's really easy to get a simple game environment up and running in Flash. With just these two files we've created a cool, tilemap-based level for the player to explore, and created a player that you can control to do that exploring. That's all we have space for in this article, but all the source code for this project is online, and includes some important additions for real gameplay: some coins to collect, a win state, and a lose state.

EZPlatformer Final.png


Homework

Try playing with the source code - change the level layout, change the player speed, change the player jump height. If you get bored with that, go online and pull down all the source code for the complete version of EZPlatformer and start fiddling with those variables. There is some extra code in that download, but if you followed this so far, you should have no problem figuring out what's going on with the new additions. Try adding a sound effect when you pick up coins, or adding an animated image to the player. Good luck!


Online Resources

Personal tools