Intermediate Unity 3D for iOS: Part 2/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 this tutorial series, you are learning how to create a simple 3D game in Unity called “Nothing but Net”. In […] By .

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

Time to test

You've written a bit of code so now would be a good time to test that everything is working as expected. As you did before, amend the Start method with the following state to test the bouncing of the ball:

State = PlayerStateEnum.BouncingBall;

Also as you did before, disable the GameController script by unchecking the GameObjects component so that it does not interfere with your test.

And click on the play button; if all is well then you will see the ball bounce up and down as shown below!

Note: Before continuing remember to enable the GameController again and remove the test code snippet.

Throwing the Ball

Start off by declaring the following variables at the top of your Player class:

public float maxThrowForce = 5000f; 
public Vector3 throwDirection = new Vector3( -1.0f, 0.5f, 0.0f ); 

The maxThrowForce is (as the name suggests) the maximum force you will apply to the ball when throwing it, the amount is relative to how long the user held their finger down (i.e. the longer they held their finger down the larger the proportion of this force will be used). The next variable, throwDirection, determine the angle that you will launch the ball when thrown.

Next add some code to the end of your Update() method to throw the ball at the appropriate time:

if (_state == PlayerStateEnum.PreparingToThrow) {
    if (GameController.SharedInstance.State == GameController.GameStateEnum.Play &&
        GameController.SharedInstance.TouchCount == 0) {

        State = PlayerStateEnum.Throwing;

        _holdingBall = false; 
        basketBall.BallRigidbody.AddRelativeForce (
            throwDirection *
            (maxThrowForce * _animation [animPrepareThrow.name].normalizedTime));
    }
}

Earlier you added some code to your update method to set the Player's state to "PreparingToThrow" when the ball is bouncing and the player taps the screen.

Now, you've added a check to see if you're in this state and the player releases their finger. You calculate a force to throw the ball based on the time remaining of the associated animation.

normalizedTime is a property of an Animation State which indicates how far the animation has played; 0.0 means the animation is at the start, while 1.0 means the animation has played all the way through.

Next, add the logic to handle the Throwing state:

if (_state == PlayerStateEnum.Throwing ) {
	// turn on the collider as you want the ball to react to the player is it bounces back 
	if( !_animation.isPlaying && !_collider.enabled ){				
		_collider.enabled = true; 
	}
}	

While in the Throwing state you poll to determine when the throw animation has finished and once done so you turn on your collider on to ensure that the ball doesn't roll through your character.

After the ball is thrown, the Player waits for instructions from the GameController. Based on the result from GameController, a specified animation will play. For instance, if the ball goes through the hoop, a winning animation is played; otherwise, a disappointed animation is played.

Once the play has ended (either missing the hoop and landing on the ground or going through the hoop and landing on the ground), the GameController randomly selects a new shot position and notifies the player to move into position.

Positions Please

Add the following variable to the top of your Player class:

public float walkSpeed = 5.0f; 

walkSpeed determines how fast your character will move into his new _shotPosition (set by the GameController).

Also, if you look inside the Player class you'll see a parameter called shotPosition that you added earlier. This will determine where the Player is standing to perform his shot, and it is updated by the GameController after each shot.

You need to initially set the shot position, so add the following line to the bottom of Awake():

_shotPosition = _transform.position; 

Next, override the ShotPosition getter/setter as follows:

public Vector3 ShotPosition{
	get{
		return _shotPosition; 
	}
	set{
		_shotPosition = value; 
		
		if( Mathf.Abs( _shotPosition.x - _transform.position.x ) < 0.1f ){
			State = PlayerStateEnum.BouncingBall; 	
		} else{
			State = PlayerStateEnum.Walking;
		}
	}
}

As mentioned above, the ShotPosition is set by the GameController. This makes it so when the ShotPosition is changed, the Player class checks to see if it represents moving to a new position, and if so changes the state of your Player class to Walking (otherwise, reverts to bouncing ball).

Then for each Update(), you move the player closer, and once close enough, start bouncing the ball again (which means the user can now attempt another shot).

To do this, add the following code to the end of Update():

if (_state == PlayerStateEnum.Walking) {
    Vector3 pos = _transform.position; 
    pos = Vector3.Lerp (pos, _shotPosition, Time.deltaTime * walkSpeed); 
    _transform.position = pos; 
 
    if ((pos - _shotPosition).sqrMagnitude < 1.0f) {
        pos = _shotPosition;
        if (OnPlayerAnimationFinished != null) {
            OnPlayerAnimationFinished (_currentAnimation.name);
        }
    }
}

Make note of the way that object position and movement is calculated in Unity. If you’ve done game development before, you're likely aware that in order to keep your game running consistently across all devices, you must update positions and movement relative to the elapsed time. You can access this via the Time static property deltaTime.

deltaTime is the time elapsed since the last update call. Why would this be used to calculate movement on-screen? If you have ever played an old game on a modern computer, you might have noticed that the characters in the game moved around so quickly that you couldn’t control them.

This is because the updates to the character positions were not relative to the elapsed time, but rather a constant. For instance, the distance to move an object 50 pixels is dependent upon many things, including processor speed. However, moving 50 pixels in 0.5 seconds will result in a constant, fluid movement on any platform or processor.

Note: You may be wondering what "lerp" is. lerp is a mathematical function that Linearly intERPolates one value to another. For instance, if you have a start value of 0 and an end value of 10, then by linearly interpolating by 0.5 you would end up with a value of 5. Get comfortable with using lerp; you’ll use it frequently!

And that's it - you're finally done, time to test this out! :]