This is the final part of How to create a game like Jetpack Joyride in Unity 2D tutorial series. If you’ve missed previous parts you better to complete them first. Here they are: Part 1 and Part 2.
As I’ve mentioned at the end of the previous part this part has all the fun. This will be the reward for going this far :]
In this part you will add lasers, coins, sound effects, music and even parallax scrolling. So enough talking lets start the fun!
Getting Started
You can continue using the project you created in the second part or alternatively you can download the starter project for this part. They should be almost identical.
If you want to download the starter project use this link: RocketMouse_Final_Part2
When you’re ready open the RocketMouse.unity scene and let’s begin!
Adding Lasers
The mouse flying through the room is great, but what is the challenge of this game? It is time to add some obstacles, and what can be cooler than lasers> ;]
Lasers will be generated randomly, in a similar manner as you generate rooms, so you need to create a Prefab. Also you will need to create a small script controlling the laser.
Creating Laser
Here are the steps required to create a laser object:
- In the Project view find laser_on sprite and drag it to the scene.
Note: Since laser Prefab will consist of only the laser itself you don’t have to position it at the origin or something like this.
- Select it in the Hierarchy and rename it to laser.
- Set its Sorting Layer to Objects.
- Add Box Collider 2D component.
- Enable Is Trigger property in Box Collider 2D component.
Note: When the Is Trigger property is enabled collider will trigger collision events, but will be ignored by the physics engine. In other words if the mouse touches the laser you will get notified. However, the laser will not block the mouse movement.
It is very convenient for many reasons. For example if the mouse dies on top of the laser it wont hang in the air lying on the laser. Also the mouse can still move a little forward after hitting the laser due to inertia, instead of bouncing back from the laser
Besides that, real lasers are not some hard objects, so by enabling this property you’re just simulating the real laser.
- Set Size of collider, X to 0.18 and Y to 3.1.
Note: This creates a collider only where the laser is, leaving emitters on both ends absolutely safe.
- Create new C# Script named LaserScript and attach it to laser.
Here is the full list of steps displayed:
Turning laser On and Off from the Script
Open LaserScript in MonoDevelop and add following instance variables:
//1 public Sprite laserOnSprite; public Sprite laserOffSprite; //2 public float interval = 0.5f; public float rotationSpeed = 0.0f; //3 private bool isLaserOn = true; private float timeUntilNextToggle; |
It might seem like a lot of variables, but in fact everything is quite trivial.
- The Laser is going to be in two states: On and Off. There is a separate image for each state. You will specify each state image in just a moment.
- These properties allow you to add a bit of random fluctuation. You can set a different
interval
so that all lasers on the level didn’t work exactly the same. By setting a lowinterval
you will create a laser that will turn On and Off real quick, and by setting a highinterval
you will create a laser that will stay in its state for quite long, and who knows, maybe the mouse can even fly trough the laser when it is Off.The
rotationSpeed
variable serves similar purpose. It specifies the speed of the laser rotation. - Finally there are two private variables that are used to toggle the laser state.
Here is an example of lasers. Each has a different interval
and rotationSpeed
.
Add the following code in Start
:
timeUntilNextToggle = interval; |
This will set the time until the laser should toggle its state for the first time.
To toggle and rotate the laser add FixedUpdate
with following:
void FixedUpdate () { //1 timeUntilNextToggle -= Time.fixedDeltaTime; //2 if (timeUntilNextToggle <= 0) { //3 isLaserOn = !isLaserOn; //4 collider2D.enabled = isLaserOn; //5 SpriteRenderer spriteRenderer = ((SpriteRenderer)this.renderer); if (isLaserOn) spriteRenderer.sprite = laserOnSprite; else spriteRenderer.sprite = laserOffSprite; //6 timeUntilNextToggle = interval; } //7 transform.RotateAround(transform.position, Vector3.forward, rotationSpeed * Time. fixedDeltaTime); } |
Here is what that code does:
- Decreases the time left until next toggle.
- If
timeUntilNextToggle
is zero or even less then zero, it is time to toggle the laser state. - Sets the correct state of the laser in the private variable.
- The laser collider is enabled only when the laser is On. This means that mouse can fly through the laser freely if it is Off.
- Casts the more general
Renderer
class to aSpriteRenderer
since you know the laser is a Sprite. It also sets the correct laser sprite. This will displaylaser_on
sprite when the laser is On andlaser_off
sprite when the laser is Off. - Resets
timeUntilNextToggle
variable since the laser has just been toggled. - Rotates the laser around the z-axis using its
rotationSpeed
.
Note: To disable rotation you can just set rotationSpeed to zero.
Setting the Laser Script Parameters
Switch back to Unity and select laser in the Hierarchy. Make sure the Laser Script component is visible.
Drag laser_on sprite from the Project view to the Laser On Sprite property of the Laser Script component in the Inspector.
Then drag laser_off sprite to Laser Off Sprite property.
Set Rotation Speed to 30.
Now set the laser Position at (2, 0.25, 0). This is to test that everything works correctly.
Run the scene. You should see a laser nicely rotating.
Now, turn the laser into a prefab.
Killing the Mouse
Right now the mouse can easily pass through the enabled laser without so much as a whisker getting bent. This is not a good example for kids. Kids should see the consequences of playing with lasers. :]
Better get to fixing that.
Open MouseController script and add dead
instance variable.
private bool dead = false; |
This instance variable indicates a dead mouse. Once this variable is true, you will not be able to activate the jetpack, move forward, and so on.
Now add the following two methods somewhere within the MouseController
class:
void OnTriggerEnter2D(Collider2D collider) { HitByLaser(collider); } void HitByLaser(Collider2D laserCollider) { dead = true; } |
The OnTriggerEnter2D
method is called when mouse collides with any laser. Currently, it just makes marks the mouse dead.
Note: It might seem strange why you need to create a separate method for one line of code, but you will add more code to both OnTriggerEnter2D
and HitByLaser
so it is just a way to make future changes more convenient.
Now, when the mouse is dead it shouldn’t move forward or fly using the jetpack. You’re not filming The Flying Dead, aren’t you? :]
Make the following changes in FixedUpdate
to make sure this doesn’t happen:
void FixedUpdate () { bool jetpackActive = Input.GetButton("Fire1"); jetpackActive = jetpackActive && !dead; if (jetpackActive) { rigidbody2D.AddForce(new Vector2(0, jetpackForce)); } if (!dead) { Vector2 newVelocity = rigidbody2D.velocity; newVelocity.x = forwardMovementSpeed; rigidbody2D.velocity = newVelocity; } UpdateGroundedStatus(); AdjustJetpack(jetpackActive); } |
Note that now jetpackActive
is always false
when the mouse is dead. This means that no upward force will be applied to the mouse and also, since jetpackActive
is passed to AdjustJetpack
, the particle system will be disabled.
In addition to that you don’t set the mouse velocity
if its dead, which is pretty obvious thing to do.
Switch back to Unity and run the scene. Make the mouse fly into the laser.
Hm.., it looks like you can no longer use the jetpack and the mouse doesn’t move forward, but why is the mouse running like crazy?
Guesses? Anyone? Bueller? Bueller?
The reason for this strange behavior is that you have two states for the mouse: run
and fly
, and when the mouse falls down on the floor it becomes grounded so run animation is activated.
Since the game cannot end like this, you need to add few more states to show that the mouse is dead.
Adding the Fall and Die Mouse Animations
Select mouse GameObject in the Hierarchy and open the Animation view.
Create new animation called die. Save new animation to Animations folder.
After that, follow these steps to complete the animation:
- Open Sprites folder in the Project view.
- Select and drag mouse_die_0 and mouse_die_1 sprites to the Animation view’s timeline.
- Set Samples to 8 to make the animation slower.
- Note that recording mode is on. The easiest way to notice this is by looking at playback buttons, which has turned red. Click on the recording button to stop the recording. This will make the playback buttons to return to normal color.
That was easy. In fact I think you can create fall animation yourself. This time just use the mouse_fall sprite as a single frame. However, if you get stuck feel free to expand the section below for detailed instructions.
Transitioning to Fall and Die Animations
After creating the animations, you need to make the Animator switch to the corresponding animation at the right time. To do this, you’re going to transition from a special state called Any State, since it doesn’t matter what the current state the mouse is currently in, when it hits the laser.
Since you created two animations(fall
and die
), there is a difference if the mouse hits the laser in the air or while running on the ground. In the first case, the mouse should switch to the fall animation state and only after hitting the ground play die animation.
However, in both cases you need one new parameter. Open the Animator view and create new Bool parameter called dead.
After this Make Transition from Any State to fall.
Select this transition and in Conditions, set dead to true and add grounded as a second parameter by clicking +. Set grounded to false.
Select this transition and in Conditions set both dead and grounded parameters to true.
This way there are two possible combinations:
- dead but not grounded
- dead and grounded
This way, if the mouse is dead, but still in the air (not grounded) the state is switched to fall. However, if the mouse is dead and grounded, or was dead and becomes grounded after falling to the ground, the state is switched to die.
The only thing left to do is update the dead parameter from MouseController script.
Open MouseController script and add the following line to the end of HitByLaser
:
animator.SetBool("dead", true); |
This will set the dead
parameter of the Animator component to true
.
Run the scene and fly into the laser.
As you can see, when the mouse hits the laser, the script sets a dead
parameter to true
and the mouse switches to fall state (since grounded is still false). However, when the mouse reaches the floor, the script sets grounded parameter to true
. Now, all conditions are met to switch to die state.
Using the Trigger to Make the Mouse Die Once
There is an old saying that you only can die once, however the mouse right now is dying all the time. You can check this yourself by looking at Animator view after the mouse dies.
This happens because the transition from Any State
to die
happens all the time. The grounded
and dead
parameters are always true, which triggers the animator to transition from Any State
To fix this, you use a special parameter type called Trigger. Trigger parameters are very similar to Bool, with the exception that they are automatically reset after used. This is a relatively new featured added in Unity 4.3.
Open the Animator view and add a new Trigger parameter called dieOnceTrigger. Set its state to On, by checking the checkbox next to it.
Next, select the transition from Any State to die, and add dieOnceTrigger in the Conditions section.
Run the scene and fly into the laser once again.
I wouldn’t say that this makes things better, but fortunately this is very easy to fix. This happens only because die
animation is set to loop by default.
Open Animations folder in the Project view and select animation. In the Inspector uncheck Loop Time. This disables the animation looping.
Run the scene and collide with a laser.
This time the mouse stays on the floor after dying.
Adding Coins
While death dealing lasers are fun to implement, how about adding some coins for mouse to collect.
Creating Coin Prefab
Creating a coin Prefab is so easy and similar to creating laser so you should try doing this yourself. Just use the coin sprite and follow these tips:
- Don’t create any scripts for coin.
- Use Circle Collider 2D instead of Box Collider 2D.
- Enable Is Trigger option for the collider, since you don’t want the coin to stop the mouse movement.
If you have any questions just take a look at the expandable section below.
After creating a coin GameObject, drag it from the Hierarchy and into the Prefabs folder in the Project view to create a coin Prefab.
Now add several coins to the scene by dragging coin Prefabs to the Scene view. Create something like this:
Run the scene.
Wait a second – the mouse died the moment it touched the coins? Are they poisoned?
No, the coins are okay. The mouse dies because of the code in MouseController script, which handles any collision as a collision with a laser.
Using Tags to Distinguish Coins from Lasers
To distinguish coins from lasers you will use Tags, which are made exactly for that purpose.
Select coin Prefab right in Prefabs folder in the Project view. This will open the Prefab properties in the Inspector. Find the Tag dropdown right below the name field, open it, and chose Add Tag…..
This will open the already familiar Tags & Layers editor in the Inspector. In the Tags section add a tag named Coins.
Note: It will automatically increase the Size to 2 and add Element 1, but that’s ok.
Now select coin Prefab in the Project view once again and set its Tag to Coins in the Inspector.
Of course just setting the Tag property doesn’t make the script to distinguish coins from lasers, you’ll still need to modify some code.
Updating MouseController Script to use Tags
Open the MouseController script and add a coins
counter variable:
private uint coins = 0; |
This is where you’ll store the coin count.
Then add CollectCoin
method:
void CollectCoin(Collider2D coinCollider) { coins++; Destroy(coinCollider.gameObject); } |
This method increases the coin count and removes the coin from the scene so that you don’t collide with it a second time.
Finally, make following changes in OnTriggerEnter2D
:
void OnTriggerEnter2D(Collider2D collider) { if (collider.gameObject.CompareTag("Coins")) CollectCoin(collider); else HitByLaser(collider); } |
With this change, you call CollectCoin
in case of a coin and HitByLaser
in all other cases.
Note: In this game there are only two types of objects so it is okay to use else case for lasers. In a real game, you should assign tags for all object types and check them implicitly.
Run the scene.
Now that’s much better. The mouse collects coins and dies if it hits a laser. It looks like you’re ready to generate lasers and coins using a script.
Generating Coins and Lasers
Generating coins and lasers is similar to what you did when you generated rooms. The algorithm is almost identical, but before writing the code, you need to improve the coin generation so as to make it more fun for the player.
Currently you have a Prefab that consists of only one coin, so if you write generation code you will simply generate only one coin here and there on the level. This is not fun! How about creating different figures from coins and generating a pack of coins at once?
Creating Pack of Coins Prefab
Open the Prefabs folder in the Project viewer and create 9 coins on the scene using the coin Prefab. It should look something like this:
Select any coin and set its Position to (0, 0, 0). This will be central coin. You will add all coins into Empty GameObject, so you need to build your figure around the origin.
After placing the central coin, build a face down triangle shaped figure around the coin. Don’t forget that you can use Vertex Snapping by holding V key.
Now create an Empty GameObject by choosing GameObject\Create Empty. Select it in the Hierarchy and rename it to coins_v.
Set its Position to (0, 0, 0) so that it has the same position as the central coin. After that select all coins in the Hierarchy and add them to coins_v. You should get something like this in the Hierarchy:
Select coins_v in the Hierarchy and drag it to Prefabs folder in the Project view to create Prefab.
Note: You can create as many different coins combinations as you want, just as with rooms, the generator script will provide a property where you will specify all the possible objects to generate.
You’re done. Now remove all the coins and lasers from the scene since they will be generated by the script.
Adding New Parameters to GeneratorScript
Open GeneratorScript and add following instance variables:
public GameObject[] availableObjects; public List<GameObject> objects; public float objectsMinDistance = 5.0f; public float objectsMaxDistance = 10.0f; public float objectsMinY = -1.4f; public float objectsMaxY = 1.4f; public float objectsMinRotation = -45.0f; public float objectsMaxRotation = 45.0f; |
The availableObjects
array, will hold all objects that the script can generate (i.e. different coins packs and laser). The objects
list will store the created objects, so that you could check if you need to add more ahead of the player or remove them when they have left the screen.
Note: Just as with rooms, you can create several lasers or coins at the beginning of the level where you don’t want to rely on random generation code. Just don’t forget to add them to objects list.
The variables objectsMinDistance
and objectsMaxDistance
are used to pick a random distance between the last object and the currently added object, so that the objects don’t appear at fixed interval.
By using objectsMinY
and objectsMaxY
you can configure the maximum and minimum height at which objects are placed, and by using objectsMinRotation
and objectsMaxRotation
you can configure the rotation range.
Adding The Method to Add New Object
New objects are added in AddObject
in a similar way to how rooms are added.
Add the following:
void AddObject(float lastObjectX) { //1 int randomIndex = Random.Range(0, availableObjects.Length); //2 GameObject obj = (GameObject)Instantiate(availableObjects[randomIndex]); //3 float objectPositionX = lastObjectX + Random.Range(objectsMinDistance, objectsMaxDistance); float randomY = Random.Range(objectsMinY, objectsMaxY); obj.transform.position = new Vector3(objectPositionX,randomY,0); //4 float rotation = Random.Range(objectsMinRotation, objectsMaxRotation); obj.transform.rotation = Quaternion.Euler(Vector3.forward * rotation); //5 objects.Add(obj); } |
This method takes the position of the last (rightmost) object and creates a new object at the random position (within given interval) after it. By calling this method, each time the last object is about to show on the screen – you create a new object off screen and keep an endless flow of new coins and lasers.
Here is the description of each code block:
- Generates a random index for the object to generate. This can be a laser or one of the coin packs.
- Creates an instance of the object that was just randomly selected.
- Sets the object’s position, using a random interval and a random height. This is controlled by script parameters.
- Adds a random rotation to the newly placed objects.
- Adds the newly created object to the objects list for tracking and ultimately, removal (when it leaves the screen).
With the code in place, the only thing left to do is actually use it.
Generating and Removing Objects When Required
Add the following in the GeneratorScript:
void GenerateObjectsIfRequired() { //1 float playerX = transform.position.x; float removeObjectsX = playerX - screenWidthInPoints; float addObjectX = playerX + screenWidthInPoints; float farthestObjectX = 0; //2 List<GameObject> objectsToRemove = new List<GameObject>(); foreach (var obj in objects) { //3 float objX = obj.transform.position.x; //4 farthestObjectX = Mathf.Max(farthestObjectX, objX); //5 if (objX < removeObjectsX) objectsToRemove.Add(obj); } //6 foreach (var obj in objectsToRemove) { objects.Remove(obj); Destroy(obj); } //7 if (farthestObjectX < addObjectX) AddObject(farthestObjectX); } |
This method checks if an object should be added or removed somewhere within GeneratorScript
class. Here’s the breakdown:
- Calculates key points ahead and behind the player.
If the laser or coin pack is to the left of
removeObjectsX
, then it has already left the screen and is far behind. You will have to remove it.If there is no object after
addObjectX
point, then you need to add more objects since the last of the generated objects is about to enter the screen.The
farthestObjectX
variable is used to find the position of the last (rightmost) object to compare it withaddObjectX
. - Since you cannot remove objects from the currently iterating, you place objects that you need to remove in a separate array to be removed after the loop.
- This is the position of the object (coin pack or laser).
- By executing this code for each
objX
you get a maximumobjX
value infarthestObjectX
at the end of the cycle (or the initial value of 0, if all objects are to the left of origin, but not in our case) - If current object is far behind, it is marked removal to free some resources.
- Removes objects marked for removal.
- If the player is about to see the last object and there are no more objects ahead, the scripts adds more.
To make this method work, add a call to GenerateObjectsIfRequired
at the end of FixedUpdate
:
GenerateObjectsIfRequired(); |
This method is called with each fixed update, insuring that there will always be objects ahead of the player.
Setting up Script Parameters
To make the GeneratorScript work, you need to set few of its parameters. Switch back to Unity and select the mouse GameObject in the Hierarchy.
Find the Generator Script component in the Inspector and make sure that the Prefabs folder is opened in the Project view.
Drag the coins_v Prefab from the Project view to the Available Objects list in the GeneratorScript component. After that, drag the laser Prefab from the Project view also to the Available Objects list in the GeneratorScript.
That’s it! Run the scene.
Note: In the animated GIF, the lasers are not rotating because I set rotationSpeed parameter of LaserScript to 0. With rotating lasers, it is quite hard to record a good gameplay video :]
Now this looks like an almost complete game!
Adding GUI Elements
What’s the point of collecting coins if you can’t see how many coins that you have collected? Also, There’s no way for the player to restart the game once they have died. It’s time to fix these issues by adding a couple of GUI elements.
Displaying Coins Count
Open the MouseController script in MonoDevelop and add the following instance variable:
public Texture2D coinIconTexture; |
Then add DisplayCoinsCount
method:
void DisplayCoinsCount() { Rect coinIconRect = new Rect(10, 10, 32, 32); GUI.DrawTexture(coinIconRect, coinIconTexture); GUIStyle style = new GUIStyle(); style.fontSize = 30; style.fontStyle = FontStyle.Bold; style.normal.textColor = Color.yellow; Rect labelRect = new Rect(coinIconRect.xMax, coinIconRect.y, 60, 32); GUI.Label(labelRect, coins.ToString(), style); } |
This method uses GUI.DrawTexture
to draw a coin icon at the top left corner of the screen. Then it creates a GUIStyle
for the label to change its size, bolds the text, then changes the text color to yellow. Or should I say gold? :]
Finally, it uses GUI.Label
to display the amount of coins to the right of the coins icon.
All code to display GUI elements should be called from OnGUI
method that is called by Unity. So go ahead and add the OnGUI
method that simply calls DisplayCoinsCount
.
void OnGUI() { DisplayCoinsCount(); } |
Switch back to Unity and select mouse GameObject in the Hierarchy. Open Sprites folder in the Project view and drag coin sprite to Coin Icon Texture field of Mouse Controller component in the Inspector.
Run the scene. You should see coins count displayed in top left corner.
Raising the Dead
Open MouseController script again, but this time add DisplayRestartButton
method:
void DisplayRestartButton() { if (dead && grounded) { Rect buttonRect = new Rect(Screen.width * 0.35f, Screen.height * 0.45f, Screen.width * 0.30f, Screen.height * 0.1f); if (GUI.Button(buttonRect, "Tap to restart!")) { Application.LoadLevel (Application.loadedLevelName); }; } } |
Most of this method is only executed when mouse GameObject is dead
and grounded
. You don’t want the player to miss the mouse dripping dead, especially after you put so much efforts creating those animations :]
When the mouse is dead and grounded, you display a button right in the center of the screen with a Tap to restart! label on it. If the player taps this button, you simply reload the currently loaded scene.
Now add a call to DisplayRestartButton
at the end of OnGUI
and you’re done:
DisplayRestartButton(); |
Switch to Unity and Run the scene. Fly into some laser to kill the mouse. When the button appears, tap it, and you will restart the game.
Note: You can customize how button looks by creating a public instance variable like this:
public GUIStyle restartButtonStyle; |
Then you can use customize the button’s look and feel.
You can find out more information about customizing the button’s look and feel by checking out the Unity documentation. Keep in mind that with the arrival of Unity 4.6, the New GUI will be released which provides an entirely new way of working with Unity’s GUI tools. You can find out more about it over here
Adding Sound and Music
The game is deadly quiet. You will be amazed how much better it play once add some sounds and music to it.
Hitting Laser Sound
Open Prefabs folder in the Project view and select laser Prefab.
In the Inspector, add an Audio Source component by clicking Add Component and selecting Audio\Audio Source. Then open the Audio folder in the Project view and drag laser_zap sound to Audio Clip field.
Don’t forget to uncheck Play On Awake. Otherwise the laser zap sound will be played right at the start of the game, just as if the mouse started on the laser.
Oh, that would be a cruel game if the player character died right at the start and you couldn’t do anything about it. Maybe it will even break all the records in the AppStore since it is that much harder then Flappy Bird :]
This is what you should get:
Note: Make sure you select the laser Prefab not the laser instance in the Hierarchy. Otherwise you will need additionally to click Apply button to save changes made in instance to Prefab.
Now open MouseController script in MonoDevelop and add following code to the beginning of HitByLaser
:
if (!dead) laserCollider.gameObject.audio.Play(); |
Note: It is important to add it to the beginning of the method, before you set dead
to true
or otherwise it won’t be played even once.
When the mouse touches the laser, you get a reference to the laser’s collider in OnTriggerEnter2D
. By accessing gameObject
property of laserCollider
you then get the laser object itself. Then, you can access the Audio Source component, and make it play.
Run the scene. You will now hear a zap sound when the mouse hits any laser.
However, the zap sound is too quiet and if you have a stereo speakers or headphones, the sound is played a bit t to the left. This happens because Audio Listener component is placed on Main Camera by default, and the Audio Source component is placed on laser. Fortunately, this is an easy issue to fix.
Note: Don’t forget that Unity was originally developed to create 3D games. In 3D games, you need 3D sounds (e.g. to hear that someone shoots at you from the side or even from the back).
However, although you still might want to use 3D sound in some 2D games, most of the time you just want the sound played at the same volume independently from where the audio source is placed.
Disabling 3D Sound Mode
Open Audio folder in the Project view and select laser_zap file. This will open the Import Settings editor in the Inspector. Uncheck the 3D Sound option and click Apply.
Do the same for the rest audio files:
- coin_collect
- footsteps
- jetpack_sound
- music
Collecting Coin Sound
While you could apply the same approach with coins, you’ll be something a little bit different.
Open the MouseController script in MonoDevelop and add following instance variable:
public AudioClip coinCollectSound; |
Scroll down to the CollectCoin
method and add following line of code at the end of the method:
AudioSource.PlayClipAtPoint(coinCollectSound, transform.position); |
This way you use a static method of the AudioSource class to play the coin collect sound at the position where the mouse is currently located.
Switch back to Unity and select the mouse GameObject in the Hierarchy. Drag the coin_collect from the Project view to the Coin Collect Sound field in MouseController script.
Run the scene. You should hear a nice sound when collecting a coin :]
Jetpack and Footsteps Sound
Next, you need to add the sound of the jetpack and the mouse’s footsteps when it is running on the floor. This will be just a little bit different since mouse will have to have two Audio Source components at once.
Adding Audio Sources
Select the mouse GameObject in the Hierarchy and add two Audio Source components. Drag footsteps from the Project view to the Audio Clip of the first Audio Source component. Then drag jetpack_sound to Audio Clip field of the second Audio Source component.
Enable Play On Awake and Loop for both Audio Sources.
If you run the scene, you will hear that both sounds are playing all the time, independently of whether the mouse is flying or running on the floor. You’ll fix this in code.
Switching Between Footsteps and Jetpack Sounds
Open MouseController script in MonoDevelop and add two following instance variables:
public AudioSource jetpackAudio; public AudioSource footstepsAudio; |
These reference your newly created Audio Sources.
Now add AdjustFootstepsAndJetpackSound
method:
void AdjustFootstepsAndJetpackSound(bool jetpackActive) { footstepsAudio.enabled = !dead && grounded; jetpackAudio.enabled = !dead && !grounded; jetpackAudio.volume = jetpackActive ? 1.0f : 0.5f; } |
This method enables and disables the footsteps and the jetpack Audio Source component. The footsteps sound is enabled when the mouse is not dead and on the ground, the jetpack sound only when the mouse is not dead and not on the ground.
In addition, this method also adjusts the jetpack volume, so that it corresponds with the particle system
Finally add a call to AdjustFootstepsAndJetpackSound
at the end of FixedUpdate
:
AdjustFootstepsAndJetpackSound(jetpackActive); |
Now you need to assign references to Audio Source components within mouse GameObject to footstepsAudio
and jetpackAudio
variables.
Setting Footstep and Jetpack Script Variables
Switch back to Unity and select mouse GameObject in the Hierachy. You’re going to work only within the Inspector window. Collapse all components except Mouse Controller.
Now drag the top Audio Source component to Footsteps Audio in the Mouse Controller script component.
Note: In my case I know that the first Audio Source in the Inspector is the footsteps sound clip, but you might want temporarily expand the Audio Source component to check this.
After that drag the second Audio Source component to the Jetpack Audio in the Mouse Controller script component.
Run the scene. Now you should hear the footsteps when the mouse is running on the floor and jetpack engine when it’s flying. Also the jetpack sound should become stronger when you enable jetpack by holding the left mouse button.
Adding Music
To add music just follow these simple steps:
- Select Main Camera in the Hierarchy.
- Add an Audio Source component in the Inspector.
- Drag music asset from the Project browser to Audio Clip property.
- Make sure Play On Awake and Loop are enabled.
- Decrease the Volume to 0.3, since the music is quite loud compared to other sounds.
That’s it. Run the scene and enjoy some music!
Adding Parallax Background
Currently this room with a view is pretty boring.
However, there are two ways to solve it:
- Create something to show behind the window.
- Don’t use windows :]
Of course you’ll go with the first one, but instead of adding a motionless background image you will add a parallax background.
Note: To implement parallax scrolling for this game I used the technique described in one of the Mike Geig’s videos that you can watch over here. The link is provided in the end of this part.
Here is how things will work. You will add two Quads, one for background and one for foreground parallax layer.
Note: You can read more about Quads in Unity documentation. To simplify things a little, you can think of them as just a rectangles with a texture stretched on it. Well, at least in this case.
You might wonder why do you need to use Quad
instead of a typical Sprite
? The reason for this is that you can’t change the Sprite
’s image wrapping mode. Well, at least at the moment of writing this tutorial. And we need to change the wrapping mode to make sure the texture is endlessly repeated while we moving it to the left. It will get clearer in a moment.
You will set a texture for each quad, and instead of moving quads to simulate movement you will simply move the textures within the quad at a different speed for the background and the foreground layer.
Preparing Background Images
To use background images with quads you need to adjust how they are imported to Unity.
Open Sprites folder in the Project view and select window_background. In the Inspector change its Texture Type to Texture instead of Sprite. This will change how the look of the Inspector.
After that change Wrap Mode that just appeared to Repeat. Click Apply.
Do the same for window_foreground image.
Creating Another Camera
Wait, what, another camera? The Main Camera is reserved for following the mouse through the level. This new camera will render the parallax background and won’t move.
Create new camera by selecting GameObject\Create Other\Camera. Select it in the Hierarchy and make following changes in the Inspector:
- Rename to ParallaxCamera.
- Set Position to (0, 10, 0)
- Set Projection to Orthographic
- Set Size to 3.2, the same size as Main Camera has.
Since you have two cameras you also have 2 audio listeners in the scene. Disable Audio Listener in ParallaxCamera or you will get following warning.
There are 2 audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene. |
Creating Quads
Create two Quad objects by choosing GameObject\Create Other\Quad. Name the first quad parallaxBackground and the second name parallaxForeground. Drag both quads to ParallaxCamera to add them as children.
Select parallaxBackground and change its Position to (0, 10, 10) and Scale to (11.36, 4.92, 0).
Note: You get this scale because background images have size of 1136 × 492 px.
Select parallaxForeground and set its Position to (0, 10, 9) and Scale to (11.36, 4.92, 0).
Note: This time you set the z-coordinate position. Since you cannot use Sorting Layers for quads you need to set the background quad behind the foreground quad.
Setting Quad Textures
Open Sprites folder in the Project view. Drag the window_background over to the parallaxBackground and window_foreground over parallaxForeground in the Hierarchy.
Then select parallaxForeground in the Hierarchy. You will see that a Mesh Renderer component was added. Click on the Shader drop down and select Unlit\Transparent.
Do the same for parallaxBackground.
This is what you should see in the Scene view right now.
If you disable 2D mode and rotate a scene a little, this is how things are in the scene right now.
Run the scene. You will see that the background is in front of the main level. This is useful so you can see how the textures move with ParallaxScrolling. Once you have the textures moving, you will move it back to the background.
Making Textures Move
You will not move the Quads. Instead, you’re going to move the textures of the quads by changing the texture offset. Since you set the Wrap Mode to Repeat the texture will repeat itself.
Note: This doesn’t won’t work with any image. They background images designed to be repeated. In other words if you repeat the background horizontally many times and each left side of the picture will perfectly fit the ride side.
Create a new C# Script called ParallaxScroll and attach it to ParallaxCamera.
Open ParallaxScript in MonoDevelop and add the following instance variables:
public Renderer background; public Renderer foreground; public float backgroundSpeed = 0.02f; public float foregroundSpeed = 0.06f; |
The Renderer
variables will hold a reference to the Mesh Renderer component of each of the quads so that you could adjust their texture properties. The backgroundSpeed
and foregroundSpeed
just define speed for each background.
Add following code to Update
:
float backgroundOffset = Time.timeSinceLevelLoad * backgroundSpeed; float foregroundOffset = Time.timeSinceLevelLoad * foregroundSpeed; background.material.mainTextureOffset = new Vector2(backgroundOffset, 0); foreground.material.mainTextureOffset = new Vector2(foregroundOffset, 0); |
This code increases the texture offset of each the quad’s texture with time, thus moving it. The speed is different since the script uses backgroundSpeed
and foregroundSpeed
coefficients.
Switch back to Unity and select ParallaxCamera in the Hierarchy. Drag the parallaxBackground quad to Background field of the ParallaxScroll script and parallaxForeground to Foreground.
Run the scene. You will now see some nice parallax scrolling.
But what about the level itself? You can’t see it!
Fixing the Order of Cameras
Select ParallaxCamera in the Hierarchy. In the Inspector, find Camera component and look for a Depth field, set it to -2.
Note: The Depth of the ParallaxCamera should be lower then Depth of Main Camera, so check your Main Camera Depth if required and adjust Depth of ParallaxCamera to be lower.
However, if you run the game right now you won’t see parallax background through the window.
To fix this, select Main Camera in the Hierarchy and set its Clear Flags to Depth Only. This way it won’t clear out the picture drawn by parallax camera.
Run the scene. Now you will see parallax background through the window.
Few Improvements
Although you can see the treetops and clouds throughout the window, it is better to move background quads a bit higher, so that you can see the hills.
Another thing to improve is that the background keeps moving even after the mouse dies.
Stoping Parallax Scrolling After the Mouse Dies
Open ParallaxScroll script in MonoDevelop and add public offset variable.
public float offset = 0; |
You will use it instead of Time.timeSinceLevelLoad
, so in Update
replace the code where you calculate offsets with this:
float backgroundOffset = offset * backgroundSpeed; float foregroundOffset = offset * foregroundSpeed; |
Now open MouseController script and add following public variable:
public ParallaxScroll parallax; |
Then add following code to the end of FixedUpdate
:
parallax.offset = transform.position.x; |
This way you will use the mouse position as offset instead of time.
Switch back to Unity and select mouse GameObject in the Hierarchy. Make sure MouseController script is visible in the Inspector.
Drag ParallaxCamera from the Hierarchy to Parallax field in the Inspector.
This will allow MouseController script to change the offset
variable of ParallaxScroll script.
Moving Background a bit higher
Set Y component of Position of both parallaxBackground and parallaxForeground quads to 0.7. This will make them a little higher so that you can see the hills.
Run the scene. Now the background stops moving as soon as the mouse dies, and you can see more background through the window.
Note: In fact, the mouse doesn’t have to die in order to stop the background. As soon as the mouse stops, the background will also stop. If for some reason the mouse flies backward, the background will also move in the opposite direction.
However, this is too sad an image to end this tutorial, so I decided to add a kitty :]
Where To Go from Here?
I hope you liked this tutorial as much as I liked creating it.
You can download the final project here: Download Final Project
If you want to know more about the making of the actual Jetpack Joyride game check out this video.
Creating a parallax background is heavily inspired by this video by Mike Geig (he has a lot of really cool videos on Unity).
The cat image is made by Nicolas Suzor and you can find it on Flickr.
Please post your questions and comments below. Thank you for completing this tutorial :]
How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3 is a post from: Ray Wenderlich
The post How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3 appeared first on Ray Wenderlich.