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?
Character animation
Unity provides a rich set of classes that handle the importing and use of animations from 3D packages. When you imported the player that was created in Blender, it came packaged with a set of animations. If you select the Animation Component of the Player object in the editor you’ll see the following:
The Animation Component has 10 slots, each containing a separate Animation Clip. You can play any of these animations in script by asking the Animation Component to play a specific Animation Clip.
Note: To find out more about the Animation Component, check out the official Unity documentation here: http://docs.unity3d.com/Documentation/Components/class-Animation.html
Note: To find out more about the Animation Component, check out the official Unity documentation here: http://docs.unity3d.com/Documentation/Components/class-Animation.html
Inside the Player script, add some variables to manage the current animation and AnimationClips that will reference the various animations:
private AnimationClip _currentAnimation = null;
public AnimationClip animIdle;
public AnimationClip animBounceDown;
public AnimationClip animBounceUp;
public AnimationClip animWalkForward;
public AnimationClip animWalkBackward;
public AnimationClip animPrepareThrow;
public AnimationClip animThrow;
public AnimationClip animScore;
public AnimationClip animMiss;
Referencing the animations via a variable provides the flexibility to easily update the animations without having to rely on a specific animation file or index/name.
Of course, for this to work you have to connect the appropriate animation to each public property on the script component, so go ahead and do that now:
The next step is to set up each of the animations. You do this by calling a dedicated method from the Player Start method (along with getting reference to the attached animation Component. Add the following code to do this:
void Start(){
_animation = GetComponent<Animation>();
InitAnimations();
}
private void InitAnimations ()
{
_animation.Stop ();
_animation [animIdle.name].wrapMode = WrapMode.Once;
_animation [animBounceDown.name].wrapMode = WrapMode.Once;
_animation [animBounceUp.name].wrapMode = WrapMode.Once;
_animation [animWalkForward.name].wrapMode = WrapMode.Loop;
_animation [animWalkBackward.name].wrapMode = WrapMode.Loop;
_animation [animPrepareThrow.name].wrapMode = WrapMode.Once;
_animation [animThrow.name].wrapMode = WrapMode.Once;
_animation [animScore.name].wrapMode = WrapMode.Once;
_animation [animMiss.name].wrapMode = WrapMode.Once;
_animation [animBounceDown.name].speed = 2.0f;
_animation [animBounceUp.name].speed = 2.0f;
}
The Animation Component acts as a controller and repository for your animations. Each animation is wrapped in a class called AnimationState. You can access each one by index position or key, where key is the name of the animation. This is shown visually in the editor screenshot above.
Take a look at two properties in each animation: wrapMode and speed. Speed determines the playback speed for the specific animation, whereas wrapMode determines how the animation is ‘wrapped’; in other words, what the animation does once it has come to the end. Here the animations are either being played just once, or are looping.
The only thing left to do is to play the animations! :] Add the following code to the Player class:
public bool IsAnimating{
get{
return _animation.isPlaying;
}
}
public AnimationClip CurrentAnimation {
get {
return _currentAnimation;
}
set {
SetCurrentAnimation (value);
}
}
public void SetCurrentAnimation (AnimationClip animationClip)
{
_currentAnimation = animationClip;
_animation [_currentAnimation.name].time = 0.0f;
_animation.CrossFade (_currentAnimation.name, 0.1f);
if (_currentAnimation.wrapMode != WrapMode.Loop) {
Invoke ("OnAnimationFinished", _animation [_currentAnimation.name].length /
_animation [_currentAnimation.name].speed);
}
}
private void OnAnimationFinished ()
{
if (OnPlayerAnimationFinished != null) {
OnPlayerAnimationFinished (_currentAnimation.name);
}
}
The above code shows all the methods associated with handling animation. The main method is SetCurrentAnimation().
Here the current animation time is reset to 0 (i.e. back to the start) and then the Animation Component is requested to crossFade the specified animation. Cross fading permits fading in an animation over the currently playing animation over the specified time. This means the current animation will be slowly ‘blended’ out to give a smooth transition between the current and new animation.
After requesting the animation to play via crossFade, check if the animation is a loop. If not, then request a delayed call to OnAnimationFinished() using the Invoke method. The call will be delayed by the time of the animation.
Finally, OnAnimationFinished() is responsible for raising the associated event, and thus notifying the GameController that an animation has finished so that it is aware of the current state and actions of the Player GameObject.
Time to test
Lets make sure your animations are all set up and running correctly. To do this, add the following line to the end of your Player's start method:
CurrentAnimation = animPrepareThrow;
Then disable the GameController script by unchecking the GameObjects component (as shown below):
And click on the play button; if all is well then you will see the basketball player play the "prepare to throw" animation! :]
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.
Managing state
Time to flesh out your State property (that you created previously); but before you do that lets stub out a method that you need.
private void AttachAndHoldBall(){
}
This method will be explained when we talk through how the basketball player bounces the ball. For now, replace your previous State property with the follow code snippet:
public PlayerStateEnum State{
get{
return _state;
}
set{
CancelInvoke("OnAnimationFinished");
_state = value;
_elapsedStateTime = 0.0f;
switch( _state ){
case PlayerStateEnum.Idle:
SetCurrentAnimation( animIdle );
break;
case PlayerStateEnum.BouncingBall:
_collider.enabled = false;
AttachAndHoldBall();
SetCurrentAnimation( animBounceUp );
break;
case PlayerStateEnum.PreparingToThrow:
SetCurrentAnimation( animPrepareThrow );
break;
case PlayerStateEnum.Throwing:
SetCurrentAnimation( animThrow );
break;
case PlayerStateEnum.Score:
SetCurrentAnimation( animScore );
break;
case PlayerStateEnum.Miss:
SetCurrentAnimation( animMiss );
break;
case PlayerStateEnum.Walking:
if( _shotPosition.x < _transform.position.x ){
SetCurrentAnimation( animWalkForward );
} else{
SetCurrentAnimation( animWalkBackward );
}
break;
}
}
}
Most of the code is related to setting up the appropriate animation based on the currently set state, using your SetCurrentAnimation method we created above. So lets concentrate on the less trivial code.
One of the first statements is:
CancelInvoke("OnAnimationFinished");
This statement asks Unity to cancel any invoke that maybe queued up called OnAnimationFinished, which should look pretty familiar to you because we created this invoke when playing a non-looping animation.
The next interesting piece of code is for the state PlayerStateEnum.Walking; in this block you are determining the animation based on the target (shot) position compared to your current position to determine whether the basketball player is walking backwards or forwards.