UNITY Unity 2d Game Development Project
UNITY Unity 2d Game Development Project
Lauren S. Ferro
Francesco Sapio
BIRMINGHAM - MUMBAI
Unity 2017 2D Game
Development Projects
Copyright © 2018 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or
transmitted in any form or by any means, without the prior written permission of the publisher,
except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information
presented. However, the information contained in this book is sold without warranty, either express
or implied. Neither the authors, nor Packt Publishing or its dealers and distributors, will be held
liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and
products mentioned in this book by the appropriate use of capitals. However, Packt Publishing
cannot guarantee the accuracy of this information.
ISBN 978-1-78646-027-1
www.packtpub.com
mapt.io
Mapt is an online digital library that gives you full access to over 5,000
books and videos, as well as industry leading tools to help you plan your
personal development and advance your career. For more information,
please visit our website.
Why subscribe?
Spend less time learning and more time coding with practical
eBooks and Videos from over 4,000 industry professionals
Improve your learning with Skill Plans built especially for you
Packt Publishing - For the opportunity to write this book and be a part of
the authoring community.
Francesco Sapio - For your help and guidance throughout this book.
Andreas Oehlke - For your time, comments, and suggestions.
My family - For their motivation and encouragement.
You, the reader - I hope that this book takes you on marvelous and intrepid
adventures.
Finally, Francesco loves math, philosophy, logic, puzzle solving, and, most
importantly, creating video games.
What this book covers
, Press Start, is our first chapter and will provide you with a
Chapter 1
primer about what game design and development are involved. This
chapter offers an overview of the three different games that will be made
throughout the book.
, Working with Assets, will get you started with Unity. This chapter
Chapter 2
Chapter 3 , Let's Make Some Prefabs, continues the development of our first
game by introducing fundamental concepts in Unity. It also explains how
to start scripting with C# to create most of the gameplay of this first
project.
, It's about U and I, wraps up the first game of the book, and will
Chapter 4
third-party tool for creating tilesets. Furthermore, you will learn how to
incorporate these tiles into Unity to build 2D worlds for your third game.
, Look, It Moves, starts the actual development of the third and last
Chapter 9
game of this book by expanding the concepts learned for animation state
machines and extending them for the main hero animation system.
data of your games, and how to use these techniques to create a save/load
system.
, The Nature of Export, dives into how to export the games you
Chapter 12
and tech professionals, just like you, to help them share their insight with
the global tech community. You can make a general application, apply for
a specific hot topic that we are recruiting an author for, or submit your
own idea.
Table of Contents
Preface
Conventions used
Get in touch
Reviews
1. Press Start
Atoms of games
Workflow
Concept development
Implementation
Testing
Iteration
Finalizing
A-Team
Developing 2D games
X, Y, and Z-axis
Full 3D
Orthographic (3D)
Full 2D
Downloading Unity
Hierarchy Window
Scene View
Game View
Inspector window
Console window
Project Window
Asset Workflow
Images
3D model files
Audio files
Assets
Editor
Plugins
Gizmos
Standard Assets
StreamingAssets folder
Resources
Hidden Assets
Project 3 - RETROformer
Summary
n mind
Sprite Render
Sprite Editor
Sprite Packer
Folder setup
9-slicing Sprites
Best practices
Textures
Scaling
Naming
Summary
Importing audio
Audio Listener
Audio Source
Choosing sounds for background and FX
Happy
Sad
Retro
Colliders
Enforcing components
Collectable system
Triggering cake
Summary
4. It’s about U and I
Overview of the UI
Designing the user interface
Four types of UI
Diegetic
Non-diegetic
Spatial
Meta
UI is not UX
Designing UIs for games
Feedback
Multi-device design
Goals
Enjoyment
Test
ms
Setting up the UI
Last tweaks
Testing
Adding animations
Devils
Summary
An overview of game #2
First-Person
Third-Person
Top-down
Designing game #2
Setting up the project for game #2
Summary
Enemy controller
Requirements of the enemy controller
Enforcing components
Hit spaceships
Exercises
Summary
Building the UI
Setting up the UI
Rotating satellites
Including power-ups
Spawning system
Coroutines
Exercises
Other things you could consider adding to the game
Timer
Combos
Summary
8. Building a Tilemap and Importing it into Unity
Platforming games
Side-scrolling
Creating tiles
Introduction to the program - Tiled and Tiled2Unity
Mini-map
Post-Tiled2Unity
Summary
9. Look, It Moves
Advanced animations
Summary
10. Let’s Get Physical
Physics Material 2D
Player Controller
Summary
PlayerPrefs
PlayerPrefs functions
Jump pads
Water zones
Winning zone
Testing
Summary
MovingController
JumpController
For Android
Summary
Once the file is downloaded, please make sure that you unzip or extract
the folder using the latest version of:
The code bundle for the book is also hosted on GitHub at https://github.com/
PacktPublishing/Unity-2017-2D-Game-Development-Projects. We also have other
code bundles from our rich catalog of books and videos available at https:/
/github.com/PacktPublishing/. Check them out!
Download the color images
We also provide a PDF file that has color images of the
screenshots/diagrams used in this book. You can download it here: http://w
ww.packtpub.com/sites/default/files/downloads/Unity20172DGameDevelopmentProjects_Co
.
lorImages.pdf
Conventions used
There are a number of text conventions used throughout this book.
void FixedUpdate () {
//Get the new position of our character
var x = transform.position.x + Input.GetAxis("Horizontal") *
Time.deltaTime * speed;
var y = transform.position.y + Input.GetAxis("Vertical") *
Time.deltaTime * speed;
}
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
}
Bold: Indicates a new term, an important word, or words that you see
onscreen. For example, words in menus or dialog boxes appear in the text
like this. Here is an example: "Select System info from the Administration
panel."
Errata: Although we have taken every care to ensure the accuracy of our
content, mistakes do happen. If you have found a mistake in this book, we
would be grateful if you would report this to us. Please visit www.packtpub.com
/submit-errata, selecting your book, clicking on the Errata Submission Form
Piracy: If you come across any illegal copies of our works in any form on
the Internet, we would be grateful if you would provide us with the
location address or website name. Please contact us at [email protected]
with a link to the material.
In some instances throughout this chapter and the rest of the book, there
are various links to resources (such as Unity documentation). In many of
these instances, both the link and a QR code are provided. The intention
here is to save time entering the URL into your browser if you are reading
a hard copy of this book. Now with that said, this is what we will cover in
this chapter:
Game design
Developing 2D games
Unity Engine
This book does not assume that you know anything about creating games
or any of the skills that are associated with game development. Over the
course of the book, you will learn the basics of programming in C#, how
to create three different types of 2D games in Unity, and a whole lot more
about the game design process in general. Fear not about the art or the
sound, as we will be using some free asset packages and sounds so that
you can create something magical. Of course, if you dare to push yourself,
I encourage you to dabble in these areas, as even a basic level of
understanding goes a long way.
Atoms of games
We can think of game designers like chemists, except chemists that create
mixtures of entertainment, emotionally driven narratives, and exciting
experiences. If you think back to chemistry in high school, we all learned
that some things make smoke, other things make bubbles, and a few things
when combined make explosions; games work in a similar way, just with
different substances (for example, game elements and mechanics).
On the other hand, since game elements are the outcome, then the process
of how we get them is what we will define as game mechanics. This could
be anything from trading with other players, exploring mysterious worlds,
and even Winning or Losing. Performing or engaging in these actions will
result in obtaining game elements.
When you're playing games, think about things that you are
receiving during gameplay and how you get them. By being a
bit more conscious during your own experiences and
reflecting on them, you not only develop an awareness of the
"reactions" that certain game elements and mechanics
afford, it also provides you with a way to consider
implementing these into your own games. As a result, you
will learn how different combinations create different
outcomes.
Different approaches to games
Games are enjoyable to play, we play them to entertain ourselves and
friends. Over many decades, as games have developed along with the
technologies that make them available, the context of their uses has also
evolved. For example, most games are created to entertain us (for
example, Assassin's Creed). That is, there is no underlying intention, such
as to supplement/support curriculum material or teach us about science
(for example, Ludwig) all the way to operational health and safety. Finally,
there are games that have been developed for entertainment but have
ended up being used for more intuitive reasons. This is because of the
educational potential that they afford (for example, Minecraft and Sid
Meier's Civilization series) some games that have been created for
entertainment purposes have provided ways for students to learn and
practice various topics. Lastly, there are game-like experiences or
"gamified" experiences that apply game design principles to achieve
everyday tasks, from becoming more productive at work, eating better and
maintaining a healthier lifestyle, to managing finances and developing
new skills (for example, Habitica).
Now that we have a brief understanding of what games are made of and
their use in different contexts, it's time to understand the game
design process.
The game design process
As I mentioned before, there are many things that define what is game
design. For example, it is an opportunity to transport players to realities
that we can only imagine to be people that we may only dream of.
However, just as important as what it is and isn't, is how it is done.
Generally speaking there is a systematic approach that game designers
follow to create a game. It begins with the "idea"—the concept of what the
game will be. This is usually not static and will change over the course of
a game's development. From there, and depending on who is on your
team, it will go through many design phases before becoming a prototype.
Beyond the prototype phase, the actual game will enter the development
process where the actual assets (and not placeholders) will be created and
implemented. From this point, the game will go through an iterative cycle,
where the game will undergo playtesting, changes, more playtesting, more
changes, and then eventually it will be refined to the point where it will
enter the last stages. Here, we will briefly explore each of these stages.
Workflow
The workflow process of game design is linear in the sense that there is an
order that it takes place: concept development, prototyping,
implementation, testing, and hitting release. This concept is illustrated
here:
Of course, the diagram only outlines the process in general. As you
develop games and work with others, you will find that this process loops
in many other parts. Sometimes, through designing and prototyping your
concept, you may end up having to change the initial idea. The important
thing to remember is that while to some extent game design is a linear
process, in terms of developmental stages, these stages do and will often
form iterative cycles.
Concept development
We begin the process of game design by first brainstorming and then
designing and prototyping a concept. The ideas at this stage can be
adventurous, out there, and completely bizarre, because anything goes. It
may be useful to get yourself a small notebook to write down your
thoughts because they will come to you at any moment. Another thing to
think about is who you are designing this game for. Do you know enough
about them or do you need to do a bit more research? Defining your
demographics early on can help when it comes to refining your idea. For
example, if the people you are designing play role-playing games (RPGs)
then they are likely to prefer a detailed narrative and character
customizations, as opposed to premade characters and a simple story.
Therefore, the kind of environment, what kind of narrative that it can
afford, and the characters that you will create (and possible options for
them) will need to be thought about while you're brainstorming ideas.
Some useful tips when it comes to concept development are listed here:
Keep all your ideas in a journal or somewhere safe so that you can
refer to them later.
Play games that you wouldn't normally play, and if you don't play,
then start!
By playing more games in different contexts and on different
platforms (mobile, PC, PlayStation, and so on), you will begin to
learn how experiences change depending on the hardware you're
using to interact with. Pay attention to how the player controls
differ between a mobile and console game. These little things will
help you later when you start to design and prototype your game.
Learn about games! Don't just become the game designer, become
a researcher, an explorer of games. Invest a little bit of time to
learn what other game designers have done, how they have done
it, what worked or didn't. There are many postmortem videos
about games that explain the development process. In addition, it
also gives you a bit more insight into the overall game design
process and the roles and responsibility that each team member
has. In this way, if you're thinking about creating a small (or large)
team, you can understand what's involved.
Generally speaking, the z-axis is used for depth and is utilized so that the
player can explore an environment like they would in reality whether it is
from a third person or first-person perspective camera. The z-axis allows
the added third dimension to an environment. 2.5D games utilize the z-
axis to provide a sense of depth because the game is still fixed to the x and
y-axes.
If you had to turn your current environment into one that would be in a
video game, what kinds of objects would you need to create? Perhaps a
desk or chair, maybe a lamp? Then, how would players interact with these
objects? What kind of effects would occur, such as a player turning the
lamp on or off?
There are many basic and intricate details that go into making a 3D
environment. Of course, they are not exclusive to 3D environments, as we
will see in the other types of games, but these are some fundamental
considerations.
Screenshot of Assassin's Creed
Orthographic (3D)
Just like the name suggests, orthographic 3D is a 3D game that is played
from an orthographic perspective. Sometimes, these are also referred to as
Isometric games and are given this name because of the perspective that
they are played in. Some popular orthographic games include Diablo,
Q*bert, Clash of Clans, and Monument Valley (featured next).
Unlike 3D, and depending on the type of gameplay that is required (for
example, can the player rotate the environment?), the player will more
than likely not see all sides of the game environment.
Screenshot of Monument Valley
2D with 3D graphics, also
known as 2.5D
Think of 2.5D simply as a 3D environment constrained to a 2D plane (for
example, x, y-axis). In other words, the player can only move up and
down, left or right; they are not able to move on the z-axis. The use of 3D
is merely used mostly for an aesthetic purpose, such as to show the depth
of an environment. An example is shown next, with a screenshot taken
from the game New'n'Tasty. In this screenshot, you can see the characters
are constricted to a platform, with various levels (depths) of environmental
assets visible in the background. When designing for 2.5D games, only a
portion of the environmental assets are seen, as if you walked into a room
and that is it, you can walk no further.
There are different practices and approaches to modeling assets for games
like this, so I encourage you to explore them if 2.5D is something that
piques your interest. Some other great examples of 2.5D games are Little
Big Planet, Trine, Bastion, and Raymond Origins.
Screenshot of New'n'Tasty
Full 2D
Games that are restricted to the x and y-axis (and don't show any depth)
are what we call 2D games, such as Pac Man (featured next). They can be
top-down or side-scrolling (up/down/left/right). Unlike 3D games, players
only see one side of the game object at any given moment. Sometimes this
can be quite linear in the sense that there is no perception of depth (with
the exclusion of parallax).
Often though, sprites can be brought to life, as can the environment, with
beautiful (frame-by-frame) animations and effects. Other popular
examples of 2D games are Mario, Donkey Kong, Space Invaders,
Monaco: What's Yours Is Mine, Mark of the Ninja, and Castlevania:
Symphony of the Night.
Screenshot of Pac Man
2D with a perspective camera
Like 2.5D games, 2D games can also utilize "depth" by using a
perspective camera or utilizing a concept known as parallax, while not
having any 3D assets within the environment. To understand the concept
of the parallax effect at a more scientific level, let us look at the following
diagram:
Depending on where you are, certain items are in view instead of others. If
a player is at viewpoint 1, then they will see the black circle and
blue hexagon; if they are at viewpoint 2 then they will see the black circle
and a red hexagon. Moreover, you can also think of it like this as well,
when you move within a game space, the "background" that is closest to
you moves faster than the "background" that is further away. It is the same
when you're driving in an area, such as the country, and you can see the
mountains moving slower than the fence. There are many games that use
2D with a perspective camera/parallax effect, such as Braid (featured
next).
To begin, you can grab your own free copy of Unity by heading to the
following URL: https://unity3d.com/get-unity/download.
1. Tutorials https://unity3d.com/learn/tutorials
2. Documentation https://docs.unity3d.com/2017.3/Documenta
tion/Manual/
3. Community https://unity3d.com/community
4. Social (for example, Twitter, Facebook groups, and
so on)
Not only will you be able to explore a lot of the added 2D
(and 3D) options that Unity offers, which aren’t covered
here, you can also learn about them through their own
tutorials. Lastly, I highly encourage you to become part of
the Unity Community and engage with their social media. In
this way, you will be able to connect with other Unity game
developers while at the same time being up to date with the
latest news and be immersed in a great platform for you to
share your own projects and questions with other game
developers.
Once you have installed Unity, you will be required to log in. If you
haven't created an account, don't fret, it's a straightforward process. Simply
click on the create one link to do it. The process is simple, free, and will
take little time to go through. Once you're done, come back and enter your
login details, and click Sign In.
Once you've done this, there will be a short survey about you (basic
demographic information) and your intended use of Unity. This is the final
step before we will use Unity. When you have submitted the survey
response, you will see a screen with three different options: Projects,
Learn, and Activity, like this:
Projects simply show all your current projects that are either found on
your computer (On Disk) or online (In the Cloud). However, what I want
to draw your attention to is the Learn tab, which is shown here:
There are several types of tutorials that you can explore at your own pace,
like the ones in this book, which will teach you some interesting things
about Unity. You will need to download each tutorial to go through it and
depending on your connection, it may take a little to no time. Here is an
example of the Survival shooter tutorial, along with the instructions within
the browser (left):
(left) The Survival Shooter tutorial that Unity offers. (right) The tutorial as viewed within Unity
itself.
An overview of built-in features
in Unity
Unity has some great built-in features. Let's begin by exploring some of
the basics. Unity has many fantastic features, and we don't have enough
time to cover them all. However, as we venture further into this book we
will start to cover the fundamental and important ones. As you progress
through the projects, we will begin to cover more advanced features.
Creating a new 2D project in
Unity
To begin a new project within Unity, we first need set up the project files.
When you open Unity, be sure to click on the New icon in the top
navigation menu, on the right. Once you have done this, you will see a
screen like this:
Give your project a unique name, something that isn't too long, but
also descriptive enough.
Next, in this window, we need to specify a location for your
project that has enough space available and isn't going to be
modified by other people (other than team members). This is more
the case when it comes to shared workspaces and networks.
Once you have done that, click on the radio button for 2D as
shown in the preceding screenshot.
Once you have done this, unity will open the project space, which we will
discuss in more detail in the next section.
A brief overview of the Unity
User Interface (UI)
Unity will begin setting up the project and once it's completed, you will
see a screen like this:
This is an example of the Unity UI once you open Unity. As you can see there are no project files at
this stage.
This might be a bit overwhelming if you're new to Unity, but don't worry,
you will be up to speed in no time. We'll go through each part of it in more
detail later in this chapter and throughout the book, starting in the top left
corner with Hierarchy, Scene, Game, Inspector, Console, and finishing in
the bottom left corner, with the Project panel.
The main components of the UI
in Unity
These are the main "default" components that you will see. By default, this
means they are here once you launch Unity for the first time or when you
reset the workspace; we'll discuss how to do this later.
Hierarchy Window
The Hierarchy Window contains all of the GameObject within the current
Scene. A GameObject can be anything from a prop or character to the
scenery. In essence, GameObjects are like a container that we can place
things into what we refer to Component. In Unity, Component can be for
lighting, animation, character control, rendering, physics, colliders, and so
on. It is these that give GameObject functionality. An example of the
default Hierarchy window is this:
Therefore, the main GameObject becomes a Parent and all the other
objects become Children. You can easily parent and unparent
GameObject by simply selecting the main object and dragging it to where
you want it to sit within the hierarchical structure. You can see this process
illustrated here:
Lastly, it is possible to have more than one Scene opened in the Hierarchy
window, but we won't need to do this in the projects in the book. If you
would like to know more about it, head on over to the official Unity
documentation by going to this link or scanning the QR code: https://docs.u
nity3d.com/Manual/MultiSceneEditing.html
Next, we will look at the "big picture" or the Scene View within Unity.
Scene View
The Scene View is where all the magic happens. In here is where you will
create your game. You can see this part of the Unity UI, in the following
screenshot. This is an interactive space where you can select, rearrange,
and position all the GameObject within your project. We will cover
Scene View navigation and how to move GameObject around in the next
chapters.
Game View
The Game View is very similar to the Scene View, giving a window into
your game. In particular, the Game View shows which is the final result,
what the player will see in your game. Also, the Game View can simulate
different screen ratios. This allows you to control how your game will look
on the different screens on which your game will run. Although this is a
good preview, before a final release, you should still test on as many as
real screens possible to ensure the best quality of your game. This is how
it looks in Unity:
Inspector window
The Inspector window is where all the information related to the
selected GameObject is presented. It will become one of the most used
windows within the Unity UI. As we have already discussed, Scenes
contain GameObject and these contain Component, and it is in the
Inspector window (like in the following screenshot) that all the
information of this Component is visible. This information relates to the
properties and settings of not just GameObject, but to pretty much
everything else within Unity, such as assets, materials, audio, and so on.
We will go into more details about properties that are listed within the
Inspector window throughout the book, but for now, this is just an
overview of what it does.
You will notice these errors as you play a Scene or try to run scripts. Each
error will present itself with a name and a brief description of what the
error is, and where it can be found within the script (for example, line
number within a code file). Here is an example of a simple log:
Project Window
The Project window will display every file within your project that you
have either imported or created within Unity, such as models, scripts,
plugins, and so on. Everything within your Unity project is (and should
be) contained within a folder structure, with the Assets folder being the root
—or parent folder, like in the following screenshot:
When you select a folder within the Project window the contents will be
shown in the panel to the right, like in this screenshot:
In this image, you can see the various Sprites that have been cut from the Sprite Sheet for Angel
Cakes.
Customizing the workspace
It is quite easy to customize the layout of the workspace within Unity.
There are many different panels which serve different purposes: the
Window menu item in the top bar can be navigated to open additional
panels or panels that have been closed earlier. To change the layout, move
the mouse cursor over the panel's name you want to move and then press
and hold the left mouse button. Holding down, drag the tab where you
would like it to be. It can be a floating (for example, separate window, see
the following screenshot) or anchored to another part of the Unity UI.
You can see the difference between floating (image on the left) and
anchored next to the Hierarchy tab (image on the right) of the Project
panel:
If you have rearranged the workspace and don't like it, you can reset to
Unity's default layout. To do this, go to Window | Layouts | Revert Factory
Settings..., as here:
You will be prompted with a confirmation box, as shown here, click
Continue and the layout will be reset.
From the same menu, It is also possible to save and load custom layouts.
By doing this, you can switch between your favorites layouts when you
need to work on different aspects of your game.
Hotkeys to keep in mind
We all like to take shortcuts from time to time, and working with Unity,
hotkeys can help make performing simple tasks a whole lot more efficient.
Here is a list of useful hotkeys for actions that you'll perform on a regular
basis throughout this book. Feel free to make a copy of this page or print it
to reference later (the list has been taken from the official documentation
of Unity at the following link:
https://docs.unity3d.com/Manual/UnityHotkeys.html). Here are the most used ones:
Tools
Keystroke Command
Q Pan
W Move
E Rotate
R Scale
T Rect Tool
V Vertex Snap
Ctrl/command +
New empty game object
Shift + N
Ctrl/command +
Move to view
Alt + F
Ctrl/command +
Align with view
Shift + F
Window
Ctrl/command + 1 Scene
Ctrl/command + 2 Game
Ctrl/command + 3 Inspector
Ctrl/command + 4 Hierarchy
Ctrl/command + 5 Project
Ctrl/command + 6 Animation
Ctrl/command + 7 Profiler
These objects are a cube, sphere, plane, capsule, cylinder, and quad, and
they are displayed here:
In the image above, you can see an example of each type of primitive/placeholder objects within
Unity, along with their pivot point
(arrows located in the center point to each of the 3 axis - x, y, and z).
You can add them by navigating through the top bar GameObject / 3D
Object. Since these are 3D objects, we won't be discussing them in this
book, but you can read more about these objects by visiting the link in the
official documentation
at https://docs.unity3d.com/Manual/PrimitiveObjects.html or by scanning this QR
code:
Images
In general, the most common image files types are BMP, TIF, TGA, PNG, JPG, and
PSD (photoshop file). Each file format serves a different purpose, with
different settings, and varying file sizes. For example, PNG file formats
allow you to have an alpha channel, which allows part of an image to be
transparent, where a JPG does not. These are important things to keep in
mind when deciding on what image format that you will need to export
from your graphics program. We will cover how to import images with
alpha channels, as well as sprite sheets and tilemaps later in the book.
The folders Assets, Editor, Editor Default Resources, Resources, Gizmos, Plugins,
Standard Assets, StreamingAssets, and Hidden Assets, which are described in more
detail later, are not automatically created within Unity. Therefore, each
time that you create a new project, you will need to create them manually.
Keep in mind the names will trigger Unity to treat them accordingly, so be
sure to not use them if they're not going to do what you intend them to.
Here is a screenshot of what the Unity Project window would look like if
you have created all the special folders.
some functions need to have the Assets folder, which is part of a file's
pathname. For example, certain functions in the AssetDatabase class require
that the Assets folder is included in the pathname. As a result, it is better to
include this from the beginning of your path to Unity and your project
structure.
Editor
Scripts placed in a folder called Editor are treated as Editor scripts rather
than runtime scripts. In their essence, Editor scripts add or improve
functionality within Unity, which is particularly relevant during
development. In their essence Editor scripts extend Unity to suit your
needs by implementing additional functionality (for example, new menus).
On the other hand, Runtime scripts execute during gameplay and are the
essence of your gameplay. We will focus more on runtime scripts during
the rest of this book (for curiosity, you can keep reading this section, if
you don't understand at first, that is fine; in fact, Editor scripts are an
advanced topic in Unity development).
Lastly, like the location, the important thing to know is that Unity does not
allow components inside the Editor folder to be assigned to a GameObject
even if they derive from MonoBehaviour. What this means is that all
runtime scripts that will be used in your game (for example, movement
scripts), should be kept outside of the Editor folder.
It is important to keep in mind that where you place your Editor folder
makes a difference with respect to other scripts that you have in your
project. There is an order that Unity compiles and runs scripts, it is as
follows:
Phase 4: All remaining scripts (those that are inside a folder called
Editor)
Since we won't be needing to create any Editor scripts, this will not be an
issue, but if you want to know more about the script compilation order,
then check out this link:
https://docs.unity3d.com/Manual/ScriptCompileOrderFolders.html
Lastly, if this has peaked your curiosity, then there are some (intermediate)
tutorials in the official documentation about Editor Scripting (among
others), which can be found here:
https://unity3d.com/learn/tutorials/s/scripting
Editor default resources
Editor Scripts can make use of Asset files loaded on-demand. This is done
by using the EditorGUIUtility.Load function. This function looks directly at
the Asset files in a folder called Editor Default Resources.
Place the needed Asset files in the Editor Default Resources folder (or an
appropriate subfolder within it). Remember, if your Asset files are in
subfolders, always include the subfolder path in the path passed to the
EditorGUIUtility.Load function.
You can only have one Editor Default Resources folder within a
Unity Project. This folder must be placed in the root of the
Project directly in the Assets folder.
Plugins
You can add plugins to your project so that you can extend many features
in Unity as well as improve your own efficiency and workflow processes.
Essentially, Plugins access third-party code libraries, system calls, and
other Unity built-in functionality.
Like many other types of folders, you can only have one
Plugins folder and it must be placed in the Assets folder. You
Gizmo menu
Like many of the other special folders, you can only have one
Gizmos folder and it must be directly within the Assets folder.
Place the needed Asset files in this Gizmos folder or a subfolder within it. If
you are placing an asset within a subfolder, remember to include the path
in the Gizmos.DrawIcon function.
when it comes to script compilation, these folders are also affected by that
order.
>
It is important to remember that when you update your assets it will
replace files with the newer version. However, it will not remove obsolete
files from the earlier version of the Standard Assets. For projects where
you are not using all the files, you may want to consider backing up and
then removing the old Standard Assets from the project before installing
the updated version. This will also help to keep things clean and organized
once your project gets larger and you're working with a large number of
files. One thing to keep in mind, which we will discuss in a later chapter is
that when we will be packaging our games, Unity has a nifty feature that
only includes assets that are being used within the project. That way when
your project is built it will not have any assets that aren't being used
reducing the file size, among other things.
StreamingAssets folder
The first thing that I want you to notice is that the name of this folder is
one word. This is because we have used camel casing to name the folder.
Camel casing is writing words or phrases (without spaces) with each word
begging with a capital letter. Often, the beginning of a camel casing name
begins with a lower case letter. For example, camelCasingIsWrittenLikeThis. Of
course, you can write it like so, CamelCasingIsWrittenLikeThis. Just be sure to
remain consistent. Lastly, the StreamingAssets folder is the only special
folder that uses camel casing, so no need to worry about the others.
What this special folder allows you is to stream an asset, such as a video,
on the device. Keeping the example of the video, it will be copied as it is
in the package game (and not like other assets, such as MovieTextures). As
a result, you will be able to play (using streaming) that video on your
device, for instance on an iOS device.
What this folder allows you to do is to load assets on demand, so they are
not in memory (RAM) until you explicitly load them with the Resources.Load
function.
Hidden Assets
When you begin to import files into Unity, it is important to know that
some files and folders are completely ignored by Unity. The reason for this
is to avoid importing special and/or temporary files that are created by the
operating system (for example, Windows, OSX) and other applications.
The types of files and folders that are ignored are those:
Named .cvs
You can download the asset package for Angel Cakes here: http://player26.
com/product/angelcakes/.
Project 2 - intergalactic
shooter: Quark
Our second project will be a simple intergalactic shooter that will have us
controlling a small spaceship shooting at nearby planets and enemies to
score as high as possible within the time limit. It is quite arcade-like and
very basic, but it will draw upon the skills that you learned from Angel
Cakes while also extending on them to create more complex interaction.
You can download the asset package for Quark here: http://player26.com/prod
uct/quark/.
Project 3 - RETROformer
Our third and final game will be a platformer game titled RETROformer.
In this final game, the player can collect coins, shoot enemies, and explore
the environment. Essentially, this project will combine everything that you
have learned previously with Angel Cakes and Quark, and build upon it.
By the end of the project, you will have developed a strong understanding
of what is involved in making games in different genres with different
game elements and mechanics.
You can download the asset package for RETROformer here: http://player2
6.com/product/retroformer/
Summary
Throughout this chapter, we have explored what games are made up of
and how they are developed and learned about the processes involved in
making a game. From brainstorming to publishing, this chapter has
provided you with a brief overview of the process of developing a game
either by yourself or within a team. In addition, this chapter has shown
you how to download, install, and get Unity ready for a 2D game project,
while also covering some general features.
In Chapter 2, Working With Assets, we will begin using Unity to develop our
first project, Angel Cakes. You will learn how to import various delectable
game assets and get them ready to use and apply scripts to. So what are
you waiting for, turn the page!
Working with Assets
Welcome to Chapter 2, Working with Assets. In a way, you can think of this
as leveling up as you begin to progress through the book. Give yourself a
pat on the back, you're persisting through the early stages of game
development. Good for you! Now, this chapter gets a little bit hands-on; it
will prepare you for what is to come in Chapter 3, Let's Make Some Prefabs.
We will get you familiar with all the different settings and tools in Unity
when it comes to handling Sprites. In addition, we will also begin our
journey into 2D game development. Our first game will be a 2D top-down
arcade-style game, like the famous Pac-Man; you can see an example of
what our (finished) game will be like in the following figure:
This is what Angel Cakes will look like once we have finished implementing all our assets
Throughout this chapter and by making this game, you will learn a bit
about what is involved in creating a game that is focused on collecting
objects. We will do this by exploring some famous examples in other
games both old and new. You will then begin to import assets into Unity
and get all the graphics set up and ready to go. By the end of this chapter,
you will have your images sliced up and ready to be implemented.
Sprite Editor
Sprite Packer
Sprite Creator
Best practices for getting the project ready for applying scripts and
sound effects
The core tools for creating, modifying, and editing Sprites and
Textures
How to use the core tools and utilize them for the assets within the
project
So, let's get going!
An overview of collecting
games
Before we start making our first game, we can begin to learn and
understand what games that focus on collecting are about and how they
use the act of collecting as part of their core mechanics. There are many
examples of these types of games in both 2D and 3D, and to give you
some examples and inspiration for your own games, here is a brief
overview.
Key features of collecting in
games and things to keep in
mind
Like we have seen with the preceding examples, it goes without saying
that the key feature of a collecting game is, well... collecting something.
This could be coins or items; such things may also be used to represent
other elements, such as collecting hearts for health. The core mechanic is
that the player must collect something to meet a certain condition, such as
gaining lives, leveling up, or progressing. There are various other
components and parameters that you can implement into a collecting
game, such as enemies, the value of items, the frequency of rare items, and
so forth. In this way, it is not only about what the player is collecting, but
you as a game designer must also define how, when, and how often the
player is able to collect the items.
There isn't much that can go wrong when it comes to creating a collecting
game per se. Ideally, you want the game to be balanced and challenging.
This becomes an issue when it is time to assign points to various of
parameters. The right number that you should assign to these will be
refined through playtesting, so don't be too worried if they aren't right in
the beginning. To get a better understanding of how games focused on
collecting are balanced, I encourage you to play any of the preceding
games if you can access them or watch some playthroughs on YouTube.
What will you learn in the
remainder of this chapter?
By now, you should have a better understanding of games that focus on
collecting items. Now we're going to create our own. Of course, you're
welcome to use your own graphics and images, but for this tutorial, you
are welcome to download the free asset pack online. We'll cover this later
in this chapter.
Textures and Sprites
Before you start anything within Unity, it is useful to know that Textures
and Sprites within Unity are two separate things, although they are used in
similar contexts. To begin, a Sprite is an image that can be used as a 2D
object. It has only two coordinates: x-axis and y-axis. Therefore, all the
graphical components of 2D game development are called Sprites. Sprites
can be repositioned, scaled, and rotated like any other game object in
Unity. You can move, destroy, or create it during the game. Sprites, by
default, are rendered directly against the camera; however, you can easily
change this if you are using the Sprite Renderer in a 3D scene. They
work with the Sprite Renderer, unlike a 3D object, which works with
the Mesh Renderer. Aside from Sprites, there are other graphical
components called Textures. These are also images, but they are used to
change the appearance of an object in both 2D (for example, Sprites and
background) and 3D (for example, an object or character's appearance).
But Textures are not objects. This means that you cannot get them to move
during gameplay. Saying that, you can create images with Textures that
animate, with Sprite Sheets/Atlases. What this means is that each frame of
an animation is placed on a Sprite Sheet, which is a Texture, that will
eventually be cut up so that each frame of the animation is played
sequentially. Throughout this book, we will use the terms Sprite Sheets
and Atlases. While they are pretty much the same thing, the subtle
difference between the two is that a Sprite Sheet generally has Sprite
(frame-by-frame) animations, whereas an Atlas will contain images such
as tileable Textures for the walls and other environmental components (for
example, objects). Their purpose is to maximize the space by combining
multiple images into one Texture, whether for characters (and their
animations) or environmental Textures.
Color: Color allows you to change the color value and the value
of the Alpha channel (transparency) of a Sprite
Flip: Flip is what defines the axis that the Sprite needs to be
flipped on
Another thing that is also to keep in mind here is the Pivot of the Sprite.
Think of this as the Sprite's center. For example, if you rotate a Sprite, it
will rotate wherever its Pivot is .0.
A few more elements that you will also find useful while you are slicing
up your Sprites are the options located at the top of the Sprite Editor
window. We will discuss them now.
You can only see the Sprite Editor button if the TextureType
on the image you have selected is set to Sprite (2D and UI).
In addition, you cannot edit a Sprite which is in the Scene
view.
The Grid is better for when you have Sprites that are evenly
distributed, such as frame-by-frame animations. In these cases, it
is not recommended to use Automatic because the size differences
between each Sprite may cause unintended effects in terms of how
they appear within the game, such as the Pivot being in the wrong
location, resulting in an inaccurate animation. An example of the
Grid menu is shown in the following screenshot. Pixel Size sets
the size of the Grid in the unit of Pixels. This number will be
determined based on the size of your Sprite Sheet and distribution
of Sprites:
Sprite Packer
Using the Sprite Packer, you can combine multiple elements such as large
sets of Sprites into a single Texture known as an Atlas. However, before
using it, we must first make sure that it is enabled within Unity. To do this,
go to Edit | Project Settings | Editor.
Once you have done this, look at the Inspector; you can change the Sprite
Packer from disabled to Always Enabled or vice versa. You can see an
example of this in the following screenshot. By selecting Always Enabled.
The Sprite Packer will always be enabled whenever you start a new
project. That way, you will not need to worry about enabling it again:
One of the benefits of using this is that it can boost the performance of
your game by reducing the number of Draw Calls each frame. This is
because a significant portion of a Sprite Texture will often be taken up by
the empty space between the graphic elements. As a result, it wastes video
memory at runtime because it needs to compute this empty space even if
there is nothing there. By keeping this in mind, when you are creating
your own Sprites, try to pack graphics from several Sprite Textures
together and as close as possible to one another within an Atlas. Lastly,
keep in mind that depending on the sizes of your Sprites, an Atlas should
not be larger than 2048 x 2048 or 211 (or at least, this guarantees
compatibility with many devices). Therefore, if you need to resize some
elements within your scene, revisit the power of 2 table in Chapter 1, Press
Start, to see what values you can use to better utilize the dimensions of the
Atlas. This is handy only if you want all the Sprites to be in one Atlas.
Otherwise, if your Sprites are bigger than the Atlas's maximum
dimensions, or you're trying to pack more Sprites than a single Atlas can
handle, it will create multiple pages. We will cover this in more detail later
in the book.
Unity handles the generation and use of Sprite Atlas Textures behind the
scenes so that the user does not need to do any manual assignment. The
Atlas can optionally be packed on entering Play mode or during a build,
and the graphics for a sprite object will be obtained from the Atlas once it
is generated. Users are required to specify a Packing Tag in the Texture
Importer to enable packing for Sprites of that Texture.
To use the Sprite packer, simply go to the top navigation menu and select
Window | Sprite Packer.
Once you have done this, it will open the Sprite Packer.
Sprite Creator is your friend
when you have no assets
While we have Sprites, in this case, you might not always have them. If
you don't have Sprites, you can always add placeholders or images in the
place of where they are likely to be. This is a useful thing to use when
you're prototyping an idea and you need to get functionality working
before your images are ready to go. Using the Sprite Creator is quite
simple.
3. Now, where it says Sprite, click on the small circle located next to
the Sprite name, in this case, Hexagon. This is highlighted in
the following screenshot:
To begin, let's get the Angel Cakes asset pack, which is featured in the
following screenshot:
To download the assets, simply visit www.player26.com/product/Angelcakes and
download the .zip file. Once you have finished downloading it, simply
unzip the file with a program such as WinRAR.
Folder setup
Like we discussed in Chapter 1, Press Start, you need to make sure that you
have some foundational folders created to use with your project. To briefly
recap, have a look at the following screenshot. Remember that the Assets
folder is always the root or parent folder for the project files:
Importing assets into the
engine
With your folders set up, we now begin to import some images for our
project: the background, the player, an enemy, player collision (wall,
objects), and collectables (Angel Cakes, health, and bonuses). Importing
the assets into Unity is easy. As we discussed in Chapter 1, Press Start,
Unity accepts most image formats, including the ones within this package:
1. First, click on the folder that you want the Sprites to be imported
into, inside the Project window; for this project, we will use the
folder titled Sprites
2. Next, in the top menu, click Assets | Import New Assets and
navigate to the folder that they are located in
3. Once you have found them, select them and then click Import
4. Once they are imported, they will appear in the folder, like in the
following screenshot:
Configuring assets for the
game
The assets used in this game do not need much configuring, in comparison
to the ones that we will use later.
Once you have imported the two Sprites into Unity, do the following:
In this information box, you can rename the Sprite to whatever you would
like. Each Sprite has been given a number so that you can understand the
corresponding name conventions that are described following screenshot:
For this project, we will call each Sprite set the following:
There are many things that we will now be able to do with these images,
such as animations, which we will cover later in this book, such as Chapter
9: Look it Moves, as well as place them within the Scene.
The next thing that we need to do now is slice up the environment Atlas.
Locate the Sprite file within the Project window and open it up in the
Sprite Editor. Remember that you need to change the Sprite type to
Multiple in the Inspector, otherwise you will not be able to edit the Sprite.
Once you have it in the Sprite Editor, it should look something like the
following:
This time, instead of selecting the Slice type Grid By Cell Size, we will do
it manually. This is because if we choose to do it via the type Automatic,
we will find that there are other slices, like those on the clouds on the right
of the following screenshot. This can be tedious when there are lots of
little parts of a single Sprite, such as the Clouds:
So, for now, manually drag and click around each of the Sprites, making
sure that you get as close to the edges as possible. You may find that you
will need to zoom in on some parts (by using the mouse scroll wheel), like
the Angel Cakes. Also, the options in the top-right corner might help you
by filtering the image (for example, black and white). As you begin
refining the bounding box, you will feel the outline pull or snap toward the
edges of the Sprite; this helps you to get as close as possible to the edges,
therefore creating more efficient Sprites. Don't forget to name the Sprites
either!
For this project, we will call each Sprite set the following:
1. ACSpriteEnviroBlock
2. ACSpriteMenuBlock
3. ACSpriteBonus
4. ACSpriteHealth
5. ACSpriteCake
6. ACSpriteBackground
7. ACSpriteCloud1...2...3...and so on
To give you a better idea where each Sprite is located, have a look at the
following screenshot. The Sprites are numbered so that you can easily
locate them.
Once you have done this, click on Apply in the top-right corner of the
Sprite Editor. As a result, you should be able to see all the Sprites in the
Project window by clicking on the triangle. It should look like the
following screenshot:
9-slicing Sprites
A nice little feature of Unity that allows you to scale elements such as
Sprites without distortion is 9-slicing. Essentially, what 9-slicing does is
allow you to reuse an image at various sizes without needing to prepare
multiple Assets. As the name suggests, it involves splitting the image into
nine segments. An example of this splitting is shown in the following
screenshot:
The following four points describe what will happen if you change the
dimensions of the preceding image:
If you change the four corners (A, C, G, and I), they will not
change in size
You can see these four points illustrated in the following screenshot:
By using 9-slicing, you can re-size the Sprite in different ways and keep
the proportions. This is particularly useful for creating the walls within our
environment that will create obstacles for our little Angel and enemies to
navigate around.
Now we need to define the borders of the Sprite. To do this, perform the
following steps:
3. Now, click on the Sprite that you want to apply the 9-slicing to. In
our case, this will be the ACSpriteEnviroBlock, like in the
following screenshot:
Looking at the Sprite information box in the bottom-right corner, we need
to adjust the values for the Borders of the Sprite. For this Sprite, we will
use the value of 20 for L, R, T, and B (left, right, top, and bottom,
respectively):
In some cases, you might need to tweak the position of the borders; you
can do this by clicking and dragging the green dots located at the
intersections of each border (top, bottom, and sides). You can see this in
the following screenshot:
To test your 9-sliced Sprite, drag it from the Project window into the
Scene, like in the following screenshot:
Next, in the Inspector, change the Draw Mode from Simple to Sliced, like
in the following screenshot:
also cover other Draw Modes such as Tiled in later chapters with our third
project. For now, this is all that you need to know to get you prepared for C
hapter 3, Let's Make Some Prefabs.
Best practices
At some stage when you're creating 2D games, you will probably need to
create your own 2D Sprite, and eventually your own Sprite Sheets. For
those new to this, there are a few other things to keep in mind (other than
the power of 2) when doing this. We'll discuss some of these things in the
following section.
Textures
While we have been emphasizing the Power Of Two, it is for a reason. Not
only will they be more efficient, they also won't need rescaling at build
time. As we have already said, in Unity you can use up to 4096x4096
pixels (make sure that you use these units in your graphics program), but
2048x2048 pixels is the highest available on many graphics cards and
platforms. In other words, your game might not run with Textures higher
than 2048x2048. So, if you build an absolutely fabulous 4096x4096 pixels
Texture, it probably won't be such an absolutely fabulous experience for
those on low-end devices. The best way to overcome this issue is to create
Textures in a high resolution (4096x4096 pixels) and then downsize to a
smaller power of 2 depending on your targeted device, as this is likely to
vary. This goes for all repeated elements (for example, windows, doors,
environmental assets) that will be within your scene.
There's also the possibility to mix POT sizes per axis, for
example, 128x1024. This can work but it doesn't have to on
all graphics cards, so POT with the same size for both axes is
the best way to go for maximum co-compatibility.
Maximizing the space that you
have
Another important thing to remember when creating your own Textures is
to maximize the space within the Texture itself when you're combining
multiple images. However, be aware of different materials requiring
different parts of the same Texture. You can end up using or loading that
Texture multiple times. In addition, some Textures may also have an alpha
channel, which makes part of the Texture transparent. Lastly, for parts of a
texture that are tiled, you don't need to add the image tiled more than once.
In general, you only need the part that is tileable; Unity will take care of
the rest. Not only will this help to increase the resolution of a tileable
image, it will also save space. Fortunately, there are a few programs out
there that help to make this whole process easy, such as TexturePacker,
which you can find by either visiting https://www.codeandweb.com/texturepacker/
unity or by scanning the QR code:
Scaling
This is an issue for 2D as much as it is for 3D. Setting a scale within your
graphics program will give you much fewer (if any at all) headaches later
when it comes to bringing things into Unity. Different programs use
different measurements by default, so you will need to make sure that if
you're using graphics programs such as Adobe Illustrator or Photoshop,
your measurements are consistent – 3 mm is very different from 3 cm and
3 px.
Naming
As we have already discussed in Chapter 1, Press Start, naming your Sprites
is as important as naming any other file. Whether you're cutting up Sprite
Sheets or creating placeholder Sprites, consistent names are important.
Next, in Chapter 3, Let's Make Some Prefabs, we will learn how to write
some basic C# scripts and apply them to our Sprites. We will also learn
what Prefabs are in Unity, and how to create and use them within our
project. Lastly, we will also explore how to add basic audio and sound
effects to our game.
Let’s Make Some Prefabs
Previously, in Chapter 1, Press Start, we looked at what games are made of
and how to set up Unity. In Chapter 2, Working with Assets, we started to
import assets and get them ready for Unity. Now, in Chapter 3, Let's Make
Some Prefabs, we're going to do something cool: we're going to create our
first game.
Basics of C# in Unity
What is a PlayerController?
By the end of this chapter you will be able to achieve the following:
The density of certain materials, such as glass and plastic, allows a certain
amount of light to pass through them. This will influence how the
light will behave when it passes through them, such as bending/refracting
(that is, the index of refraction), various materials (for example, liquids,
solids, gases) have the same effect when it comes to allowing sound waves
to pass. Some materials allow the sound to pass easily, while others
dampen it. Therefore, sound studios/booths are made of certain materials
to remove things such as echoes. It has a similar effect to when you
scream underwater that there is a shark. It won't be as loud as if you
scream from your kitchen to tell everyone dinner is ready.
Within the context of 2D and 3D sounds, Unity has a parameter for this
exact thing called Spatial Blend. We will discuss this more in the Audio
Source section.
There are several ways that you can create audio within Unity, from
importing your own/downloaded sounds to recording it live. Like images,
Unity can import most standard audio file formats: AIFF, WAV, MP3, and Ogg,
and tracker modules (for example, short instrument samples): .xm, .mod, .it,
and .s3m.
Importing audio
Importing audio into Unity follows the same processes as importing any
other type of asset. We will cover the basics of what you need to know in
the following sections.
Audio Listener
Have you heard the saying, If a tree falls in a forest and no one is there to
hear it, does it still make a sound? Well, in Unity, if there is nothing to
hear your audio, then the answer is no. This is because Unity has a
component called an Audio Listener, which works like a microphone. To
locate the Audio Listener, click the Main Camera, and then look over at
the Inspector; it should be located near the bottom, like in the following
image:
If for some reason, it isn't there, you can always add it by clicking the
following button titled Add Component, type Audio Listener, and select it
(click it) from the list, like in the following image:
To see more information about all the parameters, you can check out the
official Unity documentation by visiting the link or scanning the QR code:
https://docs.unity3d.com/2017.2/Documentation/Manual/class-AudioSource.html
For this game, I have provided you with some example "moods" that you
can apply to this game. Of course, you're welcome to choose sounds other
than this that are more to your liking! All the sounds that we will use will
be from the Free Sound website: https://freesound.org. You will need to
create an account to download them, but it's free and there are many great
sounds that you can use when creating games. In saying this, if you're
intending to create your games for commercial purposes, please make sure
that you check the Terms and Conditions on Free Sound to make sure that
you're not violating any of them. Each track will have its own attribution
licenses, including those for commercial use, so always check! For this
project, we're going to stick with the "Happy" version. But I encourage
you to experiment!
Happy
Collecting Angel Cakes: Chime sound (https://freesound.org/people
)
/jgreer/sounds/333629/
Not everyone can hear well or at all, so it pays to keep this in mind when
you're developing games that may rely on audio to provide feedback to
players. While subtitles can enable dialogue to be more accessible, sound
FX can be a little trickier. Therefore, when it comes to implementing
audio, think about how you could complement it, even if the same effect
that you're trying to achieve with sound is subtle. For example, if you play
a "bleep" for every item collected, perhaps you could associate it with a
slight glow or flash of color. The choice is up to you, but it's something to
keep in mind. On the other end of the spectrum, those who can hear might
also want to turn the sounds off. We've all played that game (or several)
that really begins to become irritating, so make sure that you also check
this while you're playtesting. You don't want an awesome game to suck
because your audio is intolerable and there is not an option to TURN THE
SOUND OFF! You’ve been warned.
Integrating background
music in our game
Once you choose which music better suits the kind of feel you want to
create for your game, import both the sound and the music inside the
project. If you want, you can create two folders for them, SoundFX and Music,
respectively.
In the end, this is how the component should look (in the image, I chose
the happy theme music, and set a Volume of 0.1):
Creating the Angel and the
PlayerController
In this section, we will focus on how to create our main character, the
Angel. BUT! Before we move on, it's important to note that we will use
the built-in physics engine in Unity. Since this book teaches by example,
we will only cover it briefly and by creating our game. If you want to have
a nice in-depth explanation of the Physics engine in Unity, you can refer to
Chapter 5, Freeze! Creating an Intergalactic Shooter, of Getting Started
perfectly suitable even if you use more recent versions, such as 2017. Here
is the QR code:
Alternatively, you can also check out the official tutorial by visiting https:/
/unity3d.com/learn/tutorials/topics/physics or scanning the following QR
code:
A brief overview of Unity's
physics system
There are two different physics engines in Unity, one for 3D and one for
2D. While both use the same concepts, such as gravity, force, and velocity,
they are implemented in slightly different ways, especially since there isn't
a third dimension in 2D games. There are several components and
properties when it comes to 2D physics in Unity. However, the two main
important components to understand this book will be Colliders and
Rigidbody, since we are going to use them both to implement our
PlayerController.
Rigidbody
A Rigidbody is the main component that allows game objects to engage in
physical behavior (that is, movement). When a Rigidbody is attached to a
GameObject, the object will immediately respond to gravity. Since a
Rigidbody takes over the movement of the GameObject it is attached to, you
If one or more Collider components are also added, the GameObject is moved
by incoming collisions. We discuss Collider components in the next
section.
Colliders
Collider components define the shape of an object for the purpose of
physical collisions. You can think of this as an invisible box that is an
object that permits it from passing through it but without coming into
contact. They don't have to be the same shape as the visual representation
(Mesh, Sprite) of the object; in some cases they can be a simple primitive
type (for example, Box Collider, Sphere Collider (only in 3D), and Capsule
Collider), but the closer to the actual shape, the better. If you're using many
different primitive types of colliders within Unity, you can combine them
to create what is called a Compound Collider.
In 2D, you can also use what is called a Polygon Collider 2D or Edge
Collider 2D. A Polygon Collider 2D is a collider that is shaped by
freeform edges that you can adjust so that it better suits your Sprite. It
must also enclose the edges of the area that it is trying to provide a collider
around. An Edge Collider 2D is defined by a freeform edge made of line
segments, so you can adjust it to fit the shape of a Sprite with great
precision. In general, Edge colliders are best for surfaces (for example,
game platforms) because you are only generating geometry where you
need it.
In our specific case, we can select the Player, and just beneath the name
we can set Player as a tag, which is a default tag, as shown in the
following screenshot:
Creating the Script
Now, we are good to go to start writing some scripts. This is the first script
of this book, and since it's a book with examples, we will jump straight
into one, and learn how to code by deconstructing what we are doing.
Don't be scared if it is not clear to you at the beginning, because it might
be hard if it's the first time you see the code. Don't give up; being able to
code is worth the effort.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
At the beginning, there are three lines that allow us to use libraries. At this
stage, we won't touch them.
We can achieve this by adding two lines of code above the class definition,
as highlighted here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CircleCollider2D))]
public class PlayerController : MonoBehaviour {
//[…]
}
//[…]
public class PlayerController : MonoBehaviour {
//[…]
}
As a result, if we go back in Unity, and see our character, we will see that
now the speed is exposed in the Inspector, as shown here:
//[…]
}
Next, we need to assign the rigidbody of the character within the variable.
So, inside the Start() function we can use the GetComponent() function to
retrieve the RigidBody2D instance that is attached to the same game object
this script is attached to, in the following way:
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
}
Please note that we don't do any check of the validity of Rigidbody (if it
exists), because at the beginning of the script we have enforced
that Rigidbody must exist to use this script. As a result, we can avoid
checking the validity of the variable (null-check), since we know
Rigidbody will always exist.
From the player input to a new
position in the world
Every time the player presses a key on the keyboard, or use the thumbstick
on a controller, we need to retrieve this input. Then, we need to elaborate
it so that we can calculate the next position of our character after a frame.
void FixedUpdate () {
To retrieve the player input, we can use the default key mapping in Unity.
So, finally here is the code, in which we do the same operation once for
the x-axis and again for the y-axis, and store values within variables:
void FixedUpdate () {
//Get the new position of our character
var x = transform.position.x + Input.GetAxis("Horizontal") *
Time.deltaTime * speed;
var y = transform.position.y + Input.GetAxis("Vertical") *
Time.deltaTime * speed;
}
Moving the character to the
new position
The last step for our PlayerController is to move the character to the new
position that we calculated in the last section.
We could do this by changing the transform of the character, but since we
are using the physics engine of Unity, and we don’t want to force the
character to go against a wall ignoring the collisions, we need to find
another way.
Thankfully, the Rigidbody that we created a couple of sections ago, has a
built-in function to move the Rigidbody into a new position and respect the
collisions. Exactly what we need! The function is called MovePosition() and
we can use it as shown here:
void FixedUpdate () {
//Get the new position of our character
var x = transform.position.x + Input.GetAxis("Horizontal") *
Time.deltaTime * speed;
var y = transform.position.y + Input.GetAxis("Vertical") *
Time.deltaTime * speed;
And with this, you can save the PlayerController script, and we have
finished with it.
Testing the PlayerController
Before we move on, it's best practice to test what we have achieved so far.
If we Hit play, we can move the character in the world by using the arrows
key if you are on a keyboard. If you want to test whether the angel is
colliding properly, you can just place a wall, add a BoxCollider2D, and test it
out.
Collectable system
In this section, we start our collectible system. This includes the ability of
the Player to collect the cakes, keep track of how many cakes the player
has collected and in how much time, and trigger the end of the game once
all the cakes have been collected.
We will finish the collectible system in Chapter 4, It's about U and I, when
we will integrate the UI. Now, for the moment, we will start at step one, by
creating it by allowing the player to collect Angel's Cakes.
Setting up the Angel Cakes
Let's create a new Sprite in our scene and name it Angel's Cake. Then, in
the Sprite Renderer assign the Graphics ACSpriteCake.
Add to Angel Cakes a BoxCollider2D, likewise, we did with the character, but
this time we check the checkbox Is Trigger, as shown here:
In this case, we don't need a Rigidbody, since we won't move this object in
the physics world. However, since we would like to play a sound when the
player collects the cake, we need to add an AudioSource component. As
shown here:
And with this, we are good to go to create the script. To change from how
we create a script for the player character, we can go to the Inspector panel
and click Add Component | New Script and type the name of it, as shown
here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
[RequireComponent(typeof(AudioSource))]
public class CollectableCake : MonoBehaviour {
//[…]
}
Triggering cake
We can remove both the Start() and the Update() functions, since we won't
need them, and add a new variable:
The first thing we need to check is whether the other object is actually the
player, we can do this by using the tag we set before. As a result, we can
easily check whether the player has collected the cake:
}
}
Inside the If-statement we need to increase the number of cakes that the
player collects, but we will postpone this task to the Chapter 4, It's about U
and I, when we will integrate the UI. In the meantime, we can leave a
reminder:
The next step is to play the audio. To do so, we need to retrieve the
AudioSource component, which we can do with the same function we used
Then, we need to hide the cake from the player, and we can do this by
disabling the Renderer component. In fact, we cannot destroy the cake yet,
since it needs to finish to play the sound. So, after having disabled the
renderer, we destroy the cake after a delay that is long as the AudioClip in
the collectableSound variable. Finally, we remove the script, so the player
cannot collect the same cake twice (even if it is hidden):
//Then, destroy the cake after a delay (so the sound can finish
to play)
GameObject.Destroy(gameObject, collectableSound.length);
//Destory this script, in case the player hits again the cake
before that is destroyed
Destroy(this);
}
}
Save the script, and come back to Unity, since it's time to test it.
Testing the cake collectable
system so far
First of all, you need to drag and drop the sound FX you decided on when
the player collects the cake, and place it within the script we have just
created for the cake. You should end up with something similar to this:
times) into any number of Scenes within your Unity game project. When
you add a Prefab to a Scene, you create an instance of it. What this means
is that all Prefab instances are linked to the original Prefab and are
essentially a clone of it. This is particularly useful when you want to have
multiple instances of an object, such as a health pickup. Instead of having
to adjust each one individually, you just update the original Prefab (for
example, color, graphics, properties) and it will automatically update all
the other instances of the health pick-up within the game. You may find
that at some stage, you're editing an instance and want these changes to be
reflected in the original Prefab. Unity allows you to do this easily by way
of the Inspector. When you click a Prefab instance within your Scene, you
will see buttons: Select, Revert and Apply. The Select button selects the
original Prefab. This allows you to edit the main Prefab and so change all
its instances. However, you can also save overridden values (excluding
transform settings) from an instance, back to the original Prefab by
clicking the Apply button. If you have modified an instance of the Prefab
but decided that you don't like the changes, you can simply go back to the
default values by clicking Revert before clicking the Apply button.
Creating the Prefab for the
player and the cake
To create the Prefabs for our game, we need first to create a Prefab folder in
our Project. You can do this by following these steps:
As a result, we can easily drag and drop the components we need ready to
go, whenever we need them. We can remove all of the instances of the
cakes and the player from the Scene since we can drag and drop the
Prefabs from the Project panel now. Before we leave the chapter, there is
one more thing to do… Build our map!
Building the Map
Now that we have all the pieces, building the map becomes a piece of
cake. It is an essential part of the 2D game environment because this is
what defines your gamespace - or the area that the player interacts within.
Once you finished working on the walls, I suggest you place them inside
an empty game object to keep your scene tidy.
Also, you will need to create four walls to enclose the map. However, you
need to remove the Sprite Renderer component, so that the map looks
exactly like before, but the player cannot run away from the map.
Before we move on, we need to select them all and attach a BoxCollider2D to
all of them. At the moment of writing, there is a bug, since the BoxCollider2D
won't stretch to fit the dimensions of the wall (since it is a sliced sprite).
Until this bug is fixed, I suggest you use this free script that you find at the
following URL: http://player26.com/?p=1363.
The script is super easy to use: just select all the walls, and attach the
script to them. The script will fix the collider, and it will delete itself.
Simple, isn't it?
The next step is to place all the cakes on the map. If the Audio icon is
bothering you, you can scale all the gizmos icons to zero from the Gizmos
tab in the Scene View. Once you have placed all the cakes around the map,
you should have something like this:
Once again, you can create an empty game object in which to parent all
the cakes, so to keep the scene tidy.
The final touch is to add the player and playtest your game so far. The
character might be too big to fit the corridors we have created, so scale it
as needed.
Summary
In this chapter, we have explored what Prefabs are in Unity, how they are
used, and more importantly, how to create one. We have also learned the
basics of C# and scripts within Unity.
Building the UI
Testing
Other things that the reader could consider adding to the game
later
QR code:
Overview of the UI
Imagine a watch without a watch face to indicate the time. An interface
provides important information to us, such as time, so that we can make
informed decisions (for example, whether we have enough time to get ice
cream before the movie starts). When it comes to games, the User
Interface (UI) plays a vital role in how information is conveyed to a
player during gameplay. UIs provide important and sometimes vital
information (for example, statistics, health, ammunition count) to a player
during gameplay. The implementation of a UI is one of the main ways to
exchange information with the player about moment-to-moment
interactions and their consequences (for example, taking damage).
However, UIs are not just about the exchange of information, they are also
about how information is provided to a player and when. This can range
from the subtle glow of a health bar as it depletes, to dirt covering the
screen as you drive a high-speed vehicle through the desert. There are four
main ways that UIs are provided to players within a game, which we will
discuss shortly.
The purpose of this chapter is to prime you with the fundamentals of UIs
so that you not only know how to implement them within Unity but also
how they relate to a player's experience. Toward the end of the chapter, we
will see how Unity handles UIs, and we will implement a UI for our first
game. In fact, we will insert a scoring system as well as a Game Over
Screen. There will be some additional considerations that you can
experiment with in terms of adding additional UI elements that you can try
implementing on your own.
While it is not essential for what you will learn in this book, if you want to
further develop your skills when it comes to UIs within Unity, I
recommend the following book: Unity UI Cookbook, Packt Publishing. It
has a perfect set of recipes that are ready-to-use. There you will find all
the concepts mentioned here and much more, such as different tips and
tricks.
Just like our book example, players or users of applications are used to
certain conventions and formats. For example, a house icon usually
indicates home or the main screen, an email icon usually indicates contact,
and an arrow pointing to the right usually indicates that it will continue to
the next item in the list or the next question, and so on. Therefore, to
improve ease of use and navigation, it is ideal to stick to these or to at least
to keep these in mind during the design process. In addition to this, how
the user navigates through the application is important. If there is only one
way to get from the home screen to an option, and it's via a lot of screens,
the whole experience is going to be tiresome. Therefore, make sure that
you create navigation maps early on to determine the route for each part of
the experience. If a user has to navigate through six screens before they
can reach a certain page, then they won't be doing it for very long!
In saying all of this, don't let the design overtake the practicality of the
user's experience. For example, you may have a beautiful UI but if it
makes it really hard to play the game or it causes too much confusion, then
it is pretty much useless. Particularly during fast-paced gameplay, you
don't want the player to have to sift through 20 different on-screen
elements to find what they are looking for. You want the level mastery to
be focused on the gameplay rather than understanding the UI. Another
way to limit the number of UI elements presented to the player (at any one
time) is to have sliding windows or pop-up windows that have other UI
elements present. For example, if your player has the option to unlock
many different types of ability but can only use one or two of them at any
single moment during gameplay, there is no point in displaying them all.
Therefore, having a UI element for them to click that then displays all of
the other abilities, which they can swap for the existing ones, is one way to
minimize the UI design. Of course, you don't want to have multiple pop-
up windows, otherwise, it becomes a quest in itself to change in-game
settings.
Programming the user
interface
As we have seen in the previous section, designing the UI can be tough
and requires experience to get into, especially if you take into
consideration all the elements you should, such as the psychology of your
audience. However, this is just halfway through. In fact, designing is one
thing; making it work is another. Usually, in large teams, there are artists
who design the UI and programmers who implement it, based on the
artists' graphics.
using sound as the only way to indicate a change in the UI to the player.
Lastly, you can implement subtle feedback from the starting point of an
interaction, such as the main menu. In this way, you have the ability to
communicate things such as progress, like that in the (previous)image of
Spec Ops: The Line. As you progress through the game and complete each
chapter, the main menu screen changes from something that is relatively
neutral (top-left corner) to a devastating war-torn country (bottom-right
corner), which reflects what has occurred in the game. While it is subtle, it
in some ways sets the mood for the game and provides a sense of
progression to the player before they continue their journey.
Be bold, not ambiguous
Be loud but don't shout in the user's face, you want the impact of your UI
to be exciting and meaningful and not aesthetic noise.
Once we have the file, it's likely to have a strange name, so my suggestion
is to rename it simply Century Gothic Regular (of course, keeping the same
file extension). Then, inside Unity, we can create a specific folder, named
Fonts, and drag and drop our new font. From now on, we can use the font
Next, resize the panel so that it occupies the upper part of the screen. In
the end, you should end up with something similar to the following:
If you want to be sure how the panel will appear in-game, you can switch
to the game mode view. This is how the panel should appear according to
our design:
We need to create one more thing: a label where we'll display the score to
the player. So let's create a Text, and rename it ScoreLabel. Now, in the
Inspector, we need to tweak some values of the Text Component. In
particular, we want to change the font, to the one we imported before, and
the font size to better fit the panel. It's important to adjust the dimensions
of the box containing the text, and the anchors (in my case, I used that
they stretch), and the alignment. Also, as a text we can insert the string
Score: 0, so we can have a better idea of how it will look once the score
starts to increase; don't worry much about it though because this text will
be replaced by our script. Of course, feel free to change any other settings
you'd like to fit your personal style and mood. These are the settings I
used:
Programming the scoring
system
Finally, we get to get our hands dirty with a bit of code. Don't worry, it
will be simple.
As usual, let's start by creating a new C# script, name it UI_Score, and open
it up in your editor of choice.
First of all, if we want to use the UI system within a script, we need to
import it in the context of our script. We can achieve that with a using
statement at the beginning of our script, as highlighted here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UI_Score : MonoBehaviour {
//[…]
}
Once we have done this, we are ready to use all the Unity UI components
in our script. In fact, the first variable we need is a reference to the Text
Component we created in the previous section:
Then, we need a variable to store the actual value of the score, and another
one that stores the number of points required to pass to the next level.
Please note that in this case, we used the [SerializeField] attribute before
the private variable to still make it editable from the Inspector:
Then, we need to get the reference to the Text Component in the Start()
functions, therefore it becomes:
Then, we need to substitute the TODO line of code with the following, so
we actually call the UI script we wrote before:
if (other.tag == "Player") {
//If so, increase the number of cakes the player has collected
FindObjectOfType<UI_Score>().IncreaseScore(cakeValue);
//[…]
}
And that's it. Now, let's create a Game Over Screen before we tweak the
last settings in the Inspector and finally get our first game finished!
Creating a Game Over Screen
To create a Game Over Screen, we start by creating another panel. This
time, we can rename it Game Over Screen.
Next, create a new Image and call it Panel. In fact, we can use it as
background for our Game Over menu by using the
ACSriteMenuBlock graphics. In a similar fashion to the text for the score, we
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class UI_GameOver : MonoBehaviour {
//[…]
}
Then, just create the function to reload the current scene (the buildIndex is
the identifier that Unity assigns to this specific scene in the built version,
thus retrieving the line for the current scene, we can reload the level):
Save the script, because we are close to the end. Just a few settings away
to complete our game!
Last tweaks
To make our game fully functioning, we need to tweak a couple of things.
First of all, we need to link the previous function to reload the scene when
the player presses the Play Again? button. To do so, add the UI_GameOver
script to the button, then in the On Click() tab of the Button component,
press the + button to add an event. Drag and drop the script just added,
and select the PlayAgain() function. Here is how the final setup should look:
Next, we don't want the Game Over Screen to always be visible when we
start the game, so we need to disable it. To achieve that, select the Game
Over Screen in the Hierarchy panel, and in the Inspector uncheck the
checkbox next to its name. Here is a screenshot of that:
Lastly, we need to select our Score Label, because we need to set both the
number of points to the end of the game, and the Game Over Screen
reference. For the former, just insert the number of cakes in the game
(unless you inserted special cakes with different values; in that case, you
need to insert the sum of all the values, but see the last section for more
info). For the Game Over Screen reference, just drag and drop it from the
Hierarchy panel. Here is the final result:
However, when you've got different cakes and/or objects with different
values, you don't want to calculate the sum of their values by hand. In this
case, we can set the pointsToNextLevel variable in the Start() function of the
UI_Score script.
In any case, you can keep reading this book, and come back here later.
Devils
The greatest challenge for you would be to add some devils (enemies).
They don't need to be complex and chase the player; in fact, they can just
move from one spot to another on the map, and the player needs to avoid
them.
Here are some suggestions on how to implement them.
Remember to attach the proper colliders and set them as a trigger. Every
time the player hits the devil, you can show the Game Over menu. You can
just use the same line of code for the UI_GameOver script:
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
To make the enemy move from one spot to another, you need to create
some empty game objects in the game called waypoints. Then, you need to
give a set (or even just two of them) in the script that controls the devil
and makes it move toward one. When the devil reaches that waypoint, it
comes back to the first one (or continues on the list, if you want to
implement a list of waypoints). To check whether a devil reached a
waypoint, you can calculate the distance between the devil and the
waypoint. If this is below a certain threshold, then you can consider that
the devil has reached the waypoint.
Of course, this is quite a challenge, but I’m sure you will do fine if you
persist. Also, you can first finish reading the book, and then improve all
the games you have created once you have more confidence in Unity.
Summary
We have learned the fundamentals of UI design and explored many
different aspects of what it entails. We have also seen some examples of
different kinds of UIs and how the four types of UI—diegetic, non-
diegetic, spatial, and Meta all differ from each other and how they can be
used within a game.
Then, we explored some concepts of how Unity handles UIs before diving
into implementing the UI for our Angel Cakes game. In particular, we have
created a script for the Score, which integrates with the CollectableCake
script of Chapter 3, Let's Make Some Prefabs. We have also included a
Game Over Screen to show when the Angel collects all the cakes.
Now, with this chapter, we have concluded our first game. It is, therefore,
time to start another one. For our next game, we will go into deep space to
meet new friends in Chapter 5, Freeze! Creating an Intergalactic Shooter.
So set your phasers to stun, because things are about to get a whole lot
more interesting.
Freeze! Creating an
Intergalactic Shooter
Welcome to Chapter 5, Freeze! Creating an Intergalactic Shooter. If you
have been following this book up until now, you should have one pretty
cool game under your belt; if not, we're about to step it up with game
number two. In this chapter, we're going to learn about the second game, a
top-down arcade-style intergalactic shooter, so brace yourself. We'll also
cover some other import settings, which will build upon what we covered
in Angel Cakes (our first game). Later in this chapter, we will add a bang,
by learning about Unity's animation system, to create an explosion.
This is a screenshot of how the game should look at the end of these three
chapters:
Setting up the project for game
#2
This project will follow pretty much the same process as the last one. We
will go through the steps briefly, but if you need a more detailed process,
if you have, for instance, chosen to start with this project, then I encourage
you to review the previous chapters. Now, with that said, let's get started.
Once you have downloaded the files, unzip them into the location of your
project.
Importing assets for the space
shooter
As we have already discussed with the first project, there are few
differences when it comes to importing the assets for our second game.
Like the last project, the assets for this one also come with a series of
Sprite Sheets; these look like the following images:
As you can see from the image above, we have spaceships, explosions,
and planets. Basically, everything we need to create our space-themed
shooting game.
Once you have imported them into Unity, we will need to slice them up
and label the Sprites accordingly. You can either follow our naming
convention or choose your own. Just remember that if you choose to use
your own naming convention when referencing the Sprites, you must also
use it within your code, instead of the names we use. The naming
convention we will use should be clear, but I invite you to find a naming
convention that works for you.
Remember that the Sprite Sheets should be sliced. In the next section, we
are going to slice the Sprite Sheet to make the explosion.
Organizing the project
In organizing the project, as we did for the previous game, we need to
create a folder to keep everything tidy. In this game, we are going to use
the following folders:
Animations
(you need to fill this one with your own music and
Audio and Music
sound effects)
graphics package)
Prefabs
Scripts
tutorial: https://unity3d.com/learn/tutorials/s/animation.
When it comes to the animation system in Unity, we can talk about State
Machines. Each animation is a node and the character can change
animation according to certain variables of the State Machine. You can see
an example of an animation State Machine in the following image. Keep
in mind that, usually, animation state machines are much bigger and more
complex in a real project than the ones used to explain how they work:
For our game, we will be implementing an explosion animation each time
something is destroyed. It is a simple animation, but by showing you how
to create it, you will be able to implement other types of animations for
other elements of not only the games within this book but also your own
projects. However, for our project, the animations will come into effect
when the player's spaceship runs out of life, and when enemies'
spaceships, planets, and comets are destroyed.
Concepts of Sprite animations
Although the animation system in Unity is very powerful, and it is able to
animate to a certain degree, often external programs are used. In the case
of 3D animations, programs such as 3DStudioMax, Maya, or Blender are
used. When it comes to 2D animations, the most-used technique is an
animation Sprite Sheet. In short, it is a sprite sheet containing all the
frames of the animation. They can be created with any program that
manipulates 2D graphics. Some of these programs include Photoshop,
GIMP, or Illustrator (in case you want vector graphics).
For instance, in our case, we've got two animation Sprite Sheets, one for
each kind of explosion. Let us take one of them, for instance,
explosion02. Here is what it looks like:
As you can see, there are 12 frames, and each one of them is properly
spaced within the sheet. Having things properly spaced is essential to
simplify the workflow in Unity. If you select the sheet in the Project
window, we can see its Import Settings in the Inspector. We need to select
Multiple for the Sprite Mode, as shown in the following screenshot:
After you have clicked Apply, we need to open the Sprite Editor by
clicking the Sprite Editor button, so we can properly Slice the animation
Sprite Sheet:
On the top bar of the Sprite Editor, click Slice. Select Grid By Cell
Count as the type, and insert 12 columns and 1 row. In case you have a
different sprite sheet with a different number of frames, use the number of
columns or rows of your animation. Here is what it should look like:
Then, press the Slice button at the bottom of the window. As a result, each
frame of our animation is a different Sprite, and they are also numbered
from zero in order (something that might be very useful in large projects).
Click Apply in the top-right corner and close the Sprite Editor.
Generating the animations
from the sprites
The last step to generate the animation is to expand the explosion sheet
with all the frames as single Sprites in the Project panel, as shown here:
Select all the frames, and drag and drop them on the Scene view. Unity
will ask you where you want to save the animation and under which name.
Select the animation folder (if you do not have one, create it), and type
explosionAnimation for the name. Finally, save it.
A few considerations now. If you hit play, you can see the animation
playing continuously on the screen, but let us see how it works under the
hood. First, navigate to the animation folder, and you will notice that two
files have been created. One is the animation that we have just saved, and
the other one is the animation State Machine we were discussing just
before and it is named as the first frame of the animation Sprite Sheet, in
this case, explosion02_0, but you can rename it explosion02_animator. If you
double-click the State Machine, the Animator window comes up showing
you the State Machine, and here is how it looks:
The important thing to pick up here is that, from the Entry node, there is a
link to our animation represented by another node. This means that as
soon as the object running this machine plays, our animation is running,
which is exactly what we want. We won't dig any further into animation
during the development of this second project, but we will discuss the
animation system more in the last project of this book.
Now, coming back to the Project panel, if we select the animation, we can
see its properties in the Inspector. Since the explosion is not a continuous
animation, but rather a one-shot animation, we need to uncheck Loop
Time, as shown in the following image:
Adding a state behavior for
destroying the explosion
If you hit play, the animation will play only once. However, the object
won't be destroyed as we would expect. This can be done with a very
simple script, which is a StateMachineBehavior. We take this script from the
book Getting Started with Unity 5.x 2D Game Development (https://www.pac
ktpub.com/game-development/getting-started-unity-5x-2d-game-development), which,
once again, I suggest you read to get more insight into 2D game
development. Thus, for the detailed explanation of this script, check out
Chapter 4, No Longer Alone - Sweet Toothed Pandas Strike, of that book.
Here, we will just see how to create and use that script, without explaining
in detail how the StateMachineBehavior script works.
using UnityEngine;
public class StateMachineBehaviour_DestroyOnExit : StateMachineBehaviour {
override public void OnStateExit(Animator Animator, AnimatorStateInfo
stateInfo, int layerIndex) {
//Destroy the gameobject where the Animator is attached to
Destroy(Animator.gameObject);
}
}
Save the script, and open the state machine of our explosion. Right-click
the animation node, click Make Transition, then click the Exit node. As a
result, you will see an arrow going toward the Exit state, as shown in the
following screenshot:
Now, click the animation node, and in the Inspector, you should see an
Add Behaviour button click it and add the script we have just created.
Then, select the only Transition under the Transitions tab, and some
settings will appear. Set Exit Time to 1 and Transition Duration to 0. Here
is how it looks:
If we play the game now, the explosion should work. For more
explanations of this process, you can read the book suggested before, but
in any case, we will explore the animation system in the last project of this
book.
Saving the explosion as a
prefab
We already talked about prefabs in the last project, so you should have a
good understanding of their usefulness. The last step for creating this
explosion is to save it within a prefab, so we can use it every time we need
an explosion, and not only a random explosion every time the game starts.
In the Prefab folder, create a new prefab, and name it ExplosionPrefab. Then,
simply drag and drop it on the Explosion object in the Hierarchy window.
It should become blue after this process, so you will know that it has
become a prefab, and you can remove it from the scene. As a result, we
can use the explosion prefab any time we need one.
Summary
In this chapter, we covered the basics of the shooter genre; we also
described what our second project is and how to set it up. We covered the
basics of animation in Unity and how to create a basic explosion
animation. In the next chapter, you will learn how to add a bit more
substance to our shooter game. You will learn how to create a shooting
system so that the player can fire at enemies. You will also learn how to
create a prefab for an enemy, as well as an enemy AI to add a bit more
action to the game.
No One Is Alone Forever
Now, we've laid down the foundations for our second game. In this
chapter, it is all about adding a bit more substance to our shooter. In this
chapter, we will learn how to create an explosion and use it within our
shooting system. In particular, we will learn how to:
Create the player controller, so that the player can move the
spaceship and shoot with it
At the end of the chapter, you will find an exercise section in which you
can practice what you have learned in this chapter. However, if you really
want to dig deeper into AI, you should consider buying a specific book
about it.
Ready to learn all this awesome stuff? Very well, let's dive in!
Creating a shooting system
As we saw in Chapter 5, Freeze! Creating an Intergalactic Shooter, there
are many different ways a player can "shoot" something. As we also
learned, in our game, the player will shoot at a range of different objects.
Therefore, there are a few things that you may want to consider when it
comes to implementing the elements of shooting. One such thing is the
"consequence" for shooting or, alternatively, getting shot. For example,
when a player shoots at the enemy, how many hits can the enemy take
before it is destroyed, and vice versa for the player? Are some enemies
harder than others, perhaps more powerful? Now, the answers to these
kinds of questions become refined through playtesting. It is what we game
designers refer to as "balancing", and ideally you want a game to be not
too easy or too hard at the beginning, but something that grows in
difficulty as the player becomes more skilled at it.
For now, we will work on setting the shooting system up, then we will
look at how to balance it later. In particular, the shooting system is not just
one script, but in our case, it is made of the interactions between the
PlayerController, the EnemyController, and the BulletController. This chapter
First of all, we would like to lock the movement to only left and right; we
don't want the player going up and down. Of course, this is the choice we
made when we designed the game earlier in the chapter. Another
consideration is that the left and right movements are limited; the player
cannot go off screen. In Angel Cakes we have solved this problem by
placing walls, so we delegated to the physics system the fact that the
player cannot go beyond certain boundaries. In space, there are no walls,
and even if we could place invisible ones, for the sake of learning
something new, we will see how to limit the movement through the script.
Differently from Angel Cakes, the player here can shoot. In particular, the
player can shoot straight from the player's location. In our implementation,
this means that the player controller has to instantiate a bullet. However,
we also need to limit this process, since we don't want the player to shoot
billions of projectiles per second, but rather have a reload time before
shooting the next bullet. As such, the player controller is also responsible
for making this check.
Lastly, the spaceship can take damage, so we need to show the animation
of the explosion where the enemy's bullet has hit the player. Again, the
player controller has to deal with it. Actually, not only should it show the
explosion, but it should also play a sound and decrease the number of lives
by one. However, for these two things, we need to wait until Chapter 7,
Getting Serious About Gameplay, in which we will implement the UI, but
it was worth mentioning it here since we are designing our player
controller.
Now that we understand the requirements for our player controller, let's
implement it. If you remember in Chapter 3, Let's Make Some Prefabs, we
saw the script piece by piece. Here, to change the approach, we will see all
its functionalities one by one.
Creating the script
As we did in Chapter 3, Let's Make Some Prefabs, we need to create a
Player Controller (Script). Inside the Script folder, right-click and then
Create | C# Script. We name it PlayerController. Now the script can be used
as a component, meaning that it can be attached to a game object:
We can then open the Script by double-clicking on it. For those who didn't
follow the previous project, here is a short recap. This is the code you
should see:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
At the beginning, there are three lines that allow us to use libraries. Then,
there is the class definition, in this case, named PlayerController. Inside it,
there are two functions: Start() and Update(). The first is called every time
this script starts, whereas the second is called at every frame.
Moving the player
The first feature to implement is the most vital one: moving the player.
Let's start by copying and pasting the code from Chapter 3, Let's Make Some
Prefabs. In fact, we need to enforce both collider and rigidbody
components, since we will use them to move the character (and also to
detect collisions with bullets or enemies). Then, we actually need to move
the character. You can refer to Chapter 3, Let's Make Some Prefabs, for a
detailed explanation, but we are going to use, once again, the following
equation:
The only difference is that we don't need to move the player along the y-
axis, so the code within the FixedUpdate() function becomes shorter than one
line. Also, we need to substitute the y variable in the last line of the
function with transform.position.y.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
public class PlayerController : MonoBehaviour {
public float speed = 10.0f;
private Rigidbody2D rigidBody;
// Use this for initialization
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
}
void FixedUpdate() {
//Get the new position of our character
var x = transform.position.x + Input.GetAxis("Horizontal") *
Time.deltaTime * speed;
//Set the position of our character through the RigidBody2D
component (since we are using physics)
rigidBody.MovePosition(new Vector2(x, transform.position.y));
}
}
So far so good, the player is able to move left and right. However, the
spaceships movement is unlimited along the x-axis. Therefore, we will
need to use a wall to contain the player's movement. To begin we will
need to add a variable. We assume that the main camera is centered (as in
any 2D default scene). Thus, if the player is on x=0, then the spaceship is in
the middle of the left and right sides of the screen. As a result, we are able
to use just one variable to limit the motion both left and right
symmetrically. We can add to our code the boundX variable, make it public
(so designers can tweak it), and assign an initial value:
void FixedUpdate() {
//Get the new position of our character
var x = transform.position.x + Input.GetAxis("Horizontal") *
Time.deltaTime * speed;
//Clamp along x-value according to boundX variable
x = Mathf.Clamp(x, -boundX, boundX);
//Set the position of our character throught the RigidBody2D
component (since we are using physics)
rigidBody.MovePosition(new Vector2(x, transform.position.y));
}
Save the script and test it to see if it works before moving on.
It's time to shoot
The next step is to give the player the ability to shoot. In doing so, the
player controller creates a bulletPrefab. We don't have a bulletprefab yet, but
we will create it later in the chapter. However, we can create a variable to
hold the prefab, so it can be changed later on in the Inspector, as the
following:
Now, in the FixedUpdate() function, after all the lines we already have, we
need to instantiate the bullet every time the player shoots. In this case, we
are going to check whether the player presses the Fire1 button or the E
key. If so, we just instantiate the bullet prefab at the same position as the
spaceship and without any rotation applied, which is expressed through
the admittedly fancy Quaternion.Identity argument (you can find out more
about quaternions in Unity at the following links: https://unity3d.com/learn/t
utorials/topics/scripting/quaternions and https://docs.unity3d.com/ScriptReference/
Quaternion.html:
void FixedUpdate() {
// [...]
//Check if the player has fired
if (Input.GetKeyDown(KeyCode.E) || Input.GetButtonDown("Fire")) {
//Create the bullet
Instantiate(bulletPrefab, transform.position,
Quaternion.identity);
}
}
This works just fine, but it can be improved. As we anticipated, right now
the player can repeatedly press the E key and can shoot many bullets. We
want in some way to limit this process. In order to achieve this, we need to
write a bit more code.
First, we need two variables to store the reload time, which is how often
the player can shoot, and the last time the player has shot, which is a
private variable to check if the reload time has expired:
We have to change the code in the FixedUpdate() to reflect this change. So,
inside the if statement, it checks if the player has shot, and we can nest
another if to check whether the player can shoot or if it is too early (the
reload time hasn't expired yet). We can do this by calculating with a
subtraction how much time has passed since the last time the player shot
and compare it with the reload time. If this results as greater than the
reload time, then we need to update the last time the player shot and
actually created the bullet. In the end, the code looks like the following:
void FixedUpdate() {
// [...]
//Check if the player has fired
if(Input.GetKeyDown(KeyCode.E) || Input.GetButtonDown("Fire")) {
//Check if the player can shoot since last time the spaceship
has fired
if(Time.time - lastTimeShot > reloadTime) {
//Set the current time as the last time the spaceship has
fired
lastTimeShot = Time.time;
//Create the bullet
Instantiate(bulletPrefab, transform.position,
Quaternion.identity);
}
}
}
That explosion was bad
In Chapter 5, Freeze! Creating an Intergalactic Shooter, we created an
explosion prefab. This will come in handy now, when we need to make
our spaceship explode every time it takes damage.
If you remember from the Angel Cakes collecting system, when the player
comes into contact with a cake, it is collected. This was done using a
special Unity function named OnTriggerEnter2D, which detects when the
player enters the collider of the cake. Here, we are going to do something
similar, but to learn something new, we will implement the explosion in
this way: when the enemy bullet hits the player, it's the script of the bullet
that makes this check (while in Angel Cakes the cake was detecting this),
but then it will communicate this to the player controller, which will create
an explosion (and eventually decrease the number of lives).
Thus, in order to make the player controller communicate with the future
bullet script, we need to create a public function named Hit(), which will
be called by the future bullet script. What do we need from this function?
At the current stage, we just need to instantiate the explosion. This can be
done very easily with just a few lines of code.
First of all, add a variable that holds the explosion prefab, so we are able
to instantiate it in the Hit() function:
Then, let's write the Hit() function with a line of code that creates an
explosion based on the explosionPrefab variable at the same coordinates of
where the spaceship of the player has been hit. This means that the
coordinates are passed from the bullet script as a parameter of the
function:
public void Hit(Vector3 hitCoordinates) {
//Create an explosion on the coordinates of the hit.
Instantiate(explosionPrefab, hitCoordinates, Quaternion.identity);
}
Save the script, and as a result, the spaceship is able to take damage, or at
least show that it has been hit. We will deal with proper damage in Chapter
7, Getting Serious About Gameplay.
Testing the explosion
If you don't want to wait to implement the bullet script to test this
function, you can add in the FixedUpdate() so that every time the player
presses the key T, the Hit() function is called. Thus, by following a similar
process to shooting bullets, we can add the following lines to the function:
//DEBUG CODE: simulates the Hit() function when the player presses
the T key
if (Input.GetKeyDown(KeyCode.T)) {
Hit(transform.position);
}
Besides all of this, we have pretty much all the functionalities of the player
controller, so the enemy spaceship moves, moving the rigidbody and
implementing a Hit() function to take damage.
Creating the enemy controller
Create a new C# script and call it EnemyController. Since this controller
shares many functionalities in common with the player controller, it's
better to copy the player controller and modify it. So, copy the whole body
of the player controller onto the enemy controller. Don't forget that the
name of the class should remain EnemyController, and this is very important;
otherwise, we will have compilation errors.
New variables for the enemy
controller
Since the enemy is now controlled by the computer, we need to script a
behavior, like the one we described in the requirements section. Therefore,
we need new variables and we need to change some of the old ones.
First of all, we need two different speeds. In fact, the enemies not only
move left and right, but they are also coming down slowly. So, let's
substitute the speed variable with the following two:
It's not a mistake that the speed along y is negative. The enemy is moving
down, so with a negative speed, the enemy will move downwards.
Since the enemy moves in the two directions in two different ways, we
need to implement both the movements. Along y is easy, since we are
going to implement once more the usual equation of the motion, which is:
Moreover, we don't need to take care of the input of the player, so along y
the code within the FixedUpdate() function becomes:
Lastly, we need to remove to clamp, since the sinus (by varying only
between -1 to 1) will ensure we won't have any value greater than boundX or
smaller than negative boundX.
The full code for the movement in the FixedUpdate() function is the
following:
void FixedUpdate() {
//Get the new position of our Enemy. On X, move left and right; on Y
slowly get down.
var x = boundX * Mathf.Sin(Time.deltaTime * speedX);
var y = transform.position.y + Time.deltaTime * speedY;
//Set the position of our character through the RigidBody2D
component (since we are using physics)
rigidBody.MovePosition(new Vector2(x, y));
// [...]
}
Now, it's time to jump onto making our enemy deadly, by making it shoot
bullets.
Shooting deadly bullets
Just as, for the player controller, we don't have the bullet prefab yet, but
we do have a variable where the bullet prefab will be stored. This is great,
but so far our code makes the enemy shoot at the same time as the player;
instead, we would like the enemy to be autonomous. In order to achieve
this, we need to change the code. The simplest implementation is that the
enemy shoots a bullet after a random amount of time, for instance varying
from one to three seconds. These two values are stored in the
minShootingTime and maxShootingTime variables.
Thus, let's remove the check of the input of the player, but keep the check
on the reload time. Then, inside the if statements, we need to also reset the
reload time to a random value between minShootingTime and maxShootingTime.
We can do this with a built-in class of Unity called Random, which does
exactly this with the Range() function. As a result, the enemy will shoot
every time the reload time has expired, and the reload time changes every
time it shoots. The code now looks like the following:
void FixedUpdate() {
// [...]
// Fire as soon as the reload time is expired
if(Time.time - lastTimeShot > reloadTime) {
//Set the current time as the last time the spaceship has fired
lastTimeShot = Time.time;
//Set a random reload time
reloadTime = Random.Range(minShootingTime, maxShootingTime);
//Create the bullet
Instantiate(bulletPrefab, transform.position,
Quaternion.identity);
}
}
And with this said, congratulations. You have now finished implementing
the enemy controller, at least for this chapter; in the next one, we will
change it slightly.
Alternative enemy controller
In order to give variety to our game, this section explores an alternative to
the previous enemy controller. Once again, this shows how to create
challenging enemies with just a few lines of code, without complex AI
algorithms that can be confusing for beginners.
Creating the second enemy
controller
To create the second enemy controller, we can start by duplicating the
enemy controller; just select it and press Ctrl + D (if you are a Mac user,
you need to press Command + D). The new script should be
renamed EnemyControllerSmartAttacker.
// [...]
public class EnemyControllerSmartAttacker : MonoBehaviour {
// [...]
}
Changing the aiming system
The previous enemy was shooting every time it had a chance after a
random number of seconds. This other one, instead, shoots when it is
above the player.
store the shooting sensitivity, meaning how close the enemy is to the
straight line above the player before shooting, and the other variable stores
the player transform, so it's easier to retrieve the player's position:
assume there is only one in the scene, and from there retrieve the player's
transform:
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
playerTransform = FindObjectOfType<PlayerController>().transform;
}
Now, inside the if statement in which we check if the reload time has
expired, we need to place another nested check. In fact, we need to check
if the enemy is close enough to shoot. By taking the absolute value of the
difference between the x-axis of the enemy and the x-axis of the player,
we get the distance between them along x. We can compare this value with
shootSensitivity, and if it is smaller, we can proceed with the rest of the
code to shoot the bullet. This time we also need to remove that the reload
time is random.
In the end, the code will look like the following:
void FixedUpdate() {
// [...]
// Fire as soon as the reload time is expired
if(Time.time - lastTimeShot > reloadTime) {
//Check if the enemy is "close" on the x-axis to the player
if (Mathf.Abs(playerTransform.position.x - transform.position.x)
< shootSensitivity) {
//Set the current time as the last time the spaceship has
fired
lastTimeShot = Time.time;
//Create the bullet
Instantiate(bulletPrefab, transform.position,
Quaternion.identity);
}
}
}
Now, we get not one, but two different enemies we can use. However, we
talked a lot about shooting bullets, but we still don't have bullets! Let's
create them in the next section.
Shooting with passion
In order to finish implementing our shooting system, the last thing we
need to do is to create our bullets. At this stage, it shouldn't be a too
complicated.
Creating a bullet prefab
First, we need to drag and drop our projectile sprite that we sliced in the
previous chapter into the scene. Then, we need to add a Box Collider 2D,
and the default settings should be good to go. We also need to enable Is
Trigger, like we did for the cakes in the previous project:
Then, since our bullet will move, we also need to add a Rigidbody 2D, and
we just need to change the Gravity Scale to 0 and freeze the rotation
around Z; exactly as we did for the player:
Finally, by using the Add Component button, we can create a new C#
script and name it BulletController. We are going to implement its
functionalities in the next section, but first, we need to create two new
prefabs. Why two prefabs? One is for the bullets of the player and the
other one is for the bullets of the enemies (in case you have different
graphics you can have many more, but in this project, we will just keep
two of them). In fact, even if they don't differ in the graphics, we will
make a slight change in a public variable in the script, but we will see this
once we have finished with our script. For now, you can call the two
prefabs respectively BulletPrefab_Player and BulletPrefab_Enemy. You can drag
and drop from the Hierarchy panel the same BulletSprite on both of them
before removing the object from the scene.
Creating a bullet controller
Open the BulletController script and remove the Update() function, since we
don't need it. It's time to break through the creation of this controller.
Enforcing components
As we did with the other scripts in this book, once again we need to
enforce the components, so we can write:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
public class BulletController : MonoBehaviour {
//[...]
}
Exposing variables in the
Inspector
In order to let designers customize the bullet according to the gameplay of
the game, we need to expose some variables by declaring them public.
This concept should be clear by now. In particular, we want to expose the
speed at which the bullet moves, and the time before it is destroyed in case
it didn't hit anything. This last one is just an optimization since we don't
want bullets going around in our game world in regions that are not
visible.
// [...]
public class BulletController : MonoBehaviour {
public float speed = 10.0f;
public float timeBeforeDestruction = 10.0f;
// [...]
}
Getting the reference to the
rigidbody
As we already know, we need to get a reference to the rigidbody
component so we can use it in the FixedUpdate() function to move the bullet.
As such, let's declare another variable:
// [...]
public class BulletController : MonoBehaviour {
public float speed = 10.0f;
public float timeBeforeDestruction = 10.0f;
private Rigidbody2D rigidBody;
// [...]
}
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
}
specify the game object to destroy (in this case the bullet itself), and the
time. So, the Start() function at the end should be like the following:
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
//Destroy the bullet if it didn't hit anything after 10 seconds
Destroy(gameObject, timeBeforeDestruction);
}
If you were to place a bullet in the scene now and hit play, it would be
destroyed after ten seconds, which is the default value for the
timeBeforeDestruction variable.
Moving the bullet
In a similar way to the player controller, we need to move the bullet. We
use the same equation for the motion and the same lines of code. However,
we need to make the movement happen only on the y-axis; there is no
clamp, and the bullet moves regardless of the input of the player. Here is
the final code, which shouldn't be hard for you to understand:
void FixedUpdate() {
//Get the new position of our bullet
var y = transform.position.y + Time.deltaTime * speed;
//Set the position of our bullet through the RigidBody2D component
(since we are using physics)
rigidBody.MovePosition(new Vector2(transform.position.x, y));
}
Wrapping up, here is the final code for the OnTriggerEnter2D() function:
We have seen how to create just one enemy and one kind of bullet.
You are free to experiment with the values (such as speed,
minShootingTime, maxShootingTime or shootingSensitivity) and create a
different prefab for each one of them. In the next chapter, we will
see how to create the single prefabs of the controllers of this
chapter. However, you can get a head start and try to do them now.
The package offers different spaceships with different colors, so
just have fun experimenting.
for asteroids, planets, and so on, that can explode when hit. Try to
fix this problem on your own by figuring out a solution that could
work. It doesn't have to be complicated, you can try to do the
different casting to the different classes to call the Hit() function.
If you are in the mood for a challenge and know a bit about
interfaces in C#, then we can implement the previous exercise in a
much smarter way. Create an interface called CanExplode, in which
we have the definition of just one function: Hit(). Then, for each
controller we have created in this chapter (such as PlayerController,
EnemyController, and EnemyController2) and any other class that needs
Once you are ready, let's start by including a lives counter for our player.
Building the UI
This time around, our UI is going to be counting a few more things than in
our last game. If you remember, we used to increment a score every time a
cake was collected. Here, we are going to implement the score, and we
will use basically the same thing learned in the previous project. However,
we are going to represent the lives of the player with a discrete number of
heart icons. Although it's not a super complex UI, it still poses some
challenges that we are going to face.
Setting up the UI
The first thing to do is to prepare the UI elements so that they can be
easily scripted. Thus, we need to decide where our UI will be shown. As
per the design, we want to show both the score and the number of lives in
the bottom-left part of the screen.
Let's start by creating a new UI image, and use the heart in our package as
our icon. Then, duplicate it twice so we have three hearts, and name
them heart_0, heart_1, and heart_2. Then, place them on our panel, just after
the Level 1 label, as shown in the following screenshot:
Create an empty game object in your UI, just under the main panel, and
place it in such a way that it covers up all the three hearts. Then, parent the
hearts to this empty game object (by preserving the order), which we can
rename UIHearts. Create a new C# script named LivesCounter and attach it to
the UIHearts game object.
Inside the script, we need a variable to keep track of how many lives the
player has left, which can be just an integer value. Also, another variable
is important to keep track of the maximum number of lives the player can
have. So, let's write:
void Start () {
//Set the initial number of lives to its maximum
lives = maxNumberOfLives;
//Initialise the array of hearts
hearts = new GameObject[maxNumberOfLives];
//Cycle among children and get the hearts we need
for(int i = 0; i<maxNumberOfLives; i++) {
hearts[i] = transform.GetChild(i).gameObject;
}
}
that designers should take care in. In fact, the order of the
UIHeart game objects in the hierarchy is important. The
Save the script, but we haven't finished yet. Right now, the lives system
works, but no lives are removed when the spaceship is hit. Therefore, let's
open PlayerController and modify the Hit() function. Here, we need to place
a call to LivesCounter (assuming that there is just one instance in the game)
to remove the player's life:
Save this script, and now you can pat yourself on the back because the
lives system is working properly.
Creating the star score counter
The creation of a scoring system should be well known to you by now. If
not, you can always go back to the previous project and revise how the
scoring system is done. Then, try to implement it by yourself for this
project. You should end up with something similar to the following
screenshot in the Game view:
Building an infinite scrolling
map
So far, we have the main functionalities of the game running. This section
will explore how to create the map in which our shoot takes place. Here,
we will implement the main parts, such as making the map perform
infinite scrolling or making a satellite rotating around a planet. However,
implementing other elements and polishing the game are left as exercises
in the next section.
Repeating the background
In an infinite scrolling game, we need a background that repeats
continuously. Of course, adding variation to the background (or many
layers of background, called parallax) improves the visual appeal greatly.
However, usually, there is a basic tileable background that repeats at the
very end.
From an operative point of view, we have two images: one that occupies
the whole screen, and the other one just above. They will scroll down, and
as soon as the second image fills the whole screen, both images are
repositioned. We will create a single script that we will attach to both the
images. This is an easy approach, but we need to remind the designers that
they should share the same speed, otherwise they won't scroll seamlessly.
Even if it is good practice to enforce this by code, for simplicity's sake we
will delegate the responsibility of matching the two speeds to the
designers.
Drag and drop the background image of the package, and then duplicate it.
Place one in such a way that it fills the screen completely, and the other
one just above, as shown in the following screenshot:
Then, we need to initialize these two variables at the Start() function. For
the initial position, it is easy; for the offset, we have to retrieve the
SpriteRenderer and extract the height, which can be found by retrieving the
void Start () {
//Store the initial position
initialPos = transform.position;
//Store the y-length of the Sprite
offset = GetComponent<SpriteRenderer>().bounds.size.y;
}
In the Update() function, we need to move the image downward. Why not
use the FixedUpdate() function instead? For the first time with code, we need
to move something in the Update() function because we don't have a
rigidbody since the object/image doesn't have to interact (it's just a
background, after all). As a result, not only can we use the Update()
function, but we can move it by using its transform directly. Thus, let's
write:
void Update () {
//Scroll the background
transform.position += new Vector3(0, -Time.deltaTime, 0);
}
Lastly, after the object has passed the offset, it has to be repositioned.
Hence, we can check whether the absolute value of the difference between
the current y position and the original one (which is a measure of the
distance it has moved from the initial position, or in other terms the offset)
is greater than the offset variable. If so, set the position for the initial one:
void Update () {
//Scroll the background
transform.position += new Vector3(0, -Time.deltaTime, 0);
//Check if the scrolling has passed the offset, if so, reposition
the image
if(Mathf.Abs(transform.position.y - initialPos.y) > offset) {
//Reposition the image
transform.position = initialPos;
}
}
Save the script, and remember to assign the same speed to both your
background images. As a result, if you hit play, we have the background
repeating indefinitely.
Falling stars and planets
Aside from the background, other "decorations" might fall from the sky,
such as planets, satellites, or stars. In this section, we are going to
implement a quick script that makes an object fall. Again, we are going to
use the Update() function to move, instead of the FixedUpdate(), for the same
reasons of the repeating background.
Create a new script and name it FallingScript. Open it, and add the speed
variable as usual:
In the Start() function, we need to trigger the countdown for the auto-
destruction, like we did for BulletController:
void Start () {
//Destroy the falling object after timeBeforeDestruction
Destroy(gameObject, timeBeforeDestruction);
}
Then, in the Update() function, we can just move the object downwards, as
follows:
void Update () {
//Move the object downwards
transform.position += new Vector3(0, -speed * Time.deltaTime, 0);
}
Save the script, and attach it to any object you want to make fall (of
course, remember to create a prefab first and to place the ordering layer, or
the z coordinate, in such a way that the object is still rendered below
spaceships, enemies, and bullets).
Rotating satellites
If you have noticed, in our package we have some planets with some
circles around them. Even if the whole planet can be driven
by FallingScript, we can create satellites as children of the planet, rotating
around it. So, create a prefab for the planet with the circle, and
attach FallingScript. Then, as a child (or children if you want more than
one) attach satellites to the RotatingSatellite script we are going to create.
As a result, we will have a planet with satellites orbiting around, as shown
in the following image:
Now, let's create the aforementioned script (RotatingSatellite), and open it.
Once again, we need the speed variable:
Then, we need a variable to store the radius of the orbit. By adjusting this
value, designers are able to tweak how far the satellite orbits around it (or
adjusting to the circle's radiuses):
public float radius = 1f;
For the motion, we just need to follow a circular path. If you are
familiar with trigonometry, this is just having a cosine on the x and the sine
on the y; as arguments of these trigonometry functions, we need the time
scaled by the speed. Then, it's just a matter of assigning the variables to
the new position. Of course, the position needs to be in local space, since
we are assigning the new position with respect to the planet:
void Update () {
//Calculate the new x and y of the circular motion
var x = radius * Mathf.Cos(speed * Time.time);
var y = radius * Mathf.Sin(speed * Time.time);
//Assign the new position to the object transform
transform.localPosition = new Vector3(x, y, 0f);
}
Save the script, and enjoy satellites orbiting around your planets.
Creating the prefabs
In case you didn't do this during the last two chapters, it's time to prepare
all the prefabs for the game. This small section will guide you through, but
I invite you to try to do this by yourself. In fact, we did the bullet prefab,
but you should do it also for the player, the different types of enemies, and
the decorations we have seen in the previous section.
For both the player and the enemies, remember to attach Collider2D
and Rigidbody2D. As we did for the bullet, we need to zero Gravity Scale
on Rigidbody2D, but you can leave isTrigger unchecked on the collider. Also,
remember to adjust the z coordinate or the ordering layer to make them
always appear in the foreground. Needless to say, remember to attach the
respective scripts to each one of them and set their values. Pay particular
attention to the BoundX variable, which should be tweaked depending on the
map you built.
As far as decoration elements are concerned, you just need to attach the
scripts, and parenting satellites, and make sure they are always shown in
the background.
Including power-ups
In Angel Cakes, we developed a pretty basic collection system. In this
game, we're going to be using the same principles for collecting items
such as health and stars. Here is how they look in the game:
Actually, let's just take the script we used in Angel Cakes—if you
recall, CollectableCake, and modify it.
For the stars, we should change the cakeValue variable to something such
as starValue. Then, change the IncreaseScore() function call to the script you
built before.
For the health, instead, we can erase the cakeValue variable, since each heart
will grant just one life. Then, everything remains the same, just change the
IncreaseScore() function call with this line:
FindObjectOfType<LivesCounter>().AddLife();
Save the script in another file, and you are good to go.
The next step is to create the prefabs for such collectables and make sure
that the Audio Source component, as well as a Collider 2D with is Trigger,
checked.
Spawning system
If we place an enemy and the player against each other, the fight is
interesting. But, after the player has defeated the enemies, the game
becomes infinitely boring. As such, you should create a spawning system
that allows you to generate waves of enemies. Also, the spawning system
should be able to generate power-ups and background decoration at
random. This is, in general, is not an easy task. As a result, here we will
implement a simple spawning system and explore the concept of
coroutines. However, I suggest you read Chapter 7, Trading Cupcakes and
the Ultimate Battle for the Cake – Gameplay Programming, of Getting
Started with Unity 5.x 2D Game Development (https://www.packtpub.com/game-
development/getting-started-unity-5x-2d-game-development). In particular, the
section titled Panda invasion - spawning pandas, which explains
coroutines in more detail, and how to create a good and simple spawning
system.
Coroutines
Coroutines are a special structure in Unity that allows functions to be
interrupted and continued in other frames of the game. In the case of our
spawning system, we don't want to spawn all the enemies or decorations at
the same time, but a little at a time. This, over time, can be controlled with
coroutines. You can learn more and see some examples in the official
documentation at https://docs.unity3d.com/Manual/Coroutines.html.
Creating spawning points
The basic idea behind a spawning system is that there are special points in
which things are spawned. In scrolling games, such as this one, the
spawning points are just beyond the visible part of the screen, so the
player doesn't see the enemy or the decoration popping out of nowhere.
In Unity, the best things we can use to represent these points are just
empty game objects, which we can place just above the camera, as shown
in the following screenshot:
Usually, it's hard to see empty game objects, because you
need to select them. However, if you click on the icon next to
their name in the Inspector, you can choose a label color. As
a result, their name appears on the Scene view, and it is easy
to identify them. The preceding screenshot shows exactly this
process. Also, from there, you can deduce the name.
As you can see, we have created three spawning points for enemies just
above the border of our camera and five spawning points for our
decorations that are further away. Because they might be big objects and
we want them to appear out of nowhere, you should check that in those
points, the object is not visible on the camera.
As a pro-tip, you can move the pivot point of such big objects
so that when they are in those positions, they are not visible.
As a result, you will be able to use closer spawning points; if
not, use the same of the enemies if you wish.
Scripting the spawning system
Now that we have our points, it's time to create a script to handle the
spawning. In this case, we will spawn random enemies after a random
amount of time. Same for the decorations, but they will be on a different
coroutine.
Create the script and name it SpawningSystem. First of all, we need two arrays
to store the two different spawning points. We can store them as
transforms so that we can easily retrieve their position. Of course, these
should be set manually from the Inspector by designers, since this might
depend on the different levels you want to implement:
Then, we need two arrays for storing all the prefabs that we would like to
randomly spawn. This goes both for decorations and enemies:
Then, we need to implement the two coroutines; they are very similar
(which, if you want, to challenge yourself, you can transform the
coroutines by adding parameters). Let's start with the enemies one. First,
we need a loop that spawns a random enemy in a random spawning point.
Then, we need to set a random timer before spawning again:
IEnumerator SpawnEnemies() {
//Forever...
while (true) {
//...spawn a random enemy in a random location
Instantiate(enemies[Random.Range(0,
enemies.Length)],enemiesSpawningPoints[Random.Range(0,
enemiesSpawningPoints.Length)].position, Quaternion.identity);
//Set a random ammount of time between 8 and 16 seconds before
to spawn another enemy
yield return new WaitForSeconds(Random.Range(8, 16));
}
}
It's similar with the decorations, but with decorations rather than enemies:
IEnumerator SpawnDecorations() {
//Forever...
while (true) {
//...spawn a random decoration in a random location
Instantiate(decorations[Random.Range(0, decorations.Length)],
decorationSpawningPoints[Random.Range(0,
decorationSpawningPoints.Length)].position,
Quaternion.identity);
//Set a random amout of time between 4 and 9 before to spawn another
enemy
yield return new WaitForSeconds(Random.Range(4, 9));
}
}
Lastly, we need to start these two coroutines at the Start() function, so you
can write:
void Start () {
//Start the two coroutines
StartCoroutine(SpawnEnemies());
StartCoroutine(SpawnDecorations());
}
Save the script, create a new game object named SpawningSystem, and attach
this script to it. Set all the variables in the Inspector, as shown in the
following screenshot:
Of course, this is not the best solution for a spawning system, but at least
you can get your feet wet with coroutines. Once again, you are free to
experiment to improve on what this book teaches you because that is
where the real improvement happens.
Testing the game
Now, we need to test the game so that everything runs smoothly.
If we hit play, the game looks cool, but there are things we could adjust.
For instance, there is neither audio nor background music! Now, if you
remember the content that we covered in the last chapter, you can
definitely implement it. If not, I suggest going through the steps again to
learn how.
Another thing that you might see is that your player is shooting too fast, or
moves too slowly. In other instances, enemies might be too challenging,
extremely fast, and so on. As a result, you should revise all the variables
you exposed in the Inspector, and try to tweak them in such a way that the
game becomes balanced.
In any case, you might end up discovering that the game is too simple to
be fun; then, you can try to do the exercises in the next section, or draw
some new ideas on how to develop your game further.
Exercises
Like we did in the previous chapter, here is a series of exercises you can
do to not only improve your skills but also improve the overall quality of
the game:
For the lives counter, the player can have a maximum of three
lives because there are only three hearts. You can increase the
number of hearts, but they would fill the UI up quickly. So, what
about having a number next to the first heart in case the player has
more than three lives? In this way, from zero to three lives the
player has a visual feedback, and if the player has more than four
lives they are represented by a number. Modify the LivesCounter
script to include this modification.
Alternatively, if you don't like the idea of giving the player more
than three lives, you should still consider rewarding the player for
collecting a heart. So, you can modify LivesCounter so that in the
event that a life is added, but the player already has the maximum
amount of lives, then the player's score is increased by five.
Now that you have enough knowledge of the UI, you can improve
the overall look, maybe by integrating your own graphics. For
instance, try to head for something that looks like the following:
In the spawning system, we have two coroutines. As anticipated,
you can merge them to create a unique coroutine with parameters.
As a result, you will be able to launch the same coroutine twice in
the Start() function, just with different parameters (such as which
array is taking the object to spawn).
Now, if you dare, let's jump into the last project of this book!
Building a Tilemap and
Importing it into Unity
We are now on the home stretch with our third and final game for our
book—RETROformer. In this chapter, you will learn all about more
advanced map building. In particular, you will be introduced to a third-
party tool for creating tiles named Tiled. In addition, you will learn how to
incorporate these tiles into Unity to build 2D worlds. With this said, let's
jump into it.
Creating tiles
.
http://adamatomic.com/canabalt/
Now, let's get down to business and begin our third and final game.
Overview of the project for
game #3 - RETROformer
Like the other projects, we will go through how to set up RETROformer.
Tiled: Where we will create the level environment that the player
will navigate through
At this stage you should be used to the file structures for creating games
from the last 2. So for the third game, you can use the same folder
structure that you have for the previous games.
Creating tiles
One of the most iconic things about 2D games is the use of tiles. Just like
tiles within your house, combining tiles together can create patterns, and
those patterns, within a game's context, can result in its levels. In this
chapter, we will learn just how we can use tiles in a third-party program
called Tiled, and how to then take the levels that we build and import them
into Unity using the program Tiled2Unity. These are discussed more in just a
moment.
Like the other games in this book, you can find the graphics package for
this by visiting the following link:
http://player26.com/product/retroformer/.
Introduction to the program -
Tiled and Tiled2Unity
Essentially, tiled is a free 2D map editor that will save you a lot of time
working on your levels and creating TileSets. It is a tool that makes it
much easier to create a 2D level, instead of doing it within Unity by
duplicating game objects. You can download Tiled by visiting the
following link here: http://www.mapeditor.org/download.html.
As the title suggests, we will also need to get another program called
Tiled2Unity, which allows us to import levels that are made with Tiled. You
New Map: This will allow you to create a new space to create
your game environment. This will be the space where we will be
placing our tiles.
New Tileset: This will allow you to create a set of different tiles
based on an image that has been made for use as a Tileset. What
this means is that the tiles themselves have been placed in such a
way that they conform to a grid. Therefore, when you import a
Tileset into Tiled, the image will be automatically sliced based on
a grid size that you specify. We will explore this in a little bit.
Tile render order: This is a setting for orthogonal maps only. What
it does is it renders the tiles in a particular direction.
Map size: This specifies how big the game map will be:
Tile size: This is how large each tile is on the tileset. These are
generally determined by the tileset, as they will already have a set
dimension for each tile. Don't worry if you have parts of a tileset
that are made up of more than one tile. You will be able to
combine multiple tiles later, once we have created the tileset. This
will be discussed later in this chapter.
Make sure that you pay attention to the settings here. We will be using the
following for our new map. Once you have changed the following values
(Tile Layer Format, Map Size, and Tile Size), click Save As… and save it
with your project files.
Tileset
Image
Here, we will need to give our Tileset a name and link it to the source
image (where you saved the asset pack). Once you have named your
Tileset and provided the Source, click Save As… to continue.
The limitations of this process are only that of your imagination. Now, in
the main screen in Tiled, you can begin to construct your level by
selecting the tiles within the Tileset. To do this, simply:
1. Select which tile you wish to use from the Tilesets panel.
2. Inside the TileMap space, hold or click with the left mouse button
where you want to position them within the map. You can think of
this process like using a brush, and by selecting a specific tile it
can be painted onto the level canvas.
After doing this, you can end up with something like the following
screenshot. For now, just experiment:
Above in an example of the level within Tiled using Sprites from the RETROformer Sprite Sheet.
If some objects are larger than a single tile, you can select multiple tiles at
one time by holding down Ctrl and clicking on the tiles. They will be
highlighted, like in the following screenshot:
You can experiment with what you have just learned so that you can get
used to the way Tiled works. Place them anywhere and everywhere as you
please. Once you've got a feel for Tiled, let's move on to creating our first
level.
If you have placed a tile(s) and wish to remove it, you can do so by
clicking the eraser icon as highlighted in the following screenshot, or
pressing E on the keyboard:
Once you are done, simply click the stamp icon, as highlighted in the
following screenshot, or by pressing B on the keyboard:
Once you feel comfortable with using Tiled, we are ready to begin
creating the level that we will use within our game. To begin, we will
create a simple path with very few terrain features (hills, bottomless pits,
for example). For now, this level will be simple so that we can learn how
to implement it into Unity. Once we have this process down, you're more
than welcome to spend a lot more time on refining your level and making
it as detailed or complex as you wish.
In our game, we will have two different kinds of materials: Terrain and
Water. At this stage, it is important to learn that we can separate the two
types by placing them on different layers. If you look towards the top-right
corner of Tiled, you will see the Layers panel, like in the following
screenshot:
We will need to create a layer of both the water and the terrain. Since we
already have one layer, let's create a second layer. We can do this by
selecting the New Layer icon, and selecting Tile Layer, like in the
following screenshot:
Now, we should have two layers, like the following screenshot. It is
important to remember that the order of each layer plays a role in terms of
rendering, which will also be the reason that the bottom layer Tile Layer
1 will be the layer containing the water tiles:
We need to rename them so that one is Terrain and one is Water. To do this:
As you can see, there are also two other features in the Layers panel:
Now, it's time to create our level. It is important to remember that when
you drag any Terrain tiles, they are put onto the Terrain layer, and any
Water tiles onto the Water layer. This will be important for later when we
need to import it into Unity. The reason why it is important is that each
layer has its own properties, which we can modify. That way, when we
will use Tiled2Unity to import our maps into Unity, each layer's properties
will then be applied to the game objects that will be automatically created.
Essentially, this will save us a lot of time later from having to change each
thing individually inside Unity. With this in mind, let's build our level. For
our game, we will have a level that looks like the following screenshot.
Feel free, though, to create something of your own as well:
Mini-map
A useful feature of Tiled to remember is the Mini-map. When your levels
begin to get larger, it can be difficult to navigate around them. If you click
on the Mini-map panel, you can pan around your map to navigate to other
areas quickly. You can also do this by holding down the Spacebar + right-
click to pan around the environment:
1. unity:SortingLayerName
2. unity:SortingOrder
3. unity:isTrigger
4. unity:layer
5. unity:tagged
Lastly, in the Tile Collision Editor, select the rectangle tool (as highlighted
in the following screenshot), and make sure that you draw a rectangle so
that it is the same size as the tile, like so:
You will have to repeat the above steps for each tile that has been used
within the map that the player will collide with. In our example, this isn't
an issue because we will need to add colliders onto everything. But for
Tilesets which have tiles that contain decorative assets such as various
plants or other environmental adorations, you won’t need to add a collider.
Once you have finished adding colliders to all of the tiles, save it and open
Unity.
old, they cover quite a few things about Tiled that are still
relevant to the current builds.
Importing tilesets into Unity
with Tiled2Unity
Now, this is where Tiled2Unity comes in handy. Once you have created
your Tilemap in Tiled, we need to bring it into Unity so that the player will
be able to interact with it.
8. If necessary, click the Preview Map to make sure that you have the
right Tilemap. You can see an example of this in the following
screenshot:
9. Lastly, click the Export button to export the Tilemap to Unity.
And voila. We are now finished with Tiled2Unity, so it's time to head back
into Unity to continue making our game, which we will begin in Chapter 9,
Look, It Moves.
Post-Tiled2Unity
Now, if we head back to Unity, you will see a new subfolder within Assets
called Tiled2Unity, as shown in the following screenshot:
Within this folder, there is our map, which we created within Tiled. You
can find it by navigating to Tiled2Unity | Prefabs | RETROformer, as demonstrated
in the following screenshot:
Here, you can also see the two layers that we created in Tiled: Water and
Terrain as separate layers also within the prefab. Next, drag the RETROformer
prefab into the scene. You will notice that it is quite large, but we can
modify it easily within Unity.
Now, the level is ready for us to make it usable. For now, we'll finish here,
and in Chapter 9, Look, It Moves, we'll begin to create some animations.
Summary
In this chapter, we have covered how to create tiles using the third-party
program Tiled. We have learned how to set up different layers (such as
Terrain and Water), so to better distinguish the different parts of the map
once we import everything back into Unity. We used another tool named
Tiled2Unity to export the map created in Tiled into Unity and learned how to
set the environment up. We will continue with the environment in Chapter 1
0, Let's Get Physical. But for now, it's time to jump into Chapter 9, Look, It
Moves, in which we will learn more about the Animation State machine,
and we will build a more complex one.
Look, It Moves
Look, it moves! Well, not quite yet, but it soon will!
With this in mind, this chapter will cover the following topics:
When it comes to the animation system in Unity, we can talk about State
Machines. Each animation is a node, and the character can change
animation according to certain variables of the State Machine. The
character can stay only on one state per time and can change animation
only to another node that it is linked to it by transitions (there are a few
exceptions to this rule when you use nested animation machines, but we
won't see them in this book). A transition is represented as an arrow that
goes from one node into another. It contains different properties on how to
perform the transitions, but more importantly, when to execute the
transitions and change state in the animation machine.
In order to explain the process, let's take, for instance, the example in Chapt
er 5, Freeze! Creating an Intergalactic Shooter (which is similar to the
state machine built in the book Getting Started with Unity 5.x 2D Game
Development: https://www.packtpub.com/game-development/getting-started-unity-5x-
2d-game-development). Despite the title, it's a book worth having a look at. In
Now that you should have a better understanding how state and transitions
work, it’s time to try on our own by building the animation for our
character for this last project, which will be a… Panda!
Setting up the sprite sheet for
the animations
If you recall what we did for the explosion, we had an animation sprite
sheet with a single animation. Here instead, if you take
panda_animation_spritesheet.png within our asset package, we have more than
one animation:
The image above is the Sprite Sheet that we will be using for our Panda animations. As you can
see, there are many different types of animations for various situations.
However, we need to set the Texture Type to Sprite (2D and UI), as well as
set the Sprite Mode to Multiple, as shown here:
Then, open the Sprite Editor. In the Slice menu, select Type as Grid by
Cell Count (since the sprite sheet has every animation frame in a specific
cell). In particular, slice it into 6 columns by 17 rows. An example of this
is shown in the following screenshot:
The following screenshot shows the end result. Here, you will faintly see a
bounding box around each frame of the panda's animation:
An example of the Panda animation Sprite Sheet after it has been sliced.
Now we have all the animations frames done, it's time to transform them
into single animations.
Creating the animations from
the sprite sheet
As you can see from the sprite sheet, we don't need all the frames;
however, we need the following animations:
To create the preceding animations, we just need to drag the correct frames
into the scene (ignore the size for now), and Unity will ask you where to
save the single animations. Therefore, let's create a folder within our
Character folder and call it Animations, in which we will save all the
animations there.
For the single animations, here are the frames for each of them:
Idle animation: Select and drag from frame 53 to frame 58; name
the animation Idle.
Walk animation: Select and drag from frame 0 to frame 10; name
the animation Walk.
Jump Air: This is a bit more tricky. In fact, the animation is made
of a single frame (frame 48), and if we drag it into the Scene view,
Unity will just place a sprite, without creating any animation.
Thus, open the Animation window by navigating to
Window/Animation. Then use the drop-down menu to select
Create New Clip, as shown in the following screenshot. Then, just
drag the single frame (frame 48) into the Animation window, and
the trick is done:
First of all, it's not fine that from the Entry node we go directly into the
Walk node. In fact, we need to start with the Idle node. Hence, if Idle is
not highlighted, then right-click on it and select Set as Layer Default State,
as shown in the following screenshot:
As a result, the Idle animation should be highlighted, and an arrow should
go from the Entry node to the Idle one:
Next, from the Idle node, we can go both to the Jump_Start (because the
character can jump when the player is not moving), or to the Walk node
(because the character can start moving). As such, with the right-click and
then Make Transition, create these two arrows. So far, you should have
something similar to this:
Now, it's time to connect the other nodes. From the Walk node, we can go
back to Idle, in case we stop moving, or jumping, and thus go to
Jump_Start. Make these other two transitions, and you will end up with
the following:
From the Jump_Start node, we cannot go back to Idle or Walk because we
need to finish the Jump loop, so we need to go first to Jump_Air and then
from there to Jump_Landing. This is the result so far:
Finally, from the Jump_Landing, we need to go back to Walk or Idle
(depending on whether the character is moving or not), and thus create
two arrows towards them. This is the final result:
However, we haven't finished yet. In fact, we didn't establish any rule on
how to fire the transitions. To do so, we need parameters. Go to the
Parameter tab in the upper-left corner and create a couple of parameters.
One parameter is of type Float and we will name it Speed; the other one is a
Boolean and we will name it Jump. The idea is to have the Speed stored in the
float parameter, to determine whether the character is moving or not, and
in the Jump Boolean to determine whether the character is performing a
jump or not.
Walk - > Idle: Triggers if the Speed parameter goes below 0.01.
That was quite a lot of work, but we have finished with our state machine.
The next step is to build a controller that is able to take care of this state
machine, by setting the Speed and Jump parameters. But for now, take a
rest and read the summary of this chapter, before we have fun in the next
one.
Summary
Overall, this chapter has explained how to set up our third and final project
—RETROformer. It's continued by explaining how to import all the assets
for the project, as well as how to create some animations. Lastly, this
chapter has explained how to implement a more advanced player
controller that lets the character navigate through the environment in a
more dynamic way.
Let’s Get Physical
In this chapter, you continue to work on the third and final game in the
book—RETROformer. You will learn all about 2D physics in Unity, and
how it can be applied to the platform game. Here, we will learn this by
applying physics to actions such as jumping and moving through forces. In
particular, we will implement a Movement Component (which will take
care of controlling the Animation State Machine of the previous chapter,
as well as moving the character using physics) along with a Player
Controller (which will send input based on the keyboard to the Movement
Component). Keep in mind that if it is difficult to understand now, it will
be much easier by the end of the chapter.
Physics Material 2D
Don't worry if you didn't do too well in physics in school. This isn't a test,
but rather an explanation about the physics behind the gameplay in a
platformer. It is important to know these things for many reasons. For
example, when you're trying to increase the difficulty within a level, it is
important to remember and predict how certain elements within the game
will behave. Parts of a game, such as an arc that a player jumps, gravity
(or lack of), the velocity that a player can run, the amount of friction
between the player and the ground beneath them, and so forth, are
essential considerations that all have to do with physics.
For this section, we are just going to consider Physics Materials 2D, which
is the basic way to set some physics properties to a material.
You can create a Physics2D material with a right-click on the Project panel
and then Create | Physics Material 2D. If you double-click on it, you can
see its properties in the Inspector. When it comes to Physics2D material in
Unity, there are only two parameters to take into consideration:
Friction: Indicates the amount of friction; the less the friction, the
more slippery the material. Try to create a new material with zero
friction, then apply it to the terrain; you will end up with
something very similar to ice.
As far as the game we are creating goes, you can try to experiment with
different materials for the different surfaces of your game. However, keep
in mind that whether you're trying to outrun an enemy or beat the
countdown to a massive explosion, sliding allows you to neatly glide
across a large amount of space faster than you could run it. As a result,
you can use this game mechanic if you wish.
Since our character will collide with the ground, we need to properly set it
up.
First of all, as exported from Tiled2Unity, the Terrain comes already with
a collider, which is great. However, we need to set a new layer for it, so
when we do some checks to allow our character to jump, our system will
go flawlessly.
Navigate to Edit | Project Settings | Tags and Layers. This is the screen
that will open in the Inspector:
Open the Layers menu, and add a new one called Ground, as shown in this
next screenshot:
Also, later we will need a tag for implementing Jump Pads. Since we are
already in the right menu, open the Tag menu, and add a JumpPad tag, as
shown in the following screenshot:
Finally, select our Terrain in the Hierarchy panel, and apply the
Ground layer on it, like in the following screenshot:
If it asks you to apply the change to all the children (like in the following
screenshot), then click Yes, change children:
Now, our terrain is good to go, and it's time bring our hero to life in the
next section.
Building the panda hero
In this section, we will go through the step to create our hero: an
unstoppable panda! We will build it, keeping in mind how the Player
Controller will act on the panda. If you have followed the steps in Chapter
9, Look, It Moves, you will already have a panda in the scene (which we
can now rename Panda Hero), and the game object should already have,
besides the Transform, a Sprite Renderer as well as an Animator
component in which the Controller is the Panda_Animator_Finished:
First of all, the character is definitely out of proportion (and possibly also
rendered behind the terrain, which is not good). Unless we want to make a
variant of Godzilla or King Kong that features a panda, we'd best change
this:
In order to fix the problem that the character is rendered behind the terrain,
we need to change the sorting layer in its Sprite Renderer. We can, for
instance, set the Sorting Layer to 1, and the panda will be rendered on top:
And this is how it appears in the Scene view now:
However, the character is still out of proportion. Thus, we need to scale it
down uniformly, to fit the environment. You can either use the Scale in
Transform or select the Rect Tool and while holding Shift, drag one of the
corners to scale the character uniformly. The dimensions of our character
should be reasonable with respect to the environment:
Click on any of that long bar. As a result, you will be able to see where the
object is placed within the Scene view:
Use the Moving Tool to bring it beneath the character, as shown in the
following screenshot:
And with this, we have finished creating our character, and it's time to
implement the movement of this panda.
Moving the panda
The controls in RETROformer are quite straightforward. We have just
three actions:
Move left
Move right
Jump
However, we need to sync these actions with the animation state machine
created in the previous section. Moreover, in Chapter 9, Look, It Moves, we
extended this controller so that it can be used by mobile devices as well.
As a result, we need to build the controller in a modular way—we need a
control scheme.
The control scheme
It's important to define a control scheme that allows us to control the
character in different ways. Suppose that you want to ship the game both
for (desktop) computers and mobile devices. One has a keyboard, the
other one has a touchscreen. As a result, you need to write twice how the
character should move based on the input. Now, imagine that you add also
AI enemies; they need a script that not only makes them think but also
move. However, all this repetition of the code can be avoided with a
simple control scheme. We abstract the movement in a separate class,
which doesn't care how the input arrives, but once it arrives, this class will
make the character move. We can call this Movement Component, which
we are going to implement in this section.
Let's start by adding three private variables, one to store the Animator
component, another one for the Rigidbody2D, and the last one for the
SpriteRenderer:
We can retrieve these three inside the Awake() function, by calling the
GetComponent() function:
void Awake () {
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
}
Since we want to avoid the case in which one of these three components is
not found, we can force adding those every time we attach this script to a
game object. We can achieve this using the RequireComponent before the class
declaration, one for each of the components we want to enforce (for more
information regarding this, you can check the official documentation here:
https://docs.unity3d.com/ScriptReference/RequireComponent.html):
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(SpriteRenderer))]
public class MovementComponent : MonoBehaviour {
//[...]
}
Next, we still need some variables that define the settings of the
MovementComponent. In particular, we need three variables. The first one is
moveForce, which indicates with how much force the character is pushed
when the player moves the character. The second is maxSpeed, which is the
maximum speed along the x-axis at which the character can go, any force
on the character that breaks this limit is ignored. Finally, the last one is
jumpForce, which indicates the force applied to the character when the player
jumps. Since all of them should be private (in the sense that they cannot be
modified by external scripts) but at the same time tweakable from the
Inspector, we need to insert the SerializableField attribute, as shown in the
following code:
[SerializeField]
private float moveForce = 360f;
[SerializeField]
private float maxSpeed = 5f;
[SerializeField]
private float jumpForce = 1000f;
[SerializeField]
private Transform groundCheck;
Also, we need to check in the Awake() function that the groundCheck variable
has been correctly initialized, otherwise we launch a warning into the
console and remove the MovementComponent from the character:
void Awake () {
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
Now, we are ready to implement the two main functions to move our
character.
After having applied the force, the velocity is clamped to the maximum
that the character can go, but only on the x-axis, while leaving invariant
the y-axis:
the following code, which does exactly the same, but the comments guide
you step by step:
// Calculate the layer mask value that can be used in the following
// Physics2D.Linecast() method call. We use a bitwise left shift
// operation to find the correct value which is 256 because we
// use the 8th User Layer in our example: 1 << 8 = 256.
int groundCheckLayerMask = 1 << layerMaskIndex;
This is enough for the logic; however, we still need to animate the
character. Thus, let's fill the Update() function so we can have control over
the animation frame after frame. In particular, we need to set the Speed
parameter of the animation state machine as the velocity along the x-axis.
Likewise, we can use the velocity along the y-axis to set whether the
character is grounded or not in the state machine, by setting the Jump
parameter:
void Update () {
//Set the Speed parameter in the Animation State Machine
anim.SetFloat("Speed", Mathf.Abs(rb2d.velocity.x));
//Set the Jump parameter in the Animation State Machine
anim.SetBool("Jump", rb2d.velocity.y != 0);
}
Finally, depending on the sign of the velocity along the x-axis, we can flip
the character through the Sprite Renderer to make the character face the
correct direction:
void Update () {
//Set the Speed parameter in the Animation State Machine
anim.SetFloat("Speed", Mathf.Abs(rb2d.velocity.x));
//Set the Jump parameter in the Animation State Machine
anim.SetBool("Jump", rb2d.velocity.y != 0);
//Check in which direction the sprite should face and flip
accordingly
if (rb2d.velocity.x != 0)
spriteRenderer.flipX = rb2d.velocity.x < 0;
}
Besides tweaking the values for your game, the important setting is the
groundCheck transform variable, which needs to be properly initialized. As
such, take the Ground Check (which is attached to the Panda Hero), and
drag it into the groundCheck variable. As a result, the component should
appear in the Inspector as this:
Player Controller
In the previous section, we implemented a script to move the character.
However, if we hit Play, the character won't move, aside from idling. The
reason lies in the fact that we have two functions to move the character,
but no script calls them. If you go back to the Control Scheme section, you
see that we need a Player Controller (or a UI interface in case of mobile
games, but we will explore that later in Chapter 12, The Nature of Export),
which sends the input to the Player. In this section, we are going to
implement that script.
Now, it's time to think what the script should do. First of all, we need to
retrieve the MovementComponent attached to the character. To do so, let's create
a new variable of the type MovementComponent:
MovementComponent movementComponent;
Then, in order to initialize the variable, we can set its value in the Awake()
function. We can achieve that by using GetComponent() to retrieve the
MovementComponent. Furthermore, we need to launch a warning in case the
is missing, and remove this very script from the Panda
MovementComponent
Hero:
void Awake () {
//Retrieve reference to the Movement Component
movementComponent = GetComponent<MovementComponent>();
//Disable this script in case the Movement Component is not found,
and leave an error message.
if(movementComponent == null) {
Debug.LogError("Missing MovementComponent on "
+ gameObject.name + " to run PlayerController.
Please add one.");
Destroy(this);
}
}
Next, we need to send the input to the MovementComponent. If you recall, the
character needs to move left and right as well as jump. For the first two,
we can just gather the value of the Horizontal axis value and use it as a
normalized speed for the MoveCharacter() function. As a result, we will be
able to use the arrows on our keyboard to move the character. As for the
jump, we can use the up arrow to make the character jump. This is great,
but we need to keep in mind that the MovementComponent uses Rigidbody2D.
Hence, as we have seen for the previous projects, we need to use the
LateUpdate() function. Here is how the code should be:
void FixedUpdate() {
//Send input to the Movement Component in order to move the
character
movementComponent.MoveCharacter(Input.GetAxis("Horizontal"));
//Check if the Up Arrow has been pressed, and if so,
send the Jump input to the Movement Component
if (Input.GetKeyDown(KeyCode.UpArrow)) {
movementComponent.Jump();
}
}
Save the script and attach it to the Panda Hero. There are no settings to set
here, so we are good to go.
Summary
We started this chapter by learning how Physics Materials 2D works.
From here, we added a new layer for properly identifying our ground, and
we added a tag for Jump pads. Then, we moved on to building our Panda
Hero by setting up all the components required. Next, we moved to
exploring how we can create a Movement Component to allow our hero to
move, and we designed it in such a way that it is modular. This means that
how the input is received to the Movement Component is not important
for the movement itself. As such, we have implemented a Player
Controller to send input with a keyboard to the Movement Component.
In Chapter 11, Don't Forget to Save!, we will refine even further the
Movement Component to implement jump pads, as well as exploring how
we can save game data. In addition, you will learn how to script gameplay
for this last game, and you will be provided with hints on how to enhance
the overall quality of the game. With that said, turn the page!
Don’t Forget to Save!
In this chapter, we continue building the game by giving suggestions about
how to script gameplay. In addition, you will learn how to create and
implement a basic save and load system into your game. Before
concluding the chapter, we will extend the Movement Component to
include Jump pads, to add a bit more pizzazz to your game. Lastly, this
chapter covers some general topics for testing platformer games, which
includes what to look for to ensure that everything is running correctly.
Jump pads
There are different reasons, and consequently, ways with which you can
implement a save option for players. For example, your game may allow a
player to save any time and anywhere. Games like Tomb Raider II and
Abe's Oddysee offer such approaches to saving. Typically, this option is
provided via the pause menu, and the player is allocated a set number of
slots that they can use to save. Alternatively, you may also have
checkpoints that automatically save at specific locations, such as before
and/or after an important event during gameplay. Games like Army of Two
offer this to players. Other options can include specific locations or save
points that a player can save, which are like checkpoints, located at
specific points that a player will reach within the environment during
gameplay. Games like Final Fantasy X offer this, as shown in the
following screenshot:
Save Orb is located on the right-hand side of the screenshot taken during gameplay of Final Fantasy
X
Creating a save/load system in
Unity
There are different ways to save and load your game in Unity. For
instance, you can encode your data into a file, and this gives you
maximum freedom. In fact, you can allow players to save on your server,
encrypt the file, and decide exactly how this file is structured. However,
for beginners, this might not be the easiest approach, since Unity already
offers a basic save system called PlayerPrefs. This system is great
because it supports all the platforms, meaning you won't have to change
code depending on which platform (Computer, Android, and more) you
are going to ship in. Also, the system is very intuitive. With this said, let's
dive into it.
PlayerPrefs
PlayerPrefs, as the name suggests, has been created with the purpose to
save and store the player's preferences. In fact, this data is not sensitive,
meaning that if the player can change those externally from the game, it’s
not a big deal. As a result, PlayerPrefs is very simple, and it works with a
key-value system. However, often it is used to store game data as well, due
to its simplicity and the built-in support in Unity. Although you can extend
their functionalities with some plugins that you find in the Asset Store (for
example, in order to encrypt the data), I suggest using a custom save/load
system if you are planning to ship the game.
How do they work in practice? You can imagine that each PlayerPrefs is a
pair containing a key, which has to be unique across all the PlayerPrefs,
and a value. While the key is always a string, the value can be of different
types. In particular, the basic types that Unity supports are:
Integers (int)
Strings (string)
However, these types can be extended with wrappers around these basic
types. For instance, a Vector3 can be stored as three PlayerPrefs, one for
each axis. The wrapper then creates three keys by appending to the passed
key a suffix indicating the axis. In any case, we will stick to these basic
types, which are more than enough for what we need. Here are some
examples of possible key-value pairs, and in brackets, the type of the
value:
(
<Score, 10> int )
(
<Time, 12,5> float )
(
<PlayerName, "John"> string )
PlayerPrefs functions
From a code point of view, there are functions that allow us to interact
with the system. They are all within the PlayerPrefs class, and they are static
functions, so you don't need to instantiate a PlayerPref object. Rather, in
your code, type PlayerPrefs followed by the name of the function you
want to call: PlayerPrefs.NameOfTheFunction(). Obviously, the previous function
doesn’t exist, but it was an example of a function call.
Since these functions are not so many, let's see them all. The following
three functions (one for each value type) set the pair <key, value>:
Similar to the preceding three functions, we have the three twin functions
that instead of storing, retrieve the values based on the key. Keep in mind
that if the key doesn't exist, they return the default value of the type:
Since we have said that the value might not exist, we have a function to
check whether a specific key has an association within a pair:
: Erases all the <key, value> pairs, thus erasing all the
void DeleteAll()
saved data
There is also a special function that forces the saving of memory. In fact,
when we use a set, the save happens only within the context of the
application/game, thus improving performance. Although when you close
the game, Unity flushes everything in memory and thus saves. However,
sometimes you want to force this process to happen before the game is
closed (maybe after have saved important data, for example). This can be
done with the following function:
memory
Variables to save in
RETROformer
When you think about a save/load system, you want to save the variables
that describe the player's progress or the game world state. In general, this
might not be an easy step.
In the case of RETROformer, let's list what we would like to store to save
the game. First of all, we may want to store the level (in case we have
implemented more than one level), as well as the player's location in the
world. Moreover, we want to save the score, and the time elapsed since the
game started. This last piece of information is useful for players to
understand how long they have been playing the game. Here is an example
of saved games that contain the time elapsed (taken from Horizon Zero
Dawn for PS4):
So, let's recap the variables we want to store for our game, and associate to
each one of them a type:
Finally, we need to assign a unique key to each of these values, and then
we will be ready to go. For the association, let's have a look to the
following table:
Value
What Key
Type
PlayerN
The player's name String
ame
Building the save/load system
for our game
In order to integrate the save/load system, we need to create a static class
that will take care of saving and loading for us. Here, we need to take an
important decision: whether this class will retrieve the information that it
requires to save (such as level, score, and so on), or whether we pass them
as parameters and rely on other scripts to do that.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class SaveLoadSystem {
//[...]
}
public struct SavingData {
public int level;
public float positionX, positionY;
public int score;
public float timeElapsed;
public string playerName;
}
A struct is not much different than a class in C#, and this struct doesn't
have any methods, just the data fields we have seen in the previous table.
On the other hand, we have the Load() function, which gives back a
SavingData structure and takes as input the save slot key. As a result, the
function is able to query the PlayerPrefs with the get-functions and store all
the data within a newly created SavingData structure. At the end, it just
returns the structure:
The first one checks whether a save slot is available in the memory or not.
Although you can implement this functionality within the load function
(for example, returning null), for the sake of simplicity, we will create a
separate function that returns a Boolean indicating whether the slot is
available or not. This is useful when you need to show to the player which
slots are empty or which ones are taken. The function is really simple, it
just needs to check whether the key passed as a parameter exists. We can
do this by checking only one of the data, which we have prefixed with the
key.. In fact, if we do things right, we should have incomplete saving slots
in which just partial information is available. Thus, here is the function:
Another useful function deletes a specific save slot. This can be done by
taking in input the key and erasing all the items related to that slot. Here,
you need to be sure to erase them all, especially if you don't want to have a
problem with the function HasSlot():
Finally, a function that clears all the slots might be useful. In this case, the
only things you save with PlayerPrefs are the slots. Therefore, we can
easily use DeleteAll(), as we are about to do. Otherwise, you need a loop
that calls the DeleteSlot() function on each one of the slots. In our case, the
function is just a wrapper for the DeleteAll() function:
Save the script and congratulate yourself, since now you have a working
save and load system.
Jump pads
Up, up, and away with jump pads. These beautiful things give you that
extra lift in precarious situations or when you can’t quite reach that ledge
with the golden key. But how can we implement them?
If you are trying to simulate a material that has a lot of bounciness, such as
the trampolines that you went on when you were a kid, then just create a
material with a lot of bounciness. However, often in games, jump pads are
more platforms in which if you jump from them, your jumping boost is
enhanced. So, let's implement the possibility to place Jump Pads in our
level.
Creating the Jump Pad
First of all, we need to create the Jump Pad. Our Graphical package comes
with a very nice jump pad sprite:
First, drag one of them into the map. We need to add a collider in order to
prevent the player from going on the Jump Pad. Then, create a child
gameobject and name it Jump Pad Trigger Collider. Next, set as a Tag for
this second game object Jump Pad. Add a collider as well, and adjust the
Size and Offset in such a way that the collider is just above the Jump Pad.
Also, check the isTrigger variable, as shown in the following screenshot:
This is how it should look in the game:
If you remember from the Angel Cakes collecting system, when the player
comes in contact with a cake, it is collected. This was done using a special
Unity function named OnTriggerEnter2D, which detects when the player
entered the collider of the cake. Here, we are going to do something
similar.
Open the Movement Component script, and let's add a variable called
onJumpPad of type bool:
[SerializeField]
private float jumpPadMultiplier = 2;
Finally, in the Jump() function, we need to modify the code. If the onJumpPad
variable is true, then the JumpForce is multiplied by the JumpPadMultiplier
variable. As a result, if onJumpPad is true, then the force of the jump will be
much more:
Of course, the function will look a bit different if in the previous chapter
you used the alternative version. In any case, it's not a problem, since you
just need to substitute the highlighted code from the function you wrote.
However, the way that we have written the modified AddForce() function
above might still be confusing, due to the ternary operator ?. Again, below
is shown a simplified version, which does not use the ternary operator.
Feel free to choose whatever version makes more sense to you:
Save the script, and create a prefab of the Jump Pad you have just created.
Place them in your level, and test that everything works as it should.
Lastly, pat yourself on the back for your hard work.
Wrapping up gameplay
To efficiently complete our game, we still need to insert a couple of
gameplay components. This section will quickly go through them, to guide
you. In fact, you have already seen most of this stuff in previous chapters
and with the previous projects, so for an in-depth explanation, you can
revise your work on the other projects.
Creating the user interface
We have seen a lot of user interface in the past two projects. Thus, I won't
bother repeating how to create a score or a lives counter along with their
respective scripts. Rather, this is left as an exercise for you. In particular,
you can challenge yourself with the following:
Game Over Screen: When the player falls in the water, a screen
message should appear to the player saying that it's game over.
Once you have created this screen, you can integrate it with the
WaterZone script of the next section.
Leaderboard: If you are in the mood for a challenge, why not try
implementing a leaderboard? Order the different players that you
have for the slots in a leaderboard, showing first who has the
highest score.
Keep in mind that we're not yet done completely with the user interface. If
you recall from Chapter 10, Let's Get Physical, the Movement Component
can take input also from UI controllers, to adapt the game to run on mobile
devices. This is something that we are going to explore and learn in the
next chapter.
First of all, we need to adjust the water Sorting Layer, so that it appears
preceding the player. So, select the child of Water from the Hierarchy
panel, and change the Sorting Layer to 2 in the Sorting Layer Exposed
(Script) component, shown as follows:
Also, Unity comes with a Water layer as default, so we can just set it as
Water for good practice, and so change all its children as well:
Now, we need to create a script to restart the level once the player falls in
the water (in our case, touches the water).
Let's start by creating a new C# script and naming it WaterZone. Let's add the
requirement for a collider at the beginning of the class:
[RequireComponent(typeof(Collider2D))]
public class WaterZone : MonoBehaviour {
//[...]
}
using UnityEngine.SceneManagement;
[RequireComponent(typeof(Collider2D))]
public class WaterZone : MonoBehaviour {
//[...]
}
[SerializeField]
private float timeDelay = 1.0f;
Then, implement the coroutine. The first thing shown is a Game Over
message (which is left as an exercise), then the script waits for
timeDelay before restarting the scene:
If you don't want to hardcode the name of your scene into the script, you
can reload the same scene which is currently active in the SceneManager. Here
is the code you need:
Save the script and add it to the Water game object. From now on, every
time the player touches the water, the game will restart after a small delay.
Winning zone
Similar to what we did for the Water zone, we need to create an endpoint
for our map. Once the player reaches the endpoint, the game will show a
message (left as an exercise) and load the next level. Since the structure is
so similar to the Water zone, here is the direct script, which should be self-
explanatory:
using UnityEngine;
using UnityEngine.SceneManagement;
[RequireComponent(typeof(Collider2D))]
public class WinningZone : MonoBehaviour {
[SerializeField]
private float timeDelay = 1.0f;
[SerializeField]
private string nextLevelToLoad = "level2";
//substitute with the next level to load.
Be sure it is included in the build settings.
private IEnumerator loadNextLevel() {
//Show a winning message (left as exercise)
//Wait timeDelay
yield return new WaitForSeconds(timeDelay);
//Restart Scene
SceneManager.LoadScene(nextLevelToLoad);
//load the level named as in the nextLevelToLoad variable.
}
private void OnTriggerEnter2D(Collider2D collision) {
//If the player enters within the Water Zone,
then restart the scene
if (collision.CompareTag("Player")) {
StartCoroutine(loadNextLevel());
}
}
}
Enhancing the environment
The environment that you have created in Tiled is great, but at the same
time, it is a bit empty. Have a look at this portion of the environment now:
Thus, we need to bring decorative props into it! Thankfully, our package
comes with great props, such as trees.
If you didn't, slice them with the Sprite Editor, to have all of them on
different sprites. Then, drag them into the world and fill it as per your
taste. Just remember to set the right Sorting Layers for it. In my case, I
would go for a Sorting Layer of 0 or -1 to be aligned with the terrain or
behind it so the character will pass over it. In case you want to create an
obstacle or a collectable, you can place it to a higher Sorting Layer, such
as 2 or 3. However, you need to remember to add a collider for it (and set
isTrigger to true in the case of collectable, along with an appropriate
script).
Next, in Chapter 12, The Nature Of Export, we will learn how to export our
game to various platforms (PC, iOS, Android). In addition, we will discuss
how the input for the Movement Component can come from the UI so that
touchscreen input can be used to move the character as well.
The Nature of Export
In this final chapter, we will learn about the final steps of producing a
game. First of all, we will finish implementing our control scheme so that
the game can also be played on mobile devices with touchscreens. We will
use the Unity UI to achieve that.
Then, we will look at the very basics of the steps required to export the
game. This will be enough to see your game running independently from
Unity, either as a Standalone or on an Android device. However, building
the game involves many other steps (optimizing the game, the content, the
rendering, and so on) that are appropriate for more advanced users of
Unity, and thus outside the scope of this book.
using UnityEngine;
using UnityEngine.EventSystems;
public class UI_MovingController : MonoBehaviour {
//[...]
}
[SerializeField]
private MovementComponent movementComponent;
[SerializeField]
private float direction = +1f;
Also, we need a third variable to store whether the button is held down,
which needs to be private:
void Awake() {
//Destroy this script in case we are not running on mobile
#if !(UNITY_STANDALONE || UNITY_EDITOR)
Destroy(gameObject);
#endif
}
In the Awake() function, we can also check that the reference to the
Movement Component is properly set:
void Awake() {
//Destroy this script in case we are not running on mobile
#if !(UNITY_STANDALONE || UNITY_EDITOR)
Destroy(gameObject);
#endif
Next, we can implement our interfaces. In the first one, with the
OnPointerDown() function, we just need to set isHolding to true:
public void OnPointerDown(PointerEventData eventData) {
isHolding = true;
}
Similarly, for the second interface with the function OnPointerUp(), we set
isHolding to false:
EventSystems:
using UnityEngine;
using UnityEngine.EventSystems;
Again, the Movement Component variable is needed, but it's the only
variable we need this time:
[SerializeField]
private MovementComponent movementComponent;
void Awake() {
//Destroy this script in case we are not running on mobile
#if !(UNITY_STANDALONE || UNITY_EDITOR)
Destroy(gameObject);
#endif
}
void Awake() {
//Destroy this script in case we are not running on mobile
#if !(UNITY_STANDALONE || UNITY_EDITOR)
Destroy(gameObject);
#endif
Save the script and attach it to the Jump button. Then, set the reference to
the Movement Component, and we are good to go. This is how it should
look in the Inspector:
Exporting the game
Drum roll! The game is now finished and it's time to export. Unlike the
other chapters, we'll also cover how to export the game to mobile devices
such as iOS and Android. You can use the same approach for the other
games, should you also wish to export them to mobile devices. In addition,
we’ll also cover some best practices for exporting to mobile devices.
However, keep in mind that we didn't rearrange the input to work on a
mobile device. In that case, it's better if you refer to a specific book about
mobile game development.
1. Open the Build Settings. You can find this by navigating to the top
menu, File | Build Settings, or via the keyboard shortcut Ctrl +
Shift + B (or command + Shift + B on a Mac).
2. Highlight the PC, Mac & Linux Standalone item from the
Platform list.
3. Add the scenes by dragging them into the Scenes In Build list box.
follow the installation wizard and make sure that you install it in
an ideal location (for example, the programs folder).
2. Next, make sure that you have enabled developer options on your
Android device. The location of this setting may vary depending
on the phone, so if you can't locate it, please consult your phone's
manual. In general, you will be able to locate it here: Settings
| About phone | Build number. In the developer options, you will
also find the Install from Unknown sources option, which allows
you to install your exported game for testing . We will discuss this
later in the chapter.
1. Open the Build Settings. You can find this by navigating to the top
menu: File | Build Settings or via the keyboard shortcut Ctrl +
Shift + B (or command + Shift + B on a Mac).
2. Highlight Android from the list of platforms on the left.
Finally, you will need to add scenes into the build by dragging and
dropping them to the preceding space. In the following screenshot, I'll
insert Level1 and Level2 (which I created):
One last thing before we export the game. We need to decide if this is a
Development Build or not. Since our game has just two levels, and we
want to add more to it before we release it, we can check the Development
Build checkbox. This way, not only will the Development Build appear,
but you will have access to a series of debug tools (outside of the scope of
this book) that were otherwise not available. In fact, once checked, you
will have another three checkboxes for these debug tools:
Now, you are ready to export by pressing the Build button. Unity will ask
you where to export the .apk file to. Once you do this, you can import it to
your phone in any location and install it.
ISBN: 978-1-78839-236-5
ISBN: 978-1-78847-983-7
Use data files to save and restore game data in a way that is
platform-agnostic