Skip to content

Instantly share code, notes, and snippets.

@soraphis
Last active July 11, 2016 09:31
Show Gist options
  • Save soraphis/5b3d01d8bb1086a88cce13325c40b56b to your computer and use it in GitHub Desktop.
Save soraphis/5b3d01d8bb1086a88cce13325c40b56b to your computer and use it in GitHub Desktop.
Unity first person controller with extracted input handling
using UnityEngine;
using Assets._Game.Scripts.CharacterController;
[RequireComponent (typeof (Rigidbody))]
[RequireComponent (typeof (CapsuleCollider))]
public class FirstPersonController : MonoBehaviour {
public float RotationSpeed = 50;
public float MovementSpeed = 2;
public Vector2 Sensitivity = Vector2.one;
public Vector2 Smoothing = Vector2.one;
public GameObject Head;
public bool CanRun;
[Range(1, 10)] public float RunMultiplicator;
public bool CanJump => JumpHeight > 0;
public float JumpHeight;
private bool grounded = false;
private new Rigidbody rigidbody;
private new CapsuleCollider collider;
private Vector2 smoothMouse;
private Vector2 mouseAbsolute;
public Vector2 ClampInDegrees = new Vector2(360, 180);
private Vector2 targetDirection;
private Vector2 targetCharacterDirection;
public IFPInputProvider InputProvider;
void Awake() {
collider = this.GetComponent<CapsuleCollider>();
rigidbody = this.GetComponent<Rigidbody>();
rigidbody.freezeRotation = true;
}
void Start() {
if (Head == null) Head = GetComponentInChildren<Camera>().gameObject;
if (Head == null) this.enabled = false;
targetDirection = transform.localEulerAngles;
if (InputProvider == null) {
Debug.LogError("You need to add an FirstPersonInputProvider to this component.");
this.enabled = false;
}
}
void OnEnable() {
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void FixedUpdate () {
#region Movement
var dir = InputProvider.MoveDirection.normalized*MovementSpeed;
if (grounded) rigidbody.velocity = transform.TransformDirection(new Vector3(dir.x, 0, dir.y));
else {
rigidbody.AddForce(Physics.gravity*3, ForceMode.Acceleration);
}
var oldLayer = this.gameObject.layer;
this.gameObject.layer = 4;
Vector3 feet = new Vector3(collider.bounds.center.x, collider.bounds.min.y + collider.radius - 0.125f, collider.bounds.center.z);
grounded = Physics.CheckCapsule(collider.bounds.center, feet, collider.radius - 0.0625f, layermask: ~LayerMask.GetMask(LayerMask.LayerToName(this.gameObject.layer)));
this.gameObject.layer = oldLayer;
// todo: do jumping stuff
#endregion
#region LookAround
var mouse = Vector2.Scale(InputProvider.LookDirection, new Vector2(Sensitivity.x*Smoothing.x, Sensitivity.y + Smoothing.y));
var targetOrientation = Quaternion.Euler(targetDirection);
smoothMouse.x = Mathf.Lerp(smoothMouse.x, mouse.x, 1f/Smoothing.x);
smoothMouse.y = Mathf.Lerp(smoothMouse.y, mouse.y, 1f/Smoothing.y);
mouseAbsolute += smoothMouse;
if (ClampInDegrees.x < 360) {
mouseAbsolute.x = Mathf.Clamp(mouseAbsolute.x, -ClampInDegrees.x*0.5f, ClampInDegrees.x*0.5f);
}
if (ClampInDegrees.y < 360) {
mouseAbsolute.y = Mathf.Clamp(mouseAbsolute.y, -ClampInDegrees.y*0.5f, ClampInDegrees.y*0.5f);
}
var yrot = Quaternion.AngleAxis(mouse.x, Vector3.up);
transform.localRotation *= yrot;
Head.transform.localRotation = Quaternion.AngleAxis(- mouseAbsolute.y, targetOrientation * Vector3.right);
#endregion
}
}
using UnityEngine;
namespace Assets._Game.Scripts.CharacterController {
// would be better if this would be an Interface, but if its an interface you can't set the value in the inspector :(
// unless you use a plugin like this: https://www.assetstore.unity3d.com/en/#!/content/12117
public abstract class IFPInputProvider : MonoBehaviour {
/// <summary> Should store the mouse-delta / joystick input from the last frame. (do not confuse with mouse-screen-position) </summary>
public Vector2 LookDirection { get; protected set; }
/// <summary> Should store the Direction the player desires to walk. (horizontal/vertical move axis) </summary>
public Vector2 MoveDirection { get; protected set; }
}
}
using UnityEngine;
namespace Assets._Game.Scripts.CharacterController {
/// <summary>
/// example input provider with the Unity buildin input system
/// </summary>
public class Unity4Input_FPInputProvider : IFPInputProvider {
void Update() {
MoveDirection = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
LookDirection = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
}
}
}
using UnityEngine;
using UnityEngine.InputNew;
namespace Assets._Game.Scripts.CharacterController {
/// <summary>
/// example for use with the 2016 new input system:
/// http://blogs.unity3d.com/2016/04/12/developing-the-new-input-system-together-with-you/
/// </summary>
public class Unity5NewInput_FPInputProvider : IFPInputProvider {
public PlayerInput PlayerInput;
private PlayerActionMap actionmap;
private PlayerActionMap ActionMap => actionmap ?? (actionmap = PlayerInput.GetActions<PlayerActionMap>());
void Update() {
MoveDirection = new Vector2(ActionMap.moveHorizontal.rawValue, ActionMap.moveVertical.rawValue);
LookDirection = new Vector2(ActionMap.lookHorizontal.rawValue, ActionMap.lookVertical.rawValue);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment