Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
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 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.

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

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