How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3

Kirill Muzykov

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:

  1. 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.

  2. Select it in the Hierarchy and rename it to laser.
  3. Set its Sorting Layer to Objects.
  4. Add Box Collider 2D component.
  5. 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.

  6. 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.

  7. Create new C# Script named LaserScript and attach it to laser.

Here is the full list of steps displayed:

rocket_mouse_unity_p3_40

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.

  1. 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.
  2. 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 low interval you will create a laser that will turn On and Off real quick, and by setting a high interval 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.

  3. 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.

rocket_mouse_unity_p3_41

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:

  1. Decreases the time left until next toggle.
  2. If timeUntilNextToggle is zero or even less then zero, it is time to toggle the laser state.
  3. Sets the correct state of the laser in the private variable.
  4. 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.
  5. Casts the more general Renderer class to a SpriteRenderer since you know the laser is a Sprite. It also sets the correct laser sprite. This will display laser_on sprite when the laser is On and laser_off sprite when the laser is Off.
  6. Resets timeUntilNextToggle variable since the laser has just been toggled.
  7. 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.

rocket_mouse_unity_p3_42

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.

rocket_mouse_unity_p3_43

Now, turn the laser into a prefab.

Solution Inside: Need help creating a laser prefab? SelectShow

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.

rocket_mouse_unity_p3_45

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.

rocket_mouse_unity_p3_46

After that, follow these steps to complete the animation:

  1. Open Sprites folder in the Project view.
  2. Select and drag mouse_die_0 and mouse_die_1 sprites to the Animation view's timeline.
  3. Set Samples to 8 to make the animation slower.
  4. 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.

rocket_mouse_unity_p3_47

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.

Solution Inside: Need help creating fall animation? SelectShow

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.

rocket_mouse_unity_p3_52

After this Make Transition from Any State to fall.

rocket_mouse_unity_p3_53

Select this transition and in Conditions, set dead to true and add grounded as a second parameter by clicking +. Set grounded to false.

rocket_mouse_unity_p3_54

Select this transition and in Conditions set both dead and grounded parameters to true.

rocket_mouse_unity_p3_60

This way there are two possible combinations:

  1. dead but not grounded
  2. dead and grounded

rocket_mouse_unity_p3_55

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.

rocket_mouse_unity_p3_56

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.

rocket_mouse_unity_p3_57

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.

rocket_mouse_unity_p3_58

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.

rocket_mouse_unity_p3_61

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.

rocket_mouse_unity_p3_62

Run the scene and collide with a laser.

rocket_mouse_unity_p3_63

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.

Solution Inside: Creating coin prefab SelectShow

After creating a coin GameObject, drag it from the Hierarchy and into the Prefabs folder in the Project view to create a coin Prefab.

rocket_mouse_unity_p3_65

Now add several coins to the scene by dragging coin Prefabs to the Scene view. Create something like this:

rocket_mouse_unity_p3_66

Run the scene.

rocket_mouse_unity_p3_67

Wait a second - the mouse died the moment it touched the coins? Are they poisoned?

rocket_mouse_unity_p3_68

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.....

rocket_mouse_unity_p3_69

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.

rocket_mouse_unity_p3_71

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.

rocket_mouse_unity_p3_72

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:

rocket_mouse_unity_p3_73

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.

rocket_mouse_unity_p3_74

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:

rocket_mouse_unity_p3_75

Select coins_v in the Hierarchy and drag it to Prefabs folder in the Project view to create Prefab.

rocket_mouse_unity_p3_76

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:

  1. Generates a random index for the object to generate. This can be a laser or one of the coin packs.
  2. Creates an instance of the object that was just randomly selected.
  3. Sets the object's position, using a random interval and a random height. This is controlled by script parameters.
  4. Adds a random rotation to the newly placed objects.
  5. 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:

  1. 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 with addObjectX.

  2. 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.
  3. This is the position of the object (coin pack or laser).
  4. By executing this code for each objX you get a maximum objX value in farthestObjectX 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)
  5. If current object is far behind, it is marked removal to free some resources.
  6. Removes objects marked for removal.
  7. 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.

rocket_mouse_unity_p3_78

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 :]

rocket_mouse_unity_p3_79

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.

rocket_mouse_unity_p3_80

Run the scene. You should see coins count displayed in top left corner.

rocket_mouse_unity_p3_81

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.

rocket_mouse_unity_p3_82

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.

rocket_mouse_unity_p3_83

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.

rocket_mouse_unity_p3_86

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:

rocket_mouse_unity_p3_85

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.

rocket_mouse_unity_p3_87

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.

rocket_mouse_unity_p3_88

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.

rocket_mouse_unity_p3_89

Run the scene. You should hear a nice sound when collecting a coin :]

rocket_mouse_unity_p3_90

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.

rocket_mouse_unity_p3_91

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.

rocket_mouse_unity_p3_92

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.

rocket_mouse_unity_p3_93

After that drag the second Audio Source component to the Jetpack Audio in the Mouse Controller script component.

rocket_mouse_unity_p3_94

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.

rocket_mouse_unity_p3_95

Adding Music

To add music just follow these simple steps:

  1. Select Main Camera in the Hierarchy.
  2. Add an Audio Source component in the Inspector.
  3. Drag music asset from the Project browser to Audio Clip property.
  4. Make sure Play On Awake and Loop are enabled.
  5. Decrease the Volume to 0.3, since the music is quite loud compared to other sounds.

rocket_mouse_unity_p3_96

That’s it. Run the scene and enjoy some music!

Adding Parallax Background

Currently this room with a view is pretty boring.

rocket_mouse_unity_p3_97

However, there are two ways to solve it:

  1. Create something to show behind the window.
  2. 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.

rocket_mouse_unity_p3_98

Do the same for window_foreground image.

rocket_mouse_unity_p3_99

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:

  1. Rename to ParallaxCamera.
  2. Set Position to (0, 10, 0)
  3. Set Projection to Orthographic
  4. Set Size to 3.2, the same size as Main Camera has.

rocket_mouse_unity_p3_100

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.

rocket_mouse_unity_p3_115

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.

rocket_mouse_unity_p3_101

Select parallaxBackground and change its Position to (0, 0, 10) and Scale to (11.36, 4.92, 0).

Note: You get this scale because background images have size of 1136 × 492 px.

rocket_mouse_unity_p3_102

Select parallaxForeground and set its Position to (0, 0, 9) and Scale to (11.36, 4.92, 0).

rocket_mouse_unity_p3_103

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.

rocket_mouse_unity_p3_104

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.

rocket_mouse_unity_p3_105

Do the same for parallaxBackground.

rocket_mouse_unity_p3_106

This is what you should see in the Scene view right now.

rocket_mouse_unity_p3_107

If you disable 2D mode and rotate a scene a little, this is how things are in the scene right now.

rocket_mouse_unity_p3_108

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.

rocket_mouse_unity_p3_109

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.

rocket_mouse_unity_p3_110

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.

rocket_mouse_unity_p3_111

Run the scene. You will now see some nice parallax scrolling.

rocket_mouse_unity_p3_112

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.

rocket_mouse_unity_p3_113

However, if you run the game right now you won’t see parallax background through the window.

rocket_mouse_unity_p3_114

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.

rocket_mouse_unity_p3_116

Run the scene. Now you will see parallax background through the window.

rocket_mouse_unity_p3_117

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.

rocket_mouse_unity_p3_119

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.

rocket_mouse_unity_p3_120

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 :]

rocket_mouse_unity_p3_121

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 :]

Kirill is a founder of PixelOxygen, an independent development company creating games and applications for iOS. Also, Kirill is the author of Cocos2D iPhone 3.0 Beginner’s Guide book. You can read his Blog or check his latest Games and Apps at the Apple AppStore.

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 44 total!

Android Team

... 15 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 15 total!

Resident Authors Team

... 17 total!

Podcast Team

... 8 total!

Recruitment Team

... 9 total!