3
\$\begingroup\$

I'm currently working on an FPS shooter game and I needed a generic movement controller that supported both keyboard and Xbox One controller input. My main goal in designing this controller was to address two common issues that occurred in previous FPS controllers I've designed, which are:

  • Diagonal movement (strafing) caused the player to move about twice as fast when a keyboard setup was being used.
  • Jumping in a setting with a low ceiling caused the player to 'stick' to said ceiling.

Here's the code:

using UnityEngine;
using System.Collections;

/// <summary>
/// This class is responsible for controlling the players movement.
/// </summary>
[RequireComponent(typeof(CharacterController))]
public class CharacterMovementController : MonoBehaviour
{
    public bool UseController;
    public Vector3 JumpSpeed;
    public Vector3 GravitySpeed;
    public Vector2 MovementSpeed;
    public Vector2 CameraRotationSpeed;
    public Vector2 CameraRotationConstraints;

    public CharacterController CharacterController { get; set; }
    public Vector3 MovementVector { get; set; }
    public float VerticalRotation { get; set; }

    /// <summary>
    /// Rotate the player.
    /// </summary>
    public void Rotate()
    {
        if(this.UseController)
        {
            this.transform.Rotate(new Vector3(0.0f, Input.GetAxis("Xbox Look X"), 0.0f) * this.CameraRotationSpeed.x * Time.deltaTime);
            this.VerticalRotation += Input.GetAxis("Xbox Look Y") * this.CameraRotationSpeed.y * Time.deltaTime;
            this.VerticalRotation = Mathf.Clamp(this.VerticalRotation, this.CameraRotationConstraints.x, this.CameraRotationConstraints.y);

            Camera.main.transform.eulerAngles = new Vector3(
                -this.VerticalRotation,
                Camera.main.transform.eulerAngles.y,
                Camera.main.transform.eulerAngles.z
            );
        }

        else
        {
            this.transform.Rotate(new Vector3(0.0f, Input.GetAxis("Mouse X"), 0.0f) * this.CameraRotationSpeed.x * Time.deltaTime);
            this.VerticalRotation += -Input.GetAxis("Mouse Y") * this.CameraRotationSpeed.y * Time.deltaTime;
            this.VerticalRotation = Mathf.Clamp(this.VerticalRotation, this.CameraRotationConstraints.x, this.CameraRotationConstraints.y);

            Camera.main.transform.eulerAngles = new Vector3(
                -this.VerticalRotation,
                Camera.main.transform.eulerAngles.y,
                Camera.main.transform.eulerAngles.z
            );
        }
    }

    /// <summary>
    /// Move the player.
    /// </summary>
    public void Move()
    {
        if(this.UseController)
        {
            this.MovementVector += Vector3.forward * Input.GetAxis("Xbox Forward / Backward") * this.MovementSpeed.y;
            this.MovementVector += Vector3.right * Input.GetAxis("Xbox Left / Right") * this.MovementSpeed.x;
            this.MovementVector = this.transform.rotation * this.MovementVector;
        }

        else
        {
            bool keyboardMovingForwardBackward = false;
            bool keyboardMovingLeftRight = false;

            if(Input.GetButton("Keyboard Forward"))
            {
                this.MovementVector -= this.transform.forward * this.MovementSpeed.y;
                keyboardMovingForwardBackward = true;
            }
            else if(Input.GetButton("Keyboard Backward"))
            {
                this.MovementVector += this.transform.forward * this.MovementSpeed.y;
                keyboardMovingForwardBackward = true;
            }

            if(Input.GetButton("Keyboard Left"))
            {
                this.MovementVector -= this.transform.right * this.MovementSpeed.x;
                keyboardMovingLeftRight = true;
            }
            else if(Input.GetButton("Keyboard Right"))
            {
                this.MovementVector += this.transform.right * this.MovementSpeed.x;
                keyboardMovingLeftRight = true;
            }

            if(keyboardMovingForwardBackward && keyboardMovingLeftRight)
            {
                this.MovementVector = new Vector3(
                    this.MovementVector.x * (1.0f / Mathf.Sqrt(2.0f)),
                    this.MovementVector.y,
                    this.MovementVector.z * (1.0f / Mathf.Sqrt(2.0f))
                );
            }
        }

        if(!this.CharacterController.isGrounded)
        {
            this.MovementVector += this.GravitySpeed * Time.deltaTime;
        }
        else
        {
            this.MovementVector = new Vector3(this.MovementVector.x, 0.0f, this.MovementVector.z);
        }

        if(Input.GetButton("Xbox A") && this.CharacterController.isGrounded)
        {
            this.MovementVector += this.JumpSpeed;
        }

        RaycastHit upperGroundedRaycastHit;
        if(Physics.Raycast(this.transform.position, this.transform.up, out upperGroundedRaycastHit, (this.CharacterController.height / 2.0f) + 0.1f))
        {
            this.MovementVector = new Vector3(this.MovementVector.x, this.GravitySpeed.y, this.MovementVector.z);
        }

        this.CharacterController.Move(this.MovementVector * Time.deltaTime);
        this.MovementVector = new Vector3(0.0f, this.MovementVector.y, 0.0f);
    }

    /// <summary>
    /// Update is called once per frame.
    /// </summary>
    public void Update()
    {
        this.Rotate();
        this.Move();
    }

    /// <summary>
    /// Start is called once.
    /// </summary>
    public void Start()
    {
        this.CharacterController = this.GetComponent<CharacterController>();
    }
}

Is there anything that can be improved? Are there any potential pitfalls or problems that can occur?

\$\endgroup\$
3
\$\begingroup\$

Rotate()

In this method the only difference between the if branches are the getting of the X and Y location. It would be enough to place getting these in the branches and therefor you could remove some of the duplicated code but it would be much better just assigning the string's, which are needed, to two variables like so

var xKey = "Mouse X";
var yKey = "Mouse Y";
if(this.UseController)
{
    xKey = "Xbox Look X";
    yKey = "Xbox Look Y";
}
this.transform.Rotate(new Vector3(0.0f, Input.GetAxis(xKey), 0.0f) * this.CameraRotationSpeed.x * Time.deltaTime);
this.VerticalRotation += Input.GetAxis(yKey) * this.CameraRotationSpeed.y * Time.deltaTime;
this.VerticalRotation = Mathf.Clamp(this.VerticalRotation, this.CameraRotationConstraints.x, this.CameraRotationConstraints.y);

Camera.main.transform.eulerAngles = new Vector3(
    -this.VerticalRotation,
    Camera.main.transform.eulerAngles.y,
    Camera.main.transform.eulerAngles.z
    );
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.