1
\$\begingroup\$

I am modding a 2D game that heavily depending on Animation Event to drive game logic.

How can I analyze an animation event during runtime and by using unpack tools?

  • During runtime, I would want to know which animation fires the event, and the time point of the event in the animation.
  • By using unpack tools, I would like to view investigate the complete sequence.
    In UABE, I could see m_Events in AnimationClip, but:
    • Is "float time" in seconds?
    • How to locate SpriteRenderer, etc. from a AnimationClip? (I mean, for SpriteRenderer, Animator etc. do they always sit between 0-1 depth away, or they search each other deeper? Unity component dependencies are extremely puzzling and undocumented)
    • Where is Animation Event's receiver object stored?
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

In future, please ask one question per post. As you can see in the links below, many of these questions are answered by the documentation already,

Is "float time" in seconds?

The documentation says it is. Seconds as unit of time is the norm in Unity.

When a time is represented relative to the length of a clip, they'll call it normalizedTime instead, to distinguish this.

Where is Animation Event's receiver object stored?

According to the documentation, animation events call functions on "any script attached to the GameObject" that hosts the Animator or Animation component playing the animation. "Similar to SendMessage" as the scripting API docs say.

During runtime, I would want to know which animation fires the event, and the time point of the event in the animation.

If you have a function that's being called by an animation event, you can get a reference to the Animation or Animator component on that same object, inquire about its currently-played clip(s), and find the matching event in them.

That could look something like this - I haven't tested this code extensively, but it should give the general idea of the strategy:

(AnimationClip, float) FindAnimationEventSource(string functionName)
{
    // If fired by an Animation component, 
    // we just have to check all cross-fading animation states.
    if (TryGetComponent(out Animation animation))
    {
        foreach (AnimationState state in animation)
        {
            foreach (AnimationEvent e in state.clip.events)
            {
                if (e.functionName != functionName) continue;

                // A clip might call the same event function more than once,
                // so let's make sure this instance matches the playback time.
                if (e.time <= state.time
                    && e.time >= state.time - Time.deltaTime * state.speed)
                {

                    return (state.clip, e.time);
                }
            }
        }
    }
    // For an Animator, things get more complicated, since we
    // have to check the current and next transition states in each layer.
    else if (TryGetComponent(out Animator animator))
    {
        for (int i = 0; i < animator.layerCount; i++) {
            var state = animator.GetCurrentAnimatorStateInfo(i);

            foreach (var clipInfo in animator.GetCurrentAnimatorClipInfo(i)) {
                foreach (var e in clipInfo.clip.events) {
                    if (e.functionName != functionName) continue;

                    float time = state.normalizedTime * clipInfo.clip.length;

                    if (e.time <= time
                        && e.time >= time - Time.deltaTime * state.speed)
                    {

                        return (clipInfo.clip, e.time);
                    }
                }
            }

            state = animator.GetNextAnimatorStateInfo(i);

            foreach (var clipInfo in animator.GetNextAnimatorClipInfo(i))
            {
                foreach (var e in clipInfo.clip.events)
                {
                    if (e.functionName != functionName) continue;

                    float time = state.normalizedTime * clipInfo.clip.length;

                    if (e.time <= time
                        && e.time >= time - Time.deltaTime * state.speed)
                    {

                        return (clipInfo.clip, e.time);
                    }
                }
            }
        }
    }

    return (null, float.NaN); // No match found.
}

How to locate SpriteRenderer, etc. from a AnimationClip? (I mean, for SpriteRenderer, Animator etc. do they always sit between 0-1 depth away, or they search each other deeper? Unity component dependencies are extremely puzzling and undocumented)

It's not that the relationships of the components are undocumented, it's that those relationships are up to the developer. I can have an Animation on one object modify properties on another object arbitrarily distant in its child hierarchy, if that's what I choose to do as the creator of the game. Paths to these components are specified explicitly when authoring the clip, they are not found by searching.

Here's an excerpt of the YAML of a sample AnimationClip I just made, changing the sprite of a SpriteRenderer component located on an object named "Grandchild" which is nested under an object called "FirstChild" which is in turn nested under the root object with the Animation component.

  m_PPtrCurves:
  - curve:
    - time: 0
      value: {fileID: 21300000, guid: 7bc5fd6fd95fa87488b4b5405bfccdb7, type: 3}
    - time: 0.21666667
      value: {fileID: 21300000, guid: e2b76bfe1164e774291c9a49d5cea5f0, type: 3}
    - time: 0.38333333
      value: {fileID: 21300000, guid: 7bc5fd6fd95fa87488b4b5405bfccdb7, type: 3}
    attribute: m_Sprite
    path: FirstChild/Grandchild
    classID: 212
  • The attribute line indicates the name of the field being modified.

  • The path line gives the path to the object with this component.

  • The classID line identifies the SpriteRenderer component.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .