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 .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Intermediate Unity 3D for iOS: Part 2/3
65 mins
- Making Sure Everybody Plays Together Nicely
- Scripting, Scripting, Scripting
- ScoreBoard
- Time to test
- Controlling Collisions
- Time to test
- Player Framework
- GameController
- Variables
- Game States
- Support methods and properties
- Keeping everything up to date
- Time to test
- Handling User Input
- Ball Handling: Dealing With Messages
- Handling Messages from the Player Component
- The Player: "Stubby" No More!
- Character animation
- Time to test
- Managing state
- Time to test
- Bouncing the Ball
- Time to test
- Throwing the Ball
- Positions Please
- Testing it out!
- Where To Go From Here?
Time to test
Similar to above, let's perform a quick test to check that your states and animations are working correctly together. Add the following code to the Start method of your Player class:
State = PlayerStateEnum.Score;
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 your basketball player plays the "score" animation (the animation that will run when the user successfully gets a ball in the hoop).
Note:Before continuing remember to enable the GameController again and remove the test code snippet.
Note:Before continuing remember to enable the GameController again and remove the test code snippet.
Bouncing the Ball
One responsibility of the basketball player is to bounce the ball while waiting for user input. In this section we will cover the code and setup required to make this happen.
Start by declaring the following variables at the top of your Player class:
public Ball basketBall;
public float bounceForce = 1000f;
private Transform _handTransform;
The variable _handTransform will hold reference to the Transform Component of the bone that will be touching the ball and the bounceForce is used to determine how much force is applied to the ball when bouncing (the basketball variable should be pretty obvious).
One of the first problems to solve is how to position the ball in the player's hand when the Player's state changes to BouncingBall. Implement the AttachAndHoldBall method you stubbed out earlier to do that:
public void AttachAndHoldBall ()
{
_holdingBall = true;
Transform bTransform = basketBall.BallTransform;
SphereCollider bCollider = basketBall.BallCollider;
Rigidbody bRB = basketBall.BallRigidbody;
bRB.velocity = Vector3.zero;
bTransform.rotation = Quaternion.identity;
Vector3 bPos = bTransform.position;
bPos = _handTransform.position;
bPos.y -= bCollider.radius;
bTransform.position = bPos;
}
One of the publicly exposed variables (named basketball) holds a reference to the basketball object. This function needs a reference to the ball's transform, collider, and rigid body, so the first part of this method gets those.
With respect to the Rigidbody, any current velocity is removed and the ball (to ensure it has stopped completely and won't 'bounce' out of your hand) is then positioned to the player’s hand using the Ball's Collider to offset based on the diameter of the ball.
You may be wondering where _handTransform comes from. Recall that you added a Box Collider to one of the Player's hand when setting up the scene in Part 1.
To make use of this, add the following code to the end of Awake():
_handTransform = _transform.Find (
"BPlayerSkeleton/Pelvis/Hip/Spine/Shoulder_R/UpperArm_R/LowerArm_R/Hand_R");
This grabs a reference to the appropriate component and attaches to the _transform variable. An alternative would have been to expose it as a public property and assign it via the editor like you've done so far, but this is a nice opportunity to demonstrate you how you can traverse a GameObject to obtain reference to one of its children.
Once the Player is holding the ball, he needs to start bouncing it! :]
You do this by holding the ball and playing the BounceUp animation. If, during Update(), the game is in the BouncingBall state, the Player is holding the ball, and the Bounce Down animation has finished playing, then push the ball down via the AddRelativeForce method of the Ball's Rigidbody using your bounceForce variable. This will force the ball to the ground and make it bounce back up (hence why the force is so high).
Replace the Update method with the following code:
void Update ()
{
if( _holdingBall ){
AttachAndHoldBall();
}
_elapsedStateTime += Time.deltaTime;
}
First you check if the _holdingBall has been set. If it is, you call the AttachAndHoldBall you just implemented above to position the ball in your basketball player's hand.
The _holdingBall method is set to true within the AttachAndHoldBall method (which in turn is called when the state is changed to BouncingBall) and set to false during bouncing and once thrown the ball.
Next add the following to the end of Update():
if( _state == PlayerStateEnum.BouncingBall ){
if( _holdingBall ){
if( GameController.SharedInstance.State == GameController.GameStateEnum.Play && GameController.SharedInstance.TouchDownCount >= 1 ){
State = PlayerStateEnum.PreparingToThrow;
return;
}
}
if( _currentAnimation.name.Equals( animBounceDown.name ) ){
if( !_animation.isPlaying && _holdingBall ){
// let go of ball
_holdingBall = false;
// throw ball down
basketBall.BallRigidbody.AddRelativeForce( Vector3.down * bounceForce );
}
}
else if( _currentAnimation.name.Equals( animBounceUp.name ) ){
if( !_animation.isPlaying ){
SetCurrentAnimation( animBounceDown );
}
}
}
The above block (embedded into your Update method) first checks if we're currently holding the ball, if so asks the GameController if a touch is present. If so, it swaps to the PrepareToThrow state, otherwise checks what animation you're current playing and if it has finished.
If the down animation has finished then you push the ball to the ground, and if the up animation has finished you start the down animation.
As the ball is bouncing back up, it will collide with the hand's Box Collider trigger. Implement a method that will be called when this occurs:
public void OnTriggerEnter (Collider collider)
{
if (_state == PlayerStateEnum.BouncingBall) {
if (!_holdingBall && collider.transform == basketBall.BallTransform) {
AttachAndHoldBall ();
SetCurrentAnimation (animBounceUp);
}
}
}
This makes it re-start the bouncing sequence all over again when the ball bounces back up to the hand!
Note that trigger events don’t propagate up to the parent, whereas Collision events do. Therefore this OnTriggerEnter method of the Player Component that you just wrote will not be automatically called when the collision occurs.
However, you can write a helper script written to facilitate this. Create a new script named PlayerBallHand and enter the following code:
using UnityEngine;
using System.Collections;
[RequireComponent (typeof(Collider))]
public class PlayerBallHand : MonoBehaviour
{
private Player _player = null;
void Awake ()
{
}
void Start ()
{
Transform parent = transform.parent;
while (parent != null && _player == null) {
Player parentPlayer = parent.GetComponent<Player>();
if (parentPlayer != null) {
_player = parentPlayer;
} else {
parent = parent.parent;
}
}
}
void Update ()
{
}
void OnTriggerEnter (Collider collider)
{
_player.OnTriggerEnter (collider);
}
}
This Component is responsible for notifying the Player Component when the ball collides with the hand.
Next switch back to Unity and to attach this script to the Hand_R of the player object. Remember that you created a collider on this object in Part 1 of this tutorial.
Also, select the Player object and attach the basketball to the public variable for it.
And finally, select the BallPhyMat and set the bounciness to 1 so the basketball bounces up with enough force.