Skip to content

Instantly share code, notes, and snippets.

@Oyshoboy
Last active February 18, 2022 08:43
Show Gist options
  • Save Oyshoboy/e61ec4ce9ccca6ce25a62222edb4a999 to your computer and use it in GitHub Desktop.
Save Oyshoboy/e61ec4ce9ccca6ce25a62222edb4a999 to your computer and use it in GitHub Desktop.
A simple , but smooth flying camera controller for unity, with all basic features and configurable properties.
using System.Collections.Generic;
using UnityEngine;
public class SimpleCameraControl : MonoBehaviour
{
#region Variables
[Header("Fov Settings")] private float _currFov = 90;
[SerializeField] private float _translationSpeed = 2;
public float fovSpeedMod = 5;
[Header("Camera")] public Camera recordingCamera;
[Header("Smooth Movement")] public KeyCode speedIncreaseKey = KeyCode.LeftAlt;
public KeyCode speedDecreaseKey = KeyCode.LeftControl;
public float acceleration = 2; // how fast you accelerate
public float accStep = 0.25f;
public float accSprintMultiplier = 4; // how much faster you go when "sprinting"
public float dampingCoefficient = 4; // how quickly you break to a halt after you stop your input
public bool focusOnEnable = true; // whether or not to focus and lock cursor immediately on enable
public float lookSensitivity = 1f;
Vector3 velocity; // current velocity
public enum RotationAxes
{
MouseXAndY = 0,
MouseX = 1,
MouseY = 2
}
[Header("Smooth Rotation")] public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 1F;
public float sensitivityY = 1F;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -360F;
public float maximumY = 360F;
float rotationX = 0F;
float rotationY = 0F;
private List<float> rotArrayX = new List<float>();
float rotAverageX = 0F;
private List<float> rotArrayY = new List<float>();
float rotAverageY = 0F;
public float frameCounter = 20;
Quaternion originalRotation;
#endregion UI
private CursorLockMode _wantedMode;
bool Focused
{
get => Cursor.lockState == CursorLockMode.Locked;
set
{
Cursor.lockState = value ? CursorLockMode.Locked : CursorLockMode.None;
Cursor.visible = value == false;
}
}
private Vector3 _initPosition;
private Vector3 _initRotation;
private void Start()
{
_initPosition = transform.position;
_initRotation = transform.eulerAngles;
InitSmoothRot();
}
void OnEnable()
{
if (focusOnEnable) Focused = true;
}
void OnDisable() => Focused = false;
private void InitSmoothRot()
{
Rigidbody rb = GetComponent<Rigidbody>();
if (rb)
rb.freezeRotation = true;
originalRotation = transform.rotation;
}
void Update()
{
SpeedChangeControl();
FovHandler();
if (Focused)
{
RotationHandler();
}
}
private void SpeedChangeControl()
{
var newAcc = acceleration;
if (Input.GetKey(speedIncreaseKey))
{
newAcc += accStep;
}
if (Input.GetKey(speedDecreaseKey))
{
newAcc -= accStep;
}
newAcc = Mathf.Clamp(newAcc, 0.25f, 25f);
acceleration = newAcc;
}
private void LateUpdate()
{
if (Focused)
{
UpdateInput();
}
else if (Input.GetMouseButtonDown(0))
Focused = true;
// Physics
velocity = Vector3.Lerp(velocity, Vector3.zero, dampingCoefficient * Time.deltaTime);
transform.position += velocity * Time.deltaTime;
}
void UpdateInput()
{
// Position
velocity += GetAccelerationVector() * Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Escape))
Focused = false;
}
Vector3 GetAccelerationVector()
{
Vector3 moveInput = default;
void AddMovement(KeyCode key, Vector3 dir)
{
if (Input.GetKey(key))
moveInput += dir;
}
AddMovement(KeyCode.W, Vector3.forward);
AddMovement(KeyCode.S, Vector3.back);
AddMovement(KeyCode.D, Vector3.right);
AddMovement(KeyCode.A, Vector3.left);
AddMovement(KeyCode.E, Vector3.up);
AddMovement(KeyCode.Q, Vector3.down);
Vector3 direction = transform.TransformVector(moveInput.normalized);
if (Input.GetKey(KeyCode.LeftShift))
return direction * (acceleration * accSprintMultiplier); // "sprinting"
return direction * acceleration; // "walking"
}
private void RotationHandler()
{
if (axes == RotationAxes.MouseXAndY)
{
rotAverageY = 0f;
rotAverageX = 0f;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotArrayY.Add(rotationY);
rotArrayX.Add(rotationX);
if (rotArrayY.Count >= frameCounter)
{
rotArrayY.RemoveAt(0);
}
if (rotArrayX.Count >= frameCounter)
{
rotArrayX.RemoveAt(0);
}
for (int j = 0; j < rotArrayY.Count; j++)
{
rotAverageY += rotArrayY[j];
}
for (int i = 0; i < rotArrayX.Count; i++)
{
rotAverageX += rotArrayX[i];
}
rotAverageY /= rotArrayY.Count;
rotAverageX /= rotArrayX.Count;
rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY);
rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX);
CutOffRotationWithinLimits();
Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left);
Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up);
var newRot = originalRotation * xQuaternion * yQuaternion;
var newEuler = newRot.eulerAngles;
newEuler = new Vector3(newEuler.x, newEuler.y, 0);
transform.rotation = Quaternion.Euler(newEuler);
}
else if (axes == RotationAxes.MouseX)
{
rotAverageX = 0f;
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotArrayX.Add(rotationX);
if (rotArrayX.Count >= frameCounter)
{
rotArrayX.RemoveAt(0);
}
for (int i = 0; i < rotArrayX.Count; i++)
{
rotAverageX += rotArrayX[i];
}
rotAverageX /= rotArrayX.Count;
rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX);
Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up);
transform.rotation = originalRotation * xQuaternion;
}
else
{
rotAverageY = 0f;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotArrayY.Add(rotationY);
if (rotArrayY.Count >= frameCounter)
{
rotArrayY.RemoveAt(0);
}
for (int j = 0; j < rotArrayY.Count; j++)
{
rotAverageY += rotArrayY[j];
}
rotAverageY /= rotArrayY.Count;
rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY);
Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left);
transform.rotation = originalRotation * yQuaternion;
}
}
private void CutOffRotationWithinLimits()
{
if (rotAverageY <= minimumY)
{
rotationY = minimumY;
}
else if (rotAverageY >= maximumY)
{
rotationY = maximumY;
}
}
public static float ClampAngle(float angle, float min, float max)
{
angle = angle % 360;
if ((angle >= -360F) && (angle <= 360F))
{
if (angle < -360F)
{
angle += 360F;
}
if (angle > 360F)
{
angle -= 360F;
}
}
return Mathf.Clamp(angle, min, max);
}
private void FovHandler()
{
if (recordingCamera == null) return;
//var camera = GetComponent<Camera>();
_currFov += Input.mouseScrollDelta.y * _translationSpeed;
_currFov = Mathf.Clamp(_currFov, 1, 120);
var calcFov = Mathf.Lerp(recordingCamera.fieldOfView, _currFov, Time.deltaTime * fovSpeedMod);
var newFov = calcFov;
recordingCamera.fieldOfView = newFov;
}
// Apply requested cursor state
private void SetCursorState()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = _wantedMode = CursorLockMode.None;
}
if (Input.GetMouseButtonDown(0))
{
_wantedMode = CursorLockMode.Locked;
}
// Apply cursor state
Cursor.lockState = _wantedMode;
// Hide cursor when locking
Cursor.visible = (CursorLockMode.Locked != _wantedMode);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment