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 3 of 4 of this article. Click here to view the first page.

Showing and hiding

Rather than abruptly popping up the GUI when requested, you will gradually fade in. To do this you will manipulate the GUI's static contentColor variable (this affects all subsequent drawing done by the GUI class).

To manage the fading in you will granularly increase your _globalTintAlpha variable from 0 to 1 and assign this to your GUI.contentColor variable.

Add the following code to your OnGUI method:

_globalTintAlpha = Mathf.Min( 1.0f, Mathf.Lerp( _globalTintAlpha, 1.0f, Time.deltaTime * fadeSpeed ) ); 
			
Color c = GUI.contentColor;	
c.a = _globalTintAlpha; 
GUI.contentColor = c; 

You also need a way to initiate the showing and hiding of your menu, so create two publicly accessible method called Show and Hide (as shown below):

	public void Show(){
		// ignore if you are already enabled
		if( this.enabled ){
			return; 	
		}
		_globalTintAlpha = 0.0f; 
		_leaderboardController.FetchScores(); 
		this.enabled = true; 
	}

	public void Hide(){
		this.enabled = false; 
	}

Nothing fancy going on here! You request a new batch of scores in case the user has added a new score. Then you adjust the global tint alpha to 0 and enable/disable this Component to start/stop the OnGUI call (i.e. if this Component is disabled then all update methods e.g. Update, FixedUpdate, OnGUI wont be called).

What your menu displays will depend on what state the game is in e.g. Pause will render differently to GameOver.

Add the following code to your OnGUI method:

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

	if( _gameController.State == GameController.GameStateEnum.Paused ){				
		if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
			_gameController.ResumeGame(); 
		}
				
		if (GUI.Button( new Rect ( 229 * _scaleOffset.x, 357 * _scaleOffset.y, 100 * _scale, 100 * _scale ), restartButtonTex, GUIStyle.none) ){
			_gameController.StartNewGame(); 				
		}
	} else{
		if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), playButtonTex, GUIStyle.none) )
		{
			if( _showInstructions || _gamesPlayedThisSession > 0 ){
				_showInstructions = false; 
				_gamesPlayedThisSession++; 
				_gameController.StartNewGame(); 
			} else{
				_showInstructions = true; 	
			}
		}
	}

This should look fairly familiar to you; all you are doing is rendering textures and buttons depending on what state the GameController is in.

When paused you render two buttons to allow the user to resume or restart:

	if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
		_gameController.ResumeGame(); 
	}
				
	if (GUI.Button( new Rect ( 229 * _scaleOffset.x, 357 * _scaleOffset.y, 100 * _scale, 100 * _scale ), restartButtonTex, GUIStyle.none) ){
		_gameController.StartNewGame(); 				
	}

Note: Wondering how I got those position and size numbers? I painfully copied them across from GIMP, which I used to create the interface.

The other state it could be in is GameOver, which is when you render the play button.

Note: You may have noticed the two variables _showInstructions and _gamesPlayedThisSession. The _gamesPlayedThisSession is used to determine how many games you have played for this session, if it is the first game then you flag _showInstructions to true before playing the game. This allows use to display a set of instructions (shown next) before the user plays their first game of Nothing But Net.

Time to test

Before you finish off the GameMenuController, lets make sure everything is working at expected. Everything should be setup from your previous test so you should be able to press the play button and see something similar to the following:

Finishing off the GameMenuController

The final pieces left is the title, instructions, and score.

Drawing the title or instruction is dependent on the instruction flag (as described above); add the following code to the bottom of your OnGUI method:

if( _showInstructions ){		
	GUI.DrawTexture( new Rect( 67 * _scaleOffset.x, 80 * _scaleOffset.y, 510 * _scale, 309 * _scale ), instructionsTex );										
} else{
	GUI.DrawTexture( new Rect( 67 * _scaleOffset.x, 188 * _scaleOffset.y, 447 * _scale, 113 * _scale ), titleTex );
}						

That take care of that; the final piece is the scoreboard. OnGUI provides groups, groups allow you to group things together in a specified layout (horizontal or vertical). The following code draws the leaderboard and some dummy buttons for Facebook / Twitter and then iterates through all the scores adding them individually. Add the following code to the bottom of your OnGUI method:

	GUI.BeginGroup( new Rect( Screen.width - (214 + 10) * _scale, (Screen.height - (603 * _scale)) / 2, 215 * _scale, 603 * _scale ) ); 
	
	GUI.DrawTexture( new Rect( 0, 0, 215 * _scale, 603 * _scale ), leaderboardBgTex );
			
	Rect leaderboardTable = new Rect( 17 * _scaleOffset.x, 50 * _scaleOffset.y, 180 * _scale, 534 * _scale ); 
	if( _leaderboardController.IsFacebookAvailable && !_leaderboardController.IsLoggedIn ){			
		leaderboardTable = new Rect( 17 * _scaleOffset.x, 50 * _scaleOffset.y, 180 * _scale, 410 * _scale ); 
		GUI.DrawTexture( new Rect( 29* _scaleOffset.x, 477* _scaleOffset.y, 156 * _scale, 42 * _scale ), loginCopyTex );
		if (GUI.Button( new Rect ( 41 * _scaleOffset.x, 529 * _scaleOffset.y, 135 * _scale, 50 * _scale ), fbButtonTex, GUIStyle.none) )
		{
			_leaderboardController.LoginToFacebook(); 
		}			
	} 			
	GUI.BeginGroup( leaderboardTable ); 			
	if( _scores != null ){
		for( int i=0; i<_scores.Count; i++ ){
			Rect nameRect = new Rect( 5 * _scaleOffset.x, (20 * _scaleOffset.y) + i * 35 * _scale, 109 * _scale, 35 * _scale ); 
			Rect scoreRect = new Rect( 139 * _scaleOffset.x, (20 * _scaleOffset.y) + i * 35 * _scale, 52 * _scale, 35 * _scale ); 
			
			GUI.Label( nameRect, _scores[i].name ); 
			GUI.Label( scoreRect, _scores[i].points.ToString() ); 
		}
			}						
	GUI.EndGroup(); 	
	GUI.EndGroup();
	
}

And that's that for your GameMenuController; to finish off lets hook up your GameMenuController to your GameController class (which tells it to show and hide as required), open up the GameController and lets make your way through it.

So open up GameController and declare the following variables at the top:

private GameMenuController _menuController;
private LeaderboardController _leaderboardController;
public Alerter alerter; 

Now get reference to them in the Awake method;

	void Awake() {		
		_instance = this; 
		_menuController = GetComponent<GameMenuController>();
		_leaderboardController = GetComponent<LeaderboardController>();
	}

The most significant change is within your State property; replace your UpdateStatePlay with the following and afterwards we'll summarize the changes:

public GameStateEnum State{
	get{
		return _state; 	
	}
	set{
		_state = value; 
			
		// MENU 
		if( _state == GameStateEnum.Menu ){
			player.State = Player.PlayerStateEnum.BouncingBall;	
			_menuController.Show(); 	
		}
		
		// PAUSED 
		else if( _state == GameStateEnum.Paused ){
			Time.timeScale = 0.0f; 
			_menuController.Show(); 
		}
			
		// PLAY 
		else if( _state == GameStateEnum.Play ){
			Time.timeScale = 1.0f; 
			_menuController.Hide(); 								
				
			// notify user
			alerter.Show( "GAME ON", 0.2f, 2.0f ); 
		}
		
		// GAME OVER 
		else if( _state == GameStateEnum.GameOver ){
			// add score 
			if( _gamePoints > 0 ){
				_leaderboardController.AddPlayersScore( _gamePoints ); 	
			}
				
			// notify user
			alerter.Show( "GAME OVER", 0.2f, 2.0f ); 	
		}								
	}
}	

The code should hopefully be pretty self-explanatory; when the state is updated to Menu or Pause you ask your GameMenuController to show itself using the Show method you implemented. If the state is set to Play then you ask the GameMenuController to hide itself using the Hide method. And finally when the state is changed to GameOver you add the players score to your leaderboard (just how you did it when you created your example).

Finally, notice that this code depends on an Alerter object. So to make this work, create a new empty object, assign it the Alerter script, then drag that object onto the Alerter property on the Game Controller.