Character Controller movement with full 3D camera

Hi, there! I started learning Unity about a month ago by following a tutorial on YouTube, and the last few days I’ve been trying to make a basic first-person camera + movement object using a Character Controller component. At first I ran into a problem with the Character Controller using absolute directional values instead of relative ones, but now I’ve run into a new problem: when I rotate my view up or down at all (as in, rotating around the X axis), the player’s movement has a few hiccups. If I look up or down when I move forward, the player’s movement starts to slow down, coming to a complete stop when I look exactly 90 degrees up or down. I know that the problem is that the player is attempting to move “forward,” which in this case is either straight up (against gravity) or into the ground, I just don’t know how to fix it. I’ve been trying to think of how to fix this problem using an absolute rotational value or something, but I’m completely stuck trying to work out the math. Any help at all would be appreciated, thank you!

Here is my camera script:

public float sensitivity;
public float minTurnAngle;
public float maxTurnAngle;
private float rotX;

void Update()
{
    MouseAiming();
}

void MouseAiming()
{
    // get the mouse inputs
    float y = Input.GetAxis("Mouse X") * sensitivity;
    rotX += Input.GetAxis("Mouse Y") * sensitivity;
    // clamp the vertical rotation
    rotX = Mathf.Clamp(rotX, minTurnAngle, maxTurnAngle);
    // rotate the camera
    transform.eulerAngles = new Vector3(-rotX, transform.eulerAngles.y + y, 0);
}

And here is my movement script:

private CharacterController playerController;

private float playerHeight;

private Vector3 playerVelocity;
public float playerSpeed;
private bool playerGrounded;

private bool playerCrouching;
public float playerSpeedCrouchMultiplier;

public float playerJumpHeight;

// Start is called before the first frame update
void Start()
{
    playerController = GetComponent<CharacterController>();
    playerHeight = playerController.height;
    playerCrouching = false;
    playerGrounded = true;
}

// Update is called once per frame
void Update()
{
    // WASD movement
    Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // Get inputs

    if (playerCrouching) // check if player is crouching
    {
        input *= playerSpeedCrouchMultiplier;
    }

    Vector3 move = transform.TransformDirection(input.normalized) * playerSpeed; // Change direction

    // Is player mid-air
    playerGrounded = playerController.isGrounded;
    if (playerGrounded && playerVelocity.y < 0)
    {
        playerVelocity.y = 0f;
    }

    // Jumping
    if (Input.GetButtonDown("Jump") && playerGrounded && !playerCrouching)
    {
        playerVelocity.y += Mathf.Sqrt(playerJumpHeight * -3.0f * References.gravity);
    }

    playerVelocity.y += References.gravity * Time.deltaTime;

    // Crouching
    if (Input.GetButton("Crouch"))
    {
        playerCrouching = true;
        playerController.height = playerHeight * 0.5f;
    }
    else
    {
        playerCrouching = false;
        playerController.height = playerHeight;
    }

    //Move
    playerController.Move(move * Time.deltaTime);
    playerController.Move(playerVelocity * Time.deltaTime);
}

Rotate the player in Y axis and the camera in X axis.

1 Like