7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
  1. Code
  2. Games

Build a Stage3D Shoot-'Em-Up: Explosions, Parallax, and Collisions

Scroll to top
Read Time: 40 mins
This post is part of a series called Shoot-'Em-Up.
Starling Particle Effects for Stage3D Shooter Games
Quick Tip: A Simple Score Display for Flash Games

In this tutorial series (part free, part Premium) we're creating a high-performance 2D shoot-em-up using the new hardware-accelerated Stage3D rendering engine. In this part, we're adding eye candy with particle systems, a parallax effect, framerate-independent game loop timers, and collision detection.

Final Result Preview

Let's take a look at the final result we will be working towards: a hardware-accelerated shoot-em-up demo that includes everything from parts one and two of this series, plus an efficient particle system for loads of eye-candy, framerate-independent timers for consistent movement, a subtle background parallax effect, the ability for entities to orbit one another, and a collision detection system capable of handling tons of entities.

Check it out: every explosion is slightly different!

Introduction: Welcome to Level Three!

Let's continue to make a side-scrolling shooter inspired by retro arcade titles such as R-Type or Gradius in actionscript.

In the first part of this series, we implemented a basic 2D sprite engine that achieves great performance through the use of Stage3D hardware rendering as well as several optimizations.

In (the second part, we implemented a title screen, the main menu, sound effects and music, and an input system so that the player could control their spaceship using the keyboard.

In this part, we are going to add all the eye-candy: a particle system, complete with sparks, flying debris, shockwaves, engine fire trails and tons of explosions.

In previous versions, our game was framerate-locked and ran slower on old computers. To ensure the same timings for everything no matter what the framerate, we are going to change all movement and animation simulation units to account for the exact number of milliseconds that have passed since the previous frame. This way, whether you are running at 60fps on a modern gaming rig or your grandma's old netbook, the game experience itself will be identical.

Finally, we're going to program collision detection, which is required in nearly any game you can imagine. In order to trigger explosions and we need to be able to detect when a bullet has hit an enemy. While we're at it, we are going to throw in a little bit of additional pizazz, just for fun, including a vertical parallax effect to the starfield background and an R-Type inspired orbiting "power orb" companion that circles the player's ship.

Step 1: Open Your Existing Project

If you don't already have it, be sure to download the source code from part two. Open the project file in FlashDevelop and get ready to upgrade your game!

This source code will work in any other AS3 compiler, from CS6 to Flash Builder. If you do use FB, be sure to include "-default-frame-rate 60" in your compiler options to ensure you get the best performance.

Step 2: Get the Party Started!

We are going to take advantage of the well-optimized internals of your entity manager class from last time by adding a simple particle system to it that still uses all the same basic entity and spritesheet functionality.

This way, we are still rendering the entire game's sprites (ships, bullets and all) in a single geometry batch using a single texture. Therefore, much of the simulation of particles will be handled the same way as we currently handle the movement of the enemies. Most importantly, we are going to keep the number of draw calls to a minimum by inserting particles into our existing sprite batch.

The fist thing we need to do is define a few interesting effects. We're going to have a little fun and create some cool-looking effects such as an expanding ring of blueish energy (a "shockwave"), a bunch of different fireballs that spin and fade out, some fast moving sparks that stay nice and bright and some metallic spaceship hull debris.

Create a new file in your project called GameParticles.as and implement the basic particle and explosion helper functions.

In the code above, we created a new class that requires a reference to our existing EntityManager. In the class constructor, we initialize a list of all known particles, which might be useful in a later tutorial to save having to iterate through all known entities if all we want are the particles.

The addExplosion() function will be called in response to a collision detection between a bullet and an enemy during gameplay. It will spawn a shockwave, some debris, a huge fireball, some smaller spinnng balls of fire and a bunch of flying sparks. Let's define these different effects next - but before we can, we need a generic particle creation function.

Step 3: Define a Basic Particle

Continue to add to GameParticles.as by implementing the initialization function for a generic particle. It will use our entity manager to spawn (or respawn from the list of inactive entities) a sprite with some appropriate properties.

Some of the default values can't be inserted into the function declaration itself since they will take advantage of some randomness, so we simply use NaN ("not a number") as the optional function parameter defaults and execute some code if no value was defined when this function gets run. This way, we don't need to specifiy everything about a particular particle if the defaults will do. If we used zero as the default, then we couldn't force zero to be the actual value used.

Step 4: Eye Candy!

The final step in the creation of our awesome new particle system class is to create the functions for various special effects used by our explosions in the game as listed above.

That's it for our simplistic particle system. As you can see, we only define the behaviors of each type of visual effect in this class: the work of animating each particle is done at the same time as animating all the other entities, by our entity manager class which we will upgrade next. First, however, we need to add a bunch of new properties to our basic entity class to support these new behaviors.

Step 5: Upgrade the Entity Class

We need to add a few new properties to our basic entity class. Since we are going to make the player's ship output a steady stream of fire from the engines, for example, we want to be able to store this new information for every entity. Additionally, some new properties that pertain to particle simulation and collision detection need to be defined here. Open your existing Entity.as and make a few changes as follows.

As you can see, much of the inits are the same as in the previous tutorial. When an entity "dies" (that is to say, is made invisible and available for reuse by our optimized entity reuse pool) we turn off a few of these new values so that the next entity to reuse this sprite doesn't take on unwanted behaviors.

Step 6: Implement Collision Detection

This is the most important part of this tutorial. We are going to take our tech demo from a mere graphics demonstraton to something that feels like an actual game by implementing our collision detection routine.

To keep things simple (and fast) for now we are not going to implement bounding-box collision or box-to-sphere or ray collisions, which are often used in complex physics engines. We are going to focus on just what is needed by our game, which is a simple way to detect if something is "close enough" to something else to trigger a collision (and resulting explosion).

Sphere collision detection simply checks to see if one circle is inside the radius of another. This way, we can give different entities a "radius" size and check how far their centerpoints are to determine if these two circles are overlapping. To make our game run even faster, we are only going to do this math if both entities are set to be collide-able.

As a further optimization, instead of using the built-in Point.distance function, we are going to do the trigonometry math manually, since this has been shown in benchmarks to run approximately six times faster. It looks like more code, but all we're really doing is Pythagoras's theorem without any square roots.

By avoiding bothering with the "proper" distance and instead comparing "squared" distances using only multiplication, we actually are checking the distance to the power of two. None of this matters, however. The end result is a very fast and simple way of checking to see if two circles overlap that doesn't need to use sin, cos, divisions, power-of or square root calculations. Sleazy, but effective!

Tht's it for our newly upgraded Entity.as class. We now have entities in our game that store the stats needed for this tutorial and can calculate collisions.

Step 7: Upgrade the Entity Manager

There are many new additions to the entity manager which add the ability to request collision detection, trigger sound effects, add particles and much more. Open your existing EntityManager.as and make a few changes as follows. There are so many small changes since last time that the entire file is listed here to avoid confusion, so you might want to simply replace the entire class with this new third version.

In the code above, we are defining a large number of class variables, many of which are new to this tutorial. Of note are those that pertain to timing and speed. Instead of the relative speeds of each moving ship being tied directly to the framerate, we want the game to run at the same speed no matter what kind of machine the player is using.

By keeping track of the elapsed time since the previous frame, we can multiply various "speed per second" constants by the number of milliseconds that the current frame took in order to achieve smooth movement even if the FPS fluctuates.

Step 8: Upgrade the Spritesheet

In the code above we are storing constant values for the positions of various sprites in our spritesheet image. We're added a few new kinds of sprite that pertain to particles, and have reorganized the spritesheet to ensure that our randly-spawning enemies only come from the first few rows. These changes have neccessitated a few minor changes to our spritesheet:

Our upgraded spritesheet.
Right-click to download.

As you can see, we now have some extra sprites for explosions, shockwaves, sparks and debris.

Step 9: Upgrade the Inits

Continuing with EntityManager.as, upgrade the basic init routines to create lists for each type of entity. Each of these lists keeps track of a specific kind of entity/sprite. We can use these lists to improve performance when we need to loop through all of only a particular kind of entity, saving the time it would take to look through all known entities of any kind. The particles, however, are going to be stored in their own class instance using the new GameParticles class we implemented above.

Above, we've also upgraded the setPosition function to allow entities to move beyond any of the four edges of the screen before being respawned, since things are moving in all directions now. In last week's tutorial, entities were only destroyed if they moved off the left edge and bounced of the other three edges of the screen. The createBatch and respawn functions are virtually unchanged since last time.

To avoid entities being put into the various sub-lists we have implemented in our class constructor more than once, we add a new flag to the entity, enEntity.recycled which informs the entity manager whether or not the sprite that it returns is brand new or not. This way, only a single reference to each sprite is stored in our lists.

Step 10: Fast Random

As a further small optimization, instead of using Math.random() over and over during the game, we are going to implement a slightly faster, XOR-based pseudo-random function.

This function has a secondary benefit, apart from the fact that it runs four times faster than the built-in random function. It can optionally be seeded with a constant value as the starting fastrandomseed in order to produce the exact same set of random numbers in sequence each time.

This could be handy in future versions of your game for storing replays or for savegames. For now, however, the only reason we're doing things this way is to eke out a tiny bit more performance.

Step 11: Upgrade the Player

Continuing with EntityManager.as, implement the following minor upgrades to the player inits. In particular, we are going to set the boolean flag thePlayer.leavesTrail to true so that the player's engines emit a steady stream of fireballs that quickly shrink and fade out. This will give a nice effect.

Additionally, just for fun let's implement a "power orb" that orbits the player's ship. This "companion" is something that is heavily inspired by retro shooters like R-Type and will give our game a little more pizazz. The "orb", as we'll call it, will spin around the player, emitting a smaller trail of its own, and will be able to destroy incoming enemies.

In future versions of our game, it might be interesting to make "orb kills" give the player more points than those achieved by shooting bullets. A sort of "skill shot", so to speak. You could even implement a special achievement award for an entire level completed without ever firing a shot - by using the orb as the sole means for defending yourself.

Step 12: Bullets and Enemies

The shootBullet and addEntity functions from last time remain virtually the same, but are included here to make your life easier. Note that we are now using the new entity properties for collisions, and the random entities are now being set to the proper rotation to be facing whatever direction they are flying.

Step 13: Handy Math Utilities

Next, we need to implement some of the helper math functions used by the spawning routines above. These are very handy and can be reused in all sorts of ways in the future. Because Flash (and most game engines, at least in the low level routines) store an object's rotation using radians (instead of degrees) we've defined a constant above that speeds up these calculations.

Step 14: Collision Detection

Each frame, as each entity moves to a new location, those that have the entity property collidemode set to a non-zero value will be sent to the checkCollisions routine below.

Instead of having every single entity loop though every other known entity and check for collisions, we can optimize the vast majority of these checks out. This is because only bullets need to check for collisions, and in our demo game they can only collide with enemy ships (not other bullets, particles or the player).

Therefore, we can now take advantage of one of the "sub-lists" we filled above to search through only the entities that might require collision detection, the allEnemies list. The collision detection function that we wrote earlier (in the entity class) will check to ensure that the bullet and enemy are close enough to each other (and don't have the same "owner" which will be helpful for future versions where enemy bullets need not collide with friends).

Step 15: Update the Simulation

The final step in upgrading our speedy EntityManager.as is to upgrade the entire simulation update loop. This function goes through the entire list of active entities (enemies, bullets and particles) and updates their positions, transparency, size, rotation and more.

We need to ensure that everything animates at the same speed no matter what the current framerate of the game is, so in this new version we are keeping track of the elapsed time since the previous frame and multiplying all speeds by this value.

This way, if the framerate is a silky smooth 60FPS, an enemy might move just one pixel in a particular direction, but if the player was using an old computer with poor graphic performance and the framerate was only 15FPS the same sprite would be moved 4 pixels.

Doing things this way ensures a smooth playing experience no matter what kind of machine you are using; plus, during gameplay, even on a fast machine, the FPS will fluctuate, and we don't want the player's flying speed to fluctuate along with it.

In the code above, not only do we update the positions and rotations of each sprite, but we also optionally check for collisions, orbit other entities, and "die" (become available for reuse in our object pool) when we move off-screen, fade all the way to 100% invisible, or shrink in scale to nothingness.

That's it for the entity manager upgrades. All that remains in the eye-candy-filled third version of our game is to add a few little extras and to upgrade our main game class.

Step 16: Upgrade the Background

Just for fun, let's implement a simple and subtle vertical scrolling parallax effect to our background. We will change our background class to keep track of the player's current vertical position as a percentage of the height of the screen.

When the ship moves, we will scroll the background sprites just a little bit in the opposite direction, which will give the game a little more of a three dimensional feeling. Open your existing GameBackground.as and make a few changes as follows.

To avoid confusion, the entire class is presented here. This first section with all the inits remains unchanged since last time:

Step 17: Background Parallax

Parallax is a word that is used to describe the way that things seem to move less the farther away you are viewing them. In this case, our background is going to scroll vertically just a little so that it gives a subtle effect in response the the player's movements. Add a new function, yParallax and upgrade the existing update routine as follows:

That's all we need to do to the background class. Next up, a few new sound effects and the main game class and we're done for this week!

Step 18: Boom Time!

We definitely want to trigger some explosion sound effects. We are going to add three explosion sound effects to our game and randomly cycle through them during gameplay.

Whenever our entity manager is told that a collision was detected, it will choose one of these three new explosion sounds to play. Open your existing GameSound.as and make a few changes as follows.

Using three different sounds for the same event will help to keep things less repetitive during gameplay. Now that we've implemented particles, new sounds, collision detection above, all that remains is to make sure that all this new functionality appears in our game.

Step 19: Upgrade the Main Game Class

The cool new upgrades we implemented above to our game also require some small changes to the main game document class. Open your existing Game.as and make a few changes as follows.

(As before, to avoid confusion, the entire class is presented here. All the inits remain the same as last week, except for a couple lines of code where we add some new properties for keeping track of the current timestamp and how many milliseconds have elapsed since the previous frame. We also send a reference to the sound system to our entity manager so that it has access to our new explosion sound effects.)

Step 20: FPS Independence

Instead of moving a set amount each frame no matter what the framerate, we are now keeping track of elapsed time and multiplaying the player's speed by however much time elapsed since th previous frame.

We are also going to spawn a new particle every frame just behind the player's ship, as a sort of "vapor trail" effect.

Finally, in our input routines, we will keep track of the last time a bullet was fired and wait a short amount of time in between shots rather than allowing a new bullet to be spawned every single frame.

Therefore, we need to make a few very minor upgrades to our existing player logic routines. Continue editing Main.as as follows:

Step 21: Upgrade the Render Loop

The final set of upgrades we need to make to our game is to the "render loop" which is run every single frame in response to an ENTER_FRAME event.

We first measure the amount of time that has elapsed since the previous frame, and store the number of milliseconds by which we will be multiplying all sorts of movement and animation values, both here and in the particle animation, where we slowly fade out or scale up various entities that are part of our explosions over time.

Next, we tell the game background where the player is in relation to the screen height so that it can have the subtle parallax vertical scrolling effect.

Finally, instead of spawning a new enemy every single frame, we randomly spawn a new enemy 10% of the time so that there is a little more breathing room. We then tell the entity manager to update the game simulation and render everything.

We're done! Compile your project, fix any typos, and run the game. If you're having trouble with the code you typed in or just want the instant gratification of everything in one place, remember that you can download the full source code here. You should see something that looks like this:

Screenshot of this week's upgrades in actionScreenshot of this week's upgrades in actionScreenshot of this week's upgrades in action

Part Three Complete: Prepare for Level Four!

That's it for tutorial number three in this series. Our super-optimized Flash 11 Stage3D Shoot-em-up game is finally starting to almost feel fun! Tune in next week for the first of three Premium tutorials to watch the game slowly evolve into an eminently-playable, silky-smooth 60fps shoot-em-up.

In the next tutorial we will implement enemy AI (by creating a simple artificial intelligence class) so that bad guys no longer move in a straight line. They will also shoot at you and present more of a challenge. We will also add more visual interest to the game by programming a background "terrain" system. Imagine blasting aliens amongst asteroid fields, huge space stations, planets and galaxies. This will help add variety to the game compared to the simple, neverending starfield we currently use as our backdrop.

In future versions of our game (parts five and six) we will program health, a score, and a nice-looking HUD (heads-up-display) GUI overlay to hold these counters. We will implement game over and winning conditions, difficulty and game balance (so that some enemy ships take more than one shot to destroy). We will be adding different weapon upgrades and powerups that change the kinds of bullets you shoot and what companion "orb" your ship has following it, and finally a BOSS BATTLE!

By the end of this six part tutorial series you will have a complete, playable, high-performance shoot-em-up game that feels polished and complete, and has a beginning, middle and end. I hope you'll join me all the way.

I'd love to hear from you regarding this tutorial. I warmly welcome all readers to get in touch with me via twitter: @McFunkypants, my blog mcfunkypants.com or on Google+ any time. In particular, I'd love to see the games you make using this code and I'm always looking for new topics to write future tutorials on. Get in touch with me any time.

If you have enjoyed these tutorials thus far, perhaps you'd like to learn more about Stage3D? If so, why not buy my Stage3d book! =)

Good luck and HAVE FUN!

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.