FollowerEntity sliding when on OffMeshLink

public class FollowerJumpLinkHandler : IOffMeshLinkHandler, IOffMeshLinkStateMachine
{
	public event Action OnBeginTraversal;
	public event Action OnEndTraversal;
    public bool CancelTraversal { get; set; }

    private readonly EnemyInfo _enemyInfo;
    private readonly JumpConfig _jumpConfig;
	private readonly string _jump1AnimatorState, _jump2AnimatorState, _jump3AnimatorState, _jump4AnimatorState;

	public FollowerJumpLinkHandler(
		EnemyInfo enemyInfo, 
		JumpConfig jumpConfig,
		string jump1AnimatorState = "Jump1", 
		string jump2AnimatorState = "Jump2", 
		string jump3AnimatorState = "Jump3",
		string jump4AnimatorState = "Jump4")
	{
		_enemyInfo = enemyInfo;
		_jumpConfig = jumpConfig;
		_jump1AnimatorState = jump1AnimatorState;
		_jump2AnimatorState = jump2AnimatorState;
		_jump3AnimatorState = jump3AnimatorState;
		_jump4AnimatorState = jump4AnimatorState;
	}

	IOffMeshLinkStateMachine IOffMeshLinkHandler.GetOffMeshLinkStateMachine(AgentOffMeshLinkTraversalContext context) => this;

	void IOffMeshLinkStateMachine.OnFinishTraversingOffMeshLink(AgentOffMeshLinkTraversalContext context)
	{
		OnEndTraversal?.Invoke();
	}

	void IOffMeshLinkStateMachine.OnAbortTraversingOffMeshLink()
	{
		Debug.Log("An agent aborted traversing an off-mesh link");
	}

	IEnumerable IOffMeshLinkStateMachine.OnTraverseOffMeshLink(AgentOffMeshLinkTraversalContext ctx)
	{
		OnBeginTraversal?.Invoke();
		CancelTraversal = false;
		var start = (Vector3)ctx.link.relativeStart;
		var end = (Vector3)ctx.link.relativeEnd;
		var dir = end - start;

		// Disable local avoidance while traversing the off-mesh link.
		// If it was enabled, it will be automatically re-enabled when the agent finishes traversing the link.
		ctx.DisableLocalAvoidance();

		// Move and rotate the agent to face the other side of the link.
		// When reaching the off-mesh link, the agent may be facing the wrong direction.
		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(_jump1AnimatorState);
		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(_jump1AnimatorState);
					break;
				case 1:
					_enemyInfo.AnimatorUtils.Play(_jump2AnimatorState);
					break;
				case 2:
					_enemyInfo.AnimatorUtils.Play(_jump3AnimatorState);
					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(_jump4AnimatorState, () =>
		{
			landingFinished = true;
		});

		while (!landingFinished && !CancelTraversal)
		{
			yield return null;
		}
	}
}

I am using the code above to handle traversal of offmesh links with the follower entity. My issue is that when the agent plays the jump4AnimatorState i don’t apply any movement, but he starts to slide backwards. Am I doing something wrong? Could this be a bug? (He seems to be sliding towards the relativeStart of the link)

I’ve actually been active recently on another thread with this same issue: Bug where followerEntity moves towards link.relativeStart while traversing OffMeshLink

We’re waiting to hear back from Aron on this one but I’ll be sure to make a note in the thread that you’re also experiencing this. More information is always helpful, so thanks for posting :slight_smile:

Hi

The current version always has the agent’s internal movement code running as well. This is not great if you want the movement to be determined entirely by e.g. an animation.

In the next update, the agent will by default not use its own movement logic, unless you explicitly enable AgentOffMeshLinkTraversalContext.enableBuiltInMovement or call AgentOffMeshLinkTraversalContext.MoveTowards.

1 Like