left-icon

Unity Game Development Succinctly®
by Jim Perry

Previous
Chapter

of
A
A
A

CHAPTER 4

2-D Graphics and Sprites

2-D Graphics and Sprites


No game can exist without a graphical component. Whether it’s puzzle pieces, fantastical creatures, vehicles of some kind, or humans running around shooting each other, the graphical component is, without question, the most important aspect of a game. There’s only one game that I can think of that had no graphical part to it—a game that utilized audio only. That’s the exception to the rule, however. You’ll need something for the player to see and manipulate in your games.

The object of our game will be the player running around in a room or arena being attacked by ghosts that will randomly come out of openings in the walls of the arena. If you think about it, you might easily surmise what the graphics pieces will be:

  • The arena in which the player will be contained.
  • The character the player will control.
  • The ghosts.
  • The bullets the character will fire.

There are several other items we could add, but for a basic game this is all we’ll need. Each one of these parts will be represented in the scene by a sprite.

Sprite

A sprite is simply a 2-D image with some added functionality in order to make it easy to manipulate and work with. The image is pulled from a graphics file (usually a .png or similar graphics format) and rendered to the screen by a SpriteRenderer. Note that the graphics file can contain multiple images, not only the one that will be used for the sprite. The images can be related or unrelated to each other in what’s usually called an atlas, or they can be frames of an animation for one sprite. We put multiple images in one file in order to reduce memory cost (there could be a lot of lost memory in the area of the file that doesn’t actually contain part of the image) or to make organizing your sprites easier. You could have a large sprite composed of multiple parts, some of which will be animated—having all of these parts in their own files might get confusing when you’re trying to piece together the sprite.

Two of our sprites will have animations—the character and the ghost—and the other two will have only single images—the bullet and the arena.

We’ll begin by implementing the arena sprite. First, add the level graphic file to the Sprites folder. Clicking the item in the Sprites folder should show that the image will be treated as a sprite, as shown below in the screenshot of the Inspector in Figure 21.

Sprite Properties

Figure 21: Sprite Properties

Drag the sprite from the Sprites folder to the Hierarchy window. Figure 22 depicts what the Inspector will show.

Level Sprite Properties

Figure 22: Level Sprite Properties

The sprite is automatically positioned in the middle of the scene.

You can click on the Color bar and see what changing the RGB values will do.

The Flip X and Y values does what one would expect—it flips the sprite on the x-axis and y-axis.

The Material property of the SpriteRenderer isn’t particularly relevant to us, and you shouldn’t change the default value. Materials are more often used along with shaders in 3-D rendering to change how a 3-D object looks. For example, you can take a blank sphere and make it look like a baseball or basketball based on the material you apply to it.

The Sorting Layer and Order in Layer properties allow you to change the order in which the sprites are rendered from back to front. Sprites within the same layer are rendered lowest to highest, meaning a sprite with an Order in Layer of 0 will be covered by a sprite in the same location with an Order in Layer of 1. Sprites with the same value will be rendered in the order they were added to the scene.

Now that we have something for the character to run around in, we need to make sure he can’t run out of it. If the character is able to run through the walls of the arenas, that would look strange. That’s the behavior of the ghosts. The way we do this is with a Box Collider. This component will keep 2-D objects from moving beyond their bounds.

Select the Level object in the Hierarchy. In the Inspector, click Add Component. Select the Physics 2-D item, then the Box Collider 2-D item. Unity will try to fit the collider to the object it’s placed on as best it can, but usually it must be edited in order to get the effect you want. Clicking Edit Collider in the Box Collider 2-D section of the Inspector will show green lines that define the limits of the box. The lines may be a bit hard to see at first. You can click on the check box of the SpriteRenderer section of the level in the Inspector to see them. Of course, you’ll have to make sure the SpriteRenderer is checked so that you can see the image in order to align the collider correctly. You should see the squares in the middle of each line, even with the image being visible. Simply drag them so that the lines are on the inside of the walls of the arena, as shown by the red box in Figure 23.

Box Collider Placement

Figure 23: Box Collider Placement

When you have the collider in place, make sure you check the Is Trigger check box in the Box Collider 2-D section of the Inspector for the Level object. We need to do this because the collider not only keeps the character in the arena, it also keeps the ghosts out when we try to spawn them. The ghosts will spawn just outside of the 10 white sections that represent entrances into the arena. If we don’t set the collider as a trigger, the ghosts will hit this when they try to move toward the player and will not be able to go further. With the collider set as a trigger, we can control what happens when an object hits the collider. We can let the ghosts pass through but restrict the character. We will do this a bit later when we add a script to the scene in order to control the player.

Bullet, character, and ghost Sprites

Now that we have an arena for the character to run around in, we need to get the character and ghost sprites into the scene. Add both files to the Graphics folder.

Click the player sprite. You’ll also notice at the bottom of the Inspector that the file contains multiple images of the sprite, as shown in Figure 24.

Player Sprite Frames

Figure 24: Player Sprite Frames

The slight differences are intended to show that the character is walking when it’s moving around the screen, not simply floating or flying. The ghost sprite contains similar frames. Although it does float as it moves around, having only one frame makes things a bit boring. Having it animate as it moves makes the sprite look a bit more realistic. Getting the sprite to move through each of the frames over time is called animation. We’ll cover how to do this a bit later, after we have the sprites set up correctly.

In order for the character and ghosts to interact with each other, we need to add a collider as well as one other Physics object. Instead of a Box Collider, however, we’ll want to add a Polygon Collider 2-D. We do this in order to make the collision detection more accurate. Using a Polygon Collider is more computationally expensive, but in some cases it’s much more accurate. If we were to add a Box Collider, there would be areas inside the box detected as a collision that aren’t really collisions. Players will see that the collision detection doesn’t work as it should, which will make your game look bad. Look at the ghost sprite in Figure 25.

Ghost Sprite Box Collider vs. Polygon Collider

Figure 25: Ghost Sprite Box Collider vs. Polygon Collider

The blue dots are the area used for a Box Collider. The green lines show the area for a Polygon Collider. For the ghost, there will be areas at the top, middle, and bottom sides that will get incorrectly detected as a collision if we used the Box Collider. The player sprite is almost as bad, as we see in Figure 26.

Player Sprite Box Collider vs. Polygon Collider

Figure 26: Player Sprite Box Collider vs. Polygon Collider

Use the Edit Collider button in the Inspector to tighten the polygon around each sprite. You can click the polygon between two points in order to add a point that allows you to create a tighter polygon. Along with the Collider component, we also need a Rigidbody 2-D component. We’ll use this to help with setting up our movement. Add both of these components to the player and ghost sprites.

The character, ghost, and bullet sprites will work a bit differently than the level sprite. The level sprite stays on the screen and doesn’t change. There can be multiple instances of ghost and bullet sprites on the screen at the same time, which means we’ll set them up as prefabs in order to create the multiple instances easily. The character sprite will have only one instance on the screen, but the character can be killed by the ghosts, ending the game. We’ll remove the sprite when this happens. If the player decides to play the game again, we’ll recreate the sprite—we’ll make it a prefab, too.

Creating a prefab from a sprite is very easy. Drag each sprite from the Sprite folder into the Hierarchy, then drag each one into the Prefabs folder. Delete the sprites from the Hierarchy and we’re done.

Creating instances of the prefabs takes a bit more work than creating the prefabs themselves, but it’s not very difficult. We’ll deal with the character first. In the Game script file, add the code from Code Listing 8.

Code Listing 8: Player Creation Code

public GameObject Player;

private GameObject _player;

void Start ()

{

   _player = (GameObject)GameObject.Instantiate(Player, new Vector3(0, 0, 0), Quaternion.identity);

   ResetGame();

}

void ResetGame()

{

   _player.transform.position = new Vector3(0, 0, 0);

}

The public Player member will let us reference the character prefab in our code. The Player member is our instance of the prefab. When the scene loads, the Start method is called automatically. We then create the instance of the prefab, placing it in the center of the screen.

We’ll have a bit more code that deals with the character instance, but we’ll add that later. Let’s move on to creating the instances of the ghost prefab. We’ll need more code because we have multiple instances of the sprite, and they can appear in different locations at different times. Code Listing 9 shows how we’ll handle this.

Code Listing 9: Ghost Creation Code

public GameObject Ghost;

private void SpawnGhost()

{

       

    int index = Globals.RandomNum.Next(Globals.StartingPoints.Length);

    Vector3 vec = Globals.StartingPoints[index];

    GameObject ghost = (GameObject)Instantiate(Ghost, vec, Quaternion.identity);

    ghost.GetComponent<Ghost>().Player = _player;

    //rotate as necessary

    switch (index)

    {

        //rotate 180

        case 0:

        case 1:

        case 2:

        {

            ghost.transform.Rotate(0.0f, 0.0f, 180.0f);

               

            break;

        }

        // rotate 90 counterclockwise

        case 3:

        case 4:

        {

                ghost.transform.Rotate(0.0f, 0.0f, 90.0f);

                break;

        }

        // rotate 90 clockwise

        case 8:

        case 9:

        {

                ghost.transform.Rotate(0.0f, 0.0f, -90.0f);

                break;

        }

    }

}

Because there are 10 places a ghost can appear, we generate a random number that is an index into a list of possible start positions. These are determined simply by looking at the level sprite in the scene.

When we have the starting position, we create an instance of the ghost prefab and give it a reference to the instance of the player’s character so that the ghost prefab can chase after the player’s character.

Depending on the starting position of the ghost instance, we might need to rotate the ghost instance so that it’s facing the opening it’s supposed to come through.

This method relies on some global variables. Add the following code to the Globals class.

Code Listing 10: Ghost Creation Global Variables

public static System.Random RandomNum;

public static Vector3[] StartingPoints = new Vector3[] {

   new Vector3(-2.575f, 3.4f, 0f), new Vector3(0f, 3.4f, 0f), new Vector3(2.575f, 3.4f, 0f),

   new Vector3(5.0f, 1.2f, 0f), new Vector3(5.0f, -1.14f, 0f),

   new Vector3(2.575f, -3.4f, 0), new Vector3(0f, -3.4f, 0f),new Vector3(-2.575f, -3.4f, 0f),

   new Vector3(-5.0f, -1.14f, 0f), new Vector3(-5.0f, 1.2f, 0f)};

The random number variable will be initialized in the Start method of the Game script. Add the following code to the beginning of the method.

Code Listing 11: Random Number Initialization

if (Globals.RandomNum == null)

     Globals.RandomNum = new System.Random();

We have a character and ghosts to chase him, so now we need a way for the player to destroy the ghosts. Add the bullet graphic file to the Sprites folder, drag it into the Hierarchy window, then drag that into the Prefabs folder. Delete the bullet in the Hierarchy window, and we’re done. Creating instances of the bullet prefab is done by responding to input from the player. Read on and we’ll see how to do that.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.