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
+2 -2
View File
@@ -180,8 +180,8 @@ MonoBehaviour:
Sprite: {fileID: 0} Sprite: {fileID: 0}
Languages: Languages:
Default: 0 Default: 0
SaveWritten: {fileID: 11400000, guid: 32dca1ce9df9b064b8a0a2b3ab0fd8dd, type: 2} SaveWritten: {fileID: 0}
SaveAudio: {fileID: 11400000, guid: 225ffb773e59f8141bacbba2a9ede731, type: 2} SaveAudio: {fileID: 0}
Overrides: [] Overrides: []
Scenes: Scenes:
SceneDatas: [] SceneDatas: []
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 134e0811334c5814e96a4823d9665656
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 445844e5540fe42449d9a96cd68cbcea
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,137 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-3947132882786938876
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: StairsMaterial
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.3773585, g: 0.3773585, b: 0.3773585, a: 1}
- _Color: {r: 0.37735847, g: 0.37735847, b: 0.37735847, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5bb237e5d23b5264b8fcb45d620a4108
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: baf901c128ce7a644b7b1737ba04de04
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b278a20a2783def489d009364c988771
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3159084da89126c4f8f26519f0c6f75e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1b096230e5db7f84fbdc9ec1f398c205
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 629e2f358900b4e46b9b43539224cba1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -8,6 +8,9 @@ namespace Unheimlich.Features.Player
/// Simulates camera head bobbing motion during player movement and idle states. /// Simulates camera head bobbing motion during player movement and idle states.
/// Smoothly transitions between idle, walk and run bob parameters. /// 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. /// 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> /// </summary>
public class CameraHeadBob : MonoBehaviour public class CameraHeadBob : MonoBehaviour
{ {
@@ -15,15 +18,15 @@ namespace Unheimlich.Features.Player
private Vector3 _startLocalPos; private Vector3 _startLocalPos;
// Smoothly interpolated bob parameters to avoid brutal state switches. // Smoothly interpolated bob parameters to avoid brutal state switches.
private float _currentBobSpeed = 0f; private float _currentBobSpeed = 0f;
private float _currentBobAmplitude = 0f; private float _currentBobAmplitude = 0f;
// Strafe tilt (Z roll applied in LateUpdate, after OctoPlayer sets camera pitch). // Strafe tilt (Z roll applied in LateUpdate, after OctoPlayer sets camera pitch).
private float _currentTilt = 0f; private float _currentTilt = 0f;
private HeadBobMode _overrideMode = HeadBobMode.Normal; private HeadBobMode _overrideMode = HeadBobMode.Normal;
private bool _hasOverride = false; private bool _hasOverride = false;
private Coroutine _resetCoroutine; private Coroutine _resetCoroutine;
private void Start() private void Start()
{ {
@@ -32,7 +35,7 @@ namespace Unheimlich.Features.Player
private void Update() 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; return;
var settings = PlayerController.Instance.CameraEffects?.Settings; var settings = PlayerController.Instance.CameraEffects?.Settings;
@@ -66,20 +69,21 @@ namespace Unheimlich.Features.Player
} }
// Drive bob parameters directly from the actual physics velocity. // 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( float horizontalSpeed = new Vector3(
PlayerController.Instance.CharController.velocity.x, PlayerController.Instance.CharController.velocity.x,
0f, 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 WalkSpeedMs = 10f / 3.6f;
const float RunSpeedMs = 20f / 3.6f; const float RunSpeedMs = 20f / 3.6f;
float maxSpeed = isRunning ? RunSpeedMs : WalkSpeedMs; float maxSpeed = isRunning ? RunSpeedMs : WalkSpeedMs;
float t = Mathf.Clamp01(horizontalSpeed / maxSpeed); float t = Mathf.Clamp01(horizontalSpeed / maxSpeed);
float targetSpeed = Mathf.Lerp(settings.idleBobSpeed, isRunning ? settings.runBobSpeed : settings.walkBobSpeed, t); float targetSpeed = Mathf.Lerp(settings.idleBobSpeed, isRunning ? settings.runBobSpeed : settings.walkBobSpeed, t);
float targetAmplitude = Mathf.Lerp(settings.idleBobAmplitude, isRunning ? settings.runBobAmplitude : settings.walkBobAmplitude, 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; var settings = PlayerController.Instance.CameraEffects?.Settings;
if (settings == null) return; if (settings == null) return;
// Strafe direction in local space using actual character velocity. Vector3 velocity = PlayerController.Instance.CharController.velocity;
Vector3 velocity = PlayerController.Instance.CharController.velocity; float strafeAmount = Vector3.Dot(velocity, PlayerController.Instance.transform.right);
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 runSpeedMs = PlayerController.Instance.Movement != null ? 20f / 3.6f : 5.5f; float strafeFactor = Mathf.Clamp(strafeAmount / runSpeedMs, -1f, 1f);
float strafeFactor = Mathf.Clamp(strafeAmount / runSpeedMs, -1f, 1f);
float targetTilt = -strafeFactor * settings.strafeTiltAngle; float targetTilt = -strafeFactor * settings.strafeTiltAngle;
_currentTilt = Mathf.Lerp(_currentTilt, targetTilt, Time.deltaTime * settings.strafeTiltSpeed); _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); transform.localRotation = baseRot * Quaternion.Euler(0f, 0f, _currentTilt);
} }
@@ -121,7 +122,7 @@ namespace Unheimlich.Features.Player
public void SetMode(HeadBobMode mode) public void SetMode(HeadBobMode mode)
{ {
_overrideMode = mode; _overrideMode = mode;
_hasOverride = true; _hasOverride = true;
} }
/// <summary>Clears any active mode override and returns to Normal.</summary> /// <summary>Clears any active mode override and returns to Normal.</summary>
@@ -147,25 +148,18 @@ namespace Unheimlich.Features.Player
// ── Private helpers ─────────────────────────────────────────────────────── // ── Private helpers ───────────────────────────────────────────────────────
/// <summary>Sets bob speed and amplitude directly without lerp — velocity already carries the inertia.</summary>
private void SetBobDirect(float speed, float amplitude) private void SetBobDirect(float speed, float amplitude)
{ {
_currentBobSpeed = speed; _currentBobSpeed = speed;
_currentBobAmplitude = amplitude; _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) private void ApplySmoothedBob(float rollAmplitude)
{ {
_timer += Time.deltaTime * _currentBobSpeed; _timer += Time.deltaTime * _currentBobSpeed;
float x = Mathf.Sin(_timer * 0.5f) * _currentBobAmplitude * 0.6f; float x = Mathf.Sin(_timer * 0.5f) * _currentBobAmplitude * 0.6f;
float y = Mathf.Cos(_timer) * _currentBobAmplitude; float y = Mathf.Cos(_timer) * _currentBobAmplitude;
// Micro Z roll: half frequency of Y so it drifts independently.
float roll = Mathf.Sin(_timer * 0.5f + Mathf.PI * 0.25f) * rollAmplitude * (_currentBobAmplitude / Mathf.Max(0.001f, 0.03f)); float roll = Mathf.Sin(_timer * 0.5f + Mathf.PI * 0.25f) * rollAmplitude * (_currentBobAmplitude / Mathf.Max(0.001f, 0.03f));
Vector3 newPos = _startLocalPos; Vector3 newPos = _startLocalPos;
@@ -173,21 +167,18 @@ namespace Unheimlich.Features.Player
newPos.y += y; newPos.y += y;
transform.localPosition = newPos; transform.localPosition = newPos;
// Store roll so LateUpdate can composite it with the strafe tilt. transform.localRotation = Quaternion.Euler(0f, 0f, roll);
// We write only the bob-driven roll here; LateUpdate adds strafe tilt on top.
Quaternion bobRot = Quaternion.Euler(0f, 0f, roll);
transform.localRotation = bobRot;
} }
private System.Collections.IEnumerator LerpToZeroCoroutine(float duration) private System.Collections.IEnumerator LerpToZeroCoroutine(float duration)
{ {
Vector3 startPos = transform.localPosition; Vector3 startPos = transform.localPosition;
float elapsed = 0f; float elapsed = 0f;
while (elapsed < duration) while (elapsed < duration)
{ {
elapsed += Time.deltaTime; elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration); float t = Mathf.Clamp01(elapsed / duration);
transform.localPosition = Vector3.Lerp(startPos, _startLocalPos, t); transform.localPosition = Vector3.Lerp(startPos, _startLocalPos, t);
yield return null; yield return null;
} }
@@ -1,5 +1,5 @@
using OctoCore.Inputs; using OctoCore.Inputs;
using UnityEngine; using UnityEngine;
namespace OctoCore.Components namespace OctoCore.Components
{ {
@@ -27,15 +27,24 @@ namespace OctoCore.Components
[Tooltip("Run speed (km/h).")] [Tooltip("Run speed (km/h).")]
public float RunSpeed = 20; public float RunSpeed = 20;
[Tooltip("Jump duration (seconds).")] [Tooltip("Jump duration (seconds).")]
public float JumpDuration = .4f; public float JumpDuration = .4f;
[Tooltip("Maximum jump height reached at the apex (meters).")] [Tooltip("Maximum jump height reached at the apex (meters).")]
public float JumpHeight = .4f; public float JumpHeight = .4f;
[Tooltip("Maximum step height (meters).")]
public float StepHeight = 0.3f;
[Tooltip("Downward acceleration applied to the player (m/s²).")] [Tooltip("Downward acceleration applied to the player (m/s²).")]
public float Gravity = 9.8f; public float Gravity = 9.8f;
[Tooltip("Enable jump.")]
public bool CanJump = true;
[Tooltip("Enable sprint.")]
public bool CanRun = true;
} }
[System.Serializable] [System.Serializable]
@@ -51,7 +60,6 @@ namespace OctoCore.Components
private class FirstPerson private class FirstPerson
{ {
[Header("References")] [Header("References")]
public Camera Camera; public Camera Camera;
[Header("Settings")] [Header("Settings")]
@@ -68,7 +76,7 @@ namespace OctoCore.Components
[Tooltip("Maximum pitch (looking up).")] [Tooltip("Maximum pitch (looking up).")]
public float MaxPitch = 80f; public float MaxPitch = 80f;
} }
[SerializeField] private References _references; [SerializeField] private References _references;
[SerializeField] private Settings _settings; [SerializeField] private Settings _settings;
[SerializeField] private Mapping _mapping; [SerializeField] private Mapping _mapping;
@@ -88,23 +96,38 @@ namespace OctoCore.Components
private Vector2 _lookInput; private Vector2 _lookInput;
private Vector3 _moveInertia; private Vector3 _moveInertia;
private Vector3 _moveDirection; private Vector3 _moveDirection;
public bool IsPaused => _isPaused; public bool IsPaused => _isPaused;
public float Radius => _references.Controller ? _references.Controller.radius : 0; public float Radius => _references.Controller ? _references.Controller.radius : 0;
/// <summary>True when the player is actively sprinting (inertia > 0.1 and CanRun enabled).</summary>
public bool IsRunning => _settings.CanRun && _runInertia > 0.1f;
// Public access to yaw and pitch for external aim assist systems // Public access to yaw and pitch for external aim assist systems
public float Yaw public float Yaw
{ {
get => _yaw; get => _yaw;
set => _yaw = value; set => _yaw = value;
} }
public float Pitch public float Pitch
{ {
get => _pitch; get => _pitch;
set => _pitch = Mathf.Clamp(value, _firstPerson.MinPitch, _firstPerson.MaxPitch); set => _pitch = Mathf.Clamp(value, _firstPerson.MinPitch, _firstPerson.MaxPitch);
} }
public bool CanJump
{
get => _settings.CanJump;
set => _settings.CanJump = value;
}
public bool CanRun
{
get => _settings.CanRun;
set => _settings.CanRun = value;
}
void OnEnable() void OnEnable()
{ {
if (!Instance) Instance = this; if (!Instance) Instance = this;
@@ -132,26 +155,22 @@ namespace OctoCore.Components
void Update() void Update()
{ {
_moveInput = _isPaused ? Vector2.zero : Vector2.ClampMagnitude(_moveInput, 1); _moveInput = _isPaused ? Vector2.zero : Vector2.ClampMagnitude(_moveInput, 1);
Camera cam = (_firstPerson.Camera && _firstPerson.Camera.enabled && _firstPerson.Camera.gameObject.activeInHierarchy) ? _firstPerson.Camera : Camera.main; Camera cam = (_firstPerson.Camera && _firstPerson.Camera.enabled && _firstPerson.Camera.gameObject.activeInHierarchy)
? _firstPerson.Camera
: Camera.main;
bool accelerating = _moveInput.magnitude > .1f; bool accelerating = _moveInput.magnitude > .1f;
bool isGrounded = _references.Controller ? _references.Controller.isGrounded : true; bool isGrounded = _references.Controller ? _references.Controller.isGrounded : true;
float t = Time.deltaTime / 3.6f; float t = Time.deltaTime / 3.6f;
// Move // ── Move ────────────────────────────────────────────────────────────
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y); Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y);
if (_settings.Orientation == Orientations.Camera && cam) if (_settings.Orientation == Orientations.Camera && cam)
{ {
Vector3 camForward = cam.transform.forward; Vector3 camForward = cam.transform.forward; camForward.y = 0; camForward.Normalize();
camForward.y = 0; Vector3 camRight = cam.transform.right; camRight.y = 0; camRight.Normalize();
camForward.Normalize();
Vector3 camRight = cam.transform.right;
camRight.y = 0;
camRight.Normalize();
move = camRight * move.x + camForward * move.z; move = camRight * move.x + camForward * move.z;
} }
@@ -159,74 +178,114 @@ namespace OctoCore.Components
_moveDirection = Vector3.Lerp(_moveDirection, move, deltaSmooth * 1.5f); _moveDirection = Vector3.Lerp(_moveDirection, move, deltaSmooth * 1.5f);
deltaSmooth *= 1 - Vector3.Cross(_moveInertia, _moveDirection).magnitude; deltaSmooth *= 1 - Vector3.Cross(_moveInertia, _moveDirection).magnitude;
_moveInertia = Vector3.Lerp(_moveInertia, move, deltaSmooth); _moveInertia = Vector3.Lerp(_moveInertia, move, deltaSmooth);
if (!accelerating && _moveInertia.magnitude < .01f) _moveInertia = Vector3.zero; if (!accelerating && _moveInertia.magnitude < .01f) _moveInertia = Vector3.zero;
_runInertia = Mathf.Lerp(_runInertia, _runInput ? 1 : 0, t * (accelerating ? 5 : 3)); // Sprint inertia — clamped to 0 if CanRun is off
_runInertia = Mathf.Lerp(
_runInertia,
(_runInput && _settings.CanRun) ? 1f : 0f,
t * (accelerating ? 5 : 3)
);
Vector3 moveDelta = _moveInertia * t * Mathf.Lerp(_settings.WalkSpeed, _settings.RunSpeed, _runInertia); Vector3 moveDelta = _moveInertia * t * Mathf.Lerp(_settings.WalkSpeed, _settings.RunSpeed, _runInertia);
if (isGrounded && _jumpInput && !_jumpping) // ── Jump ────────────────────────────────────────────────────────────
if (isGrounded && _jumpInput && !_jumpping && _settings.CanJump)
{ {
_jumpInput = false; _jumpInput = false;
_jumpping = true; _jumpping = true;
_jumpLerp = 0; _jumpLerp = 0;
_jumpTime = Time.timeSinceLevelLoad; _jumpTime = Time.timeSinceLevelLoad;
_jumpAltitude = transform.position.y; _jumpAltitude = transform.position.y;
} }
// Consume the input even if CanJump is off, so it doesn't queue up
if (_jumpInput && !_settings.CanJump) _jumpInput = false;
float jumpOffset = 0; float jumpOffset = 0;
if (_jumpping) if (_jumpping)
{ {
_jumpLerp = (Time.timeSinceLevelLoad - _jumpTime) / _settings.JumpDuration; _jumpLerp = (Time.timeSinceLevelLoad - _jumpTime) / _settings.JumpDuration;
if (_jumpLerp >= 1 || (_jumpLerp > .3f && isGrounded)) // FIX: added 0.15s grace period before checking isGrounded to avoid immediate re-trigger
bool landingAllowed = (Time.timeSinceLevelLoad - _jumpTime) > 0.15f;
if (_jumpLerp >= 1 || (_jumpLerp > .5f && isGrounded && landingAllowed))
{ {
_jumpping = false; _jumpping = false;
_jumpLerp = 0; _jumpLerp = 0;
} }
else else
{ {
float x = _jumpLerp; float x = _jumpLerp * 2 - 1f;
x = x * 2 - 1f; float y = x < 0
? Mathf.Sqrt(1f - x * x)
: Mathf.Cos(x * .5f * Mathf.PI);
float y; y = _jumpAltitude + y * _settings.JumpHeight;
if (x < 0) y = Mathf.Sqrt(1f - x * x); jumpOffset = y - transform.position.y;
else y = Mathf.Cos(x * .5f * Mathf.PI);
y = _jumpAltitude + y * _settings.JumpHeight;
jumpOffset = y - transform.position.y;
} }
} }
// ── Step-up ─────────────────────────────────────────────────────────
if (_references.Controller && isGrounded && !_jumpping)
{
float feetY = transform.position.y - (_references.Controller.height * 0.5f);
float castFromY = feetY + _settings.StepHeight;
Vector3 stepOrigin = new Vector3(transform.position.x, castFromY, transform.position.z);
Vector3 stepDir = new Vector3(moveDelta.x, 0, moveDelta.z).normalized;
if (stepDir.magnitude > 0.1f)
{
float stepDist = _references.Controller.radius + 0.05f;
if (!Physics.Raycast(stepOrigin, stepDir, stepDist))
{
Vector3 probeOrigin = stepOrigin + stepDir * stepDist;
if (Physics.Raycast(probeOrigin, Vector3.down, out RaycastHit hit, _settings.StepHeight))
{
float stepUp = hit.point.y - feetY;
if (stepUp > 0.05f)
moveDelta.y = Mathf.Max(moveDelta.y, stepUp);
}
}
}
}
// ── Apply ───────────────────────────────────────────────────────────
if (_jumpping) if (_jumpping)
moveDelta.y = jumpOffset; moveDelta.y = jumpOffset;
else else if (!isGrounded)
moveDelta.y -= _settings.Gravity * Time.deltaTime; moveDelta.y -= _settings.Gravity * Time.deltaTime;
else moveDelta.y = -0.1f;
if (_references.Controller) if (_references.Controller)
_references.Controller.Move(moveDelta); _references.Controller.Move(moveDelta);
else else
transform.position += moveDelta; transform.position += moveDelta;
// Rotate // ── Rotate ──────────────────────────────────────────────────────────
switch (_settings.Mode) switch (_settings.Mode)
{ {
case PlayerModes.FirstPerson: case PlayerModes.FirstPerson:
if (cam && !_isPaused) if (cam && !_isPaused)
{ {
Vector2 delta = _lookInput; float mouseX = _lookInput.x * _firstPerson.SensitivityX;
float mouseY = _lookInput.y * _firstPerson.SensitivityY * -1;
float mouseX = delta.x * _firstPerson.SensitivityX; _yaw += mouseX;
float mouseY = delta.y * _firstPerson.SensitivityY * -1; _pitch = Mathf.Clamp(_pitch + mouseY, _firstPerson.MinPitch, _firstPerson.MaxPitch);
_yaw += mouseX; transform.rotation = Quaternion.Euler(0, _yaw, 0);
_pitch = Mathf.Clamp(_pitch + mouseY, _firstPerson.MinPitch, _firstPerson.MaxPitch);
transform.rotation = Quaternion.Euler(0, _yaw, 0);
cam.transform.localRotation = Quaternion.Euler(_pitch, 0, 0); cam.transform.localRotation = Quaternion.Euler(_pitch, 0, 0);
} }
break; break;
@@ -237,74 +296,61 @@ namespace OctoCore.Components
if (_moveInertia.magnitude > .002f) if (_moveInertia.magnitude > .002f)
{ {
Vector3 offset = _moveInertia - _moveDirection; Vector3 offset = _moveInertia - _moveDirection;
offset *= _moveInertia.magnitude * _runInertia; offset *= _moveInertia.magnitude * _runInertia;
offset = Vector3.ClampMagnitude(offset * 1f, 1f); offset = Vector3.ClampMagnitude(offset, 1f);
Vector3 up = (Vector3.up - offset).normalized; Vector3 up = (Vector3.up - offset).normalized;
Quaternion lookRot = Quaternion.LookRotation(_moveInertia, up); Quaternion lookRot = Quaternion.LookRotation(_moveInertia, up);
targetRot = Quaternion.Lerp(targetRot, lookRot, t * 30); targetRot = Quaternion.Lerp(targetRot, lookRot, t * 30);
} }
transform.rotation = targetRot; transform.rotation = targetRot;
break; break;
} }
} }
// Reset // ── Reset ───────────────────────────────────────────────────────────
_moveInput = Vector2.zero; _moveInput = Vector2.zero;
_lookInput = Vector2.zero; _lookInput = Vector2.zero;
_runInput = false; _runInput = false;
} }
public void Teleport(Vector3 position, Quaternion rotation) public void Teleport(Vector3 position, Quaternion rotation)
{ {
_isPaused = _jumpInput = _jumpping = _runInput = false; _isPaused = _jumpInput = _jumpping = _runInput = false;
_runInertia = _jumpLerp = _jumpTime = _jumpAltitude = 0; _runInertia = _jumpLerp = _jumpTime = _jumpAltitude = 0;
_moveInput = _lookInput = Vector2.zero; _moveInput = _lookInput = Vector2.zero;
_moveInertia = _moveDirection = Vector3.zero; _moveInertia = _moveDirection = Vector3.zero;
_yaw = rotation.eulerAngles.y; _yaw = rotation.eulerAngles.y;
_pitch = 0; _pitch = 0;
if (_references.Controller) if (_references.Controller) _references.Controller.enabled = false;
_references.Controller.enabled = false;
transform.position = position; transform.position = position;
transform.rotation = rotation; transform.rotation = rotation;
if (_references.Controller) _references.Controller.enabled = true;
if (_references.Controller)
_references.Controller.enabled = true;
} }
public void Pause(bool pause) public void Pause(bool pause)
{ {
_isPaused = pause; _isPaused = pause;
if (OctoCore.Instance) OctoCore.Instance.UI.SetCursor();
if (OctoCore.Instance)
OctoCore.Instance.UI.SetCursor();
} }
// Convenience methods for external scripts
public void SetCanJump(bool value) => _settings.CanJump = value;
public void SetCanRun(bool value) => _settings.CanRun = value;
private void Move(Vector2 value) { _moveInput = value; } private void Move(Vector2 value) { _moveInput = value; }
private void Look(Vector2 value) { _lookInput = value; } private void Look(Vector2 value) { _lookInput = value; }
private void Run() { _runInput = true; } private void Run() { _runInput = true; }
private void Jump() { _jumpInput = true; } private void Jump() { _jumpInput = true; }
private void SetMode() private void SetMode()
{ {
switch (_settings.Mode) switch (_settings.Mode)
{ {
case PlayerModes.FirstPerson: case PlayerModes.FirstPerson: break;
// Activate First Person Camera case PlayerModes.ThirdPerson: break;
break;
case PlayerModes.ThirdPerson:
// Activate Third Person Camera
break;
} }
} }
/*public static float Arc(float x)
{
x = Mathf.Clamp01(x) * 2 - 1f;
return Mathf.Sqrt(1f - x * x);
}*/
} }
} }
@@ -51,6 +51,31 @@
"key": "ShapeComponent.SettingsEnabled", "key": "ShapeComponent.SettingsEnabled",
"value": "{\"m_Value\":false}" "value": "{\"m_Value\":false}"
}, },
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "mesh.newShapesSnapToGrid",
"value": "{\"m_Value\":true}"
},
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "mesh.meshColliderIsConvex",
"value": "{\"m_Value\":false}"
},
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "editor.backFaceSelectEnabled",
"value": "{\"m_Value\":false}"
},
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "editor.showSceneInfo",
"value": "{\"m_Value\":false}"
},
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "editor.showEditorNotifications",
"value": "{\"m_Value\":false}"
},
{ {
"type": "UnityEngine.ProBuilder.SelectMode, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "type": "UnityEngine.ProBuilder.SelectMode, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "s_SelectMode", "key": "s_SelectMode",
@@ -70,6 +95,46 @@
"type": "UnityEngine.Quaternion, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "type": "UnityEngine.Quaternion, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "ShapeBuilder.LastRotation.Prism", "key": "ShapeBuilder.LastRotation.Prism",
"value": "{\"m_Value\":{\"x\":0.0,\"y\":0.0,\"z\":0.0,\"w\":1.0}}" "value": "{\"m_Value\":{\"x\":0.0,\"y\":0.0,\"z\":0.0,\"w\":1.0}}"
},
{
"type": "UnityEngine.Material, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "mesh.userMaterial",
"value": "{\"m_Value\":{\"instanceID\":0}}"
},
{
"type": "UnityEngine.Rendering.ShadowCastingMode, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "mesh.shadowCastingMode",
"value": "{\"m_Value\":1}"
},
{
"type": "UnityEditor.StaticEditorFlags, UnityEditor.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "mesh.defaultStaticEditorFlags",
"value": "{\"m_Value\":0}"
},
{
"type": "UnityEngine.ProBuilder.ColliderType, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "mesh.newShapeColliderType",
"value": "{\"m_Value\":2}"
},
{
"type": "UnityEngine.ProBuilder.UnwrapParameters, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "lightmapping.defaultLightmapUnwrapParameters",
"value": "{\"m_Value\":{\"m_HardAngle\":88.0,\"m_PackMargin\":20.0,\"m_AngleError\":8.0,\"m_AreaError\":15.0}}"
},
{
"type": "UnityEngine.ProBuilder.RectSelectMode, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "editor.dragSelectRectMode",
"value": "{\"m_Value\":0}"
},
{
"type": "UnityEngine.ProBuilder.ExtrudeMethod, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "editor.extrudeMethod",
"value": "{\"m_Value\":2}"
},
{
"type": "UnityEditor.ProBuilder.Actions.DetachFaces+DetachSetting, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"key": "DetachFaces.target",
"value": "{\"m_Value\":0}"
} }
] ]
} }