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.