3
\$\begingroup\$

Background

I've recently reached a bottle neck in my game's code that forced me to completely decouple the logic/networking from the graphics. Thanks to the nature of my board game, I was able to build the following architecture:

Logic update loop:

+--> wait for event -> process event -> dispatch graphic events --+
|                                                                 |
+-----------------------------------------------------------------+

Graphics update loop:

+--> wait for event -> process event -> wait for animation --+
|                                                            |
+------------------------------------------------------------+

Question

From the previous design, tracking animation timing and knowing when they end is crucial, since that marks the end of the third phase in the graphics loop, allowing us to proceed to the next phase.

I'm using Unity, and I am not sure how to go about this. I am using Unity's animation system as well as LeanTween to perform animations on the 2D NGUI level as well as the 3d game scene level.

Proposed Solutions

Solutions that have occurred to me so far are:

Callbacks

Pass on a callback, and make the animation system trigger that callback when the animation finishes. This has proven to be high maintenance and redundant, as I have to manage passing data back and forth maintaining the overall animation state somehow (if multiple animations were triggered).

Global Monitor

Animations register under a global monitor, and I can emit a signal when the monitor becomes empty, triggering the end of the animations phase. I must make sure animations are registered immediately at the beginning of the animation phase, which I should be able to do. I should also maintain a list of animation IDs or a simple "anonymous" counter, which animations increment and decrement. Kinda like manual memory management. I might do this.

Any of those seem viable? Am I overlooking details about this system? Any better alternatives come to mind?

\$\endgroup\$
1
\$\begingroup\$

The callback solutions via the Unity's animation system is kinda tricky and as you said expansive to maintain. I found out to discard that solution for my concerns.

The global monitor solution requires a bit of effort but should give some handlable ed effective solution to the problem. But it wraps up the concept of Coroutine. A Coroutine can handle your WaitForAnimation step on its own.

I use Coroutines. When the anymation starts, you can Sstart a Coroutine which calls a function that do things. You can code roughly every kind of situation.

I apologize since I'm not on my dev post now so I'll write some pseudocode to give an idea. I could update later if you think this is of eny help.

private IEnumerator HandleAction(GameObject entity){
    Animator anim = entity.GetComponent<Animator>();
    float duration = anim.GetCurrentState().GetClip().duration;//not sure of this syntax. will update later
    while(duration > 0){
        //do stuff or wait
        duration -= Time.deltaTime;
        yield return null;
    }
    //code to execute when animation ends
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer. This helps, and gives me a new perspective through coroutines, however, I think it's the correct answer to a different problem. I know when the animation ends, I am using animation clip events for that, but how to ascertain that all animations have finished executing? What's the best way to externally track the animation of GameObjects. For example, would I use your code in a external monitor, and instead of receiving GameObject, the coroutine receives a whole list, and returns when all animations have completed? \$\endgroup\$ – Mazyod Jul 25 '16 at 15:46
0
\$\begingroup\$

This has proven to be a really challenging task, especially since there is no deterministic object lifetime in C#. I mean, when I usually run into an issue where I need to track a shared resource, I'd simply wrap the resource access in a RAII object. Can't really do that in C#.

With that said, I first went with the global monitoring idea, which worked well until it didn't .. The main issue was that in some cases, animations would be canceled, and they wouldn't go through their normal lifecycle either! So, if I increment a global counter, it never gets decremented.

This all led me to a single and simple conclusion:

The only way to truly track active animations is to check if the animator is active, regardless of anything else.

So, in the case of LeanTween, I would check:

LeanTween.isTweening()

As for the Unity animator controller, I can simply use the animator controller as an FSM system, and check if the current state is idle, then no animations are running. This can be achieved by either querying the animator object, or by attaching a behavior to the animation state.

Update:

To further simplify the process, I decided to query the animation durations ahead of time in the script that performs the animations on Unity's animator controller:

animationData = new Dictionary<string, float>();

foreach (var clip in animator.runtimeAnimatorController.animationClips) {
    animationData[clip.name] = clip.length;
}

And once I trigger any of those animations, I simply kick off a dummy LeanTween animation with the same duration:

LeanTween.value(gameObject, 0f, 1f, animationData["CubeDeath"]);

I understand this is not super ideal, but works really well as far as I've tested. These animations are guaranteed to finish at some point, so I won't be left with a hanging state. Also, if I use LeanTween.cancel, that case will also be taken care of, even though the onComplete handler isn't called.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.