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:
- 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.
- Select laser_on in the Hierarchy and rename it to laser.
- Set the Sprite Renderer Sorting Layer to Objects.
- Add a Box Collider 2D component.
- 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.
- 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.
- 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.
- 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.
- 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. TherotationSpeed
variable serves a similar purpose and specifies the speed of the laser rotation. - These two private variables are used to toggle the laser’s state.
- 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>();
- This will set the time until the laser should toggle its state for the first time.
- 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:
- Decreases the time left until next toggle.
- If
timeUntilNextToggle
is equal to or 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.
- 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.
- Resets the
timeUntilNextToggle
variable since the laser has just been toggled. - Rotates the laser around the z-axis using its
rotationSpeed
.
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.
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.
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:
- Open the Sprites folder in the Project view.
- Select and drag the mouse_die_0 and mouse_die_1 sprites to the Animation view's timeline.
- 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.
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:
- 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 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.
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.
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.
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.
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:
- Generates a random index to select a random object from the array. 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);
}
}
Here's the breakdown of how this method checks if an object should be added or removed:
- 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 afteraddObjectX
point, then you need to add more objects since the last of the generated objects is about to enter the screen.ThefarthestObjectX
variable is used to find the position of the last (rightmost) object to compare it withaddObjectX
. - 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.
- 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 loop (or the initial value of 0, if all objects are to the left of origin, but not in our case). - If the current object is far behind, it is marked for removal to free up some resources.
- Removes objects marked for removal.
- 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.
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.
- Tap the coinImage Anchor Preset box and holding both Alt and Shift, tap the top left box in the grid.
- Set Pos X to 15, and Pos Y to -15.
- Adjust both the height and width to 70.
- 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.
- 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.
- In the Inspector, rename Text to coinsCollected.
- Click the Anchor Preset box and holding Alt and Shift click on the left middle grid square.
- 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:
- Change the Text from New Text to 0.
- Increase the font size to 60.
- In the Alignment property, change the top vertical alignment to middle.
- Change the horizontal overflow from Wrap to Overflow.
- 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:
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();
}
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);
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.
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:
- Select the Main Camera in the Hierarchy.
- Add an Audio Source component in the Inspector.
- Drag the music asset from the Project view to the Audio Clip property.
- Make sure Play On Awake and Loop are enabled.
- 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:
- Create something to show behind the window.
- 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.
You will add two Quads, one for the background and one for the foreground parallax layer.
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:
- Rename it to ParallaxCamera.
- Set the Position to (0, 10, 0).
- Set the Projection to Orthographic.
- 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).
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.
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:
- 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. - The
backgroundSpeed
andforegroundSpeed
just define the speed for each background. - 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.
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.