8533 Flixel 2 Tutorial [Flash, Tutorials, Games]

In this tutorial, I’ll explain how to develop a very simple Defender-style game using the open-source Flixel 2 game library for Flash. Flixel was written by Adam Saltsman and extracted from a couple of his own Flash games, including Gravity Hook, Fathom and Canabalt. There is also an iPhone version lurking around the corner, which will make Flixel even more useful.

Flixel is an AS3-only framework which means that we don’t need Adobe Flash CS4 at all. No movieclips, no timeline, no library and no high price tag. Instead, we will do everything with pure code and bitmap spritesheets.

Game demo:

Setting up the project

Before we start, you need to setup your working environment so you can compile AS3-only projects into SWF files using the free Flex SDK. For this tutorial I have used Flex SDK 4.0. If you work on a Mac I recommend using TextMate for editing the source code. Have a look at this tutorial for how to set it up for AS3 projects. If you work on Windows FlashDevelop is a popular choice and completely free. The Flixel wiki has a good tutorial on how to setup FlashDevelop for Flixel.

Next, download version 2.32 of the Flixel library from GitHub. Since new versions of Flixel are frequently released version 2.32 might not be latest one, but we can be sure the tutorial will work with this version of Flixel.

Now let’s set up the basic project directory structure. Create a project folder that contains the TextMate or FlashDevelop project file, a folder for all graphic and sound assets, a folder for all source files with the Main.as file at the root and a bin folder for the compiled SWF. Move the org folder from the Flixel library into the src folder. Create the subfolders de/pixelate/flixelprimer for the actual source files of the game.

You can find the complete source code for this tutorial on GitHub including the graphic and sound assets we will use for the tutorial.

Main.as will be the entry point for our game. Whenever it runs, it will execute the code in this class first. Make sure that you point the compiler to the Main.as file. (See links to the tutorials above on how to do this in TextMate or FlashDevelop.) When using Flixel we make the Main class a subclass of FlxGame, which is the heart of every Flixel game. The Main class imports the ActionScript files we need, sets up the screen size to 640×480 and starts the game with the PlayState.

package {
	import org.flixel.*;
	import de.pixelate.flixelprimer.*;
	[SWF(width="640", height="480", backgroundColor="#ABCC7D")]
	[Frame(factoryClass="Preloader")]
	public class Main extends FlxGame
	{
		public function Main():void
		{
			super(640, 480, PlayState, 1);
		}
	}
}

The main class also points to the Preloader class, which will show a nice 8-bit style preloader bar when loading the game in the browser. The class looks like this:

package
{
	import org.flixel.FlxPreloader;
	public class Preloader extends FlxPreloader
	{
		public function Preloader():void
		{
			className = "Main";
			super();
		}
	}
}

A Flixel game usually consists of different states which subclass FlxState. A common usage would be to have a state for the menu screen, one for the actual game and one for the highscore screen. In our case we will only use one state to keep things simple. Let’s add the scaffold for the play state. The create function will be called once PlayState is created by the main class. The update function is the main loop of the game which will be called every frame.

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

Adding the player sprite and controls

Now that we have a good scaffold for the game, let’s add a space ship for the player. Create a new class in src/de/pixelate/flixelprimer called Ship.as:

package de.pixelate.flixelprimer
{
	import org.flixel.*;
	public class Ship extends FlxSprite
	{
		[Embed(source="../../../../assets/png/Ship.png")]
		private var ImgShip:Class;
		public function Ship():void
		{
			super(50, 50, ImgShip);
		}
		override public function update():void
		{
			velocity.x = 0;
			velocity.y = 0;
			if(FlxG.keys.LEFT)
			{
				velocity.x = -250;
			}
			else if(FlxG.keys.RIGHT)
			{
				velocity.x = 250;
			}
			if(FlxG.keys.UP)
			{
				velocity.y = -250;
			}
			else if(FlxG.keys.DOWN)
			{
				velocity.y = 250;
			}
			super.update();
		}
	}
}

FlxSprite is the main game object class in Flixel and handles basic physics and animation. The Ship class extends FlxSprite, draws the sprite at position x 50 and y 50 and sets the bitmap graphic for the ship which resides in the assets folder. The player controls are handled in the update function which is called by Flixel every frame. First the velocity is set back to zero. Then we check if the player is pressing the arrow keys and change the velocity accordingly. The super.update() call takes care of changing the position of the ship based on the current velocity.

Now we have to add the ship to the game stage. Add the following code to the create function in PlayState.as and declare _ship as a private variable above the function. We also set the background color of the game state to a nice monochrome green.

private var _ship: Ship;
override public function create():void
{
	bgColor = 0xFFABCC7D;
	_ship = new Ship();
	add(_ship);
	super.create();
}

Now compile the project and start the SWF in the Flash Player. Move the ship around with the arrow keys. The player input works well but the ship can move outside the screen. Let’s fix that. Add the following code into the update function in Ship.as where we make sure that the position of the ship is always within the screen dimensions. We also add a padding of 16 pixels to the if-condition to make it look nicer. Make sure to insert the code after the super.update() call.

if(x > FlxG.width-width-16)
{
	x = FlxG.width-width-16;
}
else if(x < 16)
{
	x = 16;
}
if(y > FlxG.height-height-16)
{
	y = FlxG.height-height-16;
}
else if(y < 16)
{
	y = 16;
}

Adding enemies

Next, let’s add some enemies. Create the file Alien.as inside src/de/pixelate/flixelprimer. The Alien class takes x and y as arguments in the constructor so we can easily position the sprite when creating it. We set the horizontal velocity to -200 which makes the alien move from right to left at a constant speed. In the update function we use a cosine function to move the alien up and down in a wave motion.

package de.pixelate.flixelprimer
{
	import org.flixel.*;
	public class Alien extends FlxSprite
	{
		[Embed(source="../../../../assets/png/Alien.png")]
		private var ImgAlien:Class;
		public function Alien(x: Number, y: Number):void
		{
			super(x, y, ImgAlien);
			velocity.x = -200;
		}
		override public function update():void
		{
	    	velocity.y = Math.cos(x / 50) * 50;
			super.update();
		}
	}
}

To spawn the aliens, we need to add some more code to the PlayState. First we add a FlxGroup for the alien sprites, a logical container which helps us to organize the collision detection between the player ship and aliens. Second we add a spawn timer which controls the interval when a new alien is spawned. The interval starts at 2.5 seconds and slowly becomes faster until it reaches it’s maximum of spawning a new alien every tenth second. Finally we add a function for spawning an alien at a random position at the right border of the screen.

package de.pixelate.flixelprimer
{
	import org.flixel.*;
	public class PlayState extends FlxState
	{
		private var _ship: Ship;
		private var _aliens: FlxGroup;
		private var _spawnTimer: Number;
		private var _spawnInterval: Number = 2.5;
		override public function create():void
		{
			bgColor = 0x0ABCC7D;
			_ship = new Ship();
			add(_ship);
			_aliens = new FlxGroup();
			add(_aliens);
			resetSpawnTimer();
			super.create();
		}
		override public function update():void
		{
			_spawnTimer -= FlxG.elapsed;
			if(_spawnTimer < 0)
			{
				spawnAlien();
				resetSpawnTimer();
			}
			super.update();
		}
		private function spawnAlien():void
		{
			var x: Number = FlxG.width;
			var y: Number = Math.random() * (FlxG.height - 100) + 50;
			_aliens.add(new Alien(x, y));
		}
		private function resetSpawnTimer():void
		{
			_spawnTimer = _spawnInterval;
			_spawnInterval *= 0.95;
			if(_spawnInterval < 0.1)
			{
				_spawnInterval = 0.1;
			}
		}
	}
}

Adding bullets

Now let’s add bullets so the player can shoot to fight the aliens. Create a new class in src/de/pixelate/flixelprimer called Bullet.as. Nothing fancy here, except that we don’t use a bitmap graphic, but draw the bullet directly in the createGraphic() call.

package de.pixelate.flixelprimer
{
	import org.flixel.*;
	public class Bullet extends FlxSprite
	{
		public function Bullet(x: Number, y: Number):void
		{
			super(x, y);
			createGraphic(16, 4, 0xFF597137);
			velocity.x = 1000;
		}
	}
}

As with the aliens we add a FlxGroup for the bullets and a spawn function as well. In the update function we check if the player just pressed the space key and fire a bullet if that is the case.

package de.pixelate.flixelprimer
{
	import org.flixel.*;
	public class PlayState extends FlxState
	{
		private var _ship: Ship;
		private var _aliens: FlxGroup;
		private var _bullets: FlxGroup;
		private var _spawnTimer: Number;
		private var _spawnInterval: Number = 2.5;
		override public function create():void
		{
			bgColor = 0x0ABCC7D;
			_ship = new Ship();
			add(_ship);
			_aliens = new FlxGroup();
			add(_aliens);
			_bullets = new FlxGroup();
			add(_bullets);
			resetSpawnTimer();
			super.create();
		}
		override public function update():void
		{
			if(FlxG.keys.justPressed("SPACE") && _ship.dead == false)
			{
				spawnBullet(_ship.getBulletSpawnPosition());
			}
			_spawnTimer -= FlxG.elapsed;
			if(_spawnTimer < 0)
			{
				spawnAlien();
				resetSpawnTimer();
			}
			super.update();
		}
		private function spawnBullet(p: FlxPoint):void
		{
			var bullet: Bullet = new Bullet(p.x, p.y);
			_bullets.add(bullet);
		}
		// ...
	}
}

Note that we also added a new function to Ship.as to get the correct spawn position for the bullet from the player’s ship:

public function getBulletSpawnPosition():FlxPoint
{
	var p: FlxPoint = new FlxPoint(x + 36, y + 12);
	return p;
}

Adding collision detection

The only thing missing to make our game playable is collision detection. We handle the collision in the beginning of the update function of PlayState. The first call checks if any sprite in the aliens group overlaps with any bullet sprite and calls the overlapAlienBullet callback if so. The second call checks for collisions between the aliens and the ship sprite.

FlxU.overlap(_aliens, _bullets, overlapAlienBullet);
FlxU.overlap(_aliens, _ship, overlapAlienShip);

In the callback functions we define what happens in case of a collision. When an alien sprite gets hit by a bullet, we destroy both the alien and the bullet sprite, increase the score counter and display the updated score on the screen. When the ship gets hit by an alien we also let the screen rumble a bit using the quake function and display a game over message.

private function overlapAlienBullet(alien: Alien, bullet: Bullet):void
{
	alien.kill();
	bullet.kill();
	FlxG.score += 1;
	_scoreText.text = FlxG.score.toString();
}
private function overlapAlienShip(alien: Alien, ship: Ship):void
{
	ship.kill();
	alien.kill();
	FlxG.quake.start(0.02);
	_gameOverText = new FlxText(0, FlxG.height / 2, FlxG.width,
	"GAME OVER\nPRESS ENTER TO PLAY AGAIN");
	_gameOverText.setFormat(null, 16, 0xFF597137, "center");
	add(_gameOverText);
}

We also need to initialize the text fields. Add a private variable declaration for the text fields in PlayState:

private var _scoreText: FlxText;
private var _gameOverText: FlxText;

To allow the player to restart the game on game over we add the following code to the update function in the PlayState:

if(FlxG.keys.ENTER && _ship.dead)
{
	FlxG.state = new PlayState();
}

Create the score text field in the create function in PlayState and set the FlxG.score variable to zero.

FlxG.score = 0;
_scoreText = new FlxText(10, 8, 200, "0");
_scoreText.setFormat(null, 32, 0xFF597137, "left");
add(_scoreText);

Adding sounds

The game is fully playable now. Let’s add some sounds to give the player a better feedback of the action. Define all references to the sound files in the header of PlayState.

public class PlayState extends FlxState
{
	[Embed(source="../../../../assets/mp3/ExplosionShip.mp3")]
	private var SoundExplosionShip:Class;
	[Embed(source="../../../../assets/mp3/ExplosionAlien.mp3")]
	private var SoundExplosionAlien:Class;
	[Embed(source="../../../../assets/mp3/Bullet.mp3")]
	private var SoundBullet:Class;
	// ...
}

We call the FlxG.play function to play the sounds in the according situations.

// In spawnBullet()
FlxG.play(SoundBullet);
// In overlapAlienBullet()
FlxG.play(SoundExplosionAlien);
// In overlapAlienShip()
FlxG.play(SoundExplosionShip);

Adding explosions

Finally we add some particle explosions when the ship or an alien is destroyed using the FlxEmitter class. We want to use the same explosion for the ship and the aliens so we move the explosion code into a reusable function.

private function createEmitter():FlxEmitter
{
	var emitter:FlxEmitter = new FlxEmitter();
	emitter.delay = 1;
	emitter.gravity = 0;
	emitter.maxRotation = 0;
	emitter.setXSpeed(-500, 500);
	emitter.setYSpeed(-500, 500);
	var particles: int = 10;
	for(var i: int = 0; i < particles; i++)
	{
		var particle:FlxSprite = new FlxSprite();
		particle.createGraphic(2, 2, 0xFF597137);
		particle.exists = false;
		emitter.add(particle);
	}
	emitter.start();
	add(emitter);
	return emitter;
}

In the collision callback functions we create the emitter for the explosion and place it at the position of the ship or alien using emitter.at().

// In overlapAlienBullet()
var emitter:FlxEmitter = createEmitter();
emitter.at(alien);
// In overlapAlienShip()
var emitter:FlxEmitter = createEmitter();
emitter.at(ship);

Now you have a simple game in Flixel that should help you getting started building your own ideas. If you have any questions, leave a comment or have a look at the Flixel forums and the wiki.

Related Posts with Thumbnails

View Comments to “ Flixel 2 Tutorial [Flash, Tutorials, Games] ”

  1. It might be worth giving mine a shout if anyone out there prefers to use Xcode? > http://wiki.github.com/AdamAtomic/flixel/using-...

  2. If you would like to create a similar type of game using Unity, check out Alec Holowka's great tutorial: http://infiniteammo.ca/schpooter/

  3. great tutorial, thanks – got me a jumpstart into flixel (and AS3-coding, actually), much appreciated!

    found 2 minor typos/extra semicolons: “i <;” and “else if(x <; 16)”

    plus, of course createEmitter should be put into PlayState.

    thanks again!

  4. Thomas, I'm glad you like the tutorial. We fixed the typos, thanks for the hint!

  5. hi, thanks for the great tutorial im still learning when i can bits of programming here and there. This helped me a lot and i sued your game as a template for a little experience. I run a music blog and from time to time post a mix. This time i made it a bit different and though of a kind of interactive mix where you could play along with the songs so i used your tutorial as a template and out of that came this :) http://www.bigo.de.tp/

    Sorry for the long comment
    cheers !

  6. lol sorry about the bas english and the typo i meant used not sued !

  7. Thanks for your comment, Wilson! I'm glad you found the tutorial helpful.

  8. Hey, this is a great tutorial so far! Unfortunately, my keyboard input isn't work. I've double checked my code several times, and it just doesn't seem to be working. I'm using FlashDevelop, flixel v2.34, and Flex SDK 4.0.0.14159.

  9. @TheHanna: Could you download the complete source from GitHub and check if that is working for you?


  10. davidjmcclelland

    Apr 25, 2010
    Reply

    I had same issue as TheHanna. Downloading PlayState and commenting the parts I hadn't made classes for yet (bullets) worked. Thanks for a tut that works on latest code – much appreciated.


  11. smokeythebare

    Apr 27, 2010
    Reply

    I had a problem with key input when I was supposed to run it the first time. I went through the code over and over to make sure I didn't mess it up. I didn't mess it up, but when I added “super.update();” to the “PlayState.as” “update” function. All of a sudden, everything works! :D I hope this helps you all.

  12. @smokeythebare: Yes, that was the problem. Thanks for pointing that out! I updated the code, so it should work now right away.

  13. Seriously, your syntax highlighter keeps popping up flash debug errors when i hover over the code elements.

    Other than that, this tutorial is really, really great!


  14. twincannon

    May 7, 2010
    Reply

    Super win tutorial. Was pulling my hair out trying to find a tutorial that worked with the newer versions of flixel and this one does the job perfectly and succinctly. Thanks so much!

  15. Thanks twincannon – I'm glad the tutorial was helpful for you.

  16. great tutorial, thank you!


  17. MercureSlime

    May 20, 2010
    Reply

    Super turorial. However, when I try my games, when sounds should play, the flash debugger pop up :

    “[Fault] exception, information=TypeError: Error #1034: Type Coercion failed: cannot convert PlayState_SoundBullet@2e56b81 to flash.media.Sound.
    Fault, loadEmbedded() at FlxSound.as:100″

    Any idea?

  18. MercureSlime, can you get it to work using the complete source code from GitHub (http://github.com/pixelate/flixel_primer)?


  19. MercureSlime

    May 20, 2010
    Reply

    When I click on the link i got a cat/octopus saying “That page doesn't exist!”


  20. MercureSlime

    May 20, 2010
    Reply

    Yes, it works perfectly. I found the problem:

    When I tried to add my mp3 in the code, i used the “Insert Into Document” function of FlashDevelop and it produced this code :
    [Embed(source='../assets/ExplosionAlien.mp3', mimeType='application/octet-stream')]

    The second part of the code seems to be the origin of the exception. After watching your code, I wrote :
    [Embed(source='../assets/ExplosionAlien.mp3')]

    And now it works!

    Thank you for the help!

    (ps : your games are awesome! especially 'Understanding Games' which give me the same feeling I had reading 'The Invisible Art')

  21. Great you got it working! Thanks for the kind words – I'm glad to hear you like Understanding Games.

  22. I just followed your tutorial. Got it working with FlashBuilder in roughly 2 hours… I'm still proud :)
    Thank you so much for sharing !

  23. Vraf, I'm glad you made it through the tutorial and found it helpful.

  24. Really loved the tutorial — wonderful starting place to jump into flixel. Glad it was here. :) I do have a question though. There seems to be a bug where the player can't fire if they are simultaneously holding left and either up or down (moving up-left or down-left.) Any clue what this might be due to? Tried to track it down with no success.

  25. Hi Vancext. I can't reproduce the bug you are describing. Can you try to download the complete source from http://github.com/pixelate/flixel_primer and see if this works for you?

  26. @Vancext: The problem you described is a certain limitation with most PS/2 keyboards that cannot get some simultaneous key presses like SPACE and LEFT ARROW at the same time. Instead of buying a new keyboard, you can map the shoot key to something like “X”.

    Try this in your PlayState.as on the update() function :

    REPLACE

    if(FlxG.keys.justPressed(“SPACE”) && _ship.dead == false)
    {
    spawnBullet(_ship.getBulletSpawnPosition());
    }

    WITH THIS

    if(FlxG.keys.justPressed(“X”) && _ship.dead == false)
    {
    spawnBullet(_ship.getBulletSpawnPosition());
    }

    … and you should be fine ;)

  27. That was it. Sweet! Thanks Teo. :)

  28. Thank you for this really clear and good tutorial.
    Just a suggestion about the spawn timer, perhaps you use a solution like this:

    private var _spawnTimer:Number = 0;
    private var _spawnInterval:Number = 60;

    in the update method :
    if (_spawnTimer % _spawnInterval == 0) {
    spawnEnemy();
    }
    _spawnTimer ++;

    this should allow you to use differents interval values to generate other enemies or weapons.

    Sorry for my english…

  29. Woot! This is superbly helpful.

  30. i feel kinda dumb but i am hung up in the beginning with the Embed(source for the sprites. its just the directory list to the PNG that flixel is making but doesn't exist yet correct? i just go to properties on that folder and copy and paste the directory line to where that file will be but it doesn't work. flash develop says it can't resolve/transcode it.

  31. Do you have a png stored at that location, Steve? (I just made my own, and put them in a folder right under source called “images”, so my embed looked like

    [Embed(source="/images/Ship.png")]


  32. OmegaStorm

    Aug 9, 2010
    Reply

    I'm new to Flixel/AS3 myself and your tutorial was very helpful. I decided to keep adding to it and wrote up a tutorial (as a direct continuation of yours) to show how to do what I did. It's my first tutorial and I hope I didn't leave anything out. Let me know what you think.

    http://www.omegastormproductions.com/uber-flash...

  33. Nice addition to the tutorial, Omega. I like the power upgrades!

  34. Fantastic tutorial Andreas, Flixel should put you on their payroll ;)

    The tutorial game suffers from a known bug in the Flash player. When the bullet audio plays you’ll see a brief “hiccup” in the enemies’ graphics, as long as a previous bullet sound is not still playing. This Flash bug is related to a sound playing when no other sounds are already playing. The workaround is to always have some audio playing. Either background music or a snippet of silence. Something like FlxG.playMusic(SoundOfSilence); should do the trick.

  35. JB, thanks for the nice words and for sharing the sound trick!


1 Trackback(s)
  1. Aug 8, 2010 : Flex, Flixel and Xcode « stackofboxes

Sorry, comments for this entry are closed at this time.

blog comments powered by Disqus