Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4370

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

$
0
0

This is the final part of our three-part tutorial series on how to create a game like Jetpack Joyride in Unity 2D. If you’ve missed the previous parts, you should head back and complete Part 1 and Part 2 first.

In this part you will add lasers, coins, sound effects, music and even parallax scrolling. So enough talking, let’s get to the fun!

Getting Started

You can continue on with this tutorial using the project you created in the second part. Alternatively, you can download the RocketMouse Part 2 Final project in the materials at the top or bottom of this tutorial.

Open the RocketMouse.unity scene and get going!

Adding Lasers

The mouse flying through the room is great, but to make things interesting you’ll add some obstacles. What can be cooler than lasers?

Lasers will be generated randomly in a similar manner to the room generation, so you need to create a Prefab. You will need to create a small script to control the laser also.

Creating a Laser

Here are the steps required to create a laser object:

  1. In the Project view, find the laser_on sprite in the Sprites folder and drag it into the scene. Since the laser Prefab will consist of only the laser itself you don’t have to position it at the origin.
  2. Select laser_on in the Hierarchy and rename it to laser.
  3. Set the Sprite Renderer Sorting Layer to Objects.
  4. Add a Box Collider 2D component.
  5. Enable the Is Trigger property in the Box Collider 2D component.
    Note: When the Is Trigger property is enabled, the 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.

    This is convenient for many reasons. For example, if the mouse dies on top of the laser, it won’t hang in the air, lying there on the laser. Also, the mouse would likely still move forward a bit after hitting the laser instead of bouncing back due to inertia. Besides that, real lasers are not a hard, physical object, so enabling this property simulates the real laser.

  6. Set the Size of the collider: X to 0.18 and Y to 3.1. This creates a collider only where the laser is, leaving the emitters on both ends absolutely safe.
  7. Create a new C# Script named LaserScript and attach it to laser.

Here is the full list of steps displayed:

Turning the Laser On and Off From the Script

Open the LaserScript and add the following instance variables:

//1
public Sprite laserOnSprite;
public Sprite laserOffSprite;
//2
public float toggleInterval = 0.5f;
public float rotationSpeed = 0.0f;
//3
private bool isLaserOn = true;
private float timeUntilNextToggle;
//4
private Collider2D laserCollider;
private SpriteRenderer laserRenderer;

It might seem like a lot of variables, but in fact everything is quite trivial.

  1. The Laser has two states: On and Off, and 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 toggleInterval so that all lasers on the level don’t work exactly the same. By setting a low interval, you create a laser that will turn on and off quickly, and by setting a high interval you will create a laser that will stay in one state for some time. The rotationSpeed variable serves a similar purpose and specifies the speed of the laser rotation.
  3. These two private variables are used to toggle the laser’s state.
  4. These two private variables hold references to the laser collider and renderer so that their properties can be adjusted.

Here is an example of different laser configurations, each with different toggleInterval and rotationSpeed.

Add the following code to the Start method:

//1
timeUntilNextToggle = toggleInterval;
//2
laserCollider = gameObject.GetComponent<Collider2D>();
laserRenderer = gameObject.GetComponent<SpriteRenderer>();
  1. This will set the time until the laser should toggle its state for the first time.
  2. Here we save references to the collider and renderer as you will need to adjust their properties during their lifetime.

To toggle and rotate the laser add the following to the Update method:

//1
timeUntilNextToggle -= Time.deltaTime; 
//2
if (timeUntilNextToggle <= 0)
{
    //3
    isLaserOn = !isLaserOn;
    //4
    laserCollider.enabled = isLaserOn;
    //5
    if (isLaserOn)
    {
        laserRenderer.sprite = laserOnSprite;
    }
    else
    {
        laserRenderer.sprite = laserOffSprite;
    }
    //6
    timeUntilNextToggle = toggleInterval;
}
//7
transform.RotateAround(transform.position, Vector3.forward, rotationSpeed * Time.deltaTime);

Here is what this code does:

  1. Decreases the time left until next toggle.
  2. If timeUntilNextToggle is equal to or 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. Set the correct laser sprite depending on the laser state. This will display the laser_on sprite when the laser is on, and the laser_off sprite when the laser is Off.
  6. Resets the 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 simply set rotationSpeed to zero.

Setting the Laser Script Parameters

Switch back to Unity and select the laser in the Hierarchy. Make sure the Laser Script component is visible.

Drag the laser_on sprite from the Project view to the Laser On Sprite property of the Laser Script component in the Inspector.

Then drag the laser_off sprite to the Laser Off Sprite property.

Set Rotation Speed to 30.

Now set the laser Position to (2, 0.25, 0) to test that everything works correctly. Run the scene, and you should see the laser rotating nicely.

Now, turn the laser into a prefab. You should be able to do this on your own by now, but check the hints below if you need help.

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 bent whisker. Better get to fixing that.

Open the MouseController script and add an isDead instance variable.

private bool isDead = false;

This instance variable will indicate the player has died. When this variable is true, you will not be able to activate the jetpack, move forward, or do anything else that you’d expect from a live mouse.

Now add the following two methods somewhere within the MouseController class:

void OnTriggerEnter2D(Collider2D collider)
{
    HitByLaser(collider);
}

void HitByLaser(Collider2D laserCollider)
{
    isDead = true;
}

The OnTriggerEnter2D method is called whe then mouse collides with any laser. Currently, it simply marks the mouse as dead.

Note: It might seem strange that you need to create a separate method for one line of code, but you will add more code to both OnTriggerEnter2D and HitByLaser so this is simply a way to prepare for future changes.

Now, when the mouse is dead it shouldn’t move forward or fly using the jetpack. Make the following changes in the FixedUpdate method to make sure this doesn’t happen:


bool jetpackActive = Input.GetButton("Fire1");
jetpackActive = jetpackActive && !isDead;

if (jetpackActive)
{
    playerRigidbody.AddForce(new Vector2(0, jetpackForce));
}

if (!isDead)
{
    Vector2 newVelocity = playerRigidbody.velocity;
    newVelocity.x = forwardMovementSpeed;
    playerRigidbody.velocity = newVelocity;
}

UpdateGroundedStatus();
AdjustJetpack(jetpackActive);

Note that jetpackActive is now 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, you don’t set the mouse’s velocity if it's dead, which also makes a lot of sense. Unless they’re zombie mice. Switch back to Unity and run the scene. Make the mouse fly into the laser.

Hmm... it looks like you can no longer use the jetpack and the mouse doesn’t move forward, but the mouse seems rather OK with that. Perhaps you do have zombie mice about, after all!

The reason for this strange behavior is that you have two states for the mouse: run and fly. When the mouse falls down on the floor, it becomes grounded, so the run animation is activated. Since the game cannot end like this, you need to add a few more states to show that the mouse is dead.

Adding the Fall and Die Mouse Animations

Select the mouse GameObject in the Hierarchy and open the Animation view. Create a new clip called die. Save the new animation to the Animations folder.

After that, follow these steps to complete the animation:

  1. Open the Sprites folder in the Project view.
  2. Select and drag the mouse_die_0 and mouse_die_1 sprites to the Animation view's timeline.
  3. Set Samples to 8 to make the animation slower.

That was easy. In fact I think you can create the fall animation yourself. This time, simply 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 the 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 state the mouse is currently in when it hits the laser.

Since you created two animations (fall and die), you’ll need to handle things differently depending on whether 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, should you play the die animation.

However, in both cases you need one new parameter (as you don't yet have a parameter to handle the mouse's death by laser!) Open the Animator view and create a new Bool parameter called isDead.

Next, create a new Transition from Any State to fall.

Select this transition and in the Conditions, set isDead to true. Add isGrounded as a second parameter by clicking the + button and set its value to false.

Next, create a new transition from Any State to die. Select this transition and in Conditions set both isDead and isGrounded parameters to true.

This way there are two possible combinations:

  1. Dead but not grounded.
  2. 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 isDead parameter from the MouseController script. Open the MouseController script and add the following line to the end of the HitByLaser method:

mouseAnimator.SetBool("isDead", true);

This will set the isDead parameter of the Animator component to true. Run the scene and fly into the laser.

When the mouse hits the laser, the script sets the isDead parameter to true and the mouse switches to the fall state (since isGrounded is still false). However, when the mouse reaches the floor, the script sets the isGrounded parameter to true. Now, all conditions are met to switch to the die state.

Once again, there is something not quite right. Your poor mouse is not resting in peace. Honestly, now is not the time to pull out the dance moves and break into “The Worm”!

Using a Trigger to Make the Mouse Die Once

During play mode, click on the Animator view after the mouse dies and you will see the die animation is being played on repeat. Oh, the brutality!

This happens because you transition from Any State to die repeatedly, forever. The grounded and dead parameters are always true, which triggers the animator to transition from Any State.

To fix this, you can use a special parameter type called a Trigger. Trigger parameters are very similar to Bools, with the exception that they are automatically reset after use.

Open the Animator view and add a new Trigger parameter called dieOnceTrigger. Set its state to On, by selecting the radio button next to it.

Next, select the transition from Any State to die, and add dieOnceTrigger in the Conditions section.

Next, open the Animations folder in the RW directory in the Project view and select die. In the Inspector, uncheck Loop Time. This will stop the animation from looping.

Run the scene and collide with a laser.

This time the mouse looks far more peaceful!

Adding Coins

While death dealing lasers are fun to implement, how about adding some coins for the mouse to collect?

Creating a Coin Prefab

Creating a coin Prefab is similar to creating the laser, so you should try doing this yourself. Just use the coin sprite and follow these tips:

  • Don’t create any scripts for the 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 on how to do this, take a look at the expandable section below.

Solution Inside: Creating a coin prefab SelectShow>

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

Run the scene. Grab those coins!

Wow, the poor mouse has been having a really tough time in Part 3 of this tutorial! Why can’t you collect a coin? The mouse dies because the code in MouseController script currently treats 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 situations like this.

Select the coin Prefab in the Prefabs folder in the Project view. This will open the Prefab properties in the Inspector. Find the Tag dropdown right below the name field, click to expand it, and choose Add Tag....

This will open the already familiar Tags & Layers editor in the Inspector. In the Tags section add a tag named Coins.

Now select the 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 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 the 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 the following changes in the OnTriggerEnter2D method:


if (collider.gameObject.CompareTag("Coins"))
{
    CollectCoin(collider);
}
else
{
    HitByLaser(collider);
}

With this change, you call CollectCoin in the case of a collision with 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 the else case for lasers. In a real game, you should assign tags for all object types and check them explicitly.

Run the scene.

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. However, you currently have a Prefab that consists of only one coin. If you write generation code now, you would either generate only one coin here and there on the level, or you'd have to manually create groups of coins programmatically.

How about creating different configurations of coins and generating a pack of coins at once?

Creating a Pack of Coins Prefab

Open the Prefabs folder in the Project viewer and drag 9 coins into 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 the central coin. You will add all coins into an 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 the 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 a new coin formation 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. 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 the 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 coin packs and the laser). The objects list will store the created objects, so that you can 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 the 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 a fixed interval.

By using objectsMinY and objectsMaxY, you can configure the minimum and maximum height at which objects are placed, and by using objectsMinRotation and objectsMaxRotation you can configure the rotation range.

Adding the Method to Add a New Object

New objects are added in the following AddObject method in a similar way to how rooms are added. Add the following to the GeneratorScript:

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 a random position after it, within a given interval. By calling this method, you create a new object off screen each time the last object is about to show on the screen. This creates an endless flow of new coins and lasers.

Here is the description of each code block:

  1. Generates a random index to select a random object from the array. 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);
    }
}

Here's the breakdown of how this method checks if an object should be added or removed:

  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 an array or list while you iterate through it, you place objects that you need to remove in a separate list 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 loop (or the initial value of 0, if all objects are to the left of origin, but not in our case).
  5. If the current object is far behind, it is marked for removal to free up 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, call the method to add a new object.

To make this method work, add a call to GenerateObjectsIfRequired to GeneratorCheck just below GenerateRoomIfRequired:

GenerateObjectsIfRequired();

Like with the room prefab, this method is called a few times per second, ensuring that there will always be objects ahead of the player.

Setting up Script Parameters

To make the GeneratorScript work, you need to set a 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 open in the Project view.

Drag the coins_v Prefab from the Project view to the Available Objects list in the GeneratorScript component. Next, drag the laser Prefab from the Project view to the Available Objects list also.

That’s it! Run the scene.

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 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 Coin Count

Open the MouseController script and add the UnityEngine.UI namespace, as you will now be wielding the power of Unity’s new GUI system!

using UnityEngine.UI;

Switch back to Unity to begin creating the UI. Click GameObject/UI/Image to add an Image element.

If this is the first UI element in your scene, Unity will automatically create a couple objects to get you started: a Canvas and an EventSystem. You will notice your new Image element is a child of the canvas. All elements within the canvas will be rendered after scene rendering and by default will overlay the available screen space, which is perfect for your UI score and any other information you want to display to the player. The EventSystem is responsible for processing raycasts and inputs to your scene’s UI and handling their respective events.


In the Scene view, the canvas exists somewhat separately from the rest of your level, however, in the Game view it is rendered on top.

Now click GameObject/UI/Text to add a Text element.

Those are the only two elements you need to display the coin count; now to get them positioned and styled.

Select the Image Object in the Hierarchy and rename it coinImage in the Inspector. Unity’s UI uses a unique Rect Transform component, a more 2D-centric take on the normal Transform component. The Rect Transform additionally exposes parameters to control size, anchor and pivot point of your UI elements. This allows control of your UI scale and position with respect to screen size and aspect ratio.

Note: For a more extensive explanation of the UI system, I highly recommend having a look at our Introduction to Unity tutorial. The awesome Brian Moakley shows you how to create a start menu for this very game!

You want your image to be locked in position near the top left of the screen.

Have a look at the box in the top left of the Rect Transform component. This represents the Anchor and pivot point of your UI element. Tapping the box will bring up a grid of options titled Anchor Presets. These allow you to adjust the Anchor and stretch of the element. Holding Shift at the same time will also set the pivot and holding Alt will set the position. Let's get these UI elements set up.

  1. Tap the coinImage Anchor Preset box and holding both Alt and Shift, tap the top left box in the grid.
  2. Set Pos X to 15, and Pos Y to -15.
  3. Adjust both the height and width to 70.
  4. Still in the inspector, you can set the sprite you wish to display in the Image component. Open the Sprites folder in the Project view and drag the coin sprite to the Source Image property.
    It should look like this:

Now to adjust the tiny little mouse sized text element. Your jetpacking hero may be a mouse, but let’s assume your user is not.

  1. Select the Text in the hierarchy and drag it over the coinImage. You want these UI elements to stick together no matter the screen size or aspect ratio. Making the text a child of the image will make that simple.
  2. In the Inspector, rename Text to coinsCollected.
  3. Click the Anchor Preset box and holding Alt and Shift click on the left middle grid square.
  4. Set the Pos X to 75 and the height to 70

If everything looks the same as below, you are ready to head to the Text component to adjust the Font and Alignment.

Make the following adjustments to the Text component:

  1. Change the Text from New Text to 0.
  2. Increase the font size to 60.
  3. In the Alignment property, change the top vertical alignment to middle.
  4. Change the horizontal overflow from Wrap to Overflow.
  5. Finally pick a nice yellow or coin-gold for the color property (I used 245 red, 200 green, and 80 blue).

It should look something like this:

You are already counting the coins collected in the MouseController script, so let's hook that value up to the coinsCollected Text.

Create a new public instance variable in the MouseController.

public Text coinsCollectedLabel;

In the CollectCoin method, just after coins++;, add the following line of code:

coinsCollectedLabel.text = coins.ToString();

The coins integer is simply converted to a string and applied to the text property of the Text element.

Finally, back in Unity, drag the coinsCollected Text element from the hierarchy to the coinsCollectedLabel in the MouseController:

Hit run and start racking up those coins! The number should be displayed in the corner of the screen.

Raising the Dead

Now you need a restart button in there.

You will need another new namespace in MouseController to reload the level. Open the script and add the following at the top:

using UnityEngine.SceneManagement;

Now add the following new public method:

public void RestartGame()
{
    SceneManager.LoadScene("RocketMouse");
}

This method should be self-explanatory. When the RestartGame method is called, you ask the SceneManager to load the RocketMouse scene, starting it from the beginning again.

You only want the button to be displayed once the player has died and hit the ground. Therefore, to interact with the button in code, you need to add a public instance variable for it.

public Button restartButton;

Finally, add the following code to the end of the FixedUpdate method:

if (isDead && isGrounded)
{
    restartButton.gameObject.SetActive(true);
}

Head back into Unity to create the Button. Select GameObject ▸ UI ▸ Button.

In the Inspector rename your new button to restartButton. The button should already be centered perfectly. However, for future reference this could have been achieved by selecting the Anchor Preset Box, Holding down Alt and Shift and hitting the center and middle grid square.

Let’s make the button a little bigger. Adjust the Width to 200 and the Height to 60.

The text in the button is a child element of the Button. In the Hierarchy click the disclosure triangle next to the restartButton and select the Text element.

Back in the Inspector, change the Text to "Tap to restart!" and adjust the Font Size to 24.

You don’t want the button displayed at the start of the game, so once again select the restartButton in the hierarchy and uncheck the checkbox beside the name in the Inspector. This will leave it in the scene, but in an inactive state.

Select the mouse to display the Mouse Controller script in the Inspector, and drag the restartButton from the Hierarchy to the Restart Button in Mouse Controller.

The final step is to tell the button what method to execute when it’s tapped. Select the resetButton in the Hierarchy and in the Button component in the Inspector, find the On Click () at the bottom. Click the + button and drag the mouse GameObject from the Hierarchy into the None (Object) box. The No Function dropdown should become active. Click on it and select MouseController ▸ RestartGame().

That should be it! Hit run and have a go. When the mouse hits a laser, the "tap to restart" button should appear, and selecting it should restart the game.

Adding Sound and Music

The game is deadly quiet. You will be amazed how much better it feels once you add some sound and music.

Hitting Laser Sound

Open the Prefabs folder in the Project view and select the 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 the laser_zap sound to the 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 and give your player a fright!

This is what you should get:

Note: Make sure you select the laser Prefab and not a laser instance in the Hierarchy. Otherwise, you will need to click the Apply button as well to save changes made in the instance to the Prefab.

Now open the MouseController script and add the following code to the beginning of the HitByLaser method:

if (!isDead)
{
    AudioSource laserZap = laserCollider.gameObject.GetComponent<AudioSource>();
    laserZap.Play();
}
Note: It is important to add it to the beginning of the method, before you set isDead to true, 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 the 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.

Collecting Coin Sound

While you could apply the same approach with coins, you'll be doing something a little bit different. Open the MouseController script and add the 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);
Note: You use a static method of the AudioSource class to play the coin collect sound at the current position of the mouse. The reason this method plays the audio clip at a specific position is more for 3D games where sounds can be more positional in a 3D environment. For the purpose of this game, you'll just play the audio clip at the mouse's position.

Switch back to Unity and select the mouse GameObject in the Hierarchy. Drag the coin_collect sound from the Project view to the Coin Collect Sound field in the MouseController script.

Run the scene. Grab a coin and enjoy the resulting sound effect!

Jetpack and Footsteps Sounds

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 the 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 the 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 both sounds 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 the MouseController script and add the following two instance variables:

public AudioSource jetpackAudio;
public AudioSource footstepsAudio;

These will reference your newly created Audio Sources. Now add the AdjustFootstepsAndJetpackSound method:

void AdjustFootstepsAndJetpackSound(bool jetpackActive)
{
    footstepsAudio.enabled = !isDead && isGrounded;
    jetpackAudio.enabled = !isDead && !isGrounded;
    if (jetpackActive)
    {
        jetpackAudio.volume = 1.0f;
    }
    else
    {
        jetpackAudio.volume = 0.5f;
    }
}

This method enables and disables the footsteps and the jetpack Audio Source components. The footsteps sound is enabled when the mouse is not dead and on the ground. The jetpack sound is enabled 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 the FixedUpdate method:

AdjustFootstepsAndJetpackSound(jetpackActive);

Next you will need to assign references to the Audio Source components within the mouse GameObject to the footstepsAudio and jetpackAudio variables.

Setting Footstep and Jetpack Script Variables

Switch back to Unity and select the 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 to 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 the jetpack by holding the left mouse button.

Adding Music

To add music, follow these simple steps:

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

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

Adding a Parallax Background

Currently this room with a view is pretty... well, blue.

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 option. But instead of adding a motionless background image, you will add a parallax background.

Note: To implement parallax scrolling for this game, you will use the technique described in one of Mike Geig’s videos that you can watch over here.

You will add two Quads, one for the background and one for the foreground parallax layer.

Note: You can read more about Quads in the Unity documentation. To simplify things a little, for the purposes of this tutorial you can think of them as rectangles with a texture stretched on it.

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 the Background Images

To use background images with quads, you need to adjust how they are imported to Unity.

Open the Sprites folder in the Project view and select window_background. In the Inspector change its Texture Type to Default instead of Sprite (2D and UI). After that change Wrap Mode to Repeat and click Apply.

Do the same for the 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 a new camera by selecting GameObject ▸ Camera. Select it in the Hierarchy and make the following changes in the Inspector:

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

Since you have two cameras, you also have two audio listeners in the scene. Disable the Audio Listener in ParallaxCamera or you will get the 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 ▸ 3D Object ▸ Quad. Name the first quad parallaxBackground and the second parallaxForeground. Drag both quads to ParallaxCamera to add them as children.

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

Note: You use this scale to accommodate the background image's size of 1136 × 492 px without distortion.

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

Note: This time you set the z-coordinate position. Since you cannot use Sorting Layers for quads, you need to position the background quad behind the foreground quad.

Setting Quad Textures

Open the Sprites folder in the Project view. Drag the window_background over to 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 the scene a little, you can see how all the scene components are positioned and layered.

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 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 just any image. The background images are designed to be repeated. In other words if you repeat the background horizontally many times, each left side of the picture will perfectly fit the right side (tileable).

Create a new C# Script called ParallaxScroll and attach it to ParallaxCamera.

Open the ParallaxScroll script and add the following instance variables:

//1
public Renderer background;
public Renderer foreground;
//2
public float backgroundSpeed = 0.02f;
public float foregroundSpeed = 0.06f;
//3
public float offset = 0.0f;

Here is a breakdown of what these variables will do:

  1. The Renderer variables will hold a reference to the Mesh Renderer component of each of the quads so that you can adjust their texture properties.
  2. The backgroundSpeed and foregroundSpeed just define the speed for each background.
  3. The offset will be provided by the player’s position. This will enable you to couple the mouse’s movement to the movement of the parallax background. If your pick up a power up and boost forward, the background will move quickly. If the player dies, the movement stops.

Add the following code to the Update method:

float backgroundOffset = offset * backgroundSpeed;
float foregroundOffset = offset * foregroundSpeed;

background.material.mainTextureOffset = new Vector2(backgroundOffset, 0);
foreground.material.mainTextureOffset = new Vector2(foregroundOffset, 0);

This code increases the texture offset of each of the quad’s textures with the offset, thus moving it. The resulting speed is different since the script uses the backgroundSpeed and foregroundSpeed coefficients.

Switch back to Unity and select ParallaxCamera in the Hierarchy. Drag the parallaxBackground quad to the Background field of the ParallaxScroll script and parallaxForeground to Foreground.

Now open the MouseController script and add the following public variable:

public ParallaxScroll parallax;

Then add the following code to the end of the FixedUpdate method:

parallax.offset = transform.position.x;

Switch back to Unity and select the mouse GameObject in the Hierarchy. Make sure the MouseController script is visible in the Inspector.

Drag ParallaxCamera from the Hierarchy to the Parallax field in the Inspector.

This will allow the MouseController script to change the offset variable of the ParallaxScroll script with respect to the mouse’s position.

Run the scene, and behold the beautiful parallax effect!

But what about the level itself? You can’t see it!

Fixing the Order of the Cameras

Select ParallaxCamera in the Hierarchy. In the Inspector, find the Camera component and look for a Depth field. Set it to -2.

Note: The Depth of the ParallaxCamera should be lower than the Depth of the Main Camera, so check your Main Camera Depth if required and adjust the Depth of the ParallaxCamera to be lower.

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

To fix this, select the Main Camera in the Hierarchy and set its Clear Flags to Depth Only. This way it won't clear out the picture drawn by the parallax camera.

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

You now have a fully functioning and decorated game. Great job! Thanks for sticking with it through all three parts!.

Where to Go From Here?

That mouse sure did sacrifice himself many times in the making of this tutorial series. Hopefully you enjoyed the end result though and his deaths were not in vain! If you want to compare your end result you can download the final project from the materials at the top or bottom of this tutorial.

Check out this this video if you want to know more about the making of the actual Jetpack Joyride game.

Creating a parallax background is heavily inspired by this video by Mike Geig, who has a lot of really cool videos on Unity.

Please post your questions and comments below. Thank you for following along with this tutorial! :]

The post How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3 appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4370

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>