[CreateAssetMenu(menuName = "SO/OffMeshLinkHandler/JumpOffMeshLinkHandler")]
public class JumpOffMeshLinkHandler : OffMeshLinkHandler
{
[SerializeField] private JumpConfig JumpConfig;
[SerializeField] private string Jump1State = "Jump1", Jump2State = "Jump2", Jump3State = "Jump3", Jump4State = "Jump4";
public override IEnumerable Handle(EnemyInfo enemyInfo, AgentOffMeshLinkTraversalContext ctx, bool cancelTraversal)
{
var start = (Vector3)ctx.link.relativeStart;
var end = (Vector3)ctx.link.relativeEnd;
var dir = end - start;
ctx.DisableLocalAvoidance();
while (!ctx.MoveTowards(
position: start,
rotation: Quaternion.LookRotation(dir, ctx.movementPlane.up),
gravity: true,
slowdown: true).reached)
{
if (cancelTraversal) yield break;
yield return null;
}
enemyInfo.AnimatorUtils.PlayForce(Jump1State);
float normalizedTime = 0.0f;
while (normalizedTime < 1.0f)
{
if (cancelTraversal) yield break;
int currentFrameIndex = 0;
for (int i = 0; i < JumpConfig.JumpCurve.keys.Length; i++)
{
if (JumpConfig.JumpCurve.keys[i].time > normalizedTime) break;
currentFrameIndex = i;
}
switch (currentFrameIndex)
{
default:
case 0:
enemyInfo.AnimatorUtils.Play(Jump1State);
break;
case 1:
enemyInfo.AnimatorUtils.Play(Jump2State);
break;
case 2:
enemyInfo.AnimatorUtils.Play(Jump3State);
break;
}
float yOffset = JumpConfig.JumpCurve.Evaluate(normalizedTime);
ctx.transform.Position = Vector3.Lerp(start, end, normalizedTime) + yOffset * Vector3.up;
normalizedTime += Time.deltaTime / JumpConfig.JumpDuration;
yield return null;
}
bool landingFinished = false;
enemyInfo.AnimatorUtils.Play(Jump4State, () =>
{
landingFinished = true;
});
while (!landingFinished && !enemyInfo.FollowerOffMeshLinkHandler.CancelTraversal)
{
yield return null;
}
}
}
public class FollowerOffMeshLinkHandler : IOffMeshLinkHandler, IOffMeshLinkStateMachine
{
public bool CancelTraversal { get; set; }
private readonly EnemyInfo _enemyInfo;
private readonly Dictionary<OffMeshLinkType, OffMeshLinkHandler> _offMeshLinkHandlerMap;
public FollowerOffMeshLinkHandler(
EnemyInfo enemyInfo,
Dictionary<OffMeshLinkType, OffMeshLinkHandler> offMeshLinkHandlerMap)
{
_enemyInfo = enemyInfo;
_offMeshLinkHandlerMap = offMeshLinkHandlerMap;
}
IOffMeshLinkStateMachine IOffMeshLinkHandler.GetOffMeshLinkStateMachine(AgentOffMeshLinkTraversalContext context) => this;
void IOffMeshLinkStateMachine.OnFinishTraversingOffMeshLink(AgentOffMeshLinkTraversalContext context)
{
//Debug.Log("An agent finished traversing an off-mesh link");
}
void IOffMeshLinkStateMachine.OnAbortTraversingOffMeshLink()
{
Debug.Log("An agent aborted traversing an off-mesh link");
}
IEnumerable IOffMeshLinkStateMachine.OnTraverseOffMeshLink(AgentOffMeshLinkTraversalContext ctx)
{
OffMeshLinkInfo linkInfo = ctx.link.gameObject.GetComponent<OffMeshLinkInfo>();
Assert.IsNotNull(linkInfo, "Link is missing required OffMeshLinkInfo component");
CancelTraversal = false;
Assert.IsTrue(_offMeshLinkHandlerMap.ContainsKey(linkInfo.Type), $"The dictionary is missing the required key: '{linkInfo.Type}'");
return _offMeshLinkHandlerMap[linkInfo.Type].Handle(_enemyInfo, ctx, CancelTraversal);
}
}
Above is my traversal logic I have an issue where the execution gets stuck in the last while loop even though the animation call back is hit. The agent then proceeds to think that he is still traversing the offmesh link even if cancelTraversal becomes true. My goal is to have the animation clip finish(it is the landing animation) and then proceed with movement. Another clue could be that the bug appears when there are two node links stacked on top of each other(Imagine a small platform that the agent can simply jump on and then off on the other side)