# Rotating Character Controller to match surface

Is there an easy way to do it?

Basically the lookat function, except leaving the character upright, not facedown.

Not particularly, what I do is the following:

2. Raycast down (use world down, not object down)
3. Set up vector to surface normal
4. Set forward vector to Vector3.Cross(up, right)

I need to do the same thing (i.e. in my case if you interact with a ladder or climb a wall) - will give this a shot today sometime and report back … what you’re really after is making the forward axis perpendicular to a normal right?

Just as a side note, I work around this by defining “mount transforms” on my ladder (for example) and just rotate the character to match the position and rotation of the bottom/top mount transform accordingly.

But for allowing a wall climb or vaulting over a fence, this does not work quite as nicely (though you can apply it for vaulting).

Yes that is right, forward axis perpendicular to the normal.

Cannon, could you explain step 4. I know how to get the surface normal but I don’t know how to correctly apply it to my object.

I’m guessing something like this:

transform.rotation = Quaternion.LookRotation( -raycastHit.normal );

Which was an answer to a similar question I had myself: Facing a Wall Surface (Perpendicular) - Questions & Answers - Unity Discussions

thank you, that looks like a great method. I am going to try that now!

Something like:

Vector3 new_forward = Vector3.Cross( surface_normal, transform.right );
transform.rotation = Quaternion.LookRotation( new_forward, surface_normal );

Unity has really convenient math functions hidden in just the right places.

``````	surfaceNormal = gravityCentre.transform.position - transform.position;
surfaceNormal.Normalize();
new_forward = Vector3.Cross(surfaceNormal, transform.right);
rotationAmount = Quaternion.LookRotation(new_forward, surfaceNormal);
transform.rotation = rotationAmount;
``````

while this works in that it makes the controller now match the surface. When I try to move it shakes violently.

Any idea why?

This is just attached to a character controller and gravityCentre is just the point from which the normals are determined.

Two thoughts:

1. I think the CharacterController was designed to stay upright. I suggest either not using CharacterController at all, or instead have a proxy visible object that matches the surface while your actual character controller is invisible, then just move your proxy object to your character controllers position every update.

2. Ensure that the method you use to calculate the surface normal is independent of the rotation of the object. I initially specified using World down instead of object down for this reason; the initial reaction is to use the object’s down vector, which really will cause jittering.

Additional note, I’m picturing here something that matches terrain but will be unable to cope with matching walls. For that, you need an additional method to tell you what the closest point on the surface is, or use some other method for generating authoritative normals at any arbitrary point near the surface.

Well I am trying to get the character controller to move around a sphere.

This is still giving me a headache. I am trying to figure how to determine what the rotation should be and then using transform.rotate to move rotate towards it until it matches.

Well, make the object raycast downwards, and make it match the surface it’s on’s normal. Refer to the link i gave above, and I’ll post later (When not at work) with the exact code i used.

saying you want the character to move around a sphere puts a different spin on this.

I would think you do not want the character to align its up vector with the normal of the surface it is on.

Instead, you want the character to align his up vector with the center of the sphere below him.

For example, if there is a ramp he is going up, aligning the character to the normal of the ramp would have him leaning back as he goes up the ramp.
If the ramp is on the surface of a sphere ( like a small planet ) - you want him appearing upright on the ramp, and not leaning back, but at that same time appearing to be upright on that area of the “planet” - correct?

In that case you just need to keep his up vector pointed away from the sphere center.

P.S. I recently did a cheat making a character controller align to hills he is going up or down by doing a raycast downward a short distance in front of him. I then did a LookAt to align the character to the hit.point and it worked well - no jitter. But I think you may want to align to the sphere center and not the local surface.

``````surfaceNormal = gravityCentre.transform.position - transform.position;
surfaceNormal.Normalize();
new_forward = Vector3.Cross(surfaceNormal, transform.right);
rotationAmount = Quaternion.LookRotation(new_forward, surfaceNormal);
``````

That is what I am using to find the direction I should be pointing. I hadn’t actually thought of how I would handle ramps etc, just be happy to get it working on a single sphere.

I decieded to try Canon’s suggestion and I have made an object a child which I am using to look at the new forward direction. I am either going to transfer the rotation info across using transform.Rotate or simply hide the controller and make that the character. The thing I have noticed with it is that when using the lookAt is rotates around the y axis which I think is what has been giving me headaches.

I really need help, this is doing my head in!

I have got it working perfectly on the top of the sphere. But transitioning to the bottom half I get a wierd flip which is causing all the problems.

My method is
-put an empty game object on each side of the cube and raytrace down towards the centre of gravity (centre of the sphere)
-use transform rotate on the difference of the raytraces to align with surface
-gravity is applied in the move using controller.Move(forwardcurSpeed + turning curHSpeed + gravityDirection * gravityLevel *Time.deltaTime); (first term is back and foward, second left and right, gravity direction is var gravityDirection = gravityCentre.transform.position - transform.position; and i normalise it)

I made a video to show the issue. There must be a way to handle it. I move really slow when it happens to show it happening because it happens quickly.

Just to be clear cause I move the camera to show it, it happens as you move from the top of the sphere to the bottom over a short period.

edit:
the 4 coloured spheres are the points the raycasts are hitting so I can see visually.

Just an update if this helps someone tell me what to do. What happens is if i move of the equator it seems to flip 180 degrees and once it has done that, back to working again.

It sounds like I just need to tweak the calculation when the character is lower than the centre of gravity but I don’t know how.

It seems the info received by transform.right is reset when the up vector of the transform is exactly (0,-1,0) and moves counter clockwise or clockwise right after that.

I use transform.right to check if the player is to the left or right of a game object and make the object follow the player. I have the same kind of bug when the object is upside down of the camera.

Edit: I just found out about my bug, it’s not the transform.forward that is the culprit, it’s transform.up! When I set it (transform.up = myUpVector), at some point, there is a bug and the right vector of the transform gets inverted. When my object reaches rotation (0,0,180), it is rotated 180 around Y for a couple of milliseconds before going back to having 0 degrees in Y.