Has anyone experimented with using an FSM tool like NodeCanvas?
I just spent tonight putting together an FSM. As note.. I'm a programmer and hate visual tools like Playmaker, but lately I've began coordinating with level designers and it has made me appreciate not having to explain every single line of code I write.
What I'm curious about is... how does everyone currently code their animation controllers? With a basic enum for the state and SetAnimation method if a change is detected frame to frame? Unsure if that is more complicated than what I came up with, but the below seems to be working out well.
The EntityMovement script controls variables in the blackboard like movement speed, grounded, jumping, climbing, "triggers" for attacks where I set a boolean back to false the state it is used in and other things like getting hit.
I use one simple associated NodeCanvas task for this on each node.
The reasoning for the complex attack states is because some animations will flow into others. Attack 1 can lead to Attack 2 if you hit the attack button during the "last 25%" of Attack 1, so I detect this by always writing the normalized time of every animation to a variable and allow the boolean MeleeAttack to be turned on if within that last section or if completely not attacking at all.
All of the combat logic can also be controlled in here since OnEnter and OnExit can change booleans directly. If you enter a GetHit status you can set an input lock out until the animation completes instead of constantly checking for what animation is playing on specific tracks and running all your logic in code.
using System.Collections.Generic;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
using Spine.Unity;
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 float exitTime = .85f;
private float desiredExitTime;
public float timeScale = 1f;
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 lastTrackEventThreshold = 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;
//new code for stopping event mixing from the previous animation
if (trackEntry.mixingFrom != null)
{
trackEntry.mixingFrom.eventThreshold = lastTrackEventThreshold;
}
//set the desired exit time to x% into the animation
desiredExitTime = trackEntry.Animation.Duration * exitTime;
}
protected override void OnUpdate()
{
//if this animation doesn't loop, immediately end and transition out when completed
if (!loop && trackEntry.TrackTime >= desiredExitTime)
{
EndAction(true);
}
}
}
}
23 May 2016, 21:42
Really surprised no one has responded to this. The usage of NodeCanvas's FSM tool has been paramount to our game development. The workflow from the animator has changed a ton. I can test changing animation speed on the fly instantly during runtime and click GetHit booleans and triggers to see stuff flow and happen live without having to set up OnGUI nonsense or click the inspector for each animation. While that does work it doesn't let you see all the blending. You can create nodes at runtime and connect them... set up all your transition requirements like IsJumping, etc etc...
I can't imagine coding this all out now like I assume most people do.