Intermediate Unity 3D for iOS: Part 3/3

This is a tutorial by Joshua Newnham, the founder of We Make Play, an independent studio crafting creative digital play for emerging platforms. Welcome back to our Intermediate Unity 3D for iOS tutorial series! In the first part of the series, you learned how to use the Unity interface to lay out a game scene, […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Skins

Unity provides a way to dress up the GUI elements using something called a Skin. This can be thought of as a simple stylesheet used in conjunction with HTML elements.

I created two skin files for you (which you already imported into your project way back in the first part of this tutorial), one for 480×320 displays, and the other for the 960×640 Retina display. The following is a screenshot of the properties of the 480×320 skin:

The Skin property file exposes a lot of attributes that allow you to create unique styles for your project. For this project, you only need to be concerned with the font.

So open the GameMenuSmall skin and drag the scoreboard font onto the Font property and set the Font size to 16. Then open the GameMenuNormal and drag the scoreboard font onto the Font property and set the Font size to 32.

Next up is implementing the actual main menu!

Main Menu

This section presents the code for the GameMenuController which is responsible for rendering the main menu and interpreting user input. You’ll work quickly through the important parts of the code, and then finally hook it up to your game!

Create a new script named GameMenuController and add the following variables as shown below:

using UnityEngine;
using System.Collections;

[RequireComponent (typeof (LeaderboardController))]
public class GameMenuController : MonoBehaviour {
	
	public Texture2D backgroundTex; 	
	public Texture2D playButtonTex; 	
	public Texture2D resumeButtonTex; 	
	public Texture2D restartButtonTex; 
	public Texture2D titleTex; 
	public Texture2D leaderboardBgTex; 
	public Texture2D loginCopyTex; 
	public Texture2D fbButtonTex; 
	public Texture2D instructionsTex; 
	
	public GUISkin gameMenuGUISkinForSmall; 
	public GUISkin gameMenuGUISkinForNormal; 

	public float fadeSpeed = 1.0f; 
	private float _globalTintAlpha = 0.0f; 		

	private GameController _gameController; 		
	private LeaderboardController _leaderboardController; 
	private List<ScoreData> _scores = null;
	
	public const float kDesignWidth = 960f; 
	public const float kDesignHeight = 640f; 

	private float _scale = 1.0f; 	
	private Vector2 _scaleOffset = Vector2.one; 
	
	private bool _showInstructions = false; 	
	private int _gamesPlayedThisSession = 0; 
}

First, there is a set of publicly accessible variables, which are assigned within the editor that gives the GameMenuController access to elements used to render the main menu. Next you have variables to reference the two skins you created in the previous section.

Following that you have variables that you’ll use to fade in and out the main menu.

We also include references of the GameController and LeaderboardController with reference to the scores you retrieve back from your LeaderboardController.

Following this you have a set of constants and variables which are used to determine the scale of the user interface elements i.e. to manage the different screen resolutions of the, for example, iPhone 3GS (480×320) and iPhone 4 (960×640).

And finally you have some variables you use to manage the state of the GameMenuController Component.

Next add the Awake() and Start() methods, as shown below:

        void Awake(){
		_gameController = GetComponent<GameController>(); 
		_leaderboardController = GetComponent<LeaderboardController>(); 
	}		

	void Start(){
		_scaleOffset.x = Screen.width / kDesignWidth;
		_scaleOffset.y = Screen.height / kDesignHeight;
		_scale = Mathf.Max( _scaleOffset.x, _scaleOffset.y ); 

		_leaderboardController.OnScoresLoaded += HandleLeaderboardControllerOnScoresLoaded;

		_leaderboardController.FetchScores(); 
	}

During Start(), the scores are requested from the LeaderboardController. As well, some graphics ratios are calculated so that the GUI can be adjusted accordingly (as mentioned above).

The scale offsets in the code above are used to ensure the GUI elements are scaled appropriately. For instance, if the menu is designed for 960×640, and the current device resolution is 480×320, then you need to scale these down by 50%; your scaleOffset will be 0.5. This works fairly well if you’re using simple graphics without the need of duplication, and will become more relevant when you start porting to devices with multiple resolutions.

Once the scores are loaded, cache the scores locally (this should look familiar to you as we’ve just implemented something very similar in the above section), which will be used when rendering the GUI:

	public void HandleLeaderboardControllerOnScoresLoaded( List<ScoreData> scores ){
		_scores = scores; 
	}

Time to test

Lets take a little break and test what you have so far.

Add the following code to your GameMenuController:

	void OnGUI () {
		GUI.DrawTexture( new Rect( 0, 0, Screen.width, Screen.height ), backgroundTex ); 
		if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
			Debug.Log( "Click" );  
		}
	} 

The above snippet of code shows the OnGUI method. This method is called in a manner similar to Update(), and provides access to the GUI Component. The GUI Component provides a suite of static methods exposing standard user interface controls, click here to learn more about the OnGUI and GUI class from the official Unity site.

Within the OnGUI method you are asking a texture to be drawn across the whole screen with the following code:

GUI.DrawTexture( new Rect( 0, 0, Screen.width, Screen.height ), backgroundTex ); 

Next you wrap the GUI.Button method with a conditional statement, the GUI.Button method renders a button at the specified location (using the offsets you calculated before to handle differing screen resolutions). This method also returns a boolean whether the user has clicked on it or not i.e. will return true if the user has clicked on it otherwise false.

if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
	Debug.Log( "Click" );  
}

In this case if it has been clicked then it will output “Click” to the console.

To test, attach your GameMenuController script to the GameObject your GameController is attached to, and then set attach all the public properties to the appropriate objects, as shown below:

Now try it out! Click play and you’ll see a button appear. Click on it and you’ll see a message in the console!

Not bad, eh? The first small step of your menu is underway! :]

Using your skins

Now that you’ve confirmed you’re heading in the right direction, let’s continue by assigning the appropriate skin depending on the screen size.

Replace the body of your OnGUI method with the following code:

if( _scale < 1 ){
	GUI.skin = gameMenuGUISkinForSmall; 
} else{
	GUI.skin = gameMenuGUISkinForNormal;	
}

The skins will ensure that you use the correct font size (based on the screensize); you determine what skin to use based on the _scale you calculated previously. If it is less than 1.0 then you will use the small skin otherwise revert to the normal skin.