hjds
hjds

Reputation: 63

Rotate GameObject based on key presses and based on Terrain slope/curvature

I have code in C# in a game in Unity that needs to rotate the player based on the slope of the terrain but the RotateTowards function(calculates the slope and angle) doesn't allow the object to be rotated sideway to move it in different directions. If I take out the rotateTowards function, rotations sideways work. If I dont, the correct slope rotation works but player won'r rotate sideways when buttons are pressed.

How could I fix this so the player could rotate in both ways?

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;

 public class PlayerController1 : MonoBehaviour
 {
     [System.Serializable]
     public class MoveSettings
     {
         public float forwardVel = 10f;      // walk speed

         public float rotateVel = 100;       // character rotation speed, character can walk 360 degree

         public float jumpVel = 25f;
         public LayerMask ground;

         public Transform backLeft;   // back left feet
         public Transform backRight;  // back right feet
         public Transform frontLeft;  // front left feet 
         public Transform frontRight; // front left feet
     }

     [System.Serializable]
     public class PhysicsSettings
     {
         public float downAccel = 0.75f;     // down speed when not grounded
     }

     public GameObject Model;
      public GameObject Origin;
     public MoveSettings moveSettings = new MoveSettings();
     public PhysicsSettings physicsSettings = new PhysicsSettings();

     private Vector3 velocity = Vector3.zero;
     private Quaternion targetRotation;
     private CharacterController cc;

     private float forwardInput, turnInput, jumpInput = 0;

     private RaycastHit lr;
     private RaycastHit rr;
     private RaycastHit lf;
     private RaycastHit rf;
     private Vector3 upDir;

     private Animator Anim; // global private variable



     private void Start()

     {   
         Anim = GetComponent<Animator>(); // in the Start function
         targetRotation = transform.rotation;
         cc = GetComponent<CharacterController>();
     }


     public bool Grounded()
     {
         return cc.isGrounded;
     }



     private void FixedUpdate()
     {

         Run();  // calculate the velocity to be applied on character controller, stored in the velocity variable
         Jump(); // code for jumping
          GetInput();     // movement input keys
         Turn();         // character movement direction input

         cc.Move(transform.TransformDirection(velocity) * Time.deltaTime);
         RotateTowardsGround();
     }

     private void GetInput()
     {

 Anim.SetFloat("vSpeed", forwardInput); // in the GetInput() function
 Anim.SetFloat("Direction", 1f);
         forwardInput = Input.GetAxis("Vertical");
         turnInput = Input.GetAxis("Horizontal");
         jumpInput = Input.GetAxisRaw("Jump");
     }

     private void Turn()
     {
         targetRotation *= Quaternion.AngleAxis(moveSettings.rotateVel * turnInput * Time.deltaTime, Vector3.up);
         transform.rotation = targetRotation;



     }

     public void Jump()
     {
         if (jumpInput > 0 && Grounded())
         {
             velocity.y = moveSettings.jumpVel;
         }
         else if (jumpInput == 0 && Grounded())
         {
             velocity.y = 0;
         }
         else
         {
             velocity.y -= physicsSettings.downAccel;
         }
     }

     private void Run()
     {

         velocity.z = moveSettings.forwardVel * forwardInput;
     }

         public void RotateTowardsGround()
     {

         // we have four feet

         Physics.Raycast(moveSettings.backLeft.position + Vector3.up, Vector3.down, out lr);
         Physics.Raycast(moveSettings.backRight.position + Vector3.up, Vector3.down, out rr);
         Physics.Raycast(moveSettings.frontLeft.position + Vector3.up, Vector3.down, out lf);
         Physics.Raycast(moveSettings.frontRight.position + Vector3.up, Vector3.down, out rf);
         upDir = (Vector3.Cross(rr.point - Vector3.up, lr.point - Vector3.up) +
                  Vector3.Cross(lr.point - Vector3.up, lf.point - Vector3.up) +
                  Vector3.Cross(lf.point - Vector3.up, rf.point - Vector3.up) +
                  Vector3.Cross(rf.point - Vector3.up, rr.point - Vector3.up)
                 ).normalized;
         Debug.DrawRay(rr.point, Vector3.up);
         Debug.DrawRay(lr.point, Vector3.up);
         Debug.DrawRay(lf.point, Vector3.up);
         Debug.DrawRay(rf.point, Vector3.up);



         Model.transform.up = upDir;

     }
 }

Upvotes: 1

Views: 764

Answers (1)

Programmer
Programmer

Reputation: 125315

The proper way to rotate object based on Terrain slope/curvature is to first throw raycast then obtain the returned RaycastHit.normal value and assign it your to the object's transform.up. It's better to use Lerp or Slerp to do this form smooth ration.

As for the position of the object, you can calculate that with Terrain.activeTerrain.SampleHeight as described in this post or you can use RaycastHit.point like you did in the code from your question.

Below is an example of what I described above. It is a minimal code to move/rotate object over a terrain. You can modify it to fit your four character legs scenario.

public class Hover : MonoBehaviour
{
    public Transform objectToMove;
    public float maxSpeed = 10f;
    public float angleSpeed = 5f;
    public float groundDistOffset = 2f;
    private Vector3 toUpPos = Vector3.zero;

    void Update()
    {
        float hInput = Input.GetAxis("Horizontal");
        float vInput = Input.GetAxis("Vertical");

        Vector3 objPos = objectToMove.position;
        objPos += objectToMove.forward * vInput * maxSpeed * Time.deltaTime;
        objPos += objectToMove.right * hInput * maxSpeed * Time.deltaTime;

        RaycastHit hit;

        if (Physics.Raycast(objectToMove.position, -Vector3.up, out hit))
        {
            //Get y position
            objPos.y = (hit.point + Vector3.up * groundDistOffset).y;

            //Get rotation
            toUpPos = hit.normal;
        }

        //Assign position of the Object
        objectToMove.position = objPos;

        //Assign rotation/axis of the Object
        objectToMove.up = Vector3.Slerp(objectToMove.up, toUpPos, angleSpeed * Time.deltaTime);
    }
}

Upvotes: 2

Related Questions