- Home /
make events occur only at specific times
I want to make an object animate ( and a few other things) when the player collides with it, but I only want this animation to occur when the game time (Time.time) is a multiple of 6. so for instance I collide with the object 9.5 seconds into the game, but the animation doesn't occur for another (12 - 9.5) = 2.5 seconds, then once it is animated and the player collides with it, at say 16 seconds into the game, the animation should stop again when the time reaches 18, so 2 seconds after collision, and so on infinitely. I've written a script that does this for the most part, the problem I'm experiencing is that it works for the first collision, but once it's animated and collided with it stops immediately, as in at 16 seconds, from my example. I'm using Coroutines to add the delay after collision, but perhaps this is the wrong method?
I should add that it does seem to work if I collide with object within maybe a second of when it should change, so collide around 17 seconds, it stops at 18 as it should.
Here's my code:
public class SwitchController : MonoBehaviour {
private float rpm = 10f;
private GameObject paperRings;
private Animator animator;
private bool spinActivated ;
private float multipleof = 6f;
void Start ()
{
paperRings = GameObject.FindGameObjectWithTag ("paperrings");
animator = GetComponent<Animator> ();
}
// To find the delay
private float RoundToNearestMultiple()
{
int multiple = Mathf.RoundToInt(Time.time/multipleof);
return multiple*multipleof;
}
private float Wait()
{
return RoundToNearestMultiple () - Time.time;
}
void Update()
{
if (spinActivated == true)
{
paperRings.transform.Rotate (0, 0, 6 * rpm * Time.deltaTime);
animator.SetBool ("spinning", true);
}
if (spinActivated == false)
{
animator.SetBool ("spinning", false);
}
}
IEnumerator Spin()
{
yield return new WaitForSeconds(Wait());
spinActivated = true;
yield return null;
}
IEnumerator StopSpin()
{
yield return new WaitForSeconds(Wait());
spinActivated = false;
yield return null;
}
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "song" && spinActivated == false )
{
StartCoroutine(Spin ());
}
if (col.gameObject.tag == "song" && spinActivated == true)
{
StartCoroutine (StopSpin());
}
}
}
I've changed it a little, I think the problem is I need to calculate how long to wait at the point of collision, but I'm having trouble with the syntax. This is basically it
using UnityEngine;
using System.Collections;
public class SwitchController : MonoBehaviour {
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "song" )
{
StartCoroutine("Spin");
}
}
IEnumerator Spin()
{
float wait = (((Mathf.RoundToInt(Time.time/6f)) * 6f) - Time.time);
yield return new WaitForSeconds(wait);
Debug.Log("Hi");
}
}
but it doesn't work :(
Answer by fafase · Aug 20, 2015 at 05:39 AM
private bool collided = false;
void OnCollisionEnter2D(Collision2D col){
if(collided == false)
StartCoroutine(AnimationCoroutine());
}
IEnumerator AnimationCoroutine(){
collided = true;
while (true){
int timer = (int)Time.time;
if((timer % 6) == 0) {break;}
yield return null;
}
// Start animation
while ( animation.isPlaying ){
yield return null;
}
collided = false;
}
So the idea is that you collide and start the coroutine. The collided bool makes sure you don't call many times as a sprite can have two collision points (or more on complex polygon).
the first while loop waits for the time to be a multiple of 6. The second loop waits for the animation to be done.
Thanks fafase, this is really really helpful, all I had to do was take out the second while loop as my animation has no exit time. Thank you
However It's not very time accurate, which unfortunately for me it needs to be, perhaps InvokeRepeating would be more precise?
Why is that not? You take the time, then cut it to integer (6 to 6.9), use $$anonymous$$athf.Round other wise (5.5 to 6.5) and check if that value is a multiple of 6. That is what you were after weren't it?
Sorry, I didn't mean the maths, I meant the coroutine, for some reason it doesn't work if I collide about a second after it has stopped, basically it works, just not sure why there's that delay after. It's probably my code.
This is what I have
void Start()
{
animator = GetComponent<Animator> ();
}
void OnCollisionEnter2D(Collision2D col)
{
if (collided == false)
{
StartCoroutine(AnimationCoroutine ());
}
}
IEnumerator AnimationCoroutine()
{
collided = true;
while (true)
{
int timer = (int)Time.time;
if((timer % 6) == 0)
{
break;
}
yield return null;
}
animator.SetTrigger ("hit");
yield return new WaitForSeconds (.1f);
collided = false;
}
}
I just added WaitForSeconds and it works well now.