0
\$\begingroup\$

I'm trying to rotate my characterController player on a slope on the 'x' and 'z' axises, however things aren't going so well.

The player does rotate, but if I move and look in another direction, the characterController will still be on the same axis, and won't look down an up slope, and instead will be looking up a down slope.

void Update () 
{
   Vector3 rotateDirection = (camera.forward * Input.GetAxis("Vertical")) 
                           + (camera.right * Input.GetAxis("Horizontal"));

   Quaternion newRotation = Quaternion.LookRotation(
                               new Vector3(rotateDirection.x, 0f, rotateDirection.z));
   transform.rotation = Quaternion.Slerp(
                               transform.rotation, newRotation, rotSpeed * Time.deltaTime);
 }

private void OnControllerColliderHit(ControllerColliderHit hit) 
{
      transform.rotation = Quaternion.Euler(hit.transform.rotation.eulerAngles.x, 
                   transform.rotation.eulerAngles.y, hit.transform.rotation.eulerAngles.z);
}

Also, it doesn't work at all with spheres and terrain since their rotation is zero. Anyone know how to achieve what I am talking about? Also, the goal is to allow for the 'y' axis to be free and do what it needs to in void Update().

\$\endgroup\$
0
\$\begingroup\$

To orient ourselves to our ground surface, we'll need to establish what direction is "out" from the ground we're standing on. So let's set aside a member variable to track that.

Vector3 groundNormal;

The safest way to populate this is likely with a raycast down toward your feet each frame. If you want to continue using OnControllerColliderHit then you should include some test first to distinguish hits from the ground vs hits from walls or other obstacles that should not change your ground normal. You can use physics layers or tags, for example.

Once you have a contact point that you're confident represents ground (either a RaycastHit or a ControllerColliderHit), you can capture its normal as your current groundNormal:

groundNormal = hit.normal;

Now, we can compute a local coordinate space relative to this surface and our camera perspective, using the TurretLookRotation method shown in this answer:

Quaternion inputFrame = TurretLookRotation(camera.forward, groundNormal);

We can use this to transform our input into world space:

Vector3 localInput = new Vector3(
                           Input.GetAxis("Horizontal"),
                           0f,
                           Input.GetAxis("Vertical")
                         );

Vector3 worldInput = inputFrame * localInput;

Then construct our new rotation using this direction in the plane of our surface, and an "up" vector pointing out of our surface:

Quaternion newRotation = Quaternion.LookRotation(worldInput, groundNormal);

Note here we've never tried to break apart an Euler angle triplet into separate angles to mix and match. This will almost never have your desired effect. It's much more reliable to describe and compute your rotations in terms of quaternions or basis vectors as illustrated above.

\$\endgroup\$
  • \$\begingroup\$ I'll test this tomorrow, thanks for answering! \$\endgroup\$ – user124517 Mar 2 at 2:21
  • \$\begingroup\$ I did test it on March 3rd, however I forgot to respond. It doesn't go very well with my code for some reason. The rotation goes all wonky in the air, and hardly rotates properly on the slopes. \$\endgroup\$ – user124517 Mar 9 at 20:24
  • \$\begingroup\$ That's not enough information for us to be able to diagnose the specific problem and propose solutions. \$\endgroup\$ – DMGregory Mar 9 at 20:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy