-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMouseLook.cs
More file actions
164 lines (140 loc) · 6.72 KB
/
MouseLook.cs
File metadata and controls
164 lines (140 loc) · 6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using UnityEngine;
using Optimization.Core;
using Ytax.Core;
public class MouseLook : MonoBehaviour
{
// camera settings
[Header("Camera")]
[SerializeField] private Camera playerCamera;
[SerializeField] private float mouseSensitivity = 0.2f;
[SerializeField] private float lookSmoothTime = 0.06f;
[SerializeField] private float lookXLimit = 85.0f;
// tilt settings
[Header("Camera Tilt")]
[SerializeField] private bool useCameraTilt = true;
[SerializeField] private float tiltAngle = 10.0f;
[SerializeField] private float tiltSpeed = 8.0f;
[SerializeField] private float lookTiltSensitivity = 0.2f;
[SerializeField] private float tiltSmoothTime = 0.12f;
[SerializeField] private float mantleSmoothTime = 0.12f;
[SerializeField] private float breathSmoothTime = 0.12f;
// higher default because sensitivity is now lower (0.2 vs 10)
[SerializeField] private float mouseSpeedToTilt = 2.5f;
// references
[Header("References")]
[SerializeField] private Transform playerBody;
// runtime state
private PlayerInput input;
private Transform cameraTransform;
private float rotationX;
private Vector2 smoothedLook;
private Vector2 lookSmoothVelocity;
private float currentTilt;
private float mantleRoll;
private float targetMantleRoll;
private float mantleSmoothVelocity;
private float tiltSmoothVelocity;
private float targetBreathPitch;
private float targetBreathRoll;
private float targetBreathYaw;
private float breathPitchVelocity;
private float breathRollVelocity;
private float breathYawVelocity;
private float breathPitch;
private float breathRoll;
private float breathYaw;
// unity callbacks
private void Awake()
{
if (playerCamera == null) playerCamera = GetComponent<Camera>();
if (playerCamera == null) playerCamera = GetComponentInChildren<Camera>();
if (playerCamera != null) cameraTransform = playerCamera.transform;
input = GetComponent<PlayerInput>();
if (input == null) input = GetComponentInParent<PlayerInput>();
if (playerBody == null && input != null) playerBody = input.transform;
if (playerBody == null) playerBody = transform.parent;
}
private void OnEnable()
{
// Reset smooth state so re-enabling after the console doesn't cause a camera snap
smoothedLook = Vector2.zero;
lookSmoothVelocity = Vector2.zero;
}
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
private void Update()
{
if (playerCamera == null || input == null) return;
// Look input: mouse delta from Input System is per-frame displacement (not velocity).
// At low FPS each delta is larger, at high FPS smaller, but the total rotation
// per second is the same because fewer/more frames compensate. SmoothDamp is
// frame-rate independent internally, so the smoothed look tracks correctly.
Vector2 look = input.Look;
Vector2 target = look * mouseSensitivity;
smoothedLook = Vector2.SmoothDamp(smoothedLook, target, ref lookSmoothVelocity, lookSmoothTime);
// Apply smoothed displacement directly — no deltaTime multiplication needed
// because mouse delta is already a displacement, not a velocity.
float mouseX = smoothedLook.x;
float mouseY = smoothedLook.y;
rotationX -= mouseY;
rotationX = Mathf.Clamp(rotationX, -lookXLimit, lookXLimit);
if (useCameraTilt)
{
// Frame-rate independent tilt: raw smoothedLook.x is per-frame displacement,
// which is ~2x larger at 30 FPS vs 60 FPS. Normalizing to a 60 FPS reference
// frame ensures tilt sensitivity stays consistent at any framerate.
// dtScale = referenceDt / actualDt — at 30 FPS this is 0.5 (halves the doubled
// displacement), at 144 FPS this is 2.4 (scales up the smaller displacement).
const float kReferenceDt = 1f / 60f;
float dtScale = (Time.deltaTime > 0f) ? kReferenceDt / Time.deltaTime : 1f;
float mouseSpeed = Mathf.Abs(smoothedLook.x) * dtScale;
float tiltFactor = Mathf.Clamp01(mouseSpeed * mouseSpeedToTilt * lookTiltSensitivity);
float targetTilt = -Mathf.Sign(smoothedLook.x) * tiltAngle * tiltFactor;
// Smooth return-to-neutral: when mouse is still, smoothedLook decays to zero,
// targetTilt becomes zero, and SmoothDamp eases currentTilt back to neutral.
float effectiveTiltSmooth = Mathf.Max(0.0001f, tiltSmoothTime / Mathf.Max(0.0001f, tiltSpeed));
currentTilt = Mathf.SmoothDamp(currentTilt, targetTilt, ref tiltSmoothVelocity, effectiveTiltSmooth);
}
breathPitch = Mathf.SmoothDamp(breathPitch, targetBreathPitch, ref breathPitchVelocity, breathSmoothTime);
breathRoll = Mathf.SmoothDamp(breathRoll, targetBreathRoll, ref breathRollVelocity, breathSmoothTime);
breathYaw = Mathf.SmoothDamp(breathYaw, targetBreathYaw, ref breathYawVelocity, breathSmoothTime);
mantleRoll = Mathf.SmoothDamp(mantleRoll, targetMantleRoll, ref mantleSmoothVelocity, mantleSmoothTime);
// apply rotation math
float appliedPitch = rotationX + breathPitch;
float appliedRoll = currentTilt + breathRoll + mantleRoll;
float appliedYaw = breathYaw;
cameraTransform.localRotation = Quaternion.Euler(appliedPitch, appliedYaw, appliedRoll);
if (playerBody != null)
playerBody.rotation *= Quaternion.Euler(0f, mouseX, 0f);
}
public void SetBreathOffset(float pitch, float roll, float yaw = 0f)
{
targetBreathPitch = pitch;
targetBreathRoll = roll;
targetBreathYaw = yaw;
}
/// <summary>
/// Immediately set the absolute look angles so that when MouseLook is enabled
/// the camera and body orientations match the provided pitch/yaw.
/// </summary>
public void SetLookAngles(float pitch, float yaw)
{
rotationX = pitch;
smoothedLook = Vector2.zero;
lookSmoothVelocity = Vector2.zero;
if (playerBody != null)
{
playerBody.rotation = Quaternion.Euler(0f, yaw, 0f);
}
float appliedPitch = rotationX + breathPitch;
float appliedRoll = currentTilt + breathRoll + mantleRoll;
float appliedYaw = breathYaw;
if (cameraTransform != null)
{
cameraTransform.localRotation = Quaternion.Euler(appliedPitch, appliedYaw, appliedRoll);
}
}
}