If you’ve tried making a 2D game with earlier versions of Unity, you know it was certainly possible, but you also know you had to jump through a few hoops to do it.
Maybe you applied textures to quads, adjusting their offsets with a script to create animations. If you used physics, they were in 3D, so you had to make sure your objects had sufficient depth to interact with each other while ensuring they didn’t accidentally rotate around their x- or y-axes. Or maybe you chose to use one of the various add-ons available on Unity’s Asset Store, for example 2D Toolkit or the Orthello 2D Framework, any one of which includes some great features but also forces you to work within its own set of constraints.
While all of these options are still available, Unity 4.3 introduces native tools that add a new dimension to your workflow options: the 2nd dimension!
This is the first in a planned series of tutorials that explore Unity’s native 2D support. Over the series you’ll create Zombie Conga, a game originally conceived for our Sprite Kit book, iOS Games by Tutorials. You’ll do things differently here, but the end result will be the same – a scrolling 2D game about a happy-go-lucky zombie who just wants to dance, the cats who join him in the afterlife party, and the old ladies who try to put a stop to this adorable abomination.
This Unity 4.3 2D tutorial focuses on Unity’s new asset type – Sprite. You’ll learn everything you need to know about Sprites here, and in future tutorials you’ll learn how to control animations through Unity’s Animators and you’ll get an introduction to Unity’s new 2D physics support.
There’s a lot to cover, so you should get going.
Note: This tutorial assumes you have at least some experience with Unity. You should know the basics of working with Unity’s interface, GameObjects and Components, and you should understand an instruction like, “Add a new cat to your scene by dragging cat from the Project browser into the Hierarchy.”
If you think any of that sounds like crazy talk, or if you’d like a moment to get yourself into the right mindset for dragging cats, you may want to go through a tutorial that gives a more thorough introduction to Unity, such as this one.
Finally, note that the instructions in this tutorial are tailored toward OS X. However, if you’re running on Windows don’t worry – since Unity works the same on Windows most of these instructions will still work just fine. There will be a few minor differences (such as using Windows Explorer instead of Finder) but you’ll get through it. Or just start using OS X – reader’s choice!
Unity introduced native 2D tools in version 4.3 (both free and pro), so be sure you have the latest version installed. You can download it from Unity’s website.
Note: You are allowed to use and/or modify all art, music and sound effects from this tutorial in as many games as you want, but must include this attribution line somewhere inside your game: “Artwork/sounds: from iOS Games by Tutorials book, available at http://www.raywenderlich.com”.
Create Your Project
Open Unity and create a new project by choosing File\New Project…. Click Set… in the Create new Projecttab of the Project Wizard dialog that appears.
Name the project ZombieConga, choose a folder in which to create it, and click Save.
Finally, choose 2D in the combo box labeled Set up defaults for:, as shown below, and click Create Project:
The above-mentioned combo box is the first 2D-related feature you’ll come across in Unity. It’s supposed to change the default import settings for your project’s art assets, but so far I haven’t seen it work properly. Fortunately, this isn’t a problem because you can change this setting in your project at any time, and doing so works fine.
To ensure it’s set properly, and so you know how to change it if you ever want to, choose Edit\Project Settings\Editor to open the Editor Settings in the Inspector. In the Default Behavior Mode section, choose2D for the Mode value, as shown below:
The Default Behavior Mode defines the default import settings for your project’s art assets. When set to 3D, Unity assumes you want to create a Texture asset from an imported image file (e.g. a .PNG file); when set to2D, Unity assumes you want an asset of type Sprite. You’ll read more details about Sprite assets and import settings throughout this tutorial.
The Scene View’s 2D Mode
The next 2D feature you’re faced with is the 2D toggle button in the Scene view’s control bar.
Click the 2D toggle button to enable 2D mode, as shown below:
This button toggles the Scene view’s camera between perspective and orthographic projections. What’s the difference?
When viewed with a perspective projection, objects appear smaller as they move further away from the camera, just like objects in the real world look when you see them with your eyes. However, when viewed with an orthographic projection, an object’s distance from the camera doesn’t affect its size. Therefore, in 2D mode, an object that is further away from the camera will appear behind any closer objects, but its size will remain unchanged regardless of its position.
The following image shows two Scene views, each looking at the same two cubes from the same location. The top view is in 2D mode while the bottom one is not:
The previous screenshot also shows how 2D mode hides the Scene Gizmo that lets you change the orientation of the Scene view’s camera. With 2D mode enabled, the orientation is fixed so the positive y-axis points up and the positive x-axis points to the right.
Important: Toggling this setting has no effect on how your game finally appears when played – that’s determined by the camera(s) you set up in your scene – but it can be helpful when arranging objects. You’ll probably move back and forth between these two modes while creating your own 2D games, and even sometimes while creating 3D games, but this tutorial’s screenshots all show the Scene view in 2D mode.
Question: Are you someone who feels better following along with a tutorial when your interface matches the one you see in the screenshots? Then check out the next spoiler to ease your mind!
|Solution Inside: Copy my Unity layout, if you want to.||Show|
Sprites, Made Easily
How easy is it to add a sprite to your scene using Unity’s new features? Try the following experiment to find out.
Step 1: Drag cat.png from your Finder window into the Scene view, as demonstrated below:
Step 2: Use some of the time you save making your game to send a thank you note to the Unity devs.
Phew! That was pretty tricky! If you got lost, don’t feel bad, just re-read those instructions and try again. ;]
Note: Wondering why there are two cat images shown in the animation above? Don’t worry, I’ll explain that later on.
This demonstration was simplified by relying on Unity’s default import settings, which oftentimes won’t be correct for your images. However, this serves to illustrate a point – Unity’s new features make working in 2D amazingly easy! The rest of this tutorial covers everything you’ll need to know to really get started working with 2D graphics in Unity.
Select cat in the Hierarchy and look in the Inspector. Your Inspector most likely won’t show the same position that you see in the following screenshot, but don’t worry about that right now. What’s important to note here is that, in order to display the cat in the scene, Unity attached a Sprite Renderer component to a GameObject.
It’s not obvious, but Unity created geometry for the object, too. In the free version of Unity, each Sprite gets mapped to a simple rectangle, but Unity Pro creates a mesh for each Sprite that basically fits the non-clear pixels in your image. Notice the blue mesh in the following image of the zombie in Unity Pro:
By creating a mesh like this rather than applying your sprites as textures on a quad, Unity can improve your scene’s fill rate at render-time, meaning it will process fewer pixels, and it can pack textures more tightly when using Unity Pro’s Sprite Packer, which you’ll read about at the end of this tutorial.
Note: Don’t let the sudden appearance of the zombie startle you. I just showed the zombie because its mesh was more interesting than the one generated for the cat sprite.
You’ll learn about the Sprite Renderer’s properties throughout this tutorial, but for now, look at the field labeledSprite. This shows the name of the Sprite asset assigned to this renderer. You can only assign one Sprite at a time, but later you’ll learn how to update this field at runtime to create animations.
As you can see in the following image, the
cat GameObject has a Sprite named
cat assigned to its renderer:
Be sure the Project browser is visible. Then click inside the Sprite field in the Inspector to locate and highlight the Sprite asset in the Project browser, as shown here:
Note: The highlighted border fades away after a few seconds, so if you don’t notice it, click the Sprite field again. Of course, with only one asset in your project, it’s unlikely you’ll miss it. ;]
As you can see in the previous screenshot, Unity highlighted an item named cat inside the Project browser, which is a child of another object, also named cat. Two cats in the Project browser? Yeah, that could be confusing. Here’s what’s going on:
- The parent cat is the Texture asset. It’s a reference to the original art file you imported, cat.png, and controls the import settings used to create the Sprites from your artwork. As you can see, it shows a nice thumbnail of the file’s contents.
- The child cat is a Sprite asset that Unity created when it imported cat.png. In this case, there is only one child because Unity only created a single Sprite from the file, but later in the section on slicing sprite sheets you’ll see how to create multiple Sprites from a single image.
Note: While I’ve been claiming Unity renders Sprite objects, the
Sprite class actually only contains the information needed to access a
Texture2D object, which is what stores the real image data. You can create your own
Texture2D objects dynamically if you want to generate
Sprites at runtime, but that discussion will have to wait for a future tutorial.
As you saw with cat.png, you can add Sprites to your scene by dragging art assets from the Finder directly into the Scene view (or the Hierarchy, if you’d like). But more commonly, you’ll add assets to your project prior to adding objects to your scene.
Add to your project the remaining image files you downloaded: background.png, enemy.png, andzombie.png.
|Solution Inside: Not sure how to add image assets to your project? Find out here.||Show|
Add an enemy to your scene by dragging enemy from the Project browser to the Hierarchy.
Just like with cat, there are two items named enemy in the Project browser, but it doesn’t matter which one you choose. That’s because dragging a Sprite asset (the child) always uses that specific Sprite, whereas dragging a Texture asset (the parent) uses the first child Sprite, which is the same thing in a case like this where there is only one child.
Select enemy in the Hierarchy and set its Transform component’s Position to (2, 0, 0), as shown below:
Before your scene starts getting sloppy, select cat in the Hierarchy and set its Position to (0, 2, 0), like this:
Your scene should now be arranged like the following image:
Finally, drag background from the Project browser to the Hierarchy, and set its Position to (0,0,0), as shown below:
You’ll improve the background’s image quality a bit later, so don’t worry if it doesn’t look quite right. (Hint: Importing background.png is one of those times where Unity’s default settings aren’t correct.) Your Scene view will now look something like this:
Don’t be alarmed by the fact that you can no longer see the cat or the old lady in your Scene view. They’re each just taking a brief dirt nap, but you’ll dig them up soon enough. However, before you do that, you need to slice up a corpse! Err, a corpse’s sprites, that is.
Slicing Sprite Sheets
You already imported zombie.png into your project, but this file is different from the other ones you imported. Instead of a single zombie image, it contains several, as shown below:
Such a file is usually referred to as a sprite sheet, and you’ll want Unity to create a separate Sprite asset for each of the sheet’s individual images.
Expand zombie in the Project browser. As you can see in the following screenshot, Unity created a single child – a Sprite containing the entire image – which is not what you wanted.
Fortunately, Unity offers a simple solution you can use to treat this image as a sprite sheet. Select the top-levelzombie in the Project browser to open its Import Settings in the Inspector.
Set Sprite Mode to Multiple (see the following image) and click Apply:
Choosing this option caused a new button labeled Sprite Editor to appear. It also removed the Pivot property, because each individual sprite will define its own pivot point elsewhere.
|Solution Inside: Not sure about pivot points? Look inside for more info.||Show|
Notice in the Project browser (shown in the following image) that the zombie texture asset no longer has any children, as indicated by the lack of a small arrow on its right side:
In this state, the zombie texture is unusable. If you tried to drag it into the Hierarchy, you would get a message indicating it has no Sprites. That’s because you need to tell Unity how you want to slice the sprite sheet.
With zombie selected in the Project browser, click Sprite Editor in the Inspector to open the following window:
The Sprite Editor lets you define which portions of an image contain its individual sprites. Click the Slice button in the upper left of the window to start defining sprites, as shown below:
Unity can find your sprites automatically, but you can adjust its results. Start with the default settings shown below and click Slice.
Unity uses the transparency in the texture to identify possible sprites and displays a bounding box around each one. In this case, it found the following four sprites:
In my tests, Unity’s automatic slicing works best when images are laid out with unambiguous empty space between each item. Notice how Unity only finds the smiley face in the following image, but finds three sprites in the image after that:
The above images point out that you should arrange the images in your sprite sheets carefully. They also point out exactly why Mike had to draw the game’s sprites.
Click on any of the sprites that Unity identified to edit the details of that sprite, including its name, position, bounds, and pivot point. The following image shows the window with the second sprite selected:
You can make changes in the window’s fields, and you can adjust the bounds and pivot point directly within the image.
Normally, after you’ve made changes, you would hit Apply or Revert in the upper right of the Sprite Editor to save or discard them, respectively.
However, while the option to tweak Unity’s findings is great, you won’t need to do that here because you aren’t going to use the sprites it found. The images in zombie.png are arranged in four equally sized rectangles, and Unity has a separate option to handle cases like this one.
Click Slice in the upper left of the Sprite Editor to open the slice settings again, but this time, set Type to Grid. The splice settings change to those shown below:
The Pixel size fields allow you to specify the size of your grid’s cells. X defines the width of each cell; Y defines the height. Unity will use those values to divide the image up equally, starting in the upper left corner of the image.
Set X to 157, and Y to 102, as shown below:
Click Slice and Unity finds the following four sprites:
You can still select individual cells in the grid and tweak their settings like you could when using Unity’s Automatic slicing option, but that’s unnecessary for these sprites.
Click Apply in the upper-right of the Sprite Editor to commit your changes. Notice how Unity updates theProject browser so that the top-level zombie texture asset now contains four child Sprites, named zombie_0,zombie_1, and so on, as shown below:
To see another way to add Sprites to your scene, you’ll create the zombie’s GameObject a bit differently. But be aware that this has nothing to do with the fact that the zombie texture is sliced into multiple Sprites – you could still make it the same way you made the enemy or background objects, by simply dragging an asset into the Hierarchy.
Create a new empty GameObject by choosing GameObject\CreateEmpty. Rename the object zombie, and set its Position to (-2, 0, 0), as shown below:
With zombie selected in the Hierarchy, add a Sprite Renderer component by clicking Add Component in theInspector. In the menu that appears, choose Rendering and then choose Sprite Renderer, as shown below:
Click the small circle/target icon on the right of the Sprite Renderer‘s Sprite field to open the Select Spritedialog. The icon is shown below:
The dialog that appears contains two tabs, Assets and Scene. These show you all the Sprites you have in your project and in the current scene, respectively.
Choose the Assets tab and then click on zombie_0 to assign that Sprite to the renderer, as shown below:
Inside the Scene view, you now have a zombie relaxing on the beach, with an old lady and her cat buried somewhere below it. Pleasant.
With all the necessary sprites in your scene, it’s time to fix a few problems.
Configure Your Game View
The artwork for Zombie Conga was created for an iPhone game, so it’s meant to look good in a specific resolution. To match that environment, set your Game view’s size to a fixed resolution of 1136 x 640.
|Solution Inside: Not sure how? Find out here.||Show|
Your Game view now looks something like this:
Note: Your view may not look exactly like this image, because Unity resizes the Game view to maintain your chosen aspect ratio within the available space. Regardless of its scale, you should see the same amount of the scene in your view.
Obviously, that isn’t quite right. You’re seeing the results of three different problems here, and you’ll correct each one in turn:
- The scene’s camera is not set up properly, so the background doesn’t fill the view properly.
- The scene is rendering your game objects in the wrong order, so the cat and enemy are both buried in the sand.
- The image quality is not very good. This one might be hard to detect with the current camera settings, especially if you aren’t familiar with how the background image should look. But you can trust me, right?
Start by fixing the camera.
Fix Your Camera’s Projection
In 2D games, you’ll usually want the camera to use an orthographic projection rather than a perspective one. You already read about these two projections earlier in this tutorial regarding the Scene view’s 2D mode, but what you may not have realized is that Unity may default your game’s cameras to use a perspective projection.
Select Main Camera in the Hierarchy. Then, inside its Camera component, make sure the Projection is set toOrthographic.
Center the camera vertically on the scene by setting its Transform component’s Position to (0, 0, -10). Your Inspector now looks like this:
Right now it’s not much different from how it looked with a perspective projection. If sprites don’t change size based on their distance from the camera, how do you zoom in so that the background fills the screen? You couldtry scaling your GameObjects, but there’s a much better option – change the camera’s Size property.
The camera’s Size defines the dimensions of its viewport. It’s the number of units from the center of the view to the top of it. In other words, it’s half the height of the view. The width of the view is calculated at run time based on the view’s aspect ratio, as shown below:
In this case, you want the background image to fill the screen perfectly from top to bottom, but allow it to scroll horizontally. The background image is 640 pixels tall, so half of that would be 320 pixels. So that’s your size, right?
Select the top-level background in the Project browser to see its Import Settings in the Inspector.
Look at the Sprite Renderer’s Pixels to Units property. It is currently set to the default value of 100, shown below:
In Unity, units do not necessarily correspond to pixels on the screen. Instead, you usually size your objects relative to each other, possibly assuming a scale such as 1 unit = 1 meter. For Sprites, Unity uses Pixels to Units to determine their unscaled size in units.
For example, consider a Sprite imported from a 500 pixels wide image. The following table shows the different widths your GameObject would have when rendering that Sprite at different scales along the x-axis, using different values for Pixels to Units:
background.png is 640 pixels tall, and the background Sprite has a Pixel to Unit ratio of 100, so the
backgroundobject in the Hierarchy will be
6.4 units tall. However, the orthographic camera’s Size property measures half the height of the screen, so it should be half the height of the background, in units, or
Select Main Camera in the Hierarchy and set the Size property of the Camera component to 3.2, shown below:
Now your background fills the Game view properly, as shown below:
With the background image appearing properly, now you should be able to see problems with the image quality. The following two images point out some areas of the current view compared to what they should look like:
The problems shown above are the result of over compressing the background texture during import. You can fix that by changing the file’s Import Settings.
Correct Your Import Settings
Select the top-level background in the Project browser to view its Import Settings again, but this time look at the Preview pane at the bottom of the Inspector.
The Preview pane displays the texture generated from the import, along with the texture’s dimensions, color information, and memory usage. As you can see in the screenshot below, the background texture is currently sized at 1024 x 320 pixels. But background.png is 2048 x 640 pixels! That means Unity shrank the original image by 50% in order to fit it in a 1024 x 1024 texture.
To fix your texture, look at the Max Size and Format settings in the tabs at the bottom of the Import Settings, shown below:
Max Size defines the maximum allowed size of the generated texture, defined as a square, and it defaults to1024 pixels. Meanwhile, Format specifies the color depth of the image and defaults to Compressed.
You can set different values for each target platform (e.g. iOS, Web, Android), but for this app you’ll just deal with the Default tab.
In the Default tab, change Max Size to 2048 and click Apply. Your Import Settings should look like this:
Immediately you’ll notice both the Scene and Game views look nicer because the background is less compressed. The following image shows the Game view:
Notice in the Inspector‘s Preview area shown below that the background texture is now 0.6 MB, up from its earlier 160 KB:
Increasing the size of the texture increased its memory footprint by 4 times (the numbers you see are rounded a bit).
For some textures, you may want to adjust their Format value to improve their color quality, but that will increase the size even further. For example, if you try changing background‘s Format to 16 bits, you’ll see the texture grows to 2.5 MB, while changing it to Truecolor results in a texture of 3.8 MB.
However, if you look at the following two versions of the background, you’ll notice that the Compressed setting results in an image that looks pretty good compared to the Truecolor version:
Because the compressed image looks good enough while saving so much memory, leave Format set toCompressed. In your own games, try different combinations of these settings and choose the one that results in the smallest texture that still produces your desired results.
Ok, the camera is set up and the background looks good. Now you need to find that old lady and her kitty cat.
Controlling Draw Order
You still can’t see the cat or the enemy sprites because the scene is drawing them behind the background sprite. You could adjust the Z positions of your game objects, so that objects closer to the camera render in front of objects that are further away from it. In fact, that’s a perfectly good way to do things and is self explanatory. However, Unity now supports a great feature for ordering sprites that you should try: Sorting Layers.
Select cat in the Hierarchy and notice its Sprite Renderer’s Sorting Layer value is set to Default, as shown below:
Click the Sorting Layer drop down box and you’ll be presented with a list of all the sorting layers defined in your project, which right now is only Default.
You’ll also see an option called Add Sorting Layer…. Click it.
This brings up the same Tags & Layers editor that you can get to from various other places in Unity, but with theSorting Layers group open while the Tags and Layers groups are conveniently closed. See the following image:
Click + in the Sorting Layers group to create a new sorting layer and name it Cats. Do that two more times to create a sorting layer named Enemies and one named Zombie. Your editor should now look like the following screenshot:
These layers define the draw order – Layer 0, named Default, is the furthest in the back, with Layer 1, named Cats, in front of it, and so on.
Right now, each of the GameObjects you’ve added is using the Default Sorting Layer. For the
backgroundobject, that’s fine because you want it in the back anyway, but you need to change the Sorting Layer for the other sprites.
Select cat in the Hierarchy and set its Sorting Layer to Cats. You’ll immediately notice that the cat is now visible in both the Scene and Game views.
Select enemy in the Hierarchy and set its Sorting Layer to Enemies. This way, the old ladies will walk on top of the cats. Who knows, maybe they’ll trip?
Finally, select zombie in the Hierarchy and set its Sorting Layer to Zombie to ensure your player renders on top of all the other sprites. Your Game view now looks like this:
Note: The Sprite Renderer also has a property named Order in Layer. You can use this to set a specific sort order to GameObjects within the same Sorting Layer.
You won’t use this in Zombie Conga because I haven’t seen any Z-fighting problems in my tests. It seems that Unity renders the sprites within the layer based on when they were added to the scene, so the newest sprite added to a layer is always on top. A sprite displayed on top of another sprite in one frame won’t suddenly appear behind it in the next frame, and that behavior is good enough for this game.
Using Scripts with Sprites
You’ve got some sprites strewn about the beach, but they don’t do anything. To finish up this part of the tutorial series, you’ll write two small scripts: one to animate the zombie and one to allow the player to control the zombie’s movement. You’ll add the rest of the game behavior in later installments of this series.
First you’ll add a simple script to animate the zombie. Select zombie in the Hierarchy and click Add Component in the Inspector. Choose New Script in the menu that appears, then name the scriptZombieAnimator, choose CSharp as the Language, and click Create and Add. The following animation demonstrates these steps:
Note: You’ll replace this script-based animation with a Unity Animator in Part 2 of this tutorial series, but this example demonstrates how to access a
SpriteRenderer from your scripts.
Open ZombieAnimator.cs in MonoDevelop, the code editor that ships with Unity. There are several ways to do so, but it’s easiest to double-click ZombieAnimator wherever happens to be most convenient, either in theInspector with zombie selected or in the Project browser, as shown in the following images:
In Zombie Conga your zombie will simply walk, mindlessly, undeterred; much like you’d expect a good zombie to do. To achieve this simple animation, you’ll need a list of
Sprites and a speed at which to cycle through them. To store that information, add the following public instance variables to
Note: In C# you place instance variables within the bounds of the curly braces that mark the class definition, but outside of any function. While it technically doesn’t matter, it’s usually good practice to place them at the top of the class before any function definitions.
Public variables are exposed within Unity’s editor, so you’ll be able to modify their values in the GUI without changing your code – even while running the scene! You’ll see in a moment how easy that makes it to tweak in-game values and get them just right.
You’re going to render the animation by assigning different
Sprites to your
SpriteRenderercomponent. Rather than getting the component in every call to
Update, you’ll cache it in an instance variable when the script first starts running.
Add the following private variable to
Private variables are not exposed within Unity’s editor. In this case, you’ll initialize the variable in code by adding the following line to
Your script subclasses
MonoBehaviour, which gives it access to a variable named
GameObjects that display
renderer will be a
SpriteRenderer object. Therefore, you cast
renderer to type
SpriteRenderer before storing it.
Note: While it’s always better to test your game’s performance to see what optimizations are necessary, it is often a good idea to store references to objects that you’re script will access frequently. Common examples of this are a
Transform, the scene’s main
Camera, or any objects you might have to otherwise use a command like
GameObject.Find to access. To keep things easier to understand, this is the only time you’ll cache an object in this tutorial, even in cases where it would make sense.
To finish this short script, add the following few lines to
This takes the number of seconds since the level loaded (consult the
Time class docs for more info) and multiplies it by the number of frames that should render per second. If the frames were stored in an infinitely long array, that would give you the index into the array for the current frame.
However, since you know your array won’t be infinite, you need to loop back to the start when you reach its end. You do that by performing a modulus (%) operation, which performs an integer division between two numbers and returns the remainder.
In other words, you’re getting a whole number between 0 and one less than the size of the array, which will be a valid index into the
sprites array (assuming the array doesn’t have a length of zero, of course).
You’re done with MonoDevelop for now, so save your script (File\Save) and switch back to Unity.
Note: Unity compiles your scripts automatically, so if any errors appear in the Console, correct them before moving on.
Select zombie in the Hierarchy and notice the Zombie Animator (Script) component now displays fields for your two public variables. Thanks, Unity!
Right now, the Sprites array field (Unity capitalizes your variable names, and adds spaces between words) has no items. Your Sprite assets are named zombie_0 through zombie_3. These images are meant to be displayed in a specific order, so you’ll want the Sprites array to contain the necessary Sprites for a single cycle of animation, and then your script will loop through those Sprites forever.
In order to define that single animation cycle, you’ll need to add the following six elements to the Sprites array:zombie_0, zombie_1, zombie_2, zombie_3, zombie_2, zombie_1. There are several different ways you can do this, but here is my favorite:
With zombie still selected in the Hierarchy, click the lock button in the upper right of the Inspector so that it displays as locked, as shown below:
This will keep the current Inspector information displayed even if you select another object in the project, which is useful in cases like this.
With the zombie texture expanded in the Project browser, left click zombie_0 to select it, then shift+left clickzombie_3. That selects all four zombie Sprites.
Now drag your selected objects over to the Inspector and hover anywhere over the Sprites row in the Zombie Animator (Script) component. You should see a green plus icon appear under your cursor when you are in a good spot, as shown below:
Release the mouse button and Unity automatically increases the size of the
Sprites array and adds your selected items to it.
Note: It doesn’t matter whether or not you have the Sprites item expanded when you perform the previous steps. The screenshot above shows it expanded only because Unity automatically expands any closed item if you hover over it for long enough during a drag action. If you were trying to take a timed screenshot, for example. :]
Your Zombie Animator (Script) component now looks like this:
Now select only zombie_2 in the Project browser and drag it over in the same way. Unity increases the size of the Sprites array again and appends your new element.
Do this one more time for zombie_1, and your Sprites array should now contain all six elements in the correct order, like this:
Before you move on, click the lock button in the upper right of the Inspector again so that it displays as unlocked, as shown below:
If you had forgotten to do that, you’d get annoyed later when Unity started ignoring all your selections. ;]
Finally, set Frames Per Second to 10, as shown below:
Run the scene and marvel at your shuffling zombie!
Note: You can adjust Frames Per Second in the Inspector while playing the scene to find a speed you like, but Unity resets your values when you stop running. Therefore, be sure to remember what value you liked so you can change it for real after you’ve stopped playing.
Now that you’ve managed to animate (or is it reanimate?) the zombie, he’ll be looking to party. The next section shows you how to create a simple controller script so you can point him point him in the right direction.
Select zombie in the Hierarchy and add a new C# script component named ZombieController, just like how you added ZombieAnimator.
Do you like an old school, plodding zombie, or one of those new-fangled runners? To get it just right, you’ll want to tweak your zombie’s movement speed while you test your scene, which calls for a public variable in your script.
Open ZombieController.cs in MonoDevelop and add the following variable to it:
moveSpeed will store the number of units – not pixels – that the zombie should move per second. Because you’re Sprite sizes are 1 unit for every 100 pixels, you’ll probably want to keep this value fairly low.
As you can see in the following animations, you’ll make the zombie in Zombie Conga move in a straight line toward, and then past, the location of the latest mouse click (or the latest mouse location in cases where the user holds down the mouse button while dragging):
You most likely won’t have an input event every frame, so you’ll need to store the direction in which the zombie is headed whenever the destination changes. To accomplish this, you’ll calculate a normalized vector (a vector of length 1) that points toward the input location from the zombie.
Add the following variable to ZombieController:
You’re making a 2D game, but Unity is still working with a 3D coordinate system. As such,
Transforms store their positions as
Vector3 objects. While you could use a
Vector2 here because you know the zombie will never change its position on the z-axis, you’re storing the zombie’s direction of movement in a
Vector3 to avoid having to cast between the two types later in the script. It’s really a matter of personal preference.
Add the following code to
Update in order to update
moveDirection whenever the game receives an input event:
Here’s what you’re doing with the preceding code:
- Since you’ll be using the zombie’s current position a few times in this method, you copy the position to a local variable.
- Then you check to ensure the
Fire1button is currently pressed, because you don’t want to calculate a new direction for the zombie otherwise. See the upcoming note for more information about
- Using the scene’s main (and in this case, its only)
Camera, you convert the current mouse position to a world coordinate. With an orthographic projection, the
zvalue in the position passed to
ScreenToWorldPointhas no effect on the resulting
yvalues, so here it’s safe to pass the mouse position directly.
- You calculate the direction to move by subtracting the zombie’s current position from the target location. Because you don’t want the zombie changing its position along the z-axis, you set
0, meaning, “Move zero units along the z-axis.” Calling
moveDirectionhas a length of
1(also known as “unit length”). Unit length vectors are convenient because you can multiply them by a scalar value (like
moveSpeed) to make a vector pointing in the same direction, but a certain length (like a
moveSpeed-long vector pointing from the zombie in the direction toward the mouse cursor). You’ll use this next.
Note: You use methods on
Input to access input data in a generic way. A project defines various input names by default, referred to as axes, such as
Horizontal monitors the joystick’s position along its x-axis, as well as the state of the left and right arrow buttons on the keyboard. If you needed to know when your game received input in a horizontal direction, you could simply check for the value of the
Horizontal axis without concerning yourself with the specifics of where the input originated.
Fire1 is one of the virtual button axes defined by default. It registers events for button 0 on a joystick or mouse, and the left control key on the keyboard.
true while the specified virtual button is down, so the code you wrote will update
moveDirection every frame that the mouse button is down (not just on the frame when it was initially pressed). Yes, this also means you’ll be able to change the zombie’s direction by pressing your keyboard’s left control key, as long as you remember that you still need the mouse to steer!
If you want to see or customize your project’s input options, go to Edit\Project Settings\Input to bring up the
InputManager‘s settings in the Inspector.
Now start the zombie walking by adding the following code to the end of
The first line calculates a target location that is
moveSpeed units away from the zombie’s current position. That is, it finds the point the zombie would reach if it traveled from its current position in the direction pointed to by
moveDirection for a duration of one second.
The second line uses
Vector3.Lerp to determine the zombie’s new location along the path between its current and target locations.
Lerp is a convenient method that interpolates between two values based on a third value.
Lerp clamps the third value between zero and one and interprets it as a fraction along the path between the two points. That means a value of
0 would return
currentPosition, a value of
1 would return
target, and a value of
0.5 would return the midpoint between them.
In your case, you use
Time.deltaTime as the third value because it is a fraction of one second and you know it will most likely be less than one, giving you some point along the path that is not quite at the end point. Your game would need to be running horrendously for
Time.deltaTime to be anywhere near
1, so you should get nice, smooth motion.
Save your script in MonoDevelop and switch back to Unity.
Run the scene and click somewhere to get the zombie walking. Or not. You never set
moveSpeed, so it’s “moving” at a rate of zero!
While the scene is still running, select zombie in the Hierarchy and find the Zombie Controller (Script)component in the Inspector. Change Move Speed to 2, click on the beach, and your zombie should be on his way!
Adjust Move Speed in the Inspector until you’re happy with it. Depending on the speed you choose, you may want to adjust the Frames Per Second on the Zombie Animator (Script) component, too. Otherwise his animation may not match his motion.
When you find values you like, remember them and stop the scene. Then set the values in the Inspector again so they’ll be correct the next time you run.
While you were busy tweaking his ambulatory system, you probably noticed a few things wrong with your zombie.
- When you start playing, his legs are moving but he’s not going anywhere.
- He happily walks right off the screen.
- He doesn’t look where he’s going.
You’ll fix the zombie so he no longer wanders off screen in a later installment of this tutorial series, so ignore that issue for now. Besides, if he leaves the screen, simply click on the beach again and he’ll come strolling back eventually. Zombies are good like that.
Go back to ZombieController.cs in MonoDevelop.
The script uses
moveDirection when moving the zombie, but you only assign it a value when you get an input event. To spur the zombie forward when the scene starts, you simply need to initialize
moveDirection to point him in the right direction.
Add the following line inside
This points in the direction of the positive x-axis. In other words, it points toward the right side of the screen.
Save ZombieController.cs and then play your scene again in Unity. Now your zombie is off and running! Next you’ll make sure he faces the right direction so he doesn’t trip.
Back inside ZombieController.cs in MonoDevelop, add a public variable to
ZombieController so you can tweak the rate at which the zombie turns.
turnSpeed in your calculations to control how quickly the zombie reorients himself to a new direction.
Unity uses quaternions internally to represent rotations. If you’re curious about the details of the math, check outthis info. Then, after you’ve cleaned up the mess from your head exploding, take solace in the fact that you really don’t need to know anything about quaternions when working with 2D.
That’s because the
Quaternion.Euler method lets you create a
Quaternion object from an Euler angle. Euler angles are the ones most people are accustomed to, consisting of individual x, y and z rotations. While they aren’t ideal for 3D work because of problems like gimbal lock, Euler angles are just fine for 2D games where you probably only want to rotate around the z-axis.
Note: To learn more about Quaternions, check out our OpenGL ES Transformations with Gestures tutorial.
Add the following code to the end of
First you use
Mathf.Atan2 to find the angle between the x-axis and
Mathf.Atan2 returns the angle in radians, so you convert it to degrees by multiplying by
Quaternion.Slerp to turn towards the target angle you calculated.
Quaternion.Slerp performs aspherical linear interpolation between the two angles you specify. It’s similar to what you did earlier with
Vector3.Lerp, except you’re calculating a new rotation instead of a new position.
Earlier, when you called
Vector3.Lerp, you used
moveSpeed to adjust the distance the zombie traveled. In this case, you’re using
turnSpeed to do something similar; the larger its value, the faster the zombie will arrive at the target angle.
Note: Some of you might be thinking to yourselves, “Hey, that math’s no good! I want the zombie to take the shortest path to the new angle, and just finding the arctangent doesn’t guarantee that! You, sir, are a charlatan!”
To you I would suggest some calming yoga and maybe a bit more fiber in your diet. I would then point out that
Quarternion.Slerp is awesome and always interpolates through the shortest route between two angles.
That’s it for ZombieController.cs. Save it and switch back to Unity.
Select zombie in the Hierarchy. Set Turn Speed to 5, as shown below:
Run the game and click around on the beach. No matter how hard you try, zombies never get dizzy!
That’s all the work you’ll do for Zombie Conga in this tutorial. Save your scene by going to File\Save Scene as…. Name the scene CongaScene and click Save.
The next section describes a feature useful for 2D games that is only available in the paid version of Unity. It isn’t necessary for Zombie Conga, but it’s something you’ll probably want to know about for more complex projects — Sprite Packing.
Sprite Packing – For Professionals Only (Sort of)
Note: This section describes a feature that is only available in Unity Pro. You can certainly still use texture atlases in the free version of Unity, but you’ll need to use separate tools to create and support them. You could also use sprite slicing like you did with zombie.png to achieve the same runtime benefit you’d get from texture atlases, because all of the Sprites you create from a single art asset will use the same texture by default, but it would require you to organize and access your assets in ways that may not be intuitive and therefore will not be covered here.
Play your scene and check out its rendering statistics by clicking the Stats button in the Game view’s control bar, as shown below:
Notice that the game is currently making four draw calls and saving none by batching. Not cool!
Of course, this makes sense, because it’s rendering exactly four Sprites in the scene, and each one uses a unique texture. While a real game with only four draw calls would probably be fine, that number will likely grow as the number of objects and effects in your scene increases. Too many draw calls hurts performance, so you’ll probably need to be a bit more careful about how you organize your Sprite textures. Fortunately, there’s a basic technique you can use to help: pack your sprite images into texture atlases.
Texture atlases — large textures made up of several smaller textures, used to optimize rendering calls to the GPU — aren’t anything new, but before now you had to create them yourself. Now Unity can build them for you!
Note: As of Unity 4.3.2, Unity’s ability to create texture atlases, which they refer to as Sprite Packing, is still in Developer Preview mode. But it’s a great tool, so why not learn about it right now?
In order to pack your Sprites into texture atlases, you first need to modify their Import Settings.
Select the top level cat in the Project browser to open the Import Settings for cat.png. Notice the property named Packing Tag. Packing Tag defines the name of the texture atlas to which you want this asset’s sprites added, and it can be any string you’d like.
Set Packing Tag to toons by typing in the text field, as shown below:
Don’t forget to hit Apply whenever you make changes to any asset’s Import Settings, or the changes will not take effect. Don’t worry: you’ll get a warning dialog if you select another object in your project while you have uncommitted changes.
Now repeat that process to set the Packing Tag to toons for the enemy and zombie assets as well.
Open the Sprite Packer window by choosing Window\Sprite Packer. The menu item may read Window\Sprite Packer (Developer Preview) if it’s still in beta, but you get the idea.
What’s this, an error?
As you can see, Sprite packing is disabled by default. To enable it, go to Edit\Project Settings\Editor and set the Mode for Sprite Packer to Always Enabled, as shown below:
Choosing Always Enabled packs your Sprites when you play the game from within Unity as well as when you export a build of your project; you also have the option of only packing your Sprites for builds.
Choose Window\Sprite Packer again, and this time you see an empty Sprite Packer window, like this:
It currently warns you that Sprite packing is a developer preview feature.
Note: If Sprite packing is no longer in beta, than you won’t see the yellow message shown in the above image.
Press Pack in the upper left and you’ll see a new texture with your sprites arranged within it, as shown below:
Play your game again and check the stats in the Game view. As you can see in the following screenshot, Unity is now using only two draw calls, saving two by batching. It all adds up!
This makes sense, because even though you still have four Sprites in the scene, three of them are now sharing the same texture! And to get that performance savings, which would be more evident with more Sprites on screen simultaneously, you barely had to do anything at all! Sweet.
Sprite Packer — Options and Issues
There are a few other things you may run into when packing Sprites. The top of the Sprite Packer window contains a control bar, shown below:
- The current atlas you’re viewing. This drop down contains each of the Packing Tags you’ve used in your project, allowing you to choose which one to view.
- The current page in the atlas. The atlas you created only contains one page, but if it had more, this dropdown would let you choose which one to view. More on this in a bit.
- The packing policy used to arrange the sprites in the atlas. There is only one policy available at this time –DefaultPackerPolicy – but you can create your own by implementing the
IPackerPolicyinterface. This is an advanced feature beyond the scope of this tutorial.
The notion of a current atlas makes sense, but what you might not expect is that Unity sometimes splits up the atlas you intended to create into multiple atlases, appending group numbers to their names. This occurs when the Import Settings for the component textures don’t match.
For example, the following image shows what the toons texture atlas would look like if zombie.png had been imported with a color Format of 16 bits while cat.png and enemy.png used a Format of Compressed. Notice how it results in two atlases, named toons (Group 1) and toons (Group 2):
Even though all three Sprites have the same Packing Tag, Unity created multiple atlases out of them. To ensure you’re getting the best performance from your atlases, be sure you don’t use incompatible import settings for sprites that you intend to store in the same atlas.
As for the current page, Unity creates multiple pages for an atlas if there are too many to fit within its texture size limit. If you end up with an atlas with multiple pages, Unity has basically created different texture atlases. You generally want to arrange your textures in such a way to ensure the ones most likely to be used together end up on the same page to reduce draw calls.
Note: I must admit, I haven’t figured out how Unity decides the size limit for a texture atlas, or if you can modify it. If anyone knows, please post in the comment section and I’ll update this text. Thanks!
Where To Go From Here?
I hope you enjoyed this Unity 4.3 2D Tutorial. If you didn’t enjoy it, I hope you at least learned something. If you learned nothing, maybe you should get in touch with us about writing some tutorials of your own? ;]
You can download the completed project here.
At this point although there’s still a lot left to do with Zombie Conga, you’ve actually already seen enough to go write a lot of other 2D games. Move on to Part 2 to learn how to use Unity’s built-in animation system. In the third part of the series, you’ll get more practice creating Animation Clips, and you’ll learn how to control the playback of and transition between those clips. In the final part of this tutorial series, you’ll finish Zombie Conga with an introduction to Unity’s 2D physics engine and a bit more scripting.
While you’re learning about Unity’s 2D tools, remember there are some great resources for you to explore on Unity’s website, including:
- An extensive set of excellent documentation.
- Video tutorials. Most of these are not 2D-specific, but they have recently started adding videos to a new 2D section.
- Live training sessions. These run pretty much weekly, and there is an archive of old sessions.
- An active and helpful Community site.
As always, please consider discussing this post in the Comments section.