Build a Stage3D Shoot-'Em-Up: Interaction

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'll extend our rendering demo by adding an animated title screen and menu, music and sound effects, and a keyboard-controlled ship that can fire bullets.


Final Result Preview

Let's take a look at the final result we will be working towards: a hardware accelerated shoot-em-up demo in the making that includes an animated title screen and menu, sounds, music, and keyboard controls.

Click the logo to gain keyboard focus, then use the arrow keys to move and space bar to fire. Your shots will be reflected back at you once they reach the edge of the screen.


Introduction: Welcome to Level Two!

We're going to continue making a side-scrolling shooter inspired by retro arcade titles such as R-Type or Gradius, in AS3, using Flash 11's Stage3D API and the freeware tool FlashDevelop.

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

These optimizations include the use of a spritesheet (or texture atlas), the creation of an entity object pool which reuses incactive entities instead of creating and destroying objects at runtime, and a batched geometry rendering system which draws all of our game's sprites in a single pass rather than individually. These optimizations are the perfect foundation upon which we will build our high performance next-gen 3d shooter.

In this part, we are going to work toward evolving what is currently a mere tech demo into something that is more like a videogame. We are going to create a title screen and main menu, add some sound and music, and code an input system that lets the player control their spaceship using the keyboard.


Step 1: Open Your Existing Project

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


Step 2: The Input Class

We need to allow the player to control the game, so create a new file called GameControls.as and start by initializing the class as follows:

In the code above, we import the Flash functionality required to handle the keyboard. We also define some class variables that will be used to report the current state of the keyboard to the game.

In order to support international keyboards, special consideration has been made to ensure the game will "just work" when players try to control the game. Although most of your players will be using a English "QWERTY" keyboard, a significant proportion will use alternate international keyboard layouts, and to them a big pet peeve when playing games is when they can't use standard "WASD" style movement. Therefore, we make sure that "AZERTY" and "DVORAK" keyboards are also supported.

Keyboard controls that work on QWERTY, AZERTY and DVORAK.
Keyboard controls that work on QWERTY, AZERTY and DVORAK.

Step 3: Handle Keyboard Events

This class will be listening to the key up and key down events and will set the state of the class so that the game knows when a certain key is pressed or not. In order to support players from different parts of the world, instead of just forcing the use of the arrow keys we are going to add alternative controls such as W,A,S,D and similar schemes that work on international keyboards.

This way, no matter what the player uses to control the game, it will probably "just work" without the player having to think about it. There's no harm in making different keys do the same thing in-game.

Detect the appropriate key events as follows:

In the code above we handle the key up and key down events, as well as the events that are fired when the game gains or loses keyboard focus. This is important because sometimes when a key is held down, the key up event is not received if the user clicks another window, switches browser tabs, or gets a popup from another program. It's also common for Flash games to pause the game when focus is lost, and this is the ideal place to handle that.


Step 4: Record the Input State

The final function required by our simple GameInput class is the one that both keyboard events above call. If a key has been pressed, this function sets the appropriate flag to true so that our main game logic can query the current state of all the controls when needed.

Our game will be controllable via the arrow keys, as well as the typical upper-left keyboard area that is most often used in first person shooters, as follows:

That's it for GameInput.as - in future versions we might want to add mouse and touch events to support playing the game without the keyboard or on mobile, but for now this is enough to support almost all possible players.


Step 5: Add Input Variables

In the inits as defined in the Main.as file, add two new class variables to the top of our existing class definition, just before all the other variables from the sprite test. We need a reference to our new input class, and a state flag that we'll use to make sure the menu doesn't scroll too fast when a key is held down.


Step 6: Init the Inputs

Next, create a new input object during the game's inits. Add the following line of code to the very top of the existing init function. We will pass in a reference to the stage which is needed by the class.


Step 7: Debug the Controls

In the main game loop in your Main.as file, we want to check the state of the player input. In the existing onEnterFrame function, add this line of code:

With this in place, we should now have a working input manager. If you compile the SWF now and run it, you should see the following:

Debug display showing controls in use.
Debug display showing controls in use.

Note the stats debug text only updates once per second, to avoid spamming the displaylist and to keep the framerate high, but this is enough to prove that we have a decent set of input routines all ready to go.


Step 8: Design the Title Screen

Nearly every videogame starts with the display of a big splash screen logo and a main menu. This is often called "attract mode" and is meant to be the idle state of the game while it waits for the player to decide to dive in and start playing. This is the perfect place to put the credits and copyrights you desire, as well as a little bit of information on how to control the game.

Our title screen and menu display is going to be another LiteSprite batch layer that sits over top of the once we made in the first part of this series. It will also take advantage of the spritesheet and geometry batching system we designed, so that we can rotate, scale and move our sprites around smoothly, without any jaggies and while retaining our silky-smooth 60fps. Fire up Photoshup, GIMP, or the image editor of your choice and create a logo and name for your game.

I've decided to use the name "Kaizen", formed the first three letters in my last name, "Kai", and the word "zen", which to me implies the zen-like state of mind that you get in when you are "in the zone," dodging bullets and destroying enemies. It also evokes a Japanese arcade style, and harkens back to titles like Raiden.

Finally, add the on and off states for the menu items as well as the about and controls info screens. Since we will be using batching and a spritesheet, we need to include all images that the title screen will include in a single, power-of-two sized square image that is 512x512 and has alpha transparency. This is the final image we will be using:

The titlescreen spritesheet

Step 9: The GameMenu Class

We now need to create a quick-n-dirty menu class. Create a brand new file in your project called GameMenu.as and begin by importing the Stage3D functionality required and defining the class variables that we need to handle as follows:


Step 10: Init the Menu

Continuing with GameMenu.as, implement the initializations. We want to ensure that the menu and logo are centered. In the function below where we create the geometry batch and "chop up" the spritesheet, we define rectangular regions for each of the sprites we will be using.


Step 11: Handle Menu State

We want to define functions that will be called by our game based on user input. If the game has keyboard focus, the user can use the arrow keys to scroll up and down. Alternatively, the mouse can be used to hover over and click menu items. When we update the state of the menu, we turn off certain sprites and turn on others by simply changing the alpha transparency of the sprite.

In the activateCurrentMenuItem function above, we either return true if the play button has been pressed and it is time to start the game, or we turn off the menu entirely and display one of the two information screens for five seconds by telling the menu class to wait for 5000ms before reverting back to the idle menu state.


Step 12: Animate the Menu

To add a little polish to our menu system, we will pulse the logo in and out and rotate it slightly. This adds a little movement and pizazz to our game. We will also pulse the highlighted menu item to provide clear feedback to the user that this item has been selected. It is little touches like these that will make your game seems a bit more professional - and they're easy to implement too.

That's it for our simple animated title screen and main menu.


Step 13: Pump Up the Jam!

Music and sound effects add a lot to a game. Not only does it help to give the player an emotional connection to the action, it also sets the tone and feel. Sound is a valuable feedback mechanism as well, because it gives an auditory confirmation that what the player just did had some sort of effect. When you hear a sound effect like a laser blast, you know you did something.

Fire up your favorite sound editor (such as SoundForge, CoolEditPro, Reason, or even a microphone and your own voice) and design the music and sounds you want to use in your game. If you don't know how to make sound effects or are not a composer of music, don't worry. There are millions of free and legal sound effects and song at your diposal online.

For example, you can take advantage of the vast library of sound effects at FreeSound (just ensure that they are licensed for commercial use and be sure to give credit). You can also generate sound effects using Dr. Petter's SFXR (or its fantastic online port, BFXR), which automatically generates an infinite variety of retro beeps and bloops. Finally, for legal music you can search through giant catalogues with Jamendo and the amazing ccMixter, which are designed for indies as a resource of completely legal music.

In order to keep the size of our SWF as small as possible, once you've downloaded or created the sounds and music you want, re-encode them in the lowest tolerable sound quality. When explosions and gun sounds are firing, players will not notice if your music is in MONO and being played at a low bitrate. For the example game, all music and sounds fit in about 500k by saving the MP3s as mono 22kHz files.


Step 14: The Sound Class

For this example, we are going to create a barebones game sound system that includes four different MP3 files. The first is the music, and the other three are different gun shot sounds. Eventually, the plan is to have different sounds for the player's weapon that go with different weapon states.

Create a brand new file in your project called GameSound.as and embed the MP3s as follows:


Step 15: Trigger the Sounds

The final step in creating our sound manager is to implement the functions that our game will call when we want to trigger a new sound effect.

As simple as it looks, this is enough to handle sound and music. Note the above we need to keep track of the sound channel used by the music so that we can turn it off. Gun sounds can overlap (and in fact this is the desired behavior) but we never want two copies of the music playing if a player dies and then starts another game. Therefore, we ensure that any previous instances of the music are turned off before starting playing it.


Step 16: The Background Layer

As one last bit of polish that we are going to put into our game this week, we're going to implement a simple background starfield that scrolls slower than the action in front. This parallax effect will give the game some depth and will nook much nicer than a plain black background.

We're going to use another geometry batch and spritesheet and draw it underneath everything else. Because it is so similar to the entity manager class we made last week, we're going to use class inheritance and simply extend that for our purposes. This way, we only need to implement the routines that are different.

For now, we're just going to layer copies of the following space tile, but in future versions of our game we might add more details like asteroids or nebulae to the mix.

The game background starfield tile

Create a new file in your project called GameBackground.as and start by extending the EntityManager class as follows:


Step 17: Init the Background

We are going to create a single giant sprite out of this "spritesheet" rather than chopping it up into many small parts. Since the image is 512x512 and the game screen is wider than that, we might need up to three sprites visible, depending on where they are at a given time as they cross over the edges of the screen.


Step 18: Animate the Background

The final step in getting our simple but attractive background layer rendering is to code the update function, which will scroll the sprites and optionally "loop" them to the opposite edge for reuse. This way, the background can scroll infinitely. Continuing with GameBackground.as implement our scrolling behavior as follows:

That's it for our scrolling space background class. Now all we need to do is add it to our main game and test it out.


Step 19: Add the Player and Bullets

There's one tiny upgrade we need to make to our existing EntityManager class from last week. We want a unique entity for the player's ship that doesn't follow the rules of all the other sprites that are flying by in our demo. We also want some bullets to shoot. First, ensure that your sprite sheet image includes these kinds of sprites, since last week there were no bullets in it.

The new spritesheet with bullets and explosions
The new spritesheet with bullets and explosions

First, edit Entity.as and add one public variable that will be used to store a reference to an AI (artificial intelligence) function. In future versions of our game we might add special AI functions for different kinds of enemies, homing missiles, etc. Add this line of code alongside the other entity class variables like speed.

Now edit EntityManager.as and add the following class variable to EntityManager.as where we create all the other vars.

Next, implement two new functions that will handle the creation of this player entity and the spawning of new bullets.

Finally, upgrade the entity manager's update function to skip over the player in its standard simulation step by checking to see if that entity has an ai function defined (for now, only the player will).

We will now have a special player ship entity that moves around and can shoot bullets aplenty. Check out this lovely example of bullet hell (or would that be shoot-'em-up heaven?):

Bullet hell!

The bullets reflect when they reach the edge of the screen because of this snippet of code, which we added in the first part of the series:


Step 20: Upgrade the Game!

Now that we've implemented a sound system, a scrolling background, keyboard controls, a title screen and a main menu, we need to upgrade our game to incorporate them into the demo. This involves all sorts of tiny subtle changes to the existing Main.as file that we made last week. To avoid confusion that might result from a dozen one-liners of code scattered around in a dozen different locations, all the changes are presented below in a linear fashion.


Step 21: New Class Variables

We first need to create new class variables used to refer to and control the new systems we made this week, as follows:


Step 22: Upgrade the Inits

Continuing with Main.as, upgrade the game inits to spawn instances of this week's new classes.


Step 23: Player Movement

We want the player to be able to control their spaceship, so we are going to implement the player logic routines. They take advantage of the new GameControls class to check the state of the keyboard and change the ship's speed (or the highlighted menu item if we are at the title screen) depending on which directional keys are being pressed. We also listen for mouse events and update the state of the main menu if it is currently active.


Step 24: Start the Game!

The mouse and keyboard handlers above call the menu's activateCurrentMenuItem function which returns true if it is time to start the game. When it is, we remove the menu and logo from the sprite stage, fire up the game's music, and add a player sprite, ready to be controlled.


Step 25: Upgrade the Render Loop

The final step in upgrading our Stage3D game is to call the appropriate update functions each frame for all our new classes that we created above. Continuing with Main.as, upgrade the onEnterFrame function as follows:

Our super-optimized Flash 11 Stage3D Shoot-em-up game is really starting to take shape! 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 instand 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 action

Part Two Complete: Prepare for Level Three!

That's it for tutorial number two in this series. Tune in next week to watch the game slowly evolve into a great-looking, silky-smooth 60fps shoot-em-up.

What this project needs is a little destruction and mayhem to make it feel like a real game! In part three, we will have a lot of fun implementing all the eye candy: bullets, a particle system, and the collision detection logic that will tell our game when something needs to blow up.

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.

Good luck and HAVE FUN!

Tags:

Comments

Related Articles