- Home /
NavMeshAgent teleports to destination when OffmeshLink is Activated
I have a very particular behavior with my NavMeshAgent. If it stops at a manual OffmeshLink which is not activated, when the link becomes activated it will instantly teleport to the destination rather than navigating the Offmeshlink. Everything works fine if the Offmeshlink is activated when the NavMeshAgent reaches it.
I'm totally baffled, I don't see anything in my code that would allow a NavMeshAgent to teleport. I'm wondering if there's a bug in the NavMeshAgent involving offmeshlink activation.
I'm posting my code below, it's pretty much based on the agentLocomotion.js that came with the Unity3.5Beta preview projects, but translated into C#, and with the animation blending removed since I'm just working with a Cube right now.
using UnityEngine;
using System.Collections;
public class SimpleNavScript : MonoBehaviour {
private string locoState_ = "Locomotion_Stand";
private NavMeshAgent navAgent;
private Vector3 navAgentCurrentPosition;
public Transform navigationTarget;
private Vector3 navigatorTargetPosition;
private Vector3 linkStart;
private Vector3 linkEnd;
private Quaternion linkRotation;
public float jumpTimeDefault;
private float jumpTime;
private float jumpYOffset;
private float jumpHeightModifier;
private float jumpUpHeightModifier;
private float jumpDownHeightModifier;
public float jumpDownHeightModifierDefault;
public float jumpUpHeightModifierDefault;
private OffMeshLinkData link;
private OffMeshLinkAdditionalData offlinkdata;
private Hashtable additionalData;
// Use this for initialization
void Start () {
jumpYOffset = (collider as BoxCollider).size.y;
jumpTime = jumpTimeDefault;
jumpUpHeightModifier = jumpUpHeightModifierDefault;
jumpDownHeightModifier = jumpDownHeightModifierDefault;
navAgent = GetComponent<NavMeshAgent>();
//navAgent.updatePosition = false;
//navAgent.updateRotation = false;
navigatorTargetPosition = navigationTarget.position;
navAgent.SetDestination(navigatorTargetPosition);
StartCoroutine(maintainAnimation());
}
IEnumerator maintainAnimation()
{
while(Application.isPlaying)
{
yield return StartCoroutine(locoState_);
}
}
// Update is called once per frame
void FixedUpdate () {
if(navigatorTargetPosition != navigationTarget.position)
{
navigatorTargetPosition = navigationTarget.position;
navAgent.SetDestination(navigatorTargetPosition);
}
}
IEnumerator Locomotion_Stand()
{
do
{
// Blend Animations here
yield return null;
}while(navAgent.remainingDistance == 0 );
locoState_ = "Locomotion_Move";
yield break;
}
IEnumerator Locomotion_Move()
{
do
{
// Blend Animations here;
yield return null;
if(navAgent.isOnOffMeshLink )
{
if(navAgent.currentOffMeshLinkData.activated)
{
locoState_ = SelectLinkAnimation();
yield break;
}
}
}while(navAgent.remainingDistance != 0);
locoState_ = "Locomotion_Stand";
yield break;
}
string SelectLinkAnimation()
{
link = navAgent.currentOffMeshLinkData;
if(link.offMeshLink)
{
offlinkdata = link.offMeshLink.gameObject.GetComponent<OffMeshLinkAdditionalData>();
if(offlinkdata)
{
additionalData = offlinkdata.GetAdditionalData();
string offMeshLinkType = additionalData["offMeshLinkType"] as string;
if(offMeshLinkType.CompareTo("Jump") == 0)
{
return "Locomotion_JumpAnimation";
}
else if(offMeshLinkType.CompareTo("SlidePole") == 0)
{
return "Locomotion_SlidePole";
}
else
{
return "Error";
}
}
else
{
return "Error";
}
}else /// Default Offmesh Link... just jump with default paramaters
{
jumpTime = jumpTimeDefault;
jumpUpHeightModifier = jumpUpHeightModifierDefault;
jumpDownHeightModifier = jumpDownHeightModifierDefault;
return "Locomotion_JumpAnimation";
}
}
IEnumerator Locomotion_SlidePole()
{
Transform slidePoleStartPosition = additionalData["slidePoleStartPosition"] as Transform;
Transform slidePoleEndPosition = additionalData["slidePoleEndPosition"] as Transform;
float jumpOnHeightModifier = (float) additionalData["jumpOnHeightModifier"];
float jumpOnTime = (float) additionalData["jumpOnTime"];
float slideTime = (float) additionalData["slideTime"];
float jumpOffHeightModifier = (float) additionalData["jumpOffHeightModifier"];
float jumpOffTime = (float) additionalData["jumpOffTime"];
additionalData = null;
linkStart = link.startPos;
linkEnd = link.endPos;
Vector3 alignDir = slidePoleStartPosition.position - linkStart;
alignDir.y = 0.0f;
linkRotation = Quaternion.LookRotation(alignDir);
navAgent.Stop(true);
float jumpOnStartTime = Time.time;
transform.rotation = linkRotation;
Vector3 posStartAnim = transform.position;
//Debug.Log("Starting JumpAnimation... jumpHeightModifier : " + jumpHeightModifier.ToString());
float tLerp = 0.0f;
do
{
tLerp = (Time.time - jumpOnStartTime) / jumpOnTime;
Vector3 newPos = Vector3.Lerp(posStartAnim, slidePoleStartPosition.position, tLerp);
newPos.y += jumpOnHeightModifier * Mathf.Sin(Mathf.PI * tLerp) + jumpYOffset;
transform.position = newPos;
yield return null;
} while(tLerp < 1.0f);
tLerp = 0.0f;
float slideStartTime = Time.time;
do
{
tLerp = (Time.time - slideStartTime) / slideTime;
Vector3 newPos = Vector3.Lerp(slidePoleStartPosition.position, slidePoleEndPosition.position, tLerp);
transform.position = newPos;
yield return null;
} while(tLerp < 1.0f);
alignDir = linkEnd - slidePoleEndPosition.position;
alignDir.y = 0.0f;
linkRotation = Quaternion.LookRotation(alignDir);
transform.rotation = linkRotation;
float jumpOffStartTime = Time.time;
tLerp = 0.0f;
do
{
tLerp = (Time.time - jumpOffStartTime) / jumpOffTime;
Vector3 newPos = Vector3.Lerp(slidePoleEndPosition.position, linkEnd, tLerp);
newPos.y += jumpOffHeightModifier * Mathf.Sin(Mathf.PI * tLerp) + jumpYOffset;
transform.position = newPos;
yield return null;
} while(tLerp < 1.0f);
navAgent.CompleteOffMeshLink();
//link = null;
offlinkdata = null;
navAgent.Resume();
//transform.position = linkEnd;
locoState_ = "Locomotion_Stand";
yield break;
}
IEnumerator Locomotion_JumpAnimation()
{
if(additionalData != null)
{
if((additionalData["offMeshLinkType"] as string).CompareTo("Jump") == 0)
{
jumpUpHeightModifier = (float) additionalData["jumpUpHeightModifier"];
jumpDownHeightModifier = (float) additionalData["jumpDownHeightModifier"];
jumpTime = (float)additionalData["jumpTime"];
additionalData = null;
}
}
float distS = (transform.position - link.startPos).magnitude;
float distE = (transform.position - link.endPos).magnitude;
if(link.startPos.y < link.endPos.y)
{
jumpHeightModifier = jumpUpHeightModifier;
}
else
{
jumpHeightModifier = jumpDownHeightModifier;
}
if(distS < distE)
{
linkStart = link.startPos;
linkEnd = link.endPos;
}
else
{
linkStart = link.endPos;
linkEnd = link.startPos;
}
Vector3 alignDir = linkEnd - linkStart;
alignDir.y = 0.0f;
linkRotation = Quaternion.LookRotation(alignDir);
navAgent.Stop(true);
//blend animations here
float jumpStartTime = Time.time;
//float jumpEndTime = Time.time + jumpTime;
transform.rotation = linkRotation;
Vector3 posStartAnim = transform.position;
float tLerp = 0.0f;
Debug.Log("Starting JumpAnimation... jumpHeightModifier : " + jumpHeightModifier.ToString());
do
{
tLerp = (Time.time - jumpStartTime) / jumpTime;
Vector3 newPos = Vector3.Lerp(posStartAnim, linkEnd, tLerp);
newPos.y += jumpHeightModifier * Mathf.Sin(Mathf.PI * tLerp) + jumpYOffset;
transform.position = newPos;
yield return null;
} while(tLerp < 1.0f);
// play idle animation
navAgent.CompleteOffMeshLink();
//link = null;
offlinkdata = null;
navAgent.Resume();
//transform.position = linkEnd;
locoState_ = "Locomotion_Stand";
yield break;
}
}
Answer by DanSuper · Jan 18, 2013 at 07:06 PM
So, I think I worked it out this morning. I added code for checking for a partial path to the Locomotion_Move method. If the path is partial, I check to see if the NavAgent is close to it's current steering target. If it is, I stop the Navmesh agent then check on subsequent frames to see if the path is stale. When the path becomes stale I reset it's target and resume the agent, which will then navigate the offmeshlink correctly. I'm putting the code changes down below so others who encounter this problem can see my solution (and hopefully comment if they see improvements)
IEnumerator Locomotion_Move()
{
do
{
// Blend Animations here;
yield return null;
if(navAgent.pathStatus == NavMeshPathStatus.PathPartial)
{
if((navAgent.steeringTarget - transform.position).magnitude < 1.0f)
{
isPathPartial = true;
navAgent.Stop();
Debug.Log("Stopping NavAgent");
}
}
if( isPathPartial )
{
if(navAgent.isPathStale)
{
navAgent.SetDestination(navigatorTargetPosition);
isPathPartial = false;
navAgent.Resume();
Debug.Log ("Resuming NavAgent");
}
}
if(navAgent.isOnOffMeshLink )
{
if(navAgent.currentOffMeshLinkData.activated)
{
locoState_ = SelectLinkAnimation();
yield break;
}
}
}while(navAgent.remainingDistance != 0);
locoState_ = "Locomotion_Stand";
yield break;
}
I'm un-marking my answer as the answer... because I get the proper behavior if a short amount of time has passed (less than a $$anonymous$$ute) but if I wait more than a $$anonymous$$ute... I still get the teleporting....
Frustrating.....
Answer by nasoukikos · May 23, 2014 at 06:28 AM
I don't know if you have solved this problem but how i worked around it is by deactivating the AutoTraverseOffMeshLink from the nav mesh agent and when needed i activate it again, and reducing the navspeed to get a much more desirable effect, once he is on navmesh again, i reset my speed. how my code looks like
`if(nav.isOnOffMeshLink) { if(nav.currentOffMeshLinkData.offMeshLink.tag == "Stairs") {
nav.autoTraverseOffMeshLink = true;
nav.speed = 0.05f;
}
Your answer
Follow this Question
Related Questions
Can't make the manual offmeshlink work 1 Answer
Manual OffMeshLink Max Distance? 1 Answer
Enemies using navmesh are bumping each other through solid objects 1 Answer
How to reduce the rotation radius in NavMeshAgent ? 2 Answers
Why is my NavMeshAgent jerking over to the Off Mesh Link traversal point? 1 Answer