Jump Animation while traversing offmesh link

[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)

I removed cancelTraversal and changed how I handle logic to respect the traversal from other places in my code and it seems to be fixed now

1 Like

Thanks for posting your solution- it helps the forum be a better place to search for issues. Highly appreciated!