From 7aa4a518db1971e46fcfd54f81d0ae63e84fdbcf Mon Sep 17 00:00:00 2001 From: kerboul Date: Sat, 16 May 2026 20:52:36 +0200 Subject: [PATCH] Steering, HUD course, auto-index checkpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PlayerController: MovementSpeed 5->25, ajout turnDamping+idleDrag pour virages nets - CheckpointSystem: auto-assign checkpointIndex depuis l'array, déclenche race HUD sur CP0 - GameHUD: course visible dès passage de la porte (CP0), timer local indépendant du serveur Co-Authored-By: Claude Sonnet 4.6 --- game/Assets/PlayerController.cs | 39 +++++++++++++------- game/Assets/Scripts/Race/CheckpointSystem.cs | 21 +++++++++-- game/Assets/Scripts/UI/GameHUD.cs | 23 +++++++++--- 3 files changed, 61 insertions(+), 22 deletions(-) diff --git a/game/Assets/PlayerController.cs b/game/Assets/PlayerController.cs index 5c5910b..a3f6d24 100644 --- a/game/Assets/PlayerController.cs +++ b/game/Assets/PlayerController.cs @@ -15,9 +15,15 @@ public class PlayerController : MonoBehaviour public float JumpForce = 5f; // Force applied when jumping - public float MovementSpeed = 5f; // Speed of player movement + public float MovementSpeed = 25f; // Speed of player movement public float BoostSpeed = 2f; // Multiplicateur de vitesse sur GelOrange + [Header("Steering Feel")] + [Tooltip("Damps velocity perpendicular to input — higher = sharper turns")] + public float turnDamping = 7f; + [Tooltip("Horizontal friction when no input is held")] + public float idleDrag = 3f; + [Header("Bump Collision")] public float bumpForce = 4f; // Impulse force when bumping a remote player public float bumpCooldown = 0.25f; // Minimum time between bumps from the same player @@ -261,21 +267,28 @@ public class PlayerController : MonoBehaviour } } - if (isForwardHeld) + Vector3 inputDir = Vector3.zero; + if (isForwardHeld) inputDir += forward; + if (isBackwardsHeld) inputDir -= forward; + if (isRightHeld) inputDir += right; + if (isLeftHeld) inputDir -= right; + + if (inputDir.sqrMagnitude > 0.01f) { - rb.AddForce(forward * currentSpeed * Time.deltaTime, ForceMode.VelocityChange); + inputDir.Normalize(); + rb.AddForce(inputDir * currentSpeed * Time.deltaTime, ForceMode.VelocityChange); + + // Counter-force on the lateral component (makes turns sharper) + Vector3 horizVel = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z); + Vector3 perp = horizVel - Vector3.Project(horizVel, inputDir); + if (perp.sqrMagnitude > 0.01f) + rb.AddForce(-perp * turnDamping * Time.deltaTime, ForceMode.VelocityChange); } - if (isBackwardsHeld) + else if (!isOnGelViolet) { - rb.AddForce(-forward * currentSpeed * Time.deltaTime, ForceMode.VelocityChange); - } - if (isLeftHeld) - { - rb.AddForce(-right * currentSpeed * Time.deltaTime, ForceMode.VelocityChange); - } - if (isRightHeld) - { - rb.AddForce(right * currentSpeed * Time.deltaTime, ForceMode.VelocityChange); + // Gradual horizontal slow-down when no key is held + Vector3 horizVel = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z); + rb.AddForce(-horizVel * idleDrag * Time.deltaTime, ForceMode.VelocityChange); } // GelViolet : colle la balle à la surface (sticky) diff --git a/game/Assets/Scripts/Race/CheckpointSystem.cs b/game/Assets/Scripts/Race/CheckpointSystem.cs index 18028f1..8f6d27b 100644 --- a/game/Assets/Scripts/Race/CheckpointSystem.cs +++ b/game/Assets/Scripts/Race/CheckpointSystem.cs @@ -23,7 +23,10 @@ public class CheckpointSystem : MonoBehaviour [Tooltip("Material to apply to finish line")] public Material finishLineMaterial; - private int _localCheckpointIndex = 0; // how many checkpoints this local player passed + public int LocalCheckpointIndex => _localCheckpointIndex; + public bool RaceStarted { get; private set; } + + private int _localCheckpointIndex = 0; private Renderer[] _checkpointRenderers; private bool _finished = false; @@ -38,9 +41,11 @@ public class CheckpointSystem : MonoBehaviour for (int i = 0; i < checkpoints.Length; i++) { _checkpointRenderers[i] = checkpoints[i].GetComponent(); - - // Tag checkpoints with their index for trigger identification checkpoints[i].gameObject.name = $"Checkpoint_{i}"; + + // Auto-assign index so trigger knows its position in the sequence + var trigger = checkpoints[i].GetComponent(); + if (trigger != null) trigger.checkpointIndex = i; } // Tell HUD total checkpoints @@ -52,9 +57,15 @@ public class CheckpointSystem : MonoBehaviour public void OnLocalPlayerHitCheckpoint(int index) { if (_finished) return; - // Must hit checkpoints in order if (index != _localCheckpointIndex) return; + // CP0 = start gate: activate race HUD and start local timer + if (index == 0) + { + RaceStarted = true; + GameHUD.Instance?.SetLocalRaceActive(true); + } + _localCheckpointIndex++; NetworkManager.Instance?.SendCheckpoint(_localCheckpointIndex); @@ -81,7 +92,9 @@ public class CheckpointSystem : MonoBehaviour { _localCheckpointIndex = 0; _finished = false; + RaceStarted = false; UpdateCheckpointVisuals(); + GameHUD.Instance?.SetLocalRaceActive(false); } private void UpdateCheckpointVisuals() diff --git a/game/Assets/Scripts/UI/GameHUD.cs b/game/Assets/Scripts/UI/GameHUD.cs index 2432215..6267e54 100644 --- a/game/Assets/Scripts/UI/GameHUD.cs +++ b/game/Assets/Scripts/UI/GameHUD.cs @@ -20,6 +20,10 @@ public class GameHUD : MonoBehaviour private int _checkpointsCurrent = 0; private int _checkpointsTotal = 5; + // Local race state (activated when CP0 gate is crossed, independent of server phase) + private bool _localRaceActive = false; + private float _localRaceTimer = 0f; + // Countdown animation private float _lastCountdownShown = -1f; private float _countdownPulse = 0f; @@ -73,6 +77,8 @@ public class GameHUD : MonoBehaviour { if (_timerRunning) _roundTimer += Time.deltaTime; + if (_localRaceActive) + _localRaceTimer += Time.deltaTime; if (_countdown > 0f && _countdown != _lastCountdownShown) { @@ -87,9 +93,15 @@ public class GameHUD : MonoBehaviour public void SetRoundInfo(int round, string mode) { _roundNumber = round; _gameMode = mode; } public void SetCheckpoint(int current, int total) { _checkpointsCurrent = current; _checkpointsTotal = total; } + public void SetLocalRaceActive(bool active) + { + _localRaceActive = active; + if (!active) _localRaceTimer = 0f; + } + void OnGUI() { - if (_phase == "lobby") return; + if (_phase == "lobby" && !_localRaceActive) return; ImGuiSkin.EnsureReady(); var nm = NetworkManager.Instance; @@ -184,10 +196,11 @@ public class GameHUD : MonoBehaviour GUI.Label(new Rect(prX, panelY + 40f, 168f, 22f), "joueurs en jeu", aliveLabel); // ── Round timer (top center) ────────────────────────────────────── - if (_timerRunning) + float displayTimer = _timerRunning ? _roundTimer : (_localRaceActive ? _localRaceTimer : -1f); + if (displayTimer >= 0f) { - int mins = Mathf.FloorToInt(_roundTimer / 60f); - int secs = Mathf.FloorToInt(_roundTimer % 60f); + int mins = Mathf.FloorToInt(displayTimer / 60f); + int secs = Mathf.FloorToInt(displayTimer % 60f); var timerStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, @@ -200,7 +213,7 @@ public class GameHUD : MonoBehaviour } // ── Race: checkpoint progress (bottom center) ───────────────────── - if (_gameMode == "race" && _phase == "playing") + if (_gameMode == "race" && (_phase == "playing" || _localRaceActive)) { float bw = 300f; float bx = (Screen.width - bw) / 2f;