Search code examples
c#unity-game-enginecamera

Unity C# Assistance on player movement according to camera position to constrain it to horizontal force only


Hello I need some help on constricting the force applied to the player object so it's only on the horizontal plane. My current code allows for relatively smooth player movement but if i tilt my camera to look down the force applied to the player object is also applied downwards instead of forward.

Here is my wonky beginners code.

public class PlayerController : MonoBehaviour
{

    public float speed = 200.0f;
    public float jumpForce = 200.0f;
    public float gravityModifier;
    private Rigidbody playerRb;

    private Camera cam;

    public bool isOnGround = true;

    // Start is called before the first frame update
    void Start()
    {
        playerRb = GetComponent<Rigidbody>();
        Physics.gravity *= gravityModifier;
        cam = Camera.main;
    }

    // Update is called once per frame
    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        playerRb.AddForce(cam.transform.right * speed * Time.deltaTime * horizontalInput);
        playerRb.AddForce(cam.transform.forward * speed * Time.deltaTime * verticalInput);

        if (Input.GetKeyDown(KeyCode.Space) && isOnGround == true)
        {
            playerRb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            isOnGround = false;
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            isOnGround = true;
        }
    }
}

[Bonus: for some reason my cam.transform.right is forward and backwards movement and my cam.transform.forward is right and left movement, I have no idea why.]

[Bonus Bonus: If you have the time and energy to deal with my bullshit. A good way to have isOnGround enter false state instead of tying it to the jump. I tried having it to be

private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            isOnGround = true;
        }
    }

private void OnCollisionExit(Collision collision)
    {
        if (collision.gameObject.tag == "Ground")
        {
            isOnGround = false;
        }
    }

But it would make so when going back in the ground to set it to true for a split second before setting it back to false again.]


Solution

  • When you call cam.transform.forward or right, you are getting the vector pointing forward or to the right of the camera, so if the camera looks down, cam.transform.forward will point down. There are several solutions to solve, and they depend on how you want your game to work. For example, apply the force forward and to the right of the player, not the camera:

    playerRb.AddRelativeForce(Vector2.forward * …);
    playerRb.AddRelativeForce(Vector2.right * ...);
    

    For the bonus bonus question, using OnCollisionExit () is a good way. It probably doesn't work that well because when it returns to the ground it will make micro bounces that will send the engine into confusion, not knowing if it has entered or left the contact. If the solution you wrote at the beginning works, i.e. making it false when you jump, it works, I recommend that you use that. A solution that OnCollisonExit () can use can be this: Set a bool variable called "jumping"; set it to true when you jump, ie in the block "if (Input.GetKeyDown(KeyCode.Space) && isOnGround == true) “:

    if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
    {
        playerRb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        jumping = true;
    }
    

    In the OnCollisionEnter() method, enter:

    if (collision.gameObject.tag == "Ground")
    {
        if (jumping)
        {
            isOnGround = true;
            jumping = false;
        }
    }
    

    and the OnCollisionExit () method like this: if (collision.gameObject.tag == "Ground") { if (jumping) { isOnGround = false; } }

    I hope to be proved helpful. If I answered your question, you can accept it.