-
In Unity 4.3 we're launching our first set of 2D features.
-
To compliment this we have constructed a
-
demo project with these tools.
-
Our 2D platformer is nicknamed 'Tower Bridge Defence'
-
It depicts London's Tower Bridge in the midst
-
of an alien invasion.
-
It's a completely sprite-based, physics driven
-
2D sample level that we hope will help you
-
understand how 2D games are put together in Unity.
-
This video will discuss the background and
-
foreground construction, characters, effects,
-
camera tracking, animation and scripting
-
used in the demo.
-
To begin with, let's discuss the basics
-
of working in 2D in Unity.
-
Firstly, when working in 2D, you should set the
-
Editor Behaviour Mode to 2D
-
for various settings.
-
This can be done when making a new project
-
using the drop-down on the project wizard
-
or during a project by choosing
-
Edit - Project Settings - Editor from the top menu.
-
This means that by default textures
-
will be imported as sprites and the
-
Scene View will default to 2D mode.
-
This new mode gives you a completely orthographic
-
view in which to construct 2D games.
-
It also hides the usual 3D gizmo
-
in the top right of the view, giving you more space
-
to work in. Aside from these settings the work
-
flows for 2D have been designed to mirror
-
existing Unity approaches to 3D game creation.
-
So if you already know a little about Unity
-
you'll be in a great position to start making
-
2D games right away.
-
It's worth noting at this stage that you can
-
still mix 2D and 3D in Unity,
-
so if you want to add 3D elements to your 2D game
-
or vice versa you can do that with no worries.
-
Let's take a look at the demo project itself,
-
and how we built it up one stage at a time.
-
We began by sketching out the level design
-
for this sample level and then went about
-
recreating the layout in Photoshop.
-
Creating and exporting the layers,
-
we were able to import these in to Unity
-
using the new Sprite type.
-
In order to create parallaxing in our background later,
-
we kept some of the background elements separate
-
and placed them on to a Background Sorting Layer.
-
Yet another new feature of 2D development in Unity.
-
Having assigned all of our backgrounds to this layer,
-
we could then use the Order In Layer property
-
of the Sprite Renderer to sort them.
-
Once we were happy with their positions
-
we could lock the Background Sorting Layer
-
so that when we added foreground elements
-
we didn't need to worry about accidentally
-
dragging background elements around.
-
This is done using the Layers pull-down in the
-
top right of the interface.
-
Because the background elements are purely decorative
-
we did not require any additional components
-
on the sprite game object.
-
They were parented to an empty game object
-
that has a simple script to handle parallaxing
-
called BackgroundParallax.
-
For more information you'll find this script
-
fully commented in the Scripts folder.
-
Next up came the creation of the foreground elements
-
that our characters would actually traverse.
-
We designed London's Tower Bridge with a UFO
-
landed in the centre.
-
The character has the run of the environment
-
as enemies spawn from the skies and begin
-
to navigate around the level.
-
As such, each piece of the foreground required
-
a collider for characters to walk upon.
-
For most of the environment we used 2D
-
box colliders for efficiency,
-
but the UFO itself has a more complex shape.
-
Unity's Polygon Collider allowed us to
-
automate creation of the collider itself
-
based on the shape of the sprite.
-
It even meant that we could tweak the shape of
-
the collider later.
-
Moving, adding or subtracting points
-
of the collider shape to make it more
-
appropriate to walk over.
-
To sort these foregrounds we created a
-
Foregrounds Sorting Layer, which was drawn
-
above the backgrounds in the Tags And Layers manager.
-
Next, let's take a look at our hero.
-
Our player character was yet again designed
-
in Photoshop, and for this demo
-
we chose to make a character with independent
-
limbs and features in the style of
-
2D hits such as Rayman.
-
Our other option would have been to design
-
a sprite sheet-based animation and design each
-
each frame in Photoshop,
-
an approach we use later for the background Swan,
-
which we'll discuss later in the video.
-
Because our character had independent elements
-
that we wish to animate we finalised the design
-
and then moved his bodily elements in to separate
-
spaces on our canvas to allow Unity to isolate them
-
as separate sprites in the Importer.
-
This meant that we could then arrange all of
-
our sprites as separate elements to be animated.
-
We placed these on to a new Character Sorting Layer
-
that we created, and then used Z values in the
-
transform to sort their rendering depth.
-
All of these sprites are then arranged under an
-
empty game object, which has all of our
-
controls scripting, colliders, physics, excetera attached to it.
-
Once we'd done this we were able to use the newly
-
upgraded Animation window to create
-
Idle, Run, Jump, Shoot and Death animations
-
by animating each of the character's sprites over time.
-
With the new Dopesheet View in the Animation window
-
this is easier than ever.
-
We simply add animation to the parent object
-
and then create keyframes for any of the child objects.
-
Moving the playhead to where we want
-
and then moving the various parts of the character
-
to automatically keyframe the animation.
-
The ability to switch between Curve and Dopesheet
-
representation of our animation makes it easier than
-
ever to adjust timing and design.
-
With these animations created we can
-
then design a state machine for our character
-
so that when called upon in code, differing animations
-
could be played.
-
the animator controller for the character is
-
not driving a 3D biped, so we simply deselect
-
Apply Root Motion and select Animate Physics
-
in order to drive our animations in time with
-
the physics engine.
-
In order to traverse the environment our hero has a
-
circle collider at his feet and a box collider
-
to cover the rest of his body outline.
-
This means he can smoothly walk up and down hills
-
and when he jumps his head will hit the ceiling.
-
In order to control the character and it's animations
-
we wrote a script that moves him via 2D physics forces.
-
This means that we can apply physics to him
-
and the enemies during the game for more
-
dynamic game play.
-
In our PlayerControl script for the character
-
we check for player input.
-
We use this to apply physics forces
-
for movement and also send the value of the input
-
to the animator, which in turn defines which
-
animation should be playing and smoothly transitions
-
between the different animation clips that
-
we've created as states.
-
The great thing about using the animator to
-
create states from animation clips is
-
that we can go and tweak speeds of animation
-
to match our physics velocities
-
without having to reanimate anything
-
The FixedUpdate function evaluates with
-
each physics step, and the first thing we
-
do with this is to feed the value of Horizontal
-
input in to the Speed parameter of our animator.
-
The transition between Idle and Run
-
in our simple state machine requires
-
that the Speed parameter is above 0.1.
-
When it is, the animator blends from Idle
-
in to the Run state.
-
We then go on to add forces to the player's
-
2D physics component, the rigidbody2D,
-
in order to move him around.
-
We also handle which direction the character is facing
-
based on the value of Horizontal input,
-
checking whether it is above or below 0.
-
This is because in Unity, holding the left input key
-
returns a value of -1, where right returns positive 1.
-
Depending on input, we then call a simple flip function,
-
which reverses the X scale of the character,
-
giving him the appearance of facing the opposite direction.
-
To decide whether the player is grounded
-
we added a layer in Unity called Ground
-
and applied it to all of our walkable foreground surfaces.
-
We then used the Linecast function in 2D
-
to check whether something on the Ground layer
-
is below the character's feet.
-
To customise this more easily we created
-
an empty game object to use as a point
-
at which to check for the ground.
-
By adding a gizmo to this empty object
-
we are able to manipulate how far below
-
the character we'll check for the ground.
-
From a gameplay perspective this means that
-
the character can only jump when grounded.
-
Check out the rest of the comments in the script for more
-
information on the control of the player.
-
We will discuss the player's weapon
-
later in this video.
-
Next, let's take a look at how the camera
-
tracks the player in our demo.
-
In 2D games, much like 3D, the motion of
-
the camera tracking the action can make
-
or break your game.
-
For a classic 2D platformer, we looked at the mechanics
-
of one of the most interesting camera in 2D gaming history,
-
the camera from Super Mario World on the Super Nintendo
-
or Super Famicom.
-
In Super Mario world the camera tracks
-
horizontally, but uses a dead zone
-
or margin in the centre of the viewport
-
in which the character can move a little
-
without the camera tracking.
-
Once the character moved beyond this margin
-
the camera tracks back toward the player.
-
The Super Mario World camera used particular heights
-
to snap to vertically, but we didn't need this
-
kind of detail for our game as we
-
do not have a long level in the X axis
-
but more of a stage on which the action takes place.
-
For this reason our camera employs
-
similar tracking vertically as it does horizontally.
-
Take a look at the CameraFollow script on the
-
mainCamera game object to see comments
-
on how it achieves this effect.
-
There are several effects in the game,
-
but most important is our heroes ability
-
to slay the alien onslaught he's faced with.
-
Our hero shoots a bazooka which has animated recoil.
-
This action is made up of several parts.
-
First we listen for key input and when the Fire key is
-
pressed we instantiate a rocket,
-
play an audio clip and trigger an animation state to play.
-
Let's break this down even further.
-
in order to play the Shoot animation while other
-
animations such as Run are playing we
-
created a separate layer within our animator
-
called Shooting.
-
By setting the Weight property to 1 here
-
we can totally override motion in the base layer
-
on any parts of our character that are animated
-
by clips on the shooting layer.
-
In this layer we switch to the Shoot animation
-
from any state, when the Shoot trigger
-
parameter is called from code.
-
Let's take a look at the Gun script in charge of this.
-
Here you can see that we address the animator
-
and set that trigger to True.
-
Triggers simply act as a switch and reset themselves to false
-
on the next frame so that they can be called again,
-
which is perfect for actions such as shooting.
-
In addition to setting the animation we fire the
-
rockets themselves from this script,
-
playing an audio clip and dependent upon
-
the direction that the player is facing
-
we instantiate a rocket and give it a velocity
-
that's positive or negative in the X axis.
-
This script is attached to the Gun empty game object
-
in the hero's hierarchy.
-
We place code like this on to an empty game object
-
as it allows us to easily position
-
where the rockets are created.
-
We do this by placing the empty object
-
at the end of the barrel of the bazooka
-
and then we use it's own position as the
-
point at which to spawn the rockets.
-
The rocket itself has a 2D rigidbody
-
and we assign a velocity to that in order
-
to make it move.
-
It has a sprite swap flame exhaust
-
plus a particle system for smoke.
-
The particle system also accepts the new sprite type of graphics
-
so by adding a sprite sheet of smoke puffs
-
to a material we can assign it to the
-
texture sheet animation module of the particle system
-
and we get instant animation of our
-
sprites for the particle emission.
-
When our rockets hit an enemy or part of the environment
-
the rocket itself is destroyed and
-
an explosion is spawned.
-
The explosion is simply a sprite game object
-
that animates through a sprite sheet that we have created.
-
Yet again using sorting layers to render this
-
at the lowest layer order of our foreground objects.
-
When adding sprite-based animation like this
-
we setup the sprites themselves by
-
selecting the file in our Project Panel
-
and choosing the Sprite Mode Multiple.
-
This gives us access to the sprite editor
-
which allows us to slice manually or automatically.
-
Once happy with the selection of sprites from our file
-
we simply hit Apply and Unity
-
generates the sprites as children of that file
-
to be used in our project.
-
So that's our rocket in a nutshell.
-
We will discuss the mechanics of killing the enemies
-
later in this video in the section about enemies.
-
Let's return to the player character now and look at
-
how we handle health and taking damage.
-
Health is stored as a float and
-
with each interaction with a tagged enemy
-
we call the TakeDamage function.
-
This is only allowed to occur after the
-
repeatDamagePeriod has passed
-
to avoid the player being killed very quickly.
-
To allow the player to escape enemies more easily
-
and to show the player that they are being hurt
-
we make the act of taking damage
-
repel the character physically.
-
To achieve this the TakeDamage function briefly
-
stops the player from jumping
-
and finds a vector from the enemy to the player
-
and repels him in that direction
-
by adding a physics force.
-
The hurtForce variable is exposed in the Inspector
-
as public so that it can be tweaked
-
to adjust this element of game play without
-
returning to the script.
-
In addition to repelling the player,
-
we of course subtract from the player's health.
-
and update the player's health bar.
-
To signify the decrease in health we subtract
-
from the width of the bar and use a colour lerp
-
to transition it's colour between green and red,
-
both by finding the percentage that the current health is
-
of the full health amount.
-
The health bar simply comprises of two sprites,
-
one for the outline of the bar and the other
-
for the bar itself.
-
This was again designed in Photoshop and then
-
the two separate elements were exported.
-
In the import settings for these sprites
-
we set their pivot to the middle left of the graphic
-
so that when it scales down it shrinks towards the left.
-
These two sprites are placed under an empty
-
parent game object which has a simple script
-
on it which makes it follow the player.
-
We do this by setting the position to the
-
same as the player object's position
-
plus an offset that we've made public to allow
-
for adjustment in the Inspector.
-
When the player has 0 health remaining
-
we allow him to fall through the level by
-
setting his colliders to triggers,
-
and we move him to the very front of rendering
-
by placing his sprite renderers on the UI Sorting layer,
-
one we've made to render in front of everything in the game.
-
We have 2 animations for when the player dies.
-
1 called Death, where he loses his hat and gun
-
and another called Falling.
-
We naturally transition in to Falling once
-
the Death animation completes by using the Exit Time
-
as our transition condition in the animator.
-
Finally, to stop the player moving the character
-
or shooting during the Death sequence
-
we disable the PlayerControl and Gun scripts.
-
Because the Die function is made as public
-
we can call it from elsewhere, such as if the player
-
falls in to the water.
-
To reset the game once the player does hit the water
-
we have a KillTrigger object, which simply
-
comprises of a trigger collider and a script.
-
For most of the game the purpose of the Remover script
-
is to remove our enemy objects that fall in
-
to the river, and instantiate a splash animation
-
and sound effect.
-
However, when the player is detected by this trigger
-
we call the Die function in the PlayerHealth script
-
and also disable CameraTracking
-
whilst moving the player off screen
-
and calling a co-routiene that pauses for 2 seconds
-
and then reloads the level.
-
But let's not dwell on the death of the player,
-
let's look at his survival and the tools that we give
-
him to do that.
-
Our game features 2 airdropped crates that
-
assist the player, 1 containing a bomb,
-
the other a med kit to boost health.
-
These crate drops are made up of 2 parts.
-
The crate itself and a parachute.
-
These 2 elements are nested beneath an empty parent object
-
to allow us to animate them as a group.
-
We position the 2 sprites so that the parachute's
-
centre is at the centre of the parent.
-
This way the animation can swing left and right
-
as if the entire parachuting crate is floating to the ground.
-
We then simply add a rigidbody to cause
-
gravity to pull the object down
-
and add colliders to the crate so that
-
we can detect when it lands and when
-
the player picks up the crate.
-
Upon landing we transition to a second
-
animation state which scales the parachute down.
-
Like other parts of our game, the animator handles
-
the states of the object.
-
We can see that by default it plays the
-
floatDown animation state
-
but then switches to a landing state
-
when the trigger Land is set to true.
-
In our script we do this using an onTriggerEnter function,
-
which detects the ground via a tag.
-
We also detatch the crate itself from the parent object
-
and give it a rigidbody of it's own
-
so that it can interact with the environment
-
resting realistically on a slope if it lands on one.
-
Let's focus on the bomb first of all.
-
Picking up the bomb crate is handled on the
-
BombPickup script, attached to the crate.
-
We pickup the crate by destroying it and adding to
-
the count of bombs that the player has
-
in the script attached to the player called
-
LayBombs
-
The LayBombs script simply checks if
-
the player is carrying a bomb
-
and then instantiates an instance
-
of the Bomb prefab.
-
The Bomb prefab has a timed fuse
-
which waits by using an yield within the
-
BombDetonation co-routiene
-
before calling the Explode function.
-
The Explode function performs several actions.
-
First it resets the bombLaid variable
-
to allow another bomb to be deployed,
-
it tells the pickup spawner it is allowed to
-
spawn a new crate drop and kills
-
enemies within a defined blast radian.
-
Let's take a look at how this last part works.
-
Because bombs are lethal to enemies regardless of
-
their remaining hit points we use the
-
Physics.OverlapCircleAll to collect all objects
-
tagged Enemy within a certain radius of the bomb.
-
We then run a foreach loop for every enemy found
-
setting their health to 0 finding a vector from where the bomb was
-
to where the enemy is, in order to apply
-
a force in the direction of that vector.
-
Once the foreach loop is complete
-
we play and instantiate visual effects,
-
play an audio clip for the explosion
-
and of course destroy the bomb itself.
-
The visual of the explosion is twofold.
-
The main part is a simple circle that appears
-
briefly and is then destroyed
-
and the second part is a particle system of
-
stars that reuses the same sprites
-
as our rocket explosion.
-
For efficiency we keep this particle
-
system in the scene at all times
-
and when it is required we use code
-
to move the system to the desired position and play it.
-
This keeps the particle system in memory
-
and makes the game more efficient.
-
It's worth noting that we can do this
-
because we know we will only have 1
-
single explosion in the scene at any one time
-
as the player can only throw 1 bomb at a time.
-
This is why keeping our particle system in the
-
scene and replaying it is more
-
efficient than creating instances and then removing them.
-
With the rocket explosion however,
-
we can have many at once so we do
-
need to spawn and remove them.
-
Now let's take a look at what happens when our player
-
kills an enemy and scores points.
-
This is done in 2 parts,
-
a scoring animation that plays showing 100 points earned
-
and the score UI at the top of the screen
-
that increments.
-
The score animation is made up to 2 number sprites,
-
a 1 and a 0.
-
We place this in the scene under an empty parent object
-
and animated them using a simple destroyer script to
-
remove them from the scene when the animation is complete.
-
We call the destroyer function in the script
-
by placing an animation event
-
at the end of the timeline.
-
The ScoreUI itself is a simple GUI text component
-
with a custom font and script that manages
-
the score for the player.
-
The Score variable is public, meaning that we can address it
-
from the Enemy script when they are killed
-
and add 100 points to the value.
-
Speaking of the enemies, let's take a look
-
at them more in depth now.
-
In our game we have 2 types of alien enemy,
-
a green slug-like monster and a smarter
-
alien that brought his ship with him for
-
protection during the invasion.
-
These characters share the same script
-
as the behaviour is very similar
-
and we allow for differing movement speeds
-
and amounts of hit points by setting
-
these as public variables in the Inspector.
-
Each enemy has it's own Walk animation.
-
The slug has a moving tail, whist the
-
ship-based enemy rocks back and forth as it approaches.
-
For the slug we used the sprite importer
-
to define the tail section of the graphic
-
as a separate sprite element,
-
meaning that we could animate it individually
-
and avoid having to scale the entire sprite.
-
By setting the pivot to the right
-
we are able to animate the tail expanding and contracting
-
from that point.
-
For added character we then move the eyelid up and down
-
and we use the Z value to sort the sprites
-
amongst one another, the eye being below the eyelid
-
so that we could animate it over the top.
-
The second enemy's animation was much simpler
-
and just meant that we rotated it back and forth.
-
For the mechanics of moving the enemies
-
we drive them by setting the velocity of the rigidbody.
-
When they encounter an obstacle such as a wall
-
they detect this by using a Physics.OverlapPoint function,
-
which checks for a point in space overlapping a collider.
-
Our walls, the towers at each side of the level,
-
are tagged as obstacles.
-
When this function detects them it calls the Flip function,
-
it reverses the X scale of the enemy
-
sending them moving in a different direction.
-
To kill the enemies, each time a rocket collides
-
with them it's script calls the Hurt function on
-
the particular enemy that it's hit.
-
In this function we subtract 1 from the
-
hit points of the particular enemy.
-
We then keep tabs on the enemy's health
-
inside the fixed update function.
-
If it drops to 0 we call the Death function.
-
When they die we perform a number of functions.
-
Firstly we disable all sprite renderers
-
as our 2D characters are made up of a
-
number of sprite objects that are animated.
-
This is because we simply want to swap out the
-
animated elements for a single sprite
-
of the dead character
-
We do this with the main sprite renderer by
-
setting it to use the sprite assigned to
-
the deadEnemy variable.
-
We then add to the score using the public
-
variable in the Score script
-
and we add some visual flare to the enemy's death
-
by adding torque to spin them as they die.
-
Because we need the enemies to fall through the
-
environment and land in the river when they die
-
we find all colliders on the object
-
and set their IsTrigger parameter to true.
-
This means that they will pass through the
-
environment colliders.
-
We then choose from an array of death audio clips
-
and play one of them back, before creating
-
an instance of the Score animation we showed you earlier.
-
To remove the dead enemy from the scene
-
we rely on the killTrigger object
-
It performs the same function as discussed earlier
-
as any non-player object touching it
-
will cause a splash animation and remove that object.
-
The sound effect of them hitting the water is
-
an audio clip attached to the animated
-
splash which plays on awake
-
so that when we create an instance of the animated
-
object we hear the sound right away.
-
To finish the game level re decorated the
-
background with a number of moving props
-
in order to make our game environment feel more dynamic.
-
We added flying swans, plus buses and taxis that drive
-
along the river bank.
-
First was the swan, which was created from a sprite sheet,
-
drawn in Photoshop and imported using
-
the Multiple Sprite Mode import setting
-
in the Inspector.
-
By choosing this approach Unity automates choosing
-
each frame in the sheet and importing
-
it as a separate sprite under the parent
-
hierarchy of the asset.
-
Because this is a linear set of animation frames
-
we could simply drag all of these sprites in to the
-
scene to let Unity animate them for us.
-
Et voila, our animated swan is ready to add
-
as a background element.
-
The swan was then given a rigidbody2D
-
so that we can use velocity to send it
-
across the screen.
-
As with the bus and the cab, the swan is saved as a prefab.
-
For the bus and taxi prefabs we
-
simply separated the bodies and wheels of
-
the vehicles in the sprite importer
-
and made a simple bobbing animation.
-
Applying a 2d rigidbody to these as well,
-
we were able to drive them across the screen
-
using velocity.
-
For all 3 props, the bus, taxi and the swan
-
we created a script we could reuse
-
which is in charge of spawning them.
-
The BackgroundPropsSpawner script
-
also handles the frequency, a speed to give them
-
and where on screen to spawn them.
-
This meant that we could make 3 creator objects
-
with the same script on.
-
Changing which prefab will be spawned
-
and what properties to give it.
-
Take a look at the comments in the script to learn more.
-
Finally, for more background dynamism,
-
we added rolling clouds, panning river details and fog.
-
We did this by adding 2 instances of the background sprite
-
to a parent object and simply used animation to slowly
-
pan them across the screen.
-
Because this animation loops our animation will
-
continue indefinitely.
-
And that's how we made our game.
-
We hope this overview has given you some idea of
-
how we create 2D games in Unity,
-
and we'll be creating more simply projects and
-
tutorials in the future.
-
We look forward to your feedback on Unity4.3
-
and we can't wait to see the great 2D titles
-
that you'll come up with.
-
Thanks for watching.