Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
  1. Game Development
  2. Game Design

Build a Stage3D Shoot-'Em-Up: Full-Screen Boss Battles and Polish

Read Time:55 minsLanguages:
This post is part of a series called Shoot-'Em-Up.
Pixel-Level Collision Detection Based on Pixel Colors

In this tutorial series we will create a high-performance 2D shoot-em-up using Flash 11's new hardware-accelerated Stage3D rendering engine. We will be taking advantage of several hardcore optimization techniques to achieve great 2D sprite rendering performance.

Also available in this series:

  1. Build a Stage3D Shoot-’Em-Up: Sprite Test
  2. Build a Stage3D Shoot-’Em-Up: Interaction
  3. Build a Stage3D Shoot-’Em-Up: Explosions, Parallax, and Collisions
  4. Build a Stage3D Shoot-'Em-Up: Terrain, Enemy AI, and Level Data
  5. Build a Stage3D Shoot-’Em-Up: Score, Health, Lives, HUD and Transitions
  6. Build a Stage3D Shoot-’Em-Up: Full-Screen Boss Battles and Polish

Final Result Preview

Let's take a look at the final result we will be working towards: the final game, complete with blazingly fast sprite rendering, sound and music, multiple detailed levels, numerous enemies to destroy, score, health, lives, particle systems, level transitions, full screen rendering, an NPC character, slow-mo, a preloader progress bar, and a boss battle.

Introduction: Welcome to Level Six!

This is the final installment in the of the Stage3D shoot-em-up tutorial series. Let's finish our epic quest 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 and as several optimizations.

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

In the third part, we added all the eye-candy: a particle system complete with sparks, flying debris, shockwaves, engine fire trails and tons of explosions. We also added accurate timers, collision detection and an R-Type inspired orbiting "power orb" companion that circles the player's ship.

In the fourth part, we added A.I. (artificial intelligence) to our enemies by creating several different behaviors and movement styles, a level data parsing mechanism that allowed the use of a level editor, and a terrain background layer.

And in the fifth part, we made it such that when the player is hit they will take damage, and when out of health they die in a firey explosion. We added game states to support game overs and multiple levels, a "final credits screen" plus all sorts of visual feedback (such as level transition messages and a health bar).

In this, the last part, we are going to put the final layer of polish on our game:

  • We'll add boss battles, complete with a glowing health bar and bullets everywhere.
  • We implement full screen HD rendering at any screen resolution by using liquid layout.
  • Because our game is just over a meg in size, we'll implement a preloader progress bar.
  • Just for fun, we'll add NPC (non-player character) voiceovers to motivate players.
  • For dramatic effect, we'll implement slow motion time dilation.
  • We'll tweak the movement speed of the player, enemies and bullets.
  • We will add autofire to the game so players can concentrate solely upon movement.

When we're done, the game will be complete. The final product is a "real" videogame that has everything players expect, with all the bells and whistles.

Step 1: Open Your Existing Project

We're going to be building on the source code written in the previous tutorials, much of which will not change. If you don't already have it, be sure to download the source code from part five (download here). Open the project file in FlashDevelop (info here) and get ready to upgrade your game! This source code will work in any other AS3 compiler, from CS6 to Flash Builder, as long as you target Flash 11.

Step 2: Set Up a Preloader

Any game that requires more than a couple seconds to download should have a progress bar preloader screen. This gives players some visual confirmation that the game is indeed loading, so that they never worry that their browser has hung. A good rule of thumb is any game that is more than one meg in size should have a preloader.

Although the majority of users nowadays have extremely high bandwidth and this Flash file will only take a couple seconds to load, congested web servers, users with slower internet access, and times that the PC might be really busy mean that occasionally it will take a few moments to download. A preloader progress bar is also one of the many little touches of polish that set tech demos and "real" games apart.

Creating a preloader in FlashDevelop is extremely simple. First, begin by making a brand new file in your project called Preloader.as and right-click it in your project manager window. Set it to be the project's primary "Document Class" (which will replace the original document class, which was Main.as) as shown in the image below:

Step 3: Include the Game Itself

Before we fill in the details for rendering a nice-looking progress bar in our Preloader.as, we need to tell Flash to load the rest of the game. This is important because as coded the preloader won't import any of the game classes.

Why? because they aren't actually referred to in Preloader.as. If we don't specify that we also want to include Main.as in the .SWF, the compiler is smart enough to assume it is an unused file and will not include it. Skipping this step will mean that the .SWF we create upon compilation is only a couple kilobytes in size. We want to ensure the entire project is included in the downloaded even though it isn't referred to in the preloader code.

To do so, go into the Project menu, select Properties, go into the Compiler Options tab and click Additional Compiler Options to add a snippet to the compiler command-line. This snippet is -frame main Main which means that a second "frame" in the flash timeline will use the "Main" class that used to be the primary document class for our project.

If you aren't using FlashDevelop, simply add this to your make file command line options. If you're using pure Flex, you can also do it automatically by including the following in your preloader as3 source: [frame (factoryClass="Main")].

Step 4: Init the Progress Bar

We're now ready to implement the progress bar preloader in our currently blank Preloader.as class. Add the following code to set everything up:

Step 5: Animate the Progress Bar

Continuing with Preloader.as, implement the event handler that will be called repeatedly during the download.

In the two steps above, we added a few simple elements to the stage and animated them. A rectangular progress bar that changes size as the download progresses, plus some text that tells the user how many bytes have been downloaded, how many in total are required, and the completion percentage.

This is updated every frame as the .SWF continues to download. When the download is complete, we use the getDefinitionByName function to locate the actual game class and add it to the stage. This will start the game.

This is what the preloader will look like while the game is downloading:

That's it for the preloader! You can use this handy class in all your projects as a quick and easy way to ensure that larger downloads don't get skipped. In the age of Stage3D and high definition games filled with sprites, sounds and more, it is entirely reasonable to have a game that is many megs in size. Forcing a user to sit at a blank screen for more than a second or two will lose potential players.

Step 6: Create BOSS Entity Vars

In this final version of our game, we're going to implement a "boss battle" which will be triggered at the end of each level. This will add some additional tension at the end of a harrowing dogfight, and is a tried-and-true shooter game convention. This mechanic adds a climax to the level.

A boss battle usually involved a larger and much more powerful enemy that requires many more hits to destroy. It also won't scroll past the edge of the, thus forcing the player to deal with it. Instead of basic random single shots aimed at the player, we'll code a more varied firing pattern and force the player to dodge and weave just to stay alive.

Open the existing Entity.as, and add a few new properties to the top of the file as follows:

The rest of the entity class variables remain unchanged except, however, as a result of playtesting during the course of development, a few values have been tweaked. Locate fireDelayMin and change it to 4, so that non-boss enemies wait a little longer between firing. Change fireDelayMax to 12 for the same reason. This cuts down on bullet spam a little, since when the screen is filled with baddies we want the game to be hard but not impossible.

Step 7: Code BOSS A.I.

The last upgrade that needs to be implemented in our Entity.as file is the new artificial intelligence function for the boss. At the very bottom of the file, below all the other behaviors such as sentryAI, droneAI and the rest, add a new AI routine as follows:

In the boss AI code above, we keep track of time passing in order to trigger different behaviors at different times. Firstly, the boss will fire a rapid long line, like a machine-gun, every few seconds. At the end of that burst, it will fire a single round fo different bullets in every direction, which results in a "circle" of bullets that are hard to didge if you are too close to the boss. To make things more challenging, the boss measures the distance to the player and smoothly interpolates its position to be nearby - it prefers to sit just 256 pixels to the right of wherever the player is. That's all that is required for the boss battle upgrades to our entity class.

Step 8: Add the BOSS Sprite

Although we have all the behaviors coded for our boss battle, there's one final thing to do so that it appears in-game. We need to draw a big boss and add it to our spritesheet. Using Photoshop, Gimp or the image editor of your choice, replace some unnecessary sprites with a larger boss sprite. Later on, we'll change the way our spritesheet is "chopped up" so that the larger boss image is rendered properly. You may also notice that the bullet sprites have been tweaked to use different colors of glow, just for fun. Here's the final spritesheet texture as used in the example project:

Step 9: BOSS Health GUI Vars

Because our epic boss battles are going to feature a big enemy that can take many hits before being destroyed, we're going to update our GameGUI.as class to inlude a big red "health bar" for the boss. This will give players the visual feedback required to confirm that, yes, the boss is taking damage when being hit.

Start by adding the following lines of code for some new class variables to the top of the file, alongside the similar TextField definitions for the player's health bar and such.

Step 10: Init the BOSS Health GUI

Continuing with GameGUI.as, add the following initialization code to the onAddedHandler function. This will create a new textfield for the boss health meter in a large glowing red font. Note that we don't yet add it to the stage - we only want it to be visible during the actual boss battle.

Step 10: A Reusable Health Bar Function

In previous versions of the game, the only health bar belonged to the player. Now that there is another used by the boss, we should make a reusable function that generates the proper health display for any entity so that we avoid having copy-n-pasted duplicate code in our gui class. Add the following function to GameGUI.as as follows:

The last three steps of the tutorial added a simple health bar that will look like this when we're done:

Step 11: NPC Dialog Vars

NPCs are often used as the "quest givers" in games, and since adding a little popup dialog bar to the bottom of the screen is a trivial effort, it will tgive the game such much more pizazz with very little extra work. Therefore, just for fun, we're going to add a non-player-character (NPC) to our game.

This character will provide encouragement and will add a human touch. By using a pretty girl's face (which was sculpted and rendered in Poser Pro 2010 in this example) we add a little personality - and a reason to fight all those enemies. She will congratulate you when a boss is defeated, and will sympathize with you if you die.

Add the following variables to the top of the GameGUI.as class, right next to where you added the corresponding ones for the boss health bar GUI.

Step 11: NPC Dialog Inits

Just as we did for the boss health meter, we need to initialize the text field that will contain the NPC's dialog. During the game, we'll also trigger some voiceover sounds to go alogn with them. This text (and the overlay image specified above) are not normally visible during the game and will only be used during "transitions" such the the beginning of a level, just efore the boss battle, and when you reach a game over state.

Continuing upgrading GameGUI.as by adding the following initialization code to the onAddedHandler function.

Step 12: The NPC Overlay Image

In the steps above we created an overlay sprite as well as some text that will appear on-screen when the NPC needs to do some talking. The image we need for this overlay, which we will float to the bottom of the screen, should be a small bar that is 600x64 pixels in size. Create the background for this overlay in your image editor now. It looks like this in our example game:

Step 13: Liquid GUI Layout

Now that we've added a boss health bar and NPC dialog overlay to our GameGUI.as class, all we need to do is upgrade the update functions to deal with this new functionality. To begin with, we know that the final version of the game is going to support full screen mode. Since there are many different monotor resolutions in use, we can't know for sure what the size of the game is going to be.

This is a situation where, just like when creating an HTML page, the best solution to different screen sizes is to create a "liquid layout" function that moves everything around to the proper places on screen no matter how big it is. For our GUI class, we simply calculate what the center position of the screen is and move things around whenever a RESIZE event is fired. Continue upgrading GameGUI.as as follows:

Step 14: Upgrade the GUI Updater

The final set of upgrades required by all this new GUI functionality is to account for our new items during the render loop. As an optimization we will only change things when required (not every frame) by checking to see if the values have changed. Modify these two functions in GameGUI.as as follows:

In the code above, we have added update functionality for our two new GUI items (the boss health bar and the NPC dialog popup). We also simplified the "debug" stats that appear in the top left of the screen. Instead of cryptic sprite counts and RAM useage stats, we simply include an FPS display and a custom message that will show what level we are on during gameplay.

Step 15: New Entity Manager Vars

We're going to make of number of minor changes to our most important class, the entity manager. Open the existing file EntityManager.as in your project and begin by adding some new class variables related to the boss. In addition, a few values related to speed have been tweaked, so replace the existing definitions with these ones at the very top of your class, as follows:

All the other class vars in the section above remain unchanged and aren't included here for brevity.

Step 16: Liquid Layout

In the same way that we are now using a liquid layout sceme for the game to support running at any resolution, we need to tweak the setPosition function in EntityManager.as to ensure that no matter what size of monitor the play has the game takes place in the middle of the screen. Modify this function as follows:

Step 17: Upgrade the Respawner

A few minor upgrades are required for our respawn function to account for the fact that some entities might have invalid timer data (such as age or when it should fire next) left over from when it was last destroyed. In particular, some values used the bosses needs to be reset. We don't want new versions of these same sprites to never shoot when respawned due to having incorrect ages, which can mess up the AI routines. Continuing with EntityManager.as, make these tweaks to correct this oversight:

Step 18: Upgrade the Bullets

We've implemented a new, cool-looking firing mode to our bosses that spews tons of bullets in a circular pattern. The original shootBullet function assumed that all entities would always only fire bullets in the direction that they are facing. We need to upgrade this routine to allow for a specific angle to be passed in the function parameters. If it is not specified, then the original behavior applies.

Additionally, in previous versions of the game all bullet sprites were facing backwards and a line of code was used to correct this error. In this final version, the actual spritesheet was fixed and this hack is no longer required.

Finally, we are now using different bullet speeds for the player's bullets compared to those shot by enemies. After playtesting, giving the player a bit of an edge (by having faster bullets) just "felt right". Therefore, we take into account who the shooter is and give our projectiles the appropriate speeds.

Step 19: Upgrade the Collision Responses

Now that we have a boss battle to consider, we need to upgrade the function that handles bullet collisions. A special case has been added at the end of the checkCollisions function to detect when the boss has been hit. Instead of blindly destroying all enemies on the first hit, we deduct health and change the game state if the boss is destroyed.

Additionally, just for fun and to add a little extra eye-candy, the player and boss explosions have been made bigger by scattering multiple explosions near the point of impact. These two explosions are more important, from a gameplay perspective, and deserve a little extra "oomph".

Step 20: Upgrade the Level Streaming

The last upgrade we need to make to EntityManager.as is a subtle change to the routine that streams level data during gameplay. In previous tutorials we made it parse the level data and spawn new tiles as old ones are scrolled off-screen.

None of this logic has changed apart from one tiny change: to enable full screen and liquid layout at any resolution, we vertically center the level data so that if the screen is larger than the available level it isn't all sitting on the very top of the screen.

This way, no matter what size screen you play the game on, the action takes place near the middle. To make this change, upgrade the streamLevelEntities function as follows:

That's it for the entity manager class upgrades. In the code snippets above we enabled out boss battle action to be detected, tweaked the way bullets are fired to support extra bullet directions, added a bit more pizazz to our explosions, and ensured that the game could be played full-screen.

Step 21: Make the Background Fullscreen

We need to make a couple very minor changes to the existing GameBackground.as class to support fullscreen liquid layout. In previous tutorials, we confined the game to a mere 400 pixels in height, and thus a single 512x512 background texture, tiled horizontally, was enough to fill the background.

In this final version, we're going to add two more rows of background tiles, above and below those in the middle of the screen, so that even at 1080p HD resolution the entire screen is filled. The changes are very minor and are marked with the //v6 code comment. Virtually everything else remains the same but because the changes are scattered around such a small file, it is included here in its entirety to avoid confusion.

Step 22: Autofire!

Based on beta playtesting user feedback, we're going to add the capability to enable AUTO-FIRE to our game. There are two primary reasons for doing so. One, because the majority of users simply hold down the space bar the entire time they are playing anyways. Two, because of security restrictions in the full screen mode of Flash which disable any typing on the keyboard apart from the arrow keys.

The reason that Flash won't allow full screen .SWFs to access the entire keyboard is that they could be used as keyloggers: surrepticiously recording keystrokes or faking a bank login page by drawing normal-looking web browser chrome in a "phishing" scam.

In future version of Flash (11.3 and beyond) it is technically possible to have full keyboard input in fullscreen games, but it will force users to confirm with a pop-up security warning. This gives a bad impression, but regardless, the vast majory of players (right now) won't have the latest version of Flash installed.

It should be noted that the arrow keys and the space bar are allowed in regular full screen mode, but sadly most PC keyboards are incapable of registering left+up+space at the same time. This means that although we could turn off auto-fire and go full screen and simply use the arrow keys and the space bar, any time players tried to move up and back while firing the computer would beep and movement would stop. Not all keyboards suffer from this technical constraint but standard cheap ones do.

In light of these deficiances, and to simplify the gameplay experience to the most essential aspect of the game, we are going to enable autofire during play. The code is set up so that you can easily turn it off or on depending on your needs.

Begin by opening the existing GameControls.as class and adding one extra class variable near the top as follows:

Now tweak one line in the lostFocus function:

Finally, add one new line at the very bottom of the keyHandler function:

Step 23: Embed the Voiceovers

Now that we've added a boss and an NPC character to our game, lets give them some sound effects. This will increase the production values of our game a little, and should give both characters a little more personality.

Record some fun voiceovers in the sound editing program of your choosing (Audacity, CoolEditPro, etc.) Remember that we want to record in high quality (44.1khz) but save as low quality mp3 files (11khz, mono) so that our SWF doesn't get too big.

The voiceovers I created for our demo game are a bit cheesy and were recorded in a single take, but they will suffice for our purposes. Feel free to laugh at my silly pitch-shifted voice. You can play them in your browser just for fun:

  1. sfxboss.mp3
  2. sfxNPCwelcome.mp3
  3. sfxNPCdeath.mp3
  4. sfxNPCboss.mp3
  5. sfxNPCnextlevel.mp3
  6. sfxNPCgameover.mp3
  7. sfxNPCthanks.mp3

Once you're happy with your new voiceovers, embed them in the GameSound.as file. Simply add the new sounds to the top of the class alongside all the other MP3 files from before:

Step 24: Voiceover Trigger Functions

Continuing with GameSound.as, create functions that we will use during gameplay to trigger the new sounds as follows:

Step 25: Upgrade the Game Class

We've finished upgrading all the supplementary classes used by our game. All we need to do now is enable this enhanced functionality in the game by upgrading our primary game class. The majority of this file remains unchanged since last time, but there are 27 different minor edits to make. Search for the // v6 code comment which points out each change.

Open the existing Main.as file and begin by adding one new import, tweaking the player speed and adding few new class variables:

Step 26: Upgrade the Inits

There is only one minor change to make to all the init functions in Main.as. We simply fill in the one new GUI variable that will eventually list what level we are on. Before the game begins, we list the game version instead.

Step 27: Ensure Liquid Layout

Since we need to be able to adapt the game to fit any screen resolution, we need to tweak the onResizeEvent function such that it calls the setPosition function on more of our classes, so that each in turn can move things around as appropriate. Continuing with Main.as:

Step 28: Begin the Boss Battle

It is finally time to add boss battles to the game! Out new boss sprite, pictured above, will pose a significant challenge to the player due to its complex firing pattern of green machine-gun shots from the front and a circular burst every few seconds:

To implement our new boss battle system, we will need to create two new functions. One will initialize a new boss battle, and the other is a callback function that will be triggered when the boss is destroyed.

Our old spritesheet system assumed that all sprites in our texture are the same size. Since the boss is much bigger, we need to manually define it using the proper pixel coordinates of sprites.png.

We also need to give our new boss the proper AI function as created above, ensure that it is in the middle of the screen, and give it enough health to take a significant amount of damage before blowing up. Because the boss is a special case in the entity manager, we tell it which sprite it is so that it can be treated differently in the collision response function.

Add this new function to Main.as as follows:

Step 30: Upgrade the Game Transitions

In the previous tutorial, we created a simple state-driven game transition handler. It would announce the upcoming level or display a game over message as appropriate. Much of this function remains unchanged, except we are going to upgrade it to include the new boss battle announcements as well as the NPC voiceover sounds and subtitles. We're also going to add a fun and simple effect to player deaths: SLOW MOTION. Continuing with Main.as, modify the handleTransitions function as follows:

The next few functions (playerLogic, mouseDown, mouseMove, processInput) all remain unchanged since last time and are not included here.

Step 31: Go Fullscreen

Since we're going to be going fullscreen after the player presses the start button, we need to upgrade the stageGame function as follows:

Step 32: Turn Off Autofire

We need to upgrade our game over function to turn off autofire when the game ends to avoid the menu being automatically triggered. Continuing with Main.as add this little change to the end of the function:

The checkPlayerState function that appears next in the code from last time remains unchanged.

Step 33: Upgrade the Map State Check

We need to make one tiny change to the function that determined when it is time to switch maps. We don't want this to happen during a boss battle and instead much wait for the battle to be over.

The rollTheCredits function that appears next in the original source is also unchanged since last time.

Step 34: Upgrade the Render Loop

We've made it to the last engine uprgrade! The final function that we need to tweak is also only subtle different since last time. During player death transition animations, we set the timeDilation variable to be less than 1. This is multiplied by the elapsed milliseconds since the previous frame, which is sent to all the update functions for each class. This way, whenever it is set to 0.5, for example, all animations will run at half speed.

That's it for the many little upgrades to Main.as. The final version of our game is now ready to compile and run. It now boasts slow-mo, fullscreen, boss battles, voiceovers and an NPC, a preloader progress bar, and much more.

Step 28: Compile and Play!

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.

Here are a few tips if you experience problems. If you do use FlashBuilder, be sure to include "-default-frame-rate 60" in your compiler options to ensure you get the best performance. If you are using Linux or a Mac, you can compile this from the command-line (or in a makefile) using something similar to "mxmlc -load-config+=obj\shmup_tutorial_part4Config.xml -swf-version=13", depending on your working environment.

Remember that since we're using Flash 11 you will need to be compiling using the latest version of the flex compiler and playerglobal.swc. Most importantly, remember that your flash embed HTML has to include "wmode=direct" to enable stage3D. This source has only been tested using FlashDevelop on Windows, and the tips above have been kindly submitted by your fellow readers.

Once everything compiles and runs properly you should see something that looks like this:

Part Six Complete. Final Boss Defeated!

That's it for the Stage3D Shoot-em-up tutorial series.

We started by implementing a simple (and super fast) batched geometry sprite rendering system that made use of reusable object pools for extra speed. By taking advantage of Stage3D, our game is rendered using the hardware GPU and should achieve a silky smooth 60fps on most new computers.

We then added game entities with AI, collision detection and timers, scores and health bars, sound and music, a menu and all the other little bells-n-whistles required to call this a "real" game.

You now have in your hands an MVP (minimum viable product) from which you can expend to your heart's desire to create a world-class "bullet-hell" sidescrolling shooter. This your new quest. The student has become the master. Take this code and bring it to new heights on your own. I sincerely hope you make something amazing with it. Good luck - and have fun!

I'd love to hear from you regarding this series. 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 anytime. 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!

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.