Subtitles section Play video Print subtitles 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.
B2 UK sprite animation player script object unity Unity 4.3 - 2D Game Development Walkthrough 321 11 朱瑛 posted on 2014/05/02 More Share Save Report Video vocabulary