Skip to content

Instantly share code, notes, and snippets.

@JavadocMD
Last active February 24, 2023 04:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JavadocMD/c38c08e6e9f000a44d348f340119a8d2 to your computer and use it in GitHub Desktop.
Save JavadocMD/c38c08e6e9f000a44d348f340119a8d2 to your computer and use it in GitHub Desktop.
Developing a first person controller for Unity3D with UniRx: Part 1
using UnityEngine;
using UniRx;
using UniRx.Triggers;
// NOTE: Unity won't actually let you put two MonoBehaviours in one file.
// They're both listed here just for convenience.
namespace Assets.Scripts.v1 {
public class InputsV1 : MonoBehaviour {
// Singleton.
public static InputsV1 Instance { get; private set; }
public IObservable<Vector2> Movement { get; private set; }
public IObservable<Vector2> Mouselook { get; private set; }
private void Awake() {
Instance = this;
// Hide the mouse cursor and lock it in the game window.
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
// Movement inputs tick on FixedUpdate
Movement = this.FixedUpdateAsObservable()
.Select(_ => {
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
return new Vector2(x, y).normalized;
});
// Mouse look ticks on Update
Mouselook = this.UpdateAsObservable()
.Select(_ => {
var x = Input.GetAxis("Mouse X");
var y = Input.GetAxis("Mouse Y");
return new Vector2(x, y);
});
}
}
[RequireComponent(typeof(CharacterController))]
public class PlayerControllerV1 : MonoBehaviour {
public float walkSpeed = 5f;
[Range(-90, 0)]
public float minViewAngle = -60f; // How much can the user look down (in degrees)
[Range(0, 90)]
public float maxViewAngle = 60f; // How much can the user look up (in degrees)
private CharacterController character;
private Camera view;
private void Awake() {
character = GetComponent<CharacterController>();
view = GetComponentInChildren<Camera>();
}
private void Start() {
var inputs = InputsV1.Instance;
// Handle movement input (WASD-style).
inputs.Movement
.Where(v => v != Vector2.zero) // We can ignore this if movement is zero.
.Subscribe(inputMovement => {
// Calculate velocity (direction * speed).
var inputVelocity = inputMovement * walkSpeed;
// Translate 2D velocity into 3D player coordinates.
var playerVelocity =
inputVelocity.x * transform.right + // x (+/-) corresponds to strafe right/left
inputVelocity.y * transform.forward; // y (+/-) corresponds to forward/back
// Apply movement.
var distance = playerVelocity * Time.fixedDeltaTime;
character.Move(distance);
}).AddTo(this);
// Handle mouse input (free mouse look).
inputs.Mouselook
.Where(v => v != Vector2.zero) // We can ignore this if mouse look is zero.
.Subscribe(inputLook => {
// Translate 2D mouse input into euler angle rotations.
// inputLook.x rotates the character around the vertical axis (with + being right)
var horzLook = inputLook.x * Time.deltaTime * Vector3.up;
transform.localRotation *= Quaternion.Euler(horzLook);
// inputLook.y rotates the camera around the horizontal axis (with + being up)
var vertLook = inputLook.y * Time.deltaTime * Vector3.left;
var newQ = view.transform.localRotation * Quaternion.Euler(vertLook);
// We have to flip the signs and positions of min/max view angle here because the math
// uses the contradictory interpretation of our angles (+/- is down/up).
view.transform.localRotation = ClampRotationAroundXAxis(newQ, -maxViewAngle, -minViewAngle);
}).AddTo(this);
}
// Ripped straight out of the Standard Assets MouseLook script. (This should really be a standard function...)
private static Quaternion ClampRotationAroundXAxis(Quaternion q, float minAngle, float maxAngle) {
q.x /= q.w;
q.y /= q.w;
q.z /= q.w;
q.w = 1.0f;
float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan(q.x);
angleX = Mathf.Clamp(angleX, minAngle, maxAngle);
q.x = Mathf.Tan(0.5f * Mathf.Deg2Rad * angleX);
return q;
}
}
}
@TokyoDan
Copy link

TokyoDan commented Aug 2, 2016

To the inputs.MouseLook Observer part of PlayerControllerV1 I had to multiply inputLook.x and inputLook.y by 100.0f to get any movement from the mouse input.

@mrserverless
Copy link

Yep @TokyoDan's work around worked for me as well.

@AdityaMulukala
Copy link

Just wanna say thank you for providing this great blog post.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment