Fix : Slope bug for player & add case for inifinite stairs

This commit is contained in:
Ferlat Thibaud
2026-03-26 20:32:01 +01:00
parent 006acdb596
commit d34319ce96
24 changed files with 15908 additions and 3921 deletions
@@ -8,6 +8,9 @@ namespace Unheimlich.Features.Player
/// Simulates camera head bobbing motion during player movement and idle states.
/// Smoothly transitions between idle, walk and run bob parameters.
/// Adds a subtle Z-axis roll to the bob and a strafe tilt applied in LateUpdate.
///
/// Sprint bob is automatically disabled when OctoPlayer.CanRun is false,
/// since IsRunning already returns false in that case.
/// </summary>
public class CameraHeadBob : MonoBehaviour
{
@@ -15,15 +18,15 @@ namespace Unheimlich.Features.Player
private Vector3 _startLocalPos;
// Smoothly interpolated bob parameters to avoid brutal state switches.
private float _currentBobSpeed = 0f;
private float _currentBobSpeed = 0f;
private float _currentBobAmplitude = 0f;
// Strafe tilt (Z roll applied in LateUpdate, after OctoPlayer sets camera pitch).
private float _currentTilt = 0f;
private HeadBobMode _overrideMode = HeadBobMode.Normal;
private bool _hasOverride = false;
private Coroutine _resetCoroutine;
private HeadBobMode _overrideMode = HeadBobMode.Normal;
private bool _hasOverride = false;
private Coroutine _resetCoroutine;
private void Start()
{
@@ -32,7 +35,7 @@ namespace Unheimlich.Features.Player
private void Update()
{
if (PlayerController.Instance == null || PlayerController.Instance.CharController == null)
if (OctoCore.Components.OctoPlayer.Instance == null || PlayerController.Instance == null || PlayerController.Instance.CharController == null)
return;
var settings = PlayerController.Instance.CameraEffects?.Settings;
@@ -66,20 +69,21 @@ namespace Unheimlich.Features.Player
}
// Drive bob parameters directly from the actual physics velocity.
// This is naturally in sync with OctoPlayer's inertia — no extra lerp needed.
float horizontalSpeed = new Vector3(
PlayerController.Instance.CharController.velocity.x,
0f,
PlayerController.Instance.CharController.velocity.z).magnitude;
PlayerController.Instance.CharController.velocity.z
).magnitude;
bool isRunning = PlayerController.Instance.IsRunning;
// IsRunning already returns false when OctoPlayer.CanRun is off —
// so the bob falls back to walk params automatically.
bool isRunning = OctoCore.Components.OctoPlayer.Instance.IsRunning;
// Normalise: 0 = stopped, 1 = full walk/run speed (m/s converted from km/h ÷ 3.6).
const float WalkSpeedMs = 10f / 3.6f;
const float RunSpeedMs = 20f / 3.6f;
float maxSpeed = isRunning ? RunSpeedMs : WalkSpeedMs;
float t = Mathf.Clamp01(horizontalSpeed / maxSpeed);
float maxSpeed = isRunning ? RunSpeedMs : WalkSpeedMs;
float t = Mathf.Clamp01(horizontalSpeed / maxSpeed);
float targetSpeed = Mathf.Lerp(settings.idleBobSpeed, isRunning ? settings.runBobSpeed : settings.walkBobSpeed, t);
float targetAmplitude = Mathf.Lerp(settings.idleBobAmplitude, isRunning ? settings.runBobAmplitude : settings.walkBobAmplitude, t);
@@ -99,19 +103,16 @@ namespace Unheimlich.Features.Player
var settings = PlayerController.Instance.CameraEffects?.Settings;
if (settings == null) return;
// Strafe direction in local space using actual character velocity.
Vector3 velocity = PlayerController.Instance.CharController.velocity;
float strafeAmount = Vector3.Dot(velocity, PlayerController.Instance.transform.right);
Vector3 velocity = PlayerController.Instance.CharController.velocity;
float strafeAmount = Vector3.Dot(velocity, PlayerController.Instance.transform.right);
// Normalise by run speed (km/h → m/s: / 3.6) to get a -1..1 factor.
float runSpeedMs = PlayerController.Instance.Movement != null ? 20f / 3.6f : 5.5f;
float strafeFactor = Mathf.Clamp(strafeAmount / runSpeedMs, -1f, 1f);
float runSpeedMs = PlayerController.Instance.Movement != null ? 20f / 3.6f : 5.5f;
float strafeFactor = Mathf.Clamp(strafeAmount / runSpeedMs, -1f, 1f);
float targetTilt = -strafeFactor * settings.strafeTiltAngle;
_currentTilt = Mathf.Lerp(_currentTilt, targetTilt, Time.deltaTime * settings.strafeTiltSpeed);
float targetTilt = -strafeFactor * settings.strafeTiltAngle;
_currentTilt = Mathf.Lerp(_currentTilt, targetTilt, Time.deltaTime * settings.strafeTiltSpeed);
// Compose with the existing local rotation (OctoPlayer already wrote pitch into it).
Quaternion baseRot = transform.localRotation;
Quaternion baseRot = transform.localRotation;
transform.localRotation = baseRot * Quaternion.Euler(0f, 0f, _currentTilt);
}
@@ -121,7 +122,7 @@ namespace Unheimlich.Features.Player
public void SetMode(HeadBobMode mode)
{
_overrideMode = mode;
_hasOverride = true;
_hasOverride = true;
}
/// <summary>Clears any active mode override and returns to Normal.</summary>
@@ -147,25 +148,18 @@ namespace Unheimlich.Features.Player
// ── Private helpers ───────────────────────────────────────────────────────
/// <summary>Sets bob speed and amplitude directly without lerp — velocity already carries the inertia.</summary>
private void SetBobDirect(float speed, float amplitude)
{
_currentBobSpeed = speed;
_currentBobAmplitude = amplitude;
}
/// <summary>
/// Advances the bob timer and writes the resulting local position offset.
/// Includes a subtle Z roll (Lissajous third axis) for organic feel.
/// </summary>
private void ApplySmoothedBob(float rollAmplitude)
{
_timer += Time.deltaTime * _currentBobSpeed;
float x = Mathf.Sin(_timer * 0.5f) * _currentBobAmplitude * 0.6f;
float y = Mathf.Cos(_timer) * _currentBobAmplitude;
// Micro Z roll: half frequency of Y so it drifts independently.
float x = Mathf.Sin(_timer * 0.5f) * _currentBobAmplitude * 0.6f;
float y = Mathf.Cos(_timer) * _currentBobAmplitude;
float roll = Mathf.Sin(_timer * 0.5f + Mathf.PI * 0.25f) * rollAmplitude * (_currentBobAmplitude / Mathf.Max(0.001f, 0.03f));
Vector3 newPos = _startLocalPos;
@@ -173,21 +167,18 @@ namespace Unheimlich.Features.Player
newPos.y += y;
transform.localPosition = newPos;
// Store roll so LateUpdate can composite it with the strafe tilt.
// We write only the bob-driven roll here; LateUpdate adds strafe tilt on top.
Quaternion bobRot = Quaternion.Euler(0f, 0f, roll);
transform.localRotation = bobRot;
transform.localRotation = Quaternion.Euler(0f, 0f, roll);
}
private System.Collections.IEnumerator LerpToZeroCoroutine(float duration)
{
Vector3 startPos = transform.localPosition;
float elapsed = 0f;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float t = Mathf.Clamp01(elapsed / duration);
transform.localPosition = Vector3.Lerp(startPos, _startLocalPos, t);
yield return null;
}