My question is regarding the use of the unity Navmesh agent component and handling rotation of an object to align with the surface it is on(IE you have a Bug AI Agent pathing around on a terrain and it moves up a slope but does not align with the slope so its face ends up under ground and its tail and hind legs end up floating off the terrain). Now i know how to accomplish this but the issue is that the navmesh agent componenet takes explicit control over the objects rotation, if i disable its control it stops steering in the correct direction that its moving. Is there anyway around this or am i just stuck with it until unity adds some sort of functionality? Thanks in advance for your input.
Your agent mesh need not contain the navmesh agent.
You can create a dummy invisible object and attach the navmesh agent to it. Then take your real actor/object/GO and make it a child of this navmeshagent (or constraint it to the dummy object that contains the navmeshagent in any manner you like using code.)
Now, you can use localrotations to align the object with the normal of the topology as it is independent of the dummy.
So lets say you have a soldier. Make a sphere and add navmeshagent component. Disable the meshrenderer of the sphere. Make this sphere the parent of the soldier. Now when the sphere moves, the soldier will move too. Control the localrotation of soldier as necessary using code. Use baseoffset if necessary to align the two.
Haha that sounds like it will work perfectly, no idea why i didnt think of just parenting the model and controlling just the models rotation… Probably because my brain is fried from work and working on so many different game systems at once haha. Thanks a ton
i did try this and i am able to align the model with the surface however i cannot get the character to look in the same direction as the steering target when the nav agent is walking, only the parent object obviously is doing that. Attempted to rotate to the surface normals and rotate towards its steering target but am having no luck, the model just faces one direction. Any ideas?
I know it is a little bit old but I just happened to have the same problem and reached a different solution. I dont use terrains so I had to look for something different. Another problem was that I did not want to use the normal of the geometry under my objects, I wanted a smooth transition between the current orientation and the target orientation. So in the end I decided to use the LookAt function. This way, no matter what geometry I am moving on it just works.
1-. In my Object script I put these variables:
public bool OverrideRotation = false;
public float OrientationSpeed = 10f;
Vector3 lastPosition;
Vector3 position;
Vector3 direction;
2- Then, in the awake method:
if (OverrideRotation) {
navAgent.updateRotation = false;
lastPosition = myTransform.position;
}
3- And last, if OverrideRotation is set to true, I start the coroutine that manages the orientation:
In the first line is an “OR” missing: if(direction.magnitude > 0.1f || normal.magnitude > 0.1f)
And for me it works better if I set it to 3.0f instead of 0.1f. Otherwise my tank reaches the target point and rotates randomly.
have you tried colliding with another navmesh agent in the scene? (ex. another moving object like a car)
my setup involves a control override on my main vehicle that moves it left and right (shift lanes) and when it collides on the walls or another collider, the child object (the car mesh) is pushed away from the parent (navMesh agent) and misaligns the two, therefore causing the car to rotate itself abnormally (facing other directions while moving forward) and sometimes flips over
I am using rigidbody and a box collider on my car gameobject with a script to control the NavMesh agent to move left and right
I tried using Transform.LookAt but failed miserably (caused more abnormal rotations during collisions)
I have also tried matching both their localrotations during FixedUpdate but it doesn’t seem to work…
Any suggestions how I can make this work? — collision works normally if I put the navmeshagent component directly on my car gameObject but as the topic indicates, it does not inherit the gravity component that makes it land on the surface normally and not rotate during uphills or downhills
@aikijeet I suggest you ditch that Rigidbody+Collider and animate car mesh (as child of NavMeshAgent) from your code alone.
Using Physics.Raycast get ground position and normal vectors. After that you can Lerp positions between present and ground hit.point to simulate gravity. And Lerp rotation between present and that of ground normal to achieve good surface aligment
I used @Jakob_Unity 's code, but changed things around a bit like this…
IMPORTANT NOTE: The structure of the character hierarchy is important!
I have an empty game object, and that has the NavMeshAgent on it, and the AI script.
Then I have an empty game object under that, which I call CharacterModel.
For me, this is just a transform. It has no other components on it.
Then there is another child under that, which is the actual skinned mesh character.
It looks like this:
Deer (transform, animator, audio source, nav mesh agent, AI script
The rotation is done on the CharacterModel game object, so as to not interfere with the parent object’s NavMesh steering, etc.
The relevant bits of code look like this:
public Quaternion previousRotation;
public Quaternion desiredRotation;
public Quaternion currentRotation;
private LayerMask terrainLayermask;
public GameObject characterModel; // assign CharacterModel game object to this in Inspector
…
void Start()
{
// requires Terrain layer to be named "Terrain" in your scene
terrainLayermask = LayerMask.GetMask("Terrain");
}
Hey Jakob. I’m wondering if you can explain the reason that lookRotation is a composition of two quaternions? I’ve used similar code in my project and notice that Quaternion.FromToRotation does provide the rotation I am looking for in order to align with a ramp, however, once I begin interpolating TO that rotation (qNorm in your code), my rotation never fully completes.
My intuition is because qNorm rotation value will decrease every update call as you converge on alignment, and the interpolation param t is small enough that it keeps the interpolation on the lower end. Is this reasoning correct?
If so, how does composing qLook and qNorm provide the desired rotation? It would essentially be tacking on the new rotation every update call. In my project the code looks like
if (!grounded) {
if (Physics.Raycast(transform.position, -transform.up, out RaycastHit hit, alignGroundProbeDistance)) {
var toRotation = Quaternion.FromToRotation(transform.up, hit.normal);
var lookRotation = toRotation * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 2);
}
}