Home Archive Tutorials

How To Make a 2.5D Game With Unity Tutorial: Part 2

This is a blog post by iOS Tutorial Team member Marin Todorov, a software developer with 12+ years of experience, an independant iOS developer and the creator of Touch Code Magazine. This is the second part of a two part tutorial series on how to make a simple 2.5D game for the iPhone with the […]


  • Other, Other, Other

This is a blog post by iOS Tutorial Team member Marin Todorov, a software developer with 12+ years of experience, an independant iOS developer and the creator of Touch Code Magazine.

Learn how to create a simple 2.5D game with Unity!

Learn how to create a simple 2.5D game with Unity!

This is the second part of a two part tutorial series on how to make a simple 2.5D game for the iPhone with the Unity game development tool.

In the first part of the tutorial series, we covered the basics of using Unity and writing scripts with C#. We created a simple game where a plane could fly back and forth bombing sharks while protecting the clown fish!

In this second and final part of the tutorial series, we’re going to extend the game to add some finishing touches. We’ll add some sound effects and music, wrap up the game logic, and add multiple scenes to the game!

If you don’t have it already, grab the project where we left it off in the last tutorial, and open it up in Unity. Make sure to click the Scenes\LevelScene item to make it visible if it doesn’t load on startup.

Allright, so let’s learn some more about Unity and wrap up this game!

Adding iCandy to the game

Adding iCandy to the game

You’ve probably noticed that when a bomb hits a shark, it just quietly disappears, and thought to yourself, “meh, that’s not very cool!”

Well, be prepared to be blown away – we’re going to add a cool underwater explosion instead!

From the menu select “GameObject/Create other/Particle System”, and you’ll see a particle system appear on the scene. Rename the “Particle System” to “Explosion” in “Hierarchy” panel, and set the Explosion’s position to [1, 1, 8].

Now if you are a Particle System specialist – head to the “Inspector” and pimp it up yourself, and if you are not – just follow my lead it’s pretty easy. Copy over these values in the “Inspector”:

Creating a particle system in Unity

Here the most important property is “One shot” – when you check it the system will emit particles only once – as an explosion does. Now let’s setup also the animation values – just try to more or less match the colors below (not important if you don’t):

Particle Animator settings for Particle System in Unity

Here the one important property is “Autodestruct” – when checked the Particle System will remove itself from the scene when there are no more living particles. This is exactly what we want – it’s like automatic garbage collection.

Now you have a nice small explosion and you need to do exactly the same you did before with the bomb – make a prefab, instantiate it when needed, leave it to autodestroy itself when it’s done on the scene.

Right-click “Project” panel “Prefabs” folder, choose “Create/Prefab”, rename it to “ExplosionPrefab”. Drag the “Explosion” object from “Hierarchy” onto the new “ExplosionPrefab”. Right-click “Explosion” in “Hierarchy” and choose “Delete”.

Right-click “Project” panel and choose “Sync MonoDevelop Project” to open MonoDevelop. Load in the editor BombClass.cs and add this code:

//right under definition of "ySpeed"
public GameObject explosionPrefab;

//inside OnTriggerEnter, right after Destroy(this.gameObject)
Instantiate(explosionPrefab, transform.position, Quaternion.identity);

Now switch back to Unity and select “BombPrefab” in “Project” panel – in the “Inspector” you see the new property “ExplosionPrefab”. Just drag “ExplosionPrefab” from “Project” onto this new property field and you’re set to go.

That’s it – press play and see the explosions when you hit sharks. Sweet!

Gratuitous explosions

Adding earCandy to the Game

Don’t tell Steve, but iCandy wasn’t enough for us – we need some earCandy too!

How could we have a game be without background music?! We’re going to visit another great guy – Kevin Macleod, who is a great composer releasing film&game music under CC license. So if you use his stuff don’t forget to attribute.

Open this url: http://incompetech.com/m/c/royalty-free/index.html?keywords=the%20cannery

Once you’re there, download “The Cannery” track somewhere to your disc, and drag “The Cannery.mp3” onto the folder “Audio” in the “Project” panel.

We would like to have the music loop all the time… but which object should we attach it to?

Well, let’s attach it to the camera! The camera is a game object too, and can have other components attached.

Drag “The Cannery” from “Project” panel onto “Main Camera” in the “Hierarchy” panel. Select “Main Camera” in “Hierarchy” and in the “Inspector” find the Audio Source strip – check the “Loop” checkbox and set Volume to “0.20”.

It’s as easy as that – run the scene and enjoy the new background music!

Creating a GUI with Unity

Let’s dive into yet another new area – GUI. Unity provides you with some standard labels and buttons, but all in all it’s not the biggest strength of Unity. However we’re going to use a label to show the current score; so first we’ll need to implement the score logic.

Switch to MonoDevelop. Open up PlayerClass.cs and add a new property:

public static int score = 0;

Aha! There’s again something new – “static”. This property will be created when the class is loaded and will persist no matter whether there are instances of the class. Additionally this property is accessible from all other classes – this is why we’re keeping the score count in a static property.

Next add this method which will take care of updating the score (not much sense in it now, but just go with it) – see how the score property is accessed via the class name:

public void updateScoreBy(int deltaScore) {
	PlayerClass.score += deltaScore;

Add one more method to PlayerClass – this one will draw the score count on the screen:

void OnGUI() {
	GUIStyle style = new GUIStyle();
	style.fontSize = 20;
	GUI.Label(new Rect(10,10,100,20), "Score:"+PlayerClass.score, style );	

This event handler “OnGUI” is getting called on every frame on the GUI layer. So, you draw everything you need on the GUI in here.

GUIStyle is a class, which mimics a CSS class – so you can use fontSize, marginLeft and such if you are familiar with CSS, otherwise just keep with the font size for now. GUI.Label() is a method taking 3 arguments: a rect for the bounds of the label, the string to draw and the text style. That’s all to it.

The only task left is: update the score when we have hits or misses! Open up BombClass.cs and make the following modifications:

//add a new property
public PlayerClass player;

//replace the existing OnTriggerMethod with
void OnTriggerEnter(Collider obj) {
	if (obj.gameObject.name == "Shark") {
		//reset shark
		obj.gameObject.transform.rotation = Quaternion.identity;
		obj.gameObject.transform.position = new Vector3(20f, -3f, 8f);
		Instantiate(explosionPrefab, transform.position, Quaternion.identity);
	if (obj.gameObject.name == "ClownFish") {
		//reset fish
		obj.gameObject.transform.rotation = Quaternion.identity;
		obj.gameObject.transform.position = new Vector3(-20f, -1f, 8f);
		Instantiate(explosionPrefab, transform.position, Quaternion.identity);

It’s pretty similar to what we had before, but we’re having a new property called “player” and when we need to update the score we call player.updateScoreBy().

Also to make the game more interesting if you hit a shark you’ll get a point, if you hit a clownfish (remember? clownfish – gooood, shark – baaahhd) then you will loose a point. Now that’s shaping as a difficult game!

There’s one last thing – to set the player property on the bomb. Now we can’t do it like before because bombs are dynamically created, but fortunately the bombs are created by the player himself, so he can set the player property at creation time.

Let’s do that – open up PlayerClass.cs and just under the line “bombObject.transform.position = this.gameObject.transform.position;” add this code:

BombClass bomb = (BombClass)bombObject.GetComponent("BombClass");
bomb.player = this;

There’s something new again! Let’s discuss: bombObject is a GameObject instance (returned by Instantiate), so we call “GetComponent” on it and this way we can access all attached components to the game object – the result we cast to a BombClass – so in the end we got the reference to the C# class attached to the game object. Next we just set the “player” property to this (the PlayerClass instance).

Run the scene and you’ll see the score counter!

Creating a Score Label with Unity

Unity Objects and Components

At this point you already have enough practice with Unity to be introduced to Unity’s game object model. Better understand what you are actually doing, right? Let’s make a really short de-tour to have a look at how game objects relate to the components attached to them.

All those strips we see adding up in the “Inspector” panel – these are components that get attached to the game object. An empty game object has just its transform strip – position, rotation, scale. That’s all – everything else are components attached.

If you look at our BombPrefab – you can see many different components:

  • Transform: provides position, rotation, and scale, as described above.
  • Mesh Filter: provides the geometry of the visible object.
  • Mesh Renderer: renders the geometry.
  • Rigidbody: handles physics.
  • Audio Source: plays audio.
  • Script: can programmatically update the object.

These are just few of the possible components you can attach to an object. To even better understand let’s have a look at this diagram:

Components in Unity diagram

So, take the point-of-view of the Script component. It should now be a bit clearer why we had to call:


in order to destroy everything related to the object instance. From the gameObject property we can also access all other components, so we can adjust physics or audio volume, etc .etc.

Adding more scenes

Right now our game is getting good, but there’s no way to win or lose!

So let’s add a “you win” screen when the player makes more than 3 points.

From the menu choose “New Scene”, then again from the menu “Save Scene”, select the folder [your project’s directory]/Assets/Scenes and save the scene as “WinScene”.

Select “Main Camera” in “Hierarchy” and set: Position [0, 0, 0], Projection to “Orthographic”, Size to “10”, Near to “0.5” and Far to “22”. From the menu choose “GameObject/Create Other/Directional Light” and in the Inspector: Position [0, 0, 0].

All we want in the scene is to put a plane (like the background in our game level) and put an image on it saying “you win”, so let do as before in Part 1: From the menu “GameObject/Create Other/Plane” and in the Inspector: Position [0, 0, 8], Rotation to [90, 180, 0], Scale to [3, 1, 2].

Next download and save to your disc the image prepared by Vicki Wenderlich (click for full resolution):

Game Over image

Drag “gameover_youwin.png” onto “Project” panel in “Textures” folder. After the texture is imported it looks kind of dirty – it’s because of the compression, just select “gameover_youwin” texture in “Project” and then in the “Inspector” find “Format” change it to “16bits” and click “Apply”. Now drag “gameover_youwin” from “Project” panel onto “Plane” in “Hierarchy”. In your “Game” panel you should see the “You win” – where Vicki painted the evil shark floating bely up.

We just need to make it alive – when the scene is tapped the game should restart: Right-click “Project” panel in “Class” folder, choose “Create/C Sharp Script” and rename it to “GameOverClass”. Right-click and choose “Sync MonoDevelop Project”. In MonoDevelop open up the new GameOverClass.cs and replace the contents with this code:

using UnityEngine;
using System.Collections;

public class GameOverClass : MonoBehaviour {
	// Update is called once per frame
	void Update () {
		if (Input.anyKeyDown) {
			PlayerClass.score = 0;

When the player taps the screen, the score is reset and gameplay scene is loaded. Application.LoadLevel() just takes the name of the scene to load – pretty easy :)

Back to Unity: drag “GameOverClass” script from “Project” panel “Class” folder onto the “Main Camera”.

Now to include this scene in the project choose “File/Build Settings” and in the popup window click “Add current” button and close the window. You’ve added the scene to the project build.

Let’s quickly add the “You loose” screen too! Like last time, make a “New scene”, then “Save Scene” as “LooseScene” in the Scenes folder.

Select “Main Camera” in “Hierarchy” and set: Position [0, 0, 0], Projection to “Orthographic”, Size to “10”, Near to “0.5” and Far to “22”. From the menu “GameObject/Create Other/Directional Light” and in the Inspector: Position [0, 0, 0]. From the menu “GameObject/Create Other/Plane” and in the Inspector: Position [0, 0, 8], Rotation to [90, 180, 0], Scale to [3, 1, 2].

Download this “You Lose” image and save to your disc (click for full resolution):

You lose image

To wrap up this scene, take the following steps just like last time:

  • Drag “gameover_youlose.png” onto “Project” panel in “Textures” folder.
  • Select “gameover_youlose” texture in “Project” and then in the “Inspector” find “Format” change it to “16bits” and click “Apply”.
  • Drag “gameover_youlose” from “Project” panel onto “Plane” in “Hierarchy”.
  • Drag “GameOverClass” from “Project” panel “Class” folder onto the “Main Camera”.
  • From the main menu choose “File/Build Settings” and in the popup window click “Add current” button and close the window.

There – you have 3 scenes, but you need to connect them!

Load the LevelScene scene – by double clicking “LevelScene” in the “Project” panel. Switch to MonoDevelop and open up PlayerClass.cs. We’re going to modify the updateScoreBy method to check whether you made more than 3 points or sank under -3 points.

//replace the updateScoreBy method with this code
public void updateScoreBy(int deltaScore) {
	PlayerClass.score += deltaScore;	
	if (PlayerClass.score>3) {
	} else if (PlayerClass.score<-3) {

Now your scene workflow is set. You can give the game a try by hitting Play in Unity. Actually - why don't you hit "File/Build&Run", and when Xcode pops up - hit "Run" there as well and give the game a try on your iPhone?

Going 2.5D - at last!

Yes - it's time. The epic technique you've been expecting for 2 long parts of this tutorial series - 2.5D!

And I'll tell you in advance a secret - we're going to spice it up and make it almost 3D for your developing pleasure!

Up to now we've been setting the cameras' projection to "Orthographic", which made the scene look like a plain 2D game - well, that ends here!

In your LevelScene scene select "Main Camera" and in the Inspector change Projection to "Perspective" and "Field of View" to "100" (to compensate for the perspective). That's it - hit the Play button and see your game in 2.5D! Ain't that cool?

Using a perspective projection in Unity for a 2.5D effect

But we're not going to stop here.

Here's the plan - to make the game more difficult and demonstrate how to rotate and move the camera, every time the score increases we'll move the camera onto an arc path and change it's angle. This way, the more you progress in the game the weirder the angle you'll have to look at the characters and try to bomb the sharks!

Switch to MonoDevelop and make the following changes to PlayerClass.cs:

//add the properties
public GameObject mainCamera;
public GameObject gameBackground;
public float nextZ = 0;

//at the end of the updateScoreBy method
if (PlayerClass.score>0) {
	nextZ = PlayerClass.score*2.5f;

//at the end of the Update method
if (nextZ > mainCamera.transform.position.z) {
		3* Mathf.Sin(transform.position.z/2 ) * Time.deltaTime, 
		-Mathf.Sin(transform.position.z /2 ) * Time.deltaTime *0.3f
	mainCamera.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );
	gameBackground.gameObject.transform.RotateAroundLocal( Vector3.up, Time.deltaTime*0.1f );

Alright - lot of code, but that's about everything we need. Let's go over it bit by bit.

First, we declare properties to hold references to the Main Camera and the Background plane. We'll be moving and rotating the camera, and we will be rotating the background as well.

The Camera is moving from it's current Z position at 0 towards the background to about 7.5Z. So every time the player makes a score, the nextZ is set to 2.5, then 5.0, then 7.5 - and from these values the Main Camera is translated onto something like an arc using a sin function.

All math functions by the way are accessible via the Mathf class - as for the sin is Mathf.Sin(). Also we rotate the camera using transform.RotateAroundLocal and we pass it an axis to rotate around (Vector3.up) and the angle to rotate by. We rotate the camera and the background together, so camera faces always the background (i.e. the background doesn't run out of the screen).

One more thing - let's connect the new public properties. Switch to Unity and select "Player" object in "Hierarchy" panel. Drag "Main Camera" from "Hierarchy" onto the new "Main Camera" property in the "Inspector"; drag the "Background" object from "Hierarchy" onto the new "Game Background" property in the "Inspector".

Congrats, you're finally done! Hit File\Build and Run and run the finished game on your iPhone, and enjoy bombing sharks at weird angles :)

The finished simple 2.5D game made with Unity

Debugging with Unity

I'd like to shortly touch on few topics, because when you go on developing by yourselves you are going to run into trouble where you need to debug your game. So few simple things to keep in mind:

  • Don't forget there's a Pause button in the toolbar, so if you need to stop the execution of the game and check all object's properties - just hit Pause and then browse around your scene and lookup the values in the "Inspector" panel.
  • If you're not sure whether your method fires up, print a message in the console (much like in Xcode). Use: Debug.Log("message"); to print messages to the Console. You can bring up the Console window by choosing from the menu "Window/Console".
  • Develop this habit: When you're done coding in MonoDevelop and you switch back to Unity editor look at Unity's status bar (at the bottom of the application window) - if you wrote bad code, which won't validate - you'll get an error message in red in there, immediately after you bring Unity up.
  • If your objects don't move - triple check if your script is attached to your object!
  • When you run your game you can change values in the Inspector just to try out different values, etc. etc. When you stop the game: NB all values in the inspector are reset to the ones you had before running the game. Thus effectively if you forgot to stop the game and you made changes, they'll be lost, so be sure to stop the game after your finished testing and then continue developing.

Where To Go From Here?

Here is the complete sample project that we developed in the above tutorial series.

If you want to learn more about Unity you can look around their web-site; they have great Support section with examples, forum, etc: www.unity3D.com/support/. Also have a look at the Unity C# Reference.

If you want to practice more, you can extend Shark Bomber further by adding some of the
following features to the app:

  • Add explosion sounds, game over scene music/effects
  • Have 2 different explosions and randomly show them
  • Have different levels

This is a very very simple introduction to Unity - we barely touched on reading iPhone input, but I think you have now a solid understanding how thing in Unity work and that's more important to get you started!

If you think about it - you already know everything you need to know to also create 3D games! You've been translating objects around, rotating them to weird angles, you've been moving around the camera and basically nothing stops you from getting wild in the 3 dimensions. Just choose the right models, build some terrain (usually using a plane is a good idea) and you can pretty much do anything you want. So this was also your quick start into making 3D games with Unity!

If you have any questions, comments, or suggestions, please join in the forum discussion below!


More like this