This actually works great to default to 1f and now correctly plays events even if they're on the 2nd to last frame.. which is helpful.
I may have some logic wrong though.. If I am in the middle of Attack A and GetHit plays, the event in Attack A still isn't playing (which is what I want but not what I expect).
So Attack A executes which at .85% in, if queued attack is flagged.. flows into Attack B and everything works as expected.
If the player is hit during Attack A, the spine event somehow gets discarded which is great.. but how is that possible if the eventThreshold is 1f? I'd expect it to play all events still.
When I enter my FSM node for an animation, this is what it looks like.
All that's really relevant is OnExecute is called once when my state is entered and OnUpdate is well Update. At exit time on non looping animations like attacks, I transition out automatically but if the player is hit, then it immediately calls a GetHit animation which I would think would cause a mix to it and its eventThreshold is 1f.. I expected to have to set this to 0f to work correctly.
namespace NodeCanvas.Tasks.Actions
{
[Category("Picklefeet")]
public class PlayAnimationSpine : ActionTask<Transform>
{
private SkeletonAnimation skeletonAnimation;
private Spine.AnimationState spineAnimationState;
private Spine.TrackEntry trackEntry;
private Blackboard bb;
[RequiredField]
public string animationClip;
[SliderField(0, 4)]
public int layer = 0;
public bool loop = false;
public bool isAttack;
public float exitTime = .85f;
public float timeScale = 1f;
//input is locked while this animation is playing
public bool locksInput;
public BlendType blendType = BlendType.Set;
public enum BlendType
{
Set=1,
Add=2,
}
//0% means no events from the "from" anim will fire, 100% means mix all events from the "from" anim during transition
public float eventThreshold = 1f;
protected override string info
{
get { return "Anim " + animationClip.ToString(); }
}
protected override string OnInit()
{
if (skeletonAnimation == null)
{
skeletonAnimation = agent.GetComponentInChildren<SkeletonAnimation>();
spineAnimationState = skeletonAnimation.state;
bb = agent.GetComponent<Entity>().bbAnimation;
}
return null;
}
protected override void OnExecute()
{
if (blendType == BlendType.Set)
{
trackEntry = spineAnimationState.SetAnimation(layer, animationClip, loop);
}
else if (blendType == BlendType.Add)
{
trackEntry = spineAnimationState.AddAnimation(layer, animationClip, loop, 0f);
}
trackEntry.TimeScale = timeScale;
trackEntry.EventThreshold = eventThreshold;
//if (isAttack)
//{
bb["meleeAttack"] = false;
bb["attackChain"] = false;
//}
bb["animNormalizedTime"] = 0f;
bb["attackNormalizedTime"] = 0f;
if(locksInput)
{
bb["inputLock"] = true;
}
}
protected override void OnUpdate()
{
float normalizedTime = trackEntry.TrackTime / trackEntry.TrackEnd;
if (isAttack)
{
bb["attackNormalizedTime"] = normalizedTime;
if (normalizedTime < .25f)
{
bb["inputLock"] = true;
}
else
{
bb["inputLock"] = false;
}
}
bb["animNormalizedTime"] = normalizedTime;
//if this animation doesn't loop, immediately end and transition out when completed
if (!loop && normalizedTime >= exitTime)
{
//if this is an attack and we go past the point of no return for a chain attack, ensure everything is reset
if (isAttack)
{
bb["meleeAttack"] = false;
bb["attackChain"] = false;
}
if(locksInput && (bool)bb["inputLock"] == true)
{
bb["inputLock"] = false;
}
EndAction(true);
}
}
}
}