Hour 15. Game 3: Captain Blaster

What You’ll Learn in This Hour:

How to design the game Captain Blaster

How to build the Captain Blaster world

How to build the Captain Blaster entities

How to build the Captain Blaster controls

How to further improve Captain Blaster

Let’s make a game! In this hour, you’ll make a 2D scrolling shooter game titled Captain Blaster. You’ll start by designing the various elements of the game. From there, you’ll begin building the scrolling background. Once the idea of motion is established, you’ll begin building the various game entities. When the entities are done, you’ll construct the controls and gamify the project. You’ll finish this hour by analyzing the game and identifying ways to improve it.

Design

You learned about design elements in Hour 6, “Game 1: Amazing Racer.” This hour, you’ll dive right into them.

The Concept

As mentioned earlier, Captain Blaster is a 2D scrolling shooter-style game. The premise is that the player will be flying around a level, destroying meteors, and trying to stay alive. The neat thing about 2D scrolling games is that the players themselves don’t actually have to move at all. The scrolling background simulates the idea that the player is going forward. This reduces the required player skill and allows you to create more challenges in the form of enemies.

The Rules

The rules of this game state how to play and allude to some of the properties of the objects. The rules for Captain Blaster are as follows:

The player plays until being hit by a meteor. There is no win condition.

The player can fire bullets to destroy the meteors. The player earns 1 point per meteor destroyed.

The player can fire two bullets per second.

The player is bounded by the sides of the screen.

Meteors come continuously until the player loses.

The Requirements

The requirements for this game are simple:

A background sprite to be outer space.

A ship sprite.

A meteor sprite.

A game manager. This will be created in Unity.

Interactive scripts. These will be written in a code editor such as Visual Studio as usual.

The World

Because this game takes place in space, the world will be fairly simple to implement. The game will be 2D, and the background will move vertically behind the player to make it seem like the player is moving forward. In actuality, the player will be stationary. Before you get the scrolling in place, though, you need to set up your project. Start with these steps:

1. Create a new 2D project named Captain Blaster.

2. Create a folder named Scenes and save your scene as Main.

3. In the Game view, click the aspect ratio drop box, click the plus icon, and create a new 5:4 aspect ratio (see Figure 15.1).

Images

FIGURE 15.1
Setting the game’s aspect ratio.

The Camera

Now that the scene is set up properly, it is time to work on the camera. Because this is a 2D project, you have an orthographic camera, which lacks depth perspective and is great for making 2D games. To set up the Main Camera, simply set the Size property to 6. (See Figure 15.2 for a list of the camera’s properties.)

Images

FIGURE 15.2
The Main Camera properties.

The Background

The scrolling background can be a little tricky to set up correctly. Basically, you need to have two background objects moving down the screen. As soon as the bottom object goes offscreen, you place it above the screen. You keep flipping back and forth between them, and the player doesn’t know this is what’s happening. To create the scrolling background, follow these steps:

1. Create a new folder named Background. Locate the Star_Sky.png image from the book files and import it into Unity by dragging it into the Background folder you just created. Remember that because you made a 2D project, this image automatically imports as a sprite.

2. Select the newly imported sprite in the Project view and change its Pixels per Unit property in the Inspector view to 50. Drag the Star_Sky sprite into the scene and ensure that it is positioned at (0, 0, 0).

3. Create a new script in your Background folder named ScrollBackground and drag it onto the background sprite in the scene. Put the following code in the script:

public float speed = -2f;
public float lowerYValue = -20f;
public float upperYValue = 40;

void Update()
{
    transform.Translate(0f, speed * Time.deltaTime, 0f);
    if (transform.position.y <= lowerYValue)
    {
          transform.Translate(0f, upperYValue, 0f);
    }
}

4. Duplicate the background sprite and place it at (0, 20, 0). Run the scene. You should see the background seamlessly stream by.

Game Entities

In this game, you need to make three primary entities: the player, the meteor, and the bullet. The interaction between these items is very simple. The player fires bullets. Bullets destroy meteors. Meteors destroy the player. Because the player can technically fire a large number of bullets, and because a large number of meteors can spawn, you need a way to clean them up. Therefore, you also need to make triggers that destroy bullets and meteors that enter them.

The Player

Your player will be a spaceship. The sprites for both the spaceship and the meteors can be found in the book assets for Hour 15. (Thanks to Krasi Wasilev, at http://freegameassets.blogspot.com.) To create the player, follow these steps:

1. Create a new folder in your Project view called Spaceship and import spaceship.png from the book files into this folder. Note that the spaceship sprite is currently facing downward. This is okay.

2. Select the spaceship sprite and in the Inspector set Sprite Mode to Multiple and click Apply. Then click Sprite Editor to start slicing your sprite sheet. (If you’ve forgotten about slicing, look back at Hour 12, “2D Game Tools.”)

3. Click Slice in the upper left of the Sprite Editor window and set Type to Grid By Cell Size. Set x to 116 and y to 140 (see Figure 15.3). Click Slice and notice the outlines around the ships. Click Apply and close the Sprite Editor window.

Images

FIGURE 15.3
Slicing the spaceship sprite sheet.

4. Open the tray of the spaceship and select all the frames. You can do this by clicking the first frame, holding down Shift, and then clicking the last frame.

5. Drag the sprite frames to the Hierarchy view or Scene view. This causes a Create New Animation dialog to appear, which will save your new animation as an .anim file. Name this animation Ship. When you are done, an animated sprite will be added to the scene, and an animator controller and animation clip asset will be added to the Project view as in Figure 15.4. (You will learn more about animation in Hour 17, “Animations,” and Hour 18, “Animators.”)

Images

FIGURE 15.4
The finished spaceship sprite.

6. Set the ship’s position to (0, -5, 0) and scale it to (1, -1, 1). Note that scaling to -1 in the y axis turns the ship to face upward.

7. On the ship’s Sprite Renderer component, set the Order in Layer property to 1. This ensures that the ship always appears in front of the background.

8. Add a Polygon Collider component to the ship (by selecting Add Component > Physics2D > Polygon Collider). This collider will automatically surround your ship for very decent collision detection accuracy. Be sure to check the Is Trigger property in the Inspector to ensure that it is a trigger collider.

9. Play the game and notice the subtle animation of the ship’s engines.

You should now have a nice, animated upward-facing spaceship ready to destroy some meteors.

The Meteors

The steps for creating the meteors are similar to those for creating the spaceship. The only difference is that the meteors will end up in a prefab for later use. Follow these steps:

1. Create a new folder called Meteor and import meteor.png into it. This is a sprite sheet that contains 19 frames of animation.

2. Set Sprite Mode to Multiple and then enter the Sprite Editor as before.

3. Set Slice Type to Automatic and leave the rest of the settings at their defaults. Click Apply to apply your changes and close the Sprite Editor window.

4. Expand the tray of the meteor sprite and select all 19 frames of the meteor sprite sheet. Drag these frames into the Hierarchy view and, when prompted, name the animation Meteor. Unity creates another animated sprite with the necessary animation components for you. Handy!

5. Select the meteor_0 game object in the Hierarchy view and add a Circle Collider 2D component (by selecting Add Component > Physics2D > Circle Collider 2D). Note that the green outline roughly fits the outline of the sprite. This is good enough to work in the Captain Blaster game. A polygon collider would be less efficient and would not give you a noticeable improvement in accuracy.

6. On the meteor’s Sprite Renderer component, set the Order in Layer property to 1 to ensure that the meteor always appears in front of the background.

7. Add a Rigidbody2D component to the meteor (by selecting Add Component > Physics2D > Rigidbody2D). Set the Gravity Scale property to 0.

8. Rename the meteor_0 game object Meteor and then drag it from your Hierarchy view into your Meteor folder in the Project view (see Figure 15.5). Unity creates a prefab of the meteor that you will use later.

9. Now that you have captured the meteor setup in a prefab, delete the instance in your Hierarchy view. You now have a reusable meteor just waiting to cause havoc.

Images

FIGURE 15.5
Creating the meteor prefab.

The Bullets

Setting up bullets in this game is simple. Because they will be moving very quickly, they don’t need any detail. To create the bullet, follow these steps:

1. Create a folder named Bullet and import bullet.png into it. With the new bullet sprite selected in the Project view, set the Pixels per Unit property in the Inspector to 400.

2. Drag a bullet sprite into your scene. Using the Color property of the Sprite Renderer component, give the bullet a strong green color.

3. On the Sprite Renderer component of the bullets, set the Order in Layer property to 1 to ensure that the bullet always appears in front of the background.

4. Add a Circle Collider 2D component to the bullet. Also add a Rigidbody2D component to the bullet (by selecting Add Component > Physics2D > Rigidbody2D) and set the Gravity Scale property to 0.

5. To keep with convention, rename the bullet game object Bullet. Drag the bullet from the Hierarchy view into your Bullet folder to turn it into a prefab. Delete the Bullet object from your scene.

That’s the last of the primary entities. The only thing left to make is the triggers that will prevent the bullets and meteor from traveling forever.

The Triggers

The triggers (which in this game will be called “shredders”) are simply two colliders, one of which will sit above the screen and the other below it. Their job is to catch any errant bullets and meteors and “shred” them. Follow these steps to create the shredders:

1. Add an empty game object to the scene (by selecting GameObject > Create Empty) and name it Shredder. Position it at (0, -10, 0).

2. Add a Box Collider 2D component to the shredder object (by selecting Add Component > Physics2D > Box Collider 2D). In the Inspector view, be sure to put a check next to the Is Trigger property of the Box Collider 2D component and set its size to (16, 1).

3. Duplicate the shredder and place the new one at (0, 10, 0).

Later these triggers will be used to destroy any objects that hit them, such as stray meteors or bullets.

The UI

Finally, you need to add a simple user interface to display the player’s current score and to say “Game Over” when the player dies. Follow these steps:

1. Add a UI Text element to the scene (by selecting GameObject > UI > Text) and rename it Score.

2. Position the score text’s anchor in the upper-left corner of the canvas and set its position to (100, -30, 0) (see Figure 15.6).

Images

FIGURE 15.6
The player score settings.

3. Set the Text property of the Score object to 0 (which is the initial score), check Best Fit, and set Color to white.

Now you can add the Game Over text that will appear when the player loses:

1. Add another UI Text element to the scene and rename it Game Over. Leave the anchor in the center and set its position to (0, 0, 0).

2. Set the width of the Game Over text object to 200 and set the height to 100.

3. Change the Text to Game Over!, check the Best Fit property, set the paragraph alignment to centered, and change the color to red.

4. Finally, uncheck the box next to the Text component’s name to disable the text until it is needed (see Figure 15.7). Note that Figure 15.7 illustrates the text before being disabled so you can see what it looks like.

Images

FIGURE 15.7
The Game Over! sign settings.

Later you will connect this score display to your GameManager script so that it can be updated. Now all your entities are in place, and it is time to begin turning this scene into a game.

Controls

Various script components need to be assembled to make this game work. The player needs to be able to move the ship and shoot bullets. The bullets and meteors need to be able to move automatically. A meteor spawn object must keep the meteors flowing. The shredders need to be able to clean up objects, and a manager needs to keep track of all the action.

The Game Manager

The game manager is basic in this game, and you can add it first. To create the game manager, follow these steps:

1. Create an empty game object and name it GameManager.

2. Because the only asset you have for the game manager is a script, create a new folder called Scripts so that you have a place for any simple scripts you create.

3. Create a new script named GameManager in the Scripts folder and attach it to the GameManager game object. Overwrite the contents of the script with the following code:

using UnityEngine;
using UnityEngine.UI; // Note this new line is needed for UI

public class GameManager : MonoBehaviour
{
    public Text scoreText;
    public Text gameOverText;

   int playerScore = 0;

   public void AddScore()
   {
       playerScore++;
       // This converts the score (a number) into a string
       scoreText.text = playerScore.ToString();
    }

     public void PlayerDied()
     {
          gameOverText.enabled = true;
          // This freezes the game
          Time.timeScale = 0;
     }
}

In this code, you can see that the manager is responsible for keeping the score and knowing when the game is running. The manager has two public methods: PlayerDied() and AddScore(). PlayerDied() is called by the player when a meteor hits it. AddScore() is called by a bullet when it kills a meteor.

Remember to drag the Score and Game Over elements onto the GameManager script (see Figure 15.8).

Images

FIGURE 15.8
Attaching the text elements to the game manager.

The Meteor Script

Meteors are basically going to fall from the top of the screen and get in the player’s way. To create the meteor script:

1. Create a new script in your Meteor folder and call it MeteorMover.

2. Select your meteor prefab. In the Inspector view, locate the Add Component button (see Figure 15.9) and select Add Component > Scripts > Meteor Mover.

3. Overwrite the code in the MeteorMover script with the following:

using UnityEngine;

public class MeteorMover : MonoBehaviour
{
     public float speed = -2f;

     Rigidbody2D rigidBody;

     void Start()
     {
         rigidBody = GetComponent<Rigidbody2D>();
         // Give meteor an initial downward velocity
         rigidBody.velocity = new Vector2(0, speed);
     }

}
Images

FIGURE 15.9
Adding the Meteor script to the meteor prefab.

The meteor is very basic and contains only a single public variable to represent its downward velocity. In the Start() method, you get a reference to the Rigidbody 2D component on the meteor. You then use that rigidbody to set the velocity of the meteor; it moves downward because the speed has a negative value. Notice that the meteor is not responsible for determining collision.

Feel free to drag the meteor prefab into the Hierarchy view and click Play so you can see the effect of the code you just wrote. The meteor should slide down and spin slightly. If you do this, be sure to delete the Meteor instance from the Hierarchy view when you’re done.

The Meteor Spawn

So far, the meteors are just prefabs with no way of getting into the scene. You need an object to be responsible for spawning the meteors at an interval. Create a new empty game object, rename it Meteor Spawn, and place it at (0, 8, 0). Create a new script named MeteorSpawn in your Meteor folder and place it on the Meteor Spawn object. Overwrite the code in the script with the following:

using UnityEngine;

public class MeteorSpawn : MonoBehaviour
{
    public GameObject meteorPrefab;
    public float minSpawnDelay = 1f;
    public float maxSpawnDelay = 3f;
    public float spawnXLimit = 6f;

    void Start()
    {
        Spawn();
    }

    void Spawn()
    {
        // Create a meteor at a random x position
        float random = Random.Range(-spawnXLimit, spawnXLimit);
        Vector3 spawnPos = transform.position + new Vector3(random, 0f, 0f);
        Instantiate(meteorPrefab, spawnPos, Quaternion.identity);

        Invoke(“Spawn”, Random.Range(minSpawnDelay, maxSpawnDelay));
    }

}

This script is doing a few interesting things. First, it creates two variables to manage the meteor timing. It also declares a GameObject variable, which will be the meteor prefab. In the Start() method you call the function Spawn(). This function is responsible for creating and placing the meteors.

You can see that the meteor is spawned at the same y and z coordinate as the spawn point, but the x coordinate is offset by a number between -6 and 6. This allows the meteors to spawn across the screen and not always in the same spot. When the position for the new meteor is determined, the Spawn() function instantiates (creates) a meteor at that position with default rotation (Quaternion.identity). The last line invokes a call to the spawn function again. This method, Invoke(), calls the named function (in this case Spawn()) after a random amount of time. That random amount is controlled by the two timing variables.

In the Unity editor, click and drag your meteor prefab from the Project view onto the Meteor Prefab property of the Meteor Spawn Script component of the Meteor Spawn object. (Try saying that fast!) Run the scene, and you should see meteors spawning across the screen. (Attack, my minions!)

The DestroyOnTrigger Script

Now that you have meteors spawning everywhere, it is a good idea to begin cleaning them up. Create a new script called DestroyOnTrigger in your Scripts folder (since it is a single unrelated asset) and attach it to both the upper and the lower shredder objects you created previously. Add the following code to the script, ensuring that this code is outside a method but inside the class:

void OnTriggerEnter2D(Collider2D other)
{
    Destroy(other.gameObject);
}

This basic script simply destroys any object that enters it. Because the players cannot move vertically, you don’t need to worry about them getting destroyed. Only bullets and meteors can enter the triggers.

The ShipControl Script

Right now, meteors are falling down, and the player can’t get out of the way. You need to create a script to control the player next. Create a new script called ShipControl in your Spaceship folder and attach it to the spaceship object in your scene. Replace the code in the script with the following:

using UnityEngine;

public class ShipControl : MonoBehaviour
{
    public GameManager gameManager;
    public GameObject bulletPrefab;
    public float speed = 10f;
    public float xLimit = 7f;
    public float reloadTime = 0.5f;

    float elapsedTime = 0f;

    void Update()
    {
       // Keeping track of time for bullet firing
       elapsedTime += Time.deltaTime;

       // Move the player left and right
       float xInput = Input.GetAxis(“Horizontal”);
       transform.Translate(xInput * speed * Time.deltaTime, 0f, 0f);

       // Clamp the ship’s x position
       Vector3 position = transform.position;
       position.x = Mathf.Clamp(position.x, -xLimit, xLimit);
       transform.position = position;

       // Spacebar fires. The default InputManager settings call this “Jump”
       // Only happens if enough time has elapsed since last firing.
    if (Input.GetButtonDown(“Jump”) && elapsedTime > reloadTime)
    {
       // Instantiate the bullet 1.2 units in front of the player
       Vector3 spawnPos = transform.position;
       spawnPos += new Vector3(0, 1.2f, 0);
       Instantiate(bulletPrefab, spawnPos, Quaternion.identity);

       elapsedTime = 0f; // Reset bullet firing timer
    }
 }

 // If a meteor hits the player
 void OnTriggerEnter2D(Collider2D other)
 {
      gameManager.PlayerDied();
 }

}

This script does a lot of work. It starts by creating variables for the game manager, the bullet prefab, speed, movement limitations, and bullet timing.

In the Update() method, the script starts by getting the elapsed time. This is used to determine whether enough time has passed to fire a bullet. Remember that according to the rules, the player can fire a bullet only every half a second. The player is then moved along the x axis based on input. The player’s x axis position is clamped (that is, limited) so that the player cannot go off the screen to the left or right. After that, the script determines whether the player is pressing the spacebar. Normally in Unity, the spacebar is used for a jump action. (This could be renamed in the input manager, but it was left as it is to avoid any confusion.) If it is determined that the player is pressing the spacebar, the script checks the elapsed time against the reloadTime (currently half a second). If the time is greater, the script creates a bullet. Notice that the script creates the bullet just a little above the ship. This prevents the bullet from colliding with the ship. Finally, the elapsed time is reset to 0 so the count for the next bullet firing can start.

The last part of the script contains the OnTriggerEnter2D() method. This method is called whenever a meteor hits the player. When that happens, the GameManager script is informed that the player died.

Back in the Unity editor, click and drag the bullet prefab onto the Bullet property of the Ship Control component on the spaceship. Likewise, click and drag the Game Manager object onto the Ship Control component to give it access to the GameManager script (see Figure 15.10). Run the scene and notice that you can now move the player. The player should be able to fire bullets (although they don’t move). Also notice that the player can now die and end the game.

Images

FIGURE 15.10
Connecting the ShipControl script.

The Bullet Script

The last bit of interactivity you need is to make the bullets move and collide. Create a new script called Bullet in your Bullet folder and add it to the bullet prefab. Replace the code in the script with the following:

using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float speed = 10f;

    GameManager gameManager; // Note this is private this time

    void Start()
    {
        // Because the bullet doesn’t exist until the game is running
        // we must find the Game Manager a different way.
        gameManager = GameObject.FindObjectOfType<GameManager>();

        Rigidbody2D rigidBody = GetComponent<Rigidbody2D>();
        rigidBody.velocity = new Vector2(0f, speed);
    }

    void OnCollisionEnter2D(Collision2D other)
    {
        Destroy(other.gameObject); // Destroy the meteor
        gameManager.AddScore(); // Increment the score
        Destroy(gameObject); // Destroy the bullet
   }

}

The major difference between this script and the meteor script is that this script needs to account for collision and the player scoring. The Bullet script declares a variable to hold a reference to the GameManager script, just as the ShipControl script does. Because the bullet isn’t actually in the Scene view, however, it needs to locate the GameManager script a little differently. In the Start() method, the script searches for the GameControl object by type using the GameObject.FindObjectOfType<Type>() method. The reference to the GameManager script is then stored in the variable gameManager. It is worth noting that Unity’s Find() methods (such as the one used here) are very slow and should be used sparingly.

Because neither the bullet nor the meteor has a trigger collider on it, the use of the OnTriggerEnter2D() method will not work. Instead, the script uses the method OnCollisionEnter2D(). This method does not read in a Collider2D variable. Instead, it reads in a Collision2D variable. The differences between these two methods are irrelevant in this case. The only work being done is destroying both objects and telling the GameManager script that the player scored.

Go ahead and run the game. You should find that the game is now fully playable. Although you cannot win (that is intentional), you certainly can lose. Keep playing and see what kind of score you can get!

As a fun challenge, consider trying to make the values of minSpawnDelay and maxSpawnDelay get smaller over time, causing the meteors to spawn more quickly as the game is played.

Improvements

It is time to improve the game. As with the games you have created in earlier hours, several parts of Captain Blaster are left intentionally basic. Be sure to play through the game several times and see what you notice. What things are fun? What things are not fun? Are there any obvious ways to break the game? Note that a very easy cheat has been left in the game to allow players to get a high score. Can you find it?

Here are some things you could consider changing:

Try modifying the bullet speeds, firing delay, or bullet flight path.

Try allowing the player to fire two bullets side by side.

Make the meteors spawn faster as time goes on.

Try adding a different type of meteor.

Give the player extra health—and maybe even a shield.

Allow the player to move vertically as well as horizontally.

This is a common genre, and there are many ways you can make it unique. Try to see just how custom you can make the game. It is also worth noting that you will learn about particle systems in Hour 16, “Particle Systems,” and this game is a prime candidate for trying them out.

Summary

In this hour, you made the game Captain Blaster. You started by designing the game elements. Next, you built the game world. You constructed and animated a vertically scrolling background. From there, you built the various game entities. You added interactivity through scripting and controls. Finally, you examined the game and looked for improvements.

Q&A

Q. Did Captain Blaster really achieve the military rank of captain, or is it just a name?

A. It’s hard to say, as it is all mostly speculation. One thing is for certain: They don’t give spaceships to mere lieutenants!

Q. Why delay bullet firing by half a second?

A. Mostly it is a balance issue. If the player can fire too fast, the game has no challenge.

Q. Why use a polygon collider on the ship?

A. Because the ship has an odd size, a standard shaped collider wouldn’t be very accurate. Luckily, you can use the polygon collider to map closely to the geometry of the ship.

Workshop

Take some time to work through the questions here to ensure that you have a firm grasp of the material.

Quiz

1. What is the win condition for the game?

2. How does the scrolling background work?

3. Which objects have rigidbodies? Which objects have colliders?

4. True or False: The meteor is responsible for detecting collision with the player.

Answers

1. This is a trick question. The player cannot win the game. The highest score, however, allows the player to “win” outside the game.

2. Two identical sprites are positioned, one above the other, on the y axis. They then leapfrog across the camera to seem endless.

3. The bullets and meteors have rigidbodies. The bullets, meteors, ship, and triggers have colliders.

4. False. The ShipControl script detects the collision.

Exercise

This exercise is a little different from the ones you have done so far. A common part of the game refinement process is to have a game playtested by people who aren’t involved with the development process. This allows people who are completely unfamiliar with the game to give honest, first-experience feedback, which is incredibly useful. For this exercise, have other people play the game. Try to get a diverse group of people—some avid gamers and some people who don’t play games, some people who are fans of this genre and some people who aren’t. Compile their feedback into groupings of good features, bad features, and things that can be improved. In addition, try to see whether there are any commonly requested features that currently aren’t in the game. Finally, see if you can implement or improve your game based on the feedback received.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.200.77.92