# Adjusting unit orientation to the terrain

I'm working on RTS game and got stuck on a pretty simple question I think, but I'm not skilled enough to find the proper answer. I don't want to use Unity built-in physics to do the job for me and I think I have reasons for that, so my units won't use Rigidbody and such (however I am using box colliders to get informed about collisions). The question is how to align the unit with the slope of the terrain so that it appears to move up and down or sideways?

Let me describe the approach I've taken so far. I use following method to get height on each edge of the collider:

        private Vector3 GetTerrainHeightAtPoint(Vector3 point)
{
var height = Terrain.activeTerrain.SampleHeight(new Vector3(point.x, 0, point.z));

return new Vector3(point.x, height, point.z);
}


Then I use these vectors (topLeft, topRight, bottomLeft and bottomRight) to make two triangles out of them and calculate their normals. That's just in case if terrain height is different on every edge. Then I calculate slope vector (not on each frame, I use coroutine to update the value every tenth of a second, but moving the code to FixedUpdate doesn't make any changes) by getting these normals combined.

        Vector3 normal1 = Vector3.Cross(topRight - topLeft, bottomRight - topLeft).normalized;
Vector3 normal2 = Vector3.Cross(bottomRight - topLeft, bottomLeft - topLeft).normalized;

_slopeVector = (normal1 + normal2).normalized;


Finally I apply _slopeVector to the rotation:

        Quaternion targetRotation = Quaternion.LookRotation(target, _slopeVector);
_transform.localRotation = Quaternion.RotateTowards(_transform.localRotation, targetRotation, _turnSpeed * Time.fixedDeltaTime);


It all seems fine and results in this (aligns pretty well imho):

but not when the tank goes up or down:

I was debugging it a bit and most propably slope vector calculation is done wrong. I've tried some other approaches with no luck. There's no constraint on any axis and rotation is changed only in this one place in the code. However the unit rotates a little around X axis from time to time but it's almost not noticeable. I've also added small "debug squares" on the edges of the unit's collider just to see if height of all four points is calculated properly and it does. Just the slope calculation fails for some reason I think.

Do you know what I am doing wrong? Maybe there's better solution for my problem? Tried working this out with ChatGPT but it fails like totaly xD

FYI: tank and the terrain looks like sh*t because rn I am focused on movement, pathfinding, grouping, orders and so on.

EDIT: I've added debug line that told me that _slopeVector is calculated correctly (see the red line). So I created completly new monobehaviour class and attached it to a box. No changes:

The problem has to be somewhere here:

        Quaternion targetRotation = Quaternion.LookRotation(target, _slopeVector);

Debug.DrawLine(transform.position, transform.position + _slopeVector * 6, Color.red);

transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, turnSpeed * Time.fixedDeltaTime);


I've also tried Quaternion.Lerp and Slerp with no success.

EDIT2: It somehow started to work better when I set it that way:

        targetRotation.x = _slopeVector.x;
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, turnSpeed * Time.deltaTime);


but I don't think this is the right solution, right? :)

New contributor
AmBeam is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
• If you're using box colliders, you're using Unity's built-in physics. By not attaching a Rigidbody, you're just getting slow physics, because this signals to the physics engine "this is a stationary collider that won't move, optimize accordingly." Every time you move it, you pay excess processing cost for undoing that optimization. If you want to use colliders while having full control of the object's movement, use a Rigidbody, but set it to Kinematic. This will avoid the performance penalty for moving a collider without a body, while keeping the physics engine from moving it on your behalf. Commented Jul 31 at 17:54
• @DMGregory thanks! Didn't know that. Commented Jul 31 at 19:45
• @DMGregory Didn't they change it many years ago so that moving 3D colliders no longer has a performance penalty, even if they have no Rigidbody? See unity.com/releases/editor/whats-new/5.0.0#notes, search "moving static colliders". Last I checked, the same is not true of 2D colliders (they should still have a kinematic Rigidbody2D). Commented Jul 31 at 20:32
• Ah! I was working off outdated info / crossing 2D & 3D — thanks for the update @Kevin! I'd still recommend using a Rigidbody so you can get trigger events, even if the performance is identical, and using MovePosition/MoveRotation to control its position and orientation. Commented Jul 31 at 20:52
• My "maps" are "coded" as to elevations. The elevations are used to determine "visibility" and "fatigue delays" and limits of ascent / descent (incline angles). You could do something similar; the vehicles assume the "inclines" brought on by changes in elevation. Commented yesterday

Instead of breaking into two triangles, where you have to make a choice of which diagonal to split on, I'd recommend using a form that does not favour one orientation over the other:

// Average the "right" directions at the front and back of the tank.
var rightward = topRight + bottomRight - topLeft - bottomLeft;

// Average the "forward" directions at the left and right sides of the tank.
var forward = topRight + topLeft - bottomLeft - bottomRight;

// Forward x Right = Up
_slopeVector = Vector3.Cross(forward, rightward).normalized;


You're also using the wrong LookRotation: your forward vector is overriding the pitch rotation that your _slopeVector wants to apply, limiting it to only affect the roll. Try this instead:

var look = Quaternion.LookRotatiin(_slopeVector, -toTarget);
var targetRotation = look * Quaternion.Euler(90, 0, 0);

• Thank you, I updated the code as you suggested but unfortunately it didn't change anything. Commented Aug 1 at 12:10
• Spotted an issue with your use of LookRotation — see the edit above. Commented Aug 1 at 15:54
• IT WORKS! You're my personal hero :) Thank's man! Commented Aug 1 at 20:10