In our shooting gallery, we will need to have something to shoot. In this case, we will need to create a target, which the player can shoot:
0
, 0
,-4
so it'll be on top of other objects.duck_outline_brown
sprites to the Target object, giving the duck a Position of 0
, 1
.1
,0
to place it on top of the stick.duck_outline_brown
object:Animations
folder we created earlier and then give it a name of Idle
before saying OK.Idle
drop-down and create a new clip, which will create a new animation and we will name Flip
.Y
Rotation to 90
. Select the duck_outline_brown
object and change its sprite to the duck_outline_back
image. Then, create another keyframe
at the 20th frame with a Y
Rotation of 180
.Animations
folder, and select the Flip
animation. From the Inspector tab, uncheck the Loop Time property. This will cause the animation to play once and then stop at the end, which is exactly what we want.Now that we have the animation working, let's make it so that we play the animation via code:
TargetBehaviour
. Inside it, use the following script:using UnityEngine; using System.Collections; public class TargetBehaviour : MonoBehaviour { private bool beenHit = false; private Animator animator; private GameObject parent; void Start() { parent = transform.parent.gameObject; animator = parent.GetComponent<Animator>(); } /// <summary> /// Called whenever the player clicks on the object. /// only works if you have a collider on the object /// </summary> void OnMouseDown() { // Is it valid to hit it if (!beenHit) { beenHit = true; animator.Play("Flip"); } } }
This code will play the Flip
animation on our target once we click on it. Note that the animator variable uses the object's parent. We need to do this because our collider is attached to the duck itself, not the Target object.
duck_outline_brown
image and then attach the Target Behavior
to it.We have the animation playing correctly! This is a great start.
Being able to use Unity's built-in animation system is great and can be quite useful if you want to modify many different properties at once. However, if you're only modifying a single property or you want to animate something purely via code, you can also make use of a tweening library where given a start and an end the library will take care of all the work in the middle to get it there within a time and speed you specify.
One of my favorite tweening libraries is PixelPlacement's iTween
, which is open source and useable for free in commercial and noncommercial projects.
Target Behavior
script in your IDE of choice and add the following variable:private bool activated;
public void ShowTarget() { if (!activated) { activated = true; beenHit = false; animator.Play("Idle"); iTween.MoveBy(parent, iTween.Hash("y", 1.4, "easeType", "easeInOutExpo", "time", 0.5 )); } }
From here, we'll see our first function that we are calling from iTween the MoveBy
function. This takes in two parameters—the first being the object we wish to move and the second being a Hash or hashtable. A hashtable is a data structure that creates an associative array such that certain keys will be mapped to certain values. In iTween, their implementation takes in sets of two with the first being a property and then second being the value we want it to be.
For more information on hashtables, check out http://en.wikipedia.org/wiki/Hash_table.
For more information on getting started with iTween, check out http://itween.pixelplacement.com/gettingstarted.php.
ShowTarget();
Once the game starts, you'll note that the duck flies up in the y axis by the amount we specified via the hash!
private Vector3 originalPos;
Start
function so that we initialize it:originalPos = parent.transform.position;
public IEnumerator HideTarget() { yield return new WaitForSeconds(.5f); // Move down to the original spot iTween.MoveBy(parent.gameObject, iTween.Hash( "y", (originalPos.y – parent.transform.position.y),"easeType", "easeOutQuad", "loopType", "none", "time", 0.5, "oncomplete", "OnHidden", "oncompletetarget", gameObject)); }
Note that in this case, we added the oncomplete
and oncompletetarget
parameters to our hash map. oncomplete
will call a function (in this case, OnHidden
, which we will create after this) after the amount of time has elapsed and "oncompletetarget
" will be what object iTween will look for the function with a name of whatever was provided in the oncomplete
portion. This is only needed if the object you're animating is not the object that iTween
is called on.
OnHidden
function:/// <summary> /// After the tween finishes, we now make sure we can be shown /// again. /// </summary> void OnHidden() { //Just to make sure the object's position resets parent.transform.position = originalPos; activated = false; }
OnMouseDown
button so that we will only flip when the animation is valid by adding in the following bolded code:void OnMouseDown() { // Is it valid to hit it if (!beenHit && activated) { beenHit = true; animator.Play("Flip"); StartCoroutine(HideTarget()); } }
Now we have our duck, and it can come up and down, it also needs to move rather than just stay in place:
ShowTarget
function to the following:public void ShowTarget() { if (!activated) { activated = true; beenHit = false; animator.Play("Idle"); iTween.MoveBy(parent, iTween.Hash("y", 1.4, "easeType", "easeInOutExpo", "time", 0.5, "oncomplete", "OnShown", "oncompletetarget", gameObject)); } }
OnShown
function so that after the object has popped up, it can start moving:void OnShown() { StartCoroutine("MoveTarget"); }
MoveTarget
function, let's add in the variables needed to have our player move correctly:public float moveSpeed = 1f; // How fast to move in // x axis public float frequency = 5f; // Speed of sine // movement public float magnitude = 0.1f; // Size of sine // movement
IEnumerator MoveTarget() { var relativeEndPos = parent.transform.position; // Are we facing right or left? if (transform.eulerAngles == Vector3.zero) { // if we're going right positive relativeEndPos.x = 6; } else { // otherwise negative relativeEndPos.x = -6; } var movementTime = Vector3.Distance(parent.transform.position, relativeEndPos) * moveSpeed; var pos = parent.transform.position; var time = 0f; while (time < movementTime) { time += Time.deltaTime; pos += parent.transform.right * Time.deltaTime * moveSpeed; parent.transform.position = pos + (parent.transform.up * Mathf.Sin(Time.time * frequency) * magnitude); yield return new WaitForSeconds(0); } StartCoroutine(HideTarget()); }
Math is a game developer's best friend, and here we are using sin (pronounced sine) by using the Mathf.Sin
function.
Taking the sin of an angle number gives you the ratio of the length of the opposite side of the angle to the length of the hypotenuse of a right-angled triangle.
If this didn't make any sense to you, don't worry. The neat feature of sin is that as the number gets larger, it will continuously give us a value between 0 and 1 that will go up and down forever, giving us a smooth repetitive oscillation.
For more information on sine waves, visit http://en.wikipedia.org/wiki/Sine_wave.
This mathematical principle could be used in a lot of effects, such as having save points/portals bob up and down, or any kind of object you would want to have slight movement or some special FX:
OnMouseDown
function so that the horizontal movement won't happen anymore if we click on the duck:/// <summary> /// Called whenever the player clicks on the object. Only works if /// you have a collider /// </summary> void OnMouseDown() { // Is it valid to hit it if (!beenHit && activated) { beenHit = true; animator.Play("Flip"); StopAllCoroutines(); StartCoroutine(HideTarget()); } }
Now our target will move towards our edge with a slight bobbing motion and will automatically hide itself afterwards if it is not clicked! Perfect!