TuNguyen 2DGameDevThesis PDF
TuNguyen 2DGameDevThesis PDF
INTRODUCTION TO 2D GAME
DEVELOPMENT WITH UNITY
Bachelor’s thesis
Information Technology
2021
Author (authors) Degree title Time
Thesis title
52 pages
2D Game Development in C# with Unity 4 pages of appendices
Commissioned by
Supervisor
Timo Hynninen
Abstract
The aim of this thesis is to introduce other developers and programmers to game
development with Unity. It requires prior knowledge of C# programming. I introduce them to
the game engine Unity, one of the most popular game engines nowadays, and guide them
through the process of making a 2D game.
The game in the thesis is a 2D-platformer game called “Escape The Cave”. Through the
game, I introduce some of the functions and components that Unity has to offer and help
them get used to the interface of Unity.
At the end of my thesis, readers now can create their own 2D game through what I have
showed them using Unity and Visual Studio.
Keywords
CONTENTS
INTRODUCTION ................................................................................................................. 5
2.1.3 Unity................................................................................................................ 10
5.2 Environment.......................................................................................................... 20
6 CONCLUSION ............................................................................................................ 50
REFERENCES .................................................................................................................. 52
APPENDICES
INTRODUCTION
Gaming is becoming more and more popular over the decades and the game
business is booming. People play games to relax, to be better at competitive
games or simply just to kill time. Therefore, the game industry has become one of
the most sizeable (and interesting) businesses in the world. New games are
introduced to players every day by indie game companies and corporations, and
news about game release dates pop up in every social media and
advertisements.
I will also explain what a game engine is, why we need game engine to create
games… and introduce readers to Unity Game Engine.
1 INTRODUCTION TO GAMES
Alan Turing – the one that inspired modern computer science and artificial
intelligence – was the first person to make a computer game, a computer Chess
program in 1947. It was not his purpose to make it playable for people but to test
out artificial intelligence. It was not until 1952 that he tested the program with a
colleague, and he pretended to be the computer (Tristan Donovan, 2010).
Turing's first game inspired so many mathematicians and computer scientists
back in those days that they decided to continue his work.
Later in 1972, Pong was released into the arcade. It became so popular back
then that the arcade machine was jammed because it had so many arcade coins
in it.
July 1980, a missing slice of pizza inspired Toru Iwatani to create Pac-Man, the
first arcade game hit that people can play at home with their console.
The first generation of computer games was 2-dimensional (2D) since technology
back then was limited for 3-dimensional (3D) simulation. But nowadays, the
growth of technology allows us to make 3D simulations with the right tools. 3D
printing is now possible, 3D simulations allows us to experience things that are
normally out of our reach or too risky to try (learn to drive, learn to fly a plane,
simulate the solar system,…). So, what are 2D and 3D games?
In 2D games, the world inside it is only two-dimensional, which means that it only
consists of 2 axes: X and Y. 2D games use flat graphics, called sprites, and don’t
have three-dimensional geometry. They’re drawn to the screen as flat images,
and the camera (orthographic camera) has no perspective (Unity, 2021). normally
in 2D games, the player can only move left and right, up and down, according to
the X-Y axis since the environment in 2D games is visually flat. Some of the
popular 2D games examples: Super Mario Bros, Pac-Man, Donkey Kong, … The
limitation on movement however does not mean that 2D games are less
enjoyable than 3D games, this depends on the players’ preference. Moreover, for
players who prefer casual gaming and lightweight games, 2D games is the more
suitable choice because 3D games nowadays are often more demanding and
take more space on the hard drive.
8
The reason why I want to write for 2D game development only is because it is the
most basic and easy to learn. After learning to make 2D games, readers have the
knowledge and foundation in making games so that it is an easy transition to 3D
game development should they choose to.
2 GAME ENGINES
Then why the need for other design tools for animation and music? This is
because those tools are made professionally and specifically for those functions.
For example, a music maker tool does not need any animation components and
drawing software does not necessarily need sound design. It would be easier for
professionals to use the tool designed for one’s purpose. Thus, game design
software, or game engine, is made for game development elements.
There are many game engines out on the market, but I will name some of the
most widely known so readers can get the concept of different game engines and
their purposes.
Many triple-A games are developed with Unreal Engine due to its robust
graphical capabilities in lighting, shader, etc. The engine is also open-source, so
it is being improved constantly by its community. It is the most suitable for making
3D and VR games thanks to its graphic options. Because of its heavy graphical
components, the engine requires a more powerful computer to run compared to
Unity. Also, many developers declare that Unreal Engine is more suitable for
larger projects in which you intend to work as a team.
Popular games: Ark: Survival Evolved, Final Fantasy VII Remake, Gears 5…
10
2.1.2 Godot
The Godot game engine is rather new, it became available in 2014. Only until
recently it became popular. It is free and open-source, very suitable for beginner
developers. It is suitable for 2D and 3D game development but is lightweight. In
comparison with Unity or Unreal Engine, the quality of product is not as good. It
does not have the graphical power of Unreal Engine, nor the support community
of Unity, but it is easy to learn to use if you want to start your game development
journey. In terms of platforms, the majority of games made with Godot are mobile
games.
Popular games: Sigil Breakers, Kip, The Kingdom of Avalae…
2.1.3 Unity
Unity has been a popular choice for indie game developers around the world
since 2005. It is suitable not only for 2D and 3D game development but also great
for virtual-reality (VR) and augmented-reality (AR) design. The engine is updated
every year with new contents and thanks to a sizeable community, it has an asset
store with a huge amount of free and pay-to-use assets. Unity is free so everyone
can start using and learning how to make games easily.
11
3 PROGRAMMING LANGUAGE C#
C# is easy to learn, relatively easy to read, but because of its flexibility, it is hard
to master all that C# has to offer. C# is a complex language, mastering it may
take more time than a simpler language such as Python or HTML, users need to
learn a considerable amount of code to create an advanced program.
But it is worth the time users put in to learn it. C# is an in-demand skill that many
tech-savvy companies are looking for. In the job market, there are applications
every day and everywhere in need of developers with good C# knowledge and
experience. Therefore, people who learn C# have great job opportunities.
I chose C# because of its flexibility. Readers can learn C# not only to make
games, but also to have better opportunity in their journey to become a developer
of any aspects of the IT world.
12
Inside the example “Player” class, we have methods “Start” and “Update”. These
methods already have their defined functions described above them in the green
comment lines. We can create new methods and give them names fitted for their
functions (PlayerMove, PlayerDie, PlayerIdle…). Names for methods should not
have blank space in between. Inside the method, we start with open curly bracket
“{” and stop with close curly bracket “}”.
The three lines of “using” is for namespace. It is a way for us to save time on
writing the namespace every time we need to use a method within that
namespace. For instance, normally we have to write
System.Console.Writeline(“Hello World!); to print the phrase “Hello World!”, but
13
These are the absolute basics of C# use in Unity. To fully comprehend C# would
take more time and words than I can write in my thesis. Therefore, readers can
refer to the link in reference section for a more comprehensive understanding of
C# in Unity. I will go deeper into C# later in chapter 5 where we make our first
game.
4 UNITY ENGINE
Unity is a powerful tool for game programming. It has an asset store where we
can get game-ready assets and use it for our game. Unity is good for both
working alone and in a team. In this chapter, we go in depth with Unity Engine
and introduce some of its features.
Unity user interface can be daunting for beginners, it might take some time to get
used to because of its complexity. In this part, we walk through the layout of the
14
interface. We can customize the UI windows layout in any way we see fit for our
project. I introduce the default layout because it is the layout we see when
creating a new project.
On the top left corner, we can see the Hierarchy window. It displays every game
object we have in a scene. Whatever we add or remove in a scene, we also add
or remove in the hierarchy window.
Below the Hierarchy, we have the Project window. It not only shows all the files of
the project we are working on, it is also the main way to navigate and create new
files for our project.
Next to the Project window, we have Console window. This window is one of the
most important ones we should have on our screen, it shows the warnings, error
and other messages related to our project. These messages are vital for our
game to work without any problems. It is also the quickest way to find bugs in our
project.
On the right side of the UI, we can see the Inspector window. This window shows
the components within our currently selected game object. We can also add more
or remove components, customize the components as we see fit for our project.
In the middle of the UI, we can see the Scene window. This is the interactive view
of the world we are creating within our scene. We can interact with components
within our scene, here namely scenery, characters, platforms, environments, and
other types of game objects.
Next to Scene window, we have Game window. The game window visualizes
what the player sees when playing the game. We mostly cannot interact with
anything within the game object because its only function is to visualize the
game.
17
In this part, we go through some basic features we can do with Unity. I only
introduce the features that we use regularly in the next chapter. There are also
many ways to do each of the steps I introduce here, so readers can explore the
UI and find their own preferences.
To create an empty GameObject, simply right click on the Hierarchy and choose
“Create Empty”. As mentioned before, an empty GameObject does not have any
function yet, we must add components to it. We can see from the picture that
there are also options to create 3D Object, 2D Object, UI objects, … These are
also game objects, but it already has components added to themselves to
function. For example, if we click on “2D” and choose “Sprite”, we create a
GameObject named “New Sprite” with component “Sprite Renderer” already
added to it.
We will create many files in our project, so it is essential that we organize the files
into folders. People have different preferences on how they organize their
projects, but the way that I found effective is to organize by file types.
As an example, the way I organize on the picture above is by file types. For
example, in the “Sound” folder, I put all the sound files like background music,
sound effects. I can also have subfolders for different type of sounds if I have
many sound files (character voices, boss music, ambient world sounds…). this
way, I can keep track of what I have created and where I can find them.
The tool bar above is what we use to configure our game object in the Scene
Window:
Hand Tool: use this tool to pan around the scene.
Move Tool: move the selected GameObject.
Rotate Tool: rotate the selected GameObject.
Scale Tool: rescale the GameObject on all axes at once.
Rect Tool: scale, position, size, and anchor for rectangle GameObject.
19
Those are the basic features of Unity. Of course, there are many more features
and even more ways to do things with Unity, but they exist for our preferences on
how we want to make our game. For the next chapter, I will introduce more
features of Unity because it would be easier to explain them along with making
our first 2D game.
This chapter is where we get our hands on making an actual 2D game. After this
chapter, we will have the skills and knowledge to make an actual working game
and begin our journey into game development. I will walk us through step by step
so that you can follow along as you read. We will make a 2D platformer game
called “Escape the Cave”. For this to work, we need the latest version of Unity
and Unity Hub. Unity Hub is where we can manage our Unity projects and the
Unity version we are using. To download the latest version of Unity and Unity
Hub, go to https://unity3d.com/get-unity/download. For this game, I am using
Unity version 2020.3.1f1.
Before we dive into developing our game, I want us to think about how we will
make the game: what is the theme, what do we want the player to experience,
how do we make our game stand out among other games, what kind of game we
make, and even more questions that you can think of. Asking these questions
ourselves not only helps us shape the game in our minds but also makes us
focus on making the game the way we meant for it to be. In my experience, when
20
making a new game, I tend to create more feature for it than I originally planned.
This is not a bad habit, the game might turn out to be better, but sometimes try to
remember to ask yourself “Is this what I want the player to experience?” For
example, we are making a game about exploration and puzzle-solving, but when
creating a level, we make too many obstacles, put in too many puzzles to solve
or the puzzle is too hard. This is when we need to look back and ask ourselves
“what about the exploration aspect of the game?”
After having the game shaped in your mind, write it down so that you will not
forget. For example, in “Escape The Cave”, our character wakes up underground
and has no idea where he is, but he knows that he has to get out of this cave
first. So now that we know that he is in a cave underground, we would want our
level to be vertical. It is a 2D platformer so think of games like Super Mario,
Donkey Kong… but instead of going from left to right, we try to lead the character
to go up. There should be puzzles and challenges on our levels to keep our game
interesting.
In summary, our game is defined:
Player experience: feeling of mystery
Core mechanic: 2D platformer
Theme: escape game
As we have defined our game, we are ready to make our game!
5.2 Environment
One of the first steps to making our game is creating the environment. We need
an asset for this to be easy. Normally when making a game, we need a team that
each person has a different set of skills: programming, animation, character
design, sound design, … But we are now able to make game on our own thanks
to game-ready assets. Unity Asset Store provides us with many free assets that
we can import directly to our project such as animation, environment art, sound
effects, texture design, … Of course, there are many more places where we can
find good assets, but for this project we use “Sunny Land Forest” 2D animation
assets from Unity Asset Store. You can get it free here:
https://assetstore.unity.com/packages/2d/characters/sunny-land-forest-108124
21
After downloading it, we can import the asset to our project. Simply navigate to
Window > Packet Manager, then select “Sunny Land Forest” asset, and click
“Import”.
After a while, we can see that the asset is imported under our “Asset” folder.
Navigate to Sunny Land Forest Assets > Artwork > Environment, we can see the
file named “tileset”. We will use this file to draw our environment in Tilemap.
Tilemap is a component that we use to visualize 2D scenes (or levels). It handles
and stores tile assets when we use Tile Palette to draw upon it. It also takes care
of the relationships between the components such as Tilemap Renderer or
Tilemap Collider 2D. in the latest version of Unity, the Tilemap package is already
included, however, if it is not included in your Unity, you can always get it from
Package Manager.
We will be using square-shaped tiles, so to create a Tilemap in our scene, go to
GameObject > 2D Object > Tilemap > Rectangular. This creates a Grid
GameObject, with a Tilemap as a child GameObject. With Grid selected, it shows
rectangular grid in our scene.
22
The reason why we need to separate background and foreground is that later in
the project, we will create collision between the foreground and the player.
Because the background only serves as visualization, player cannot interact with
the background layer.
Next, to draw on the tilemaps, we need the Tile Palette. Simply navigate to
Window > 2D > Tile Palette. This opens the palette that we use to draw in our
tilemaps. You should see a Tile Palette window like this:
Click on “Create New Palette” and name it “Main Palette” and place it under a
new folder called “Palette and Tiles”. Locate the “tileset” file in Sunny Land Forest
Assets. This file is a “sprite”, a 2D graphic objects used for environments,
characters, and other elements of 2D game. A sprite can be sliced into many
pieces for display purposes. To convert “tileset” sprite into a usable palette,
simply drag and drop “tileset” into Tile Palette window with the “Main Palette” on.
Then put it in the “Palette and Tiles” folder we just created. We now have the
palette to draw our environment.
Get familiar with the palette and try to draw on your scene, be creative with your
design. Then we can use the “Eraser” to erase your drawing. While drawing,
keep in mind which layer we are drawing it on. If we accidentally draw a
background element onto our foreground, it can be hard to detect and cause
problem in our physics later, such as our character suddenly being stopped by an
invisible wall of background pieces blended in our foreground. With that in mind,
lets draw our background first.
Again, use your imagination on how the background should look like. In the
definition of “Escape The Cave”, our character is in a cave and has to get to the
surface, since it is usually dark in a cave, our background should have a dark
color. Here is an example of what the background should look like.
24
The foreground should be things that players can interact with, so it should have
brighter color than the background. Select the foreground to start drawing. At this
point you might encounter a problem that when you draw it does not appear on
the screen. This does not mean the things you draw are not on the screen, but it
has rendering problem. If we look at the inspector of both background and
foreground, the “Tilemap Renderer” component has “Sorting Layer” setting, and
in both grounds, they are rendered at default layer.
To fix this, simply add more sorting layers for background and foreground.
Remember to place “Background” layer above “Foreground” because in Unity
algorithm, it renders the layer list from the top down, which means layers at the
top are rendered first. This makes the “Background” layer visualized behind
“Foreground” layer.
This is the end for the environment. As you can see, I make minimal work on the
scene and only introduce the fundamentals of environment creation. This is
because my game is only a tutorial, I leave the imagination and expansion of the
scene to you. You can make the scene larger than mine based on the
fundamentals that I introduced in this part and create an extensive level that
satisfies your imagination. There is also another concept of layer that we should
know about, the physical layer. Newcomers often get confused about this part
because the description of the layer types are vague. As can be seen from the
picture below, there are “Sorting Layers” and “Layers”.
26
While we know the “Sorting Layers” function is to render the layers, we have no
idea what “Layers” does. “Layers” is for the physical attributes of game objects.
We will use the physical layers in later chapter.
For now, let us create the same layers as the sorting layer and add them to their
corresponding Tilemap.
After we are done with the environment, we need to make the “Main Menu” scene
and “Congratulation” scene. The “Main Menu” is where the players can choose to
start the game or quit, we do not want to force the player to play the game
without a way to quit the game. After they finish the game, we greet them with
“Congratulation” scene to congratulate them on finishing the game and ask them
if they want to play again or quit, which is basically similar to “Main Menu” scene.
With that, create a new scene by right click on the project window, Create >
Scene and name it “Main Menu”.
In the main menu, we should add the name of the game, a “Start Game” and a
“Quit Game” button. To add text into our scene, go to GameObject > UI > Text.
This adds a canvas and inside it, a text object. It also creates an “EventSystem”
27
game object which handles the input and sending events. Change the text name
to “Title”, and inside the Inspector, we have the text component where we write
the name of our game “Escape The Cave”. Inside this component, there are
many customizations for the text object (font, size, color,…), so customize your
title however you like.
Next, we add the buttons the same way we add text, GameObject > UI > Button.
Inside the buttons, there is a text object that defines the text showing on the
button, name each one “Start Game” and “Quit Game”. there are also many
customizations for the text and the button inside the button object, edit your
buttons to your preference. The “Main Menu” Scene should look similar to this.
Duplicate the “Main Menu” scene and name it “Congratulation”. This scene is
similar to main menu, but we put this at the end of our game, so there should be
a congratulation to our players who finish the game, and the “Start Game” button
should be “Play Again?”.
Later in C# scripts section, we will create functions for these buttons. For now, let
us move on to character creation.
Figure 33. Comparison between Skeletal Animation and Sprite Sheet Animation.
The character is the player’s way to interact with the game world that we created.
Players control the character’s actions through their input on the keyboards,
game pads, controllers, phones, etc. In this part, we create the animations for our
player character and a way for player to control the character.
There are two ways to create character animations:
29
In this project, we use sprite sheet animation as our method because it is easier
to implement than skeletal animation. Also, in the “Sunny Land Forest” assets,
we have a character sprite sheet provided to us. Navigate to Sunny Land Forest
> Sprite >, we can see that there are many folders containing animations for the
character’s actions like climb, jump, fall, etc. We can try to drag and drop one of
the animations into our scene, but the sprite does not show up. This is where we
must track back to our foreground and background problem. The reason why our
character does not show on the scene is because of the rendering layers. Our
character is now on the default layer, which is rendered first and is behind our
background and foreground. To make our character visible, we can simply render
it with the same layer as our foreground. But to be more scalable, we can create
another layer called “Player”, and move the layer in front of the “Foreground”
layer. This is easier for us later in our game development journey as it helps us
organize the layer and keep track of what is being rendered on which layer. To
understand how animation in Unity works, we must first understand what
Animation, Animator Controller and Animator are:
We can start creating our character animation with the knowledge given above. In
the “player” folder, we have many folders for different character movement. I take
“player-jump” folder as an example. We need sprites to put in our animation. To
view the sprites, simply click the button in the image and it shows us the
sprites. We need to select the sprite in each image, then right click > Create >
Animation. We have to do the same with other player animation folders, and then
move all the new animations to a new folder named “Animations”. After this step,
your “Animations” folder should look like this.
As we can see in the “Animator” window, there are states for our character. The
states dictate which animations should be played and when. There are three
default states in the Animator: “Any State”, “Entry” and “Exit”. These states
simplify our states map by removing the need for transition in some states. For
example, if a state is connected to “Any State”, it can transit from any state in the
states map and vice versa. To create connections between the states, right click
on the first state we want to connect, select “Make Transition”, then choose the
state that needs connecting, and we have an arrow that connects two states.
We can now test our animations on “Player” game object. Remember to select
the “Player” game object and try to play some animation that you added to the
“Animator” window. The character in your scene should have some movement
when you try some animations. At this point we have a basic knowledge of how
animation works in Unity, the differences between skeletal animation and sprite
sheet animation, how to convert sprite sheet into animation and states transition
in animator.
32
As we have grasped the basic of how animations in Unity works, let us create the
transitions between animations. We need to define which animation is for which
action, and the transition from that animation to another. For instance, if a
character is standing still, we would want it to have an idle state with idle
animation, and when the character moves, the animation immediately changes to
walking animation. To make it easy for you to follow, I have created my states
and transitions in the picture below for reference.
We also need to set the condition to trigger the states because the states do not
know when to transit to another state. To set the condition, go to the
“Parameters” tab of the “Animator” window, click on button to drop down the
list of parameters, as we can see there are 4 parameters we can choose from:
float, int, bool, trigger. We choose “Bool” for our parameters. For example, the
transition from “Player Idle” to “Player Walk”, we create a bool parameter and call
it “isWalking”, then add this parameter to the transition and set its value to “true”.
Figure 38. Condition for Player Idle > Player Walk transition.
In the opposite direction, we add this parameter as condition but set its value to
“false”, so that when the character stops moving and is standing still, the
animation goes back to idle. We do the same steps for our other, create bool
“isJumping” and add it to suitable transitions. Later in this chapter, I will explain
how to trigger these transitions in C# scripts.
33
In this chapter, we learn how to apply physics into our environment and
character. Right now, what we have on the screen is like a painting, there is no
interaction between objects yet, nothing for the character to touch, jump on or
even stand on. This is where physics definition takes place.
Physics in game is very similar to physics in the real world, except we can control
and customize the physics in the game to fit our needs. First, we need to
understand the physic components in Unity. The two components that applied
physics to a game object are “Collider” and “RigidBody”. There are many collider
components (Collider 2D, Collider 3D, Tilemap Collider,…), each has its own
function suitable for the game object it is attached to. But the main function of a
collider is to create collision between two objects like should they bounce off or
stick on each other. “RigidBody” component adds gravity definition to a game
object. Normally, without rigid body, a game object would standstill in its defined
coordinate until we control it. But with “RigidBody” component added, the game
object would fall because gravity is now applied to it and it will continue to fall
infinitely if there is no collider in its way. Now that we understand the functions of
each components, we can add them to our game.
First let us add a collider to our environment. Since our character only interacts
with the foreground, we only need to add a collider in our foreground. Select the
foreground in the hierarchy and add component “Tilemap Collider 2D”, this
component makes our job easier by auto detecting the tiles we draw and only
applies collision to the tiles. After adding that, we should see green outline on our
foreground tiles, this indicates the collision area of the foreground.
But we can see that the plants we draw in the foreground also have collision. We
do not want our character to be stopped by a small plant while moving, so this is
a problem we need to fix. There are many ways to deal with this problem: draw
the plants in the background, create another layer named “plants” and draw the
plants on it, or remove the collision area on the plants. The most scalable option,
in my own opinion, is to create another tile map and layer for the plants. This way
we can manage the plants in the future without worrying messing up other layers.
To do this, create another tile map inside “Grid” game object, and name it “Plant”.
With the same steps creating the sorting layers, add a new layer named “Plants”
and put it in between “Background” and “Foreground” layers, this way it is visible
in front of the background, but behind the foreground. We can now draw freely on
“Plants” tile map without worrying about the plants colliding with the player.
The reason why we did not add rigid body to the environment is because it is not
necessary for the environment to be pulled by gravity. Had we added rigid body
component to it, our environment would fall off the screen because of gravity.
Therefore, it is not advisable to add rigid body component to it. Also, it would
likely create more problems to solve in the future when our game is more
complex.
Now that we are done with environment collision, our character also need
collision. There are options on what to use as the collider for our character (box
collider, capsule collider, circle collider,…), this is based on your preference, but
here I use “Box Collider 2D” component. After adding that to our “Player” game
object, we can see the green outline indicating the collision area for our
character. We can edit the green outline so that the collision area fit our
character, we do not want it too large because that would cause frustration to our
players.
35
We can test the collision of our character with the foreground by clicking
button. Our character should not fall through the ground but stand on it. If
successful, we can move on. If not, check on the instructions to see if you have
done everything.
Another collision type is trigger collision, this type does not necessarily have any
physics property because its main function is to trigger an event when it detects a
collision. We use this type to create a way for our character to finish this level and
move on to the next one. You can use any image you want as the finish line, but
here I use an exit sign to represent that of the level. Drag it in to our scene and
put it at the end of the level, then create another sorting layer and physical layer
both named “Interactables”. This way it makes our game more scalable since we
can put all the interactable items in these layers if we decide to create more later.
Then we add “Box Collider 2D” component to it and mark it as “Is Trigger”. This
way, when our character collides with the exit sign, it triggers an event to load the
next level, which we will define in the next section.
36
First, create a folder named “Scripts” in our project, this is where we place all our
scripts for easy organization. Because scripting is the most challenging part, I will
37
5.6.1 Player.cs
Double click on the script to open it with Visual Studio. We need to create a
method for our character to move upon inputs of the player. To do this, write
these lines of codes inside public class “Player”. For best practice, write them
below Start and Update methods:
private void Run() : the declaration of the method “Run”. Private means that this
method can only be accessed by its class and none other. Void is the return type
of the method that specifies that the method does not return a value.
float controlMovement = Input.GetAxis("Horizontal"); : float is to declare the
floating-point numeric type of the variable “controlMovement”.
“Input.GetAxis("Horizontal");” is how we let player use input to control the
character movement horizontally. At the end of every line of code in C# ends with
seimicolon “;”. This whole line means that the variable “controlMovement” returns
the float value of the horizontal inputs. At this point, player can give input, but the
character does not move yet. The movement control comes in the next lines of
code.
void Update()
39
{
Run();
}
We can now move our character left and right, but we cannot control its speed. It
might be too slow or too fast for our player, so we need to add a speed multiplier
that is easy for us to control. For this purpose, we can create a Serialize Field. A
serialize field is basically a way for us to control the variable in Unity. We can set
an original value for the variable in Visual Studio, with the serialize field, we can
change it inside Unity without having to go back to our code to find it. Simply write
this code inside “Player” public class.
With this line of code, we created a float variable with value of 5. Together with
the serialize field, Unity shows a field inside “Player.cs” script, which allows us to
change the value of variable “controlSpeed”. Unity recognizes and automatically
add a blank space between words for easy understanding.
With this variable, we can now control the player speed by simply add it as a
multiplier for “controlMovement” inside the “playerVelocity” vector.
Save the script and try the new movement speed, change it to fit your speed
desire. But as we move our character, the animation does not change into
walking animation or flip into the moving direction. Therefore, we have two
problems on our hands: make character flip animation in the suitable walk
direction and create means for animation to change. The first problem we fix is
the flip animation. As of now, the animation of our character is visually looking at
40
the right side of the screen, we need to create a condition in our code to tell the
animation to flip when moving left.
We will use mathematics classes for flipping function so this requires some
knowledge of mathematics:
Mathf.Abs(f) : this code’s function is to return the absolute value of f
whether f is a negative or positive number. For instance, if f = -5, then
Mathf.Abs(f) = 5.
Mathf.Sign(f) : this code returns the value of 1 if f is a positive number, -1 if
f is a negative number. For example, f = -5 then Mathf.Sign(f) = -1, f = 5
then Mathf.Sign(f) = 1.
Mathf.Epsilon : this is the smallest value that a float number can have
different from 0.
Now that we understand the functions of the codes, we can use them to flip our
character. A new method called “FlipSprite” is needed:
.velocity.x), 1f); : this line is how we flip the sprite of our character. We access
the “Transform” component inside the “Player” game object and change the scale
on the x-axis of the sprite to flip it. We use Mathf.Sign() for the x velocity of the
rigid body 2D component so that it only returns 1 or -1.
41
Now add “FlipSprite” to “Update”, and our character should face the direction it is
moving.
void Update()
{
Run();
FlipSprite();
}
Next, we create the jump function for our character with the same principal and
structure with “Run” function. But we have to note that when moving, horizontally,
the player’s input is constant and the character only stops if the player stop
pushing down the move button. While with jumping mechanism, the character
only reaches a certain height and then falls back down to the ground, not moving
vertically constantly when there is input. For this method, we use
Input.GetButtonDown() function, which only input the act of pushing down
buttons, not constantly take input when the button is pushed. We create a
serialize field variable for how height the character can jump and call it
“jumpSpeed” so that we can control it inside Unity:
However, there is an exploit in the jump function. Our character can jump
whenever the player press jump, which means that it can jump from any position
possible and does not necessarily have to fall to the ground to start jumping
again. We do not want that to happen because it would make our game less fun
when players can just jump out of any challenges. To prevent this, we need to
make the player able to jump only when the character is touching the ground. We
use “if” statement once again for this logic condition: if the player is not touching
the ground, then do not let the player jump. This way, the player cannot jump
while floating in the air anymore. We add the “if” statement inside the “Jump()”
method:
if (!GetComponent<Collider2D>().IsTouchingLayers(LayerMask.GetMask("Foreground")))
{ return; }
the “!” stands for “not” in the “if” statement. Here we access the component
“Collider2D” of “Player” GameObject and using “IsTouchingLayers()”, we can
determine whether the character is touching the “Foreground” layer or not. When
we want a condition to do nothing, simply write “return”. This means that if the
character is not touching the foreground, do not let the player use “Jump()”
function.
So far, our character can move, jump, face the moving direction, but does not
play the corresponding animation for each of the actions yet because we have
not defined the conditions for these animations to be triggered. We have created
the states and the transition conditions “isJumping” and “isWalking” in the
previous “Character Creation” section. now we implement these conditions in our
code. To trigger the “Player Walk” animation, we need to set the bool “isWalking”
to true when our character has horizontal movement. This logic is the same as
the one we use for “FlipSprite” method. We can use the bool “playerHasMovement”
43
inside the “Run” method. Once we have that, we pass the bool value in the
“Animator” component. For that, inside the “Run” method should have two more
lines of code:
With “isJumping”, the logic is the same as allowing the player to jump. So inside
“if” statement of the “Jump” method, set the bool for “isJumping” to true:
if
(!GetComponent<Collider2D>().IsTouchingLayers(LayerMask.GetMask("Foreground")))
{
GetComponent<Animator>().SetBool("isJumping", true);
return;
}
But we also need an “else” statement to change the bool back to false, or the
character will continue to run the animation “Player Jump” even when the
character is not jumping:
else
{
GetComponent<Animator>().SetBool("isJumping", false);
}
5.6.2 LevelFinish.cs
In this script, we create a way for our player to finish the level they are in and
move on to the next one. We need to create another script called “LevelFinish.cs”
inside our “Script” folder, then drag and drop it in the exit sign game object as a
component.
44
In this script, we do not need “Start” and “Update” method, but we need another
namespace called “UnityEngine.SceneManagement;” which allow us to use the scene
management tool in the script. So, add that to the namespace:
using UnityEngine.SceneManagement;
We do not want to load the next scene immediately when the character touches
the exit sign, it creates a feeling of abruption for the player. Therefore, we need to
let the scene wait for a while after player finishes the level, then load the next
one. To do this, first we create a serialize field of a float called “delayLevelLoad”
and set the value of 2:
IEnumerator LoadNextLevel()
{
yield return new WaitForSecondsRealtime(delayLevelLoad);
var currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex + 1);
}
(currentSceneIndex + 1);” function is to load the scene after the one the player is
currently in.
Next, we need to create a way to trigger the coroutine. In the previous chapter,
we use “Box Collider 2D” and set it as a trigger for our Exit Sign. Now we use that
trigger in the code with “OnTriggerEnter2D”:
The script for the “Exit Sign” is now done. Whenever the character touches the
sign, it waits for 2 seconds then load the next scene.
5.6.3 MainMenu.cs
The MainMenu.cs script handles the functions of the “Start Game” and “Quit
Game” button. Let us create the script and drag it to both “Start Game” and “Quit
Game” buttons. Inside the script, we do not need “Start()” and “Update()” method,
so delete that and create the method to start the game. We also use the
namespace “UnityEngine.SceneManagement” for scene management functions. The
logic here is that the game always starts at level one, so when the player clicks
on “Start Game” button, the game loads the scene index associated with the first
level:
Save the script then drag and drop the script to both of the buttons in “Main
Menu” scene. After that, to apply the function for “Start Game” button, in “Button”
component in the “Inspector” window, there is “On Click ()” method, set this
function like so in the picture and “Start Game” button now has the function to
load the first level:
We can apply the same steps on “Quit Game” button with “QuitGame()” method.
Also, the “Congratulations” scene is similar to “Main Menu” scene, apply the
same settings to both buttons.
So far, we have only created one level (or scene) for our game. To make the
game feel expansive for the players, there should be more levels and challenges,
which is why we need to create more scenes, each is different than the others.
The level difficulty flow should be from easy to hard, so the players have time to
get accustomed to the control. We can easily create another scene by duplicating
the one we have now by pressing Ctrl + D, we save a great amount of time by
doing this because we do not have to draw the scene or add the components
again to the scene, we only need to adjust them. Our level 2 should be somewhat
more challenging than level 1, for example, down below is my level 1:
47
The player learns how high they can jump when they jump on the higher platform
to reach for the exit. Then, on level 2, there should be some challenges involving
jumping that player can overcome such as this level below:
We can create as many levels as we want in our game, but here I only create 2
levels as an example of how progressive the levels should be. Maybe in later
levels you can create more challenges, add more game mechanics, and put
48
much work into designing your levels, that is up to you. For now, we move on to
creating camera movement.
5.8 Cinemachine
So far, we have only created the level in the limit of what our main camera
shows. This can be very limited in level designing because we only have so much
space that the player can see. In the situation where the level is too big, but our
camera does not show the whole level and only stands still, it can be troublesome
for the player as they can still move, but the camera does not follow him.
Of course, we can expand the camera view to fit the level, but then the character
would be too small. Therefore, we need a way for the camera to follow our player
in the bigger level that we design with Cinemachine. Cinemachine is an add-on
for Unity projects that helps with controlling the cameras we have in play. To add
Cinemachine to our project, go to Window > Package Manager, change
“Packages” filter to “Unity Registry”, then find Cinemachine and install.
After installing Cinemachine, we can see Cinemachine on the tool bar, click on it
and select “Create 2D Camera”. This creates a virtual camera in our hierarchy
named “CM vcam1”. A virtual camera is an empty object, and it does not appear
on our scene. The function of Cinemachine is to apply all the settings from virtual
cameras to the main camera. Therefore, we mainly use virtual camera for our
settings. In the virtual camera Inspector window, we can see the option to choose
the GameObject the camera will follow, choose “Player”. As simple as that, now
the main camera will follow our character.
At this point, we have created a game where player can control the character and
many levels to play. Therefore, in this section, we build our game into a playable
game software. Go to File > Build Settings.
50
This is where we define the order of our level scenes and build our project into a
playable game. we can add our scenes here, arrange them in the order that we
want, and build the game for the targeted platforms. As can be seen from the list,
there are many platforms to build on, so we should decide where we want to
publish our game beforehand. For “Escape The Cave”, I decided that it is easier
to build it for PC, also because PC platform is included when installing Unity.
Drag and drop all your levels (or scenes) in the window, then rearrange them
according to your designs so that after finishing a level, the game jumps to the
more challenging one. When done, select your preferred platform and click on
“Build”, then choose a folder to put all the files in it, and wait for the building
process to complete. After build completion, the game is now a standalone
software ready to use.
6 CONCLUSION
development on their own is high, in my opinion, after reading the thesis as the
thesis demonstrates the ease of developing a 2D game when they already have
the programming skills.
Unity Assets store has so many free assets that helps developers save time and
effort in making their game. It also has assets that requires payment but the price
is reasonable and the amount of free assets makes developers rarely need to
pay for other assets.
The Unity’s support community is also sizeable. Game developers can visit
https://forum.unity.com/ to seek support form the community when they are in a
bind.
Unity is a great game engine that both individuals and large companies use to
make their games. It supports the publication on many platforms so that
developers have the option to introduce their game on those platforms, which
would boost the popularity and revenue of the game.
Unity’s popularity shows us the usefulness and the freedom we have when
developing games on Unity. Of course, many triple-A game companies have the
resources to make their own game engines, but the use of an already great and
free game engine like Unity cannot be denied when the gaming industry is on the
rise nowadays. Therefore, Unity has proven to be a considerable choice for
individuals to make their own indie games.
52
REFERENCES
Tristan Donovan, 2010. Replay: The History of Video Games. Available at:
https://1lib.sk/book/1103623/723bf9
Unity, 2021. Full toolset for 2D and 3D video games. Available at:
https://unity.com/how-to/difference-between-2D-and-3D-games
Unity Documentation version 2021.1, 2021. Unity Manual, Scripting. Available at:
https://docs.unity3d.com/2020.1/Documentation/Manual/ScriptingSection.html
Unity Documentation version 2021.1, 2021. Scripting API, Vector2. Available at:
https://docs.unity3d.com/ScriptReference/Vector2.html
53
Appendix 1/1
Player.cs Scripts
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
}
54
Appendix 1/2
private void FlipSprite()
{
bool playerHasMovement = Mathf.Abs(GetComponent<Rigidbody2D>().velocity.x)
> Mathf.Epsilon;
if (playerHasMovement)
{
transform.localScale = new
Vector2(Mathf.Sign(GetComponent<Rigidbody2D>().velocity.x), 1f);
}
}
}
55
Appendix 2
LevelFinish.cs Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
IEnumerator LoadNextLevel()
{
yield return new WaitForSecondsRealtime(delayLevelLoad);
var currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex + 1);
}
private void OnTriggerEnter2D(Collider2D collision)
{
StartCoroutine(LoadNextLevel());
}
}
56
Appendix 3
MainMenu.cs Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;