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): enter image description here
but not when the tank goes up or down: enter image description here
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:
enter image description here
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? :)
1 Answer 1
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);
-
\$\begingroup\$ Thank you, I updated the code as you suggested but unfortunately it didn't change anything. \$\endgroup\$AmBeam– AmBeam2024年08月01日 12:10:13 +00:00Commented Aug 1, 2024 at 12:10
-
1\$\begingroup\$ Spotted an issue with your use of LookRotation — see the edit above. \$\endgroup\$2024年08月01日 15:54:24 +00:00Commented Aug 1, 2024 at 15:54
-
\$\begingroup\$ IT WORKS! You're my personal hero :) Thank's man! \$\endgroup\$AmBeam– AmBeam2024年08月01日 20:10:39 +00:00Commented Aug 1, 2024 at 20:10
MovePosition
/MoveRotation
to control its position and orientation. \$\endgroup\$