Supprime les mini-jeux (survival/teams), corrige tous les bugs identifiés

- Supprime DeathZone.cs, ZoneCapture.cs, ArenaZoneBuilder.cs
- ArenaRoom.js : mode race uniquement, fix _checkRoundEndCondition, fix _getActiveCount
- GameState.js : supprime team (Player) et deathZoneY/teamScoreRed/teamScoreBlue (GameState)
- NetworkSchema.cs : aligne sur le nouveau schéma serveur (supprime team, indices corrigés)
- NetworkManager.cs : supprime OnDeathZoneYChanged/SendDeathZoneHit/SendInZone, OnRoundStart passe totalRounds
- GameManager.cs : subscriptions OnEnable→Start/OnDestroy, fix Lobby (player visible si connecté), HandleRoundStart(totalRounds)
- GameHUD.cs : supprime blocs survival/teams, ajoute SetTotalRounds, supprime dead code
- PlayerController.cs : cache Rigidbody, fix OnCollisionStay gel (supprime else), SetSpawnPosition
- CheckpointSystem.cs : flash le prochain checkpoint actif, supprime FinishFlash vide
- LobbyUI.cs : CancelInvoke sur connexion, appelle SetSpawnPosition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 16:28:37 +02:00
parent 456d876847
commit 4743c307a6
15 changed files with 120 additions and 1195 deletions

View File

@@ -44,6 +44,7 @@ public class PlayerController : MonoBehaviour
public float maxVelocity = 120f; // Velocity cap to prevent infinite acceleration
public float respawnY = -10f; // Y threshold for respawn
private Vector3 _spawnPos = new Vector3(0f, 3f, -30f);
private Rigidbody _rb;
// Squash & stretch
private bool _isSquashing = false;
@@ -80,10 +81,12 @@ public class PlayerController : MonoBehaviour
void Start()
{
Debug.Log("PlayerController script initialized.");
// Cursor lock is handled by LobbyUI on connect/disconnect
_meshTransform = transform; // Will be the sphere itself for squash
_rb = GetComponent<Rigidbody>();
_meshTransform = transform;
}
public void SetSpawnPosition(Vector3 pos) => _spawnPos = pos;
/// <summary>
/// Called by LobbyUI after connecting. Sets up the local player
/// with a floating name label and a 50% color tint.
@@ -208,16 +211,15 @@ public class PlayerController : MonoBehaviour
// --- Respawn if fallen off the map ---
if (transform.position.y < respawnY)
{
Rigidbody rbRespawn = GetComponent<Rigidbody>();
if (rbRespawn != null)
if (_rb != null)
{
rbRespawn.linearVelocity = Vector3.zero;
rbRespawn.angularVelocity = Vector3.zero;
_rb.linearVelocity = Vector3.zero;
_rb.angularVelocity = Vector3.zero;
_rb.useGravity = true;
}
transform.position = _spawnPos;
isOnGelViolet = false;
isOnGelOrange = false;
if (rbRespawn != null) rbRespawn.useGravity = true;
Debug.Log("[Player] Respawned after falling.");
return;
}
@@ -227,7 +229,7 @@ public class PlayerController : MonoBehaviour
_fallWarningAlpha = Mathf.Lerp(_fallWarningAlpha, fallTarget, Time.deltaTime * 5f);
// Mouvement continu selon les directions maintenues
Rigidbody rb = GetComponent<Rigidbody>();
Rigidbody rb = _rb;
if (rb != null)
{
float currentSpeed = MovementSpeed;
@@ -326,42 +328,25 @@ public class PlayerController : MonoBehaviour
void OnCollisionStay(Collision collision)
{
Collider col = collision.collider;
if (col != null && col.sharedMaterial != null)
{
if (col.sharedMaterial.name.Contains("GelOrange"))
{
isOnGelOrange = true;
}
else
{
isOnGelOrange = false;
}
if (col == null || col.sharedMaterial == null) return;
if (col.sharedMaterial.name.Contains("GelViolet"))
if (col.sharedMaterial.name.Contains("GelOrange"))
{
isOnGelOrange = true;
}
if (col.sharedMaterial.name.Contains("GelViolet"))
{
if (!isOnGelViolet)
{
if (!isOnGelViolet)
{
// Premier contact : sauvegarder le drag et augmenter le frein
Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
originalDrag = rb.linearDamping;
rb.linearDamping = 1f; // Fort frein pour éviter le catapultage aux bords
}
}
isOnGelViolet = true;
// Normale instantanée de la surface de contact
Vector3 avgNormal = Vector3.zero;
foreach (ContactPoint contact in collision.contacts)
{
avgNormal += contact.normal;
}
stickyNormal = avgNormal.normalized;
}
else
{
isOnGelViolet = false;
originalDrag = _rb != null ? _rb.linearDamping : 0f;
if (_rb != null) _rb.linearDamping = 1f;
}
isOnGelViolet = true;
Vector3 avgNormal = Vector3.zero;
foreach (ContactPoint contact in collision.contacts)
avgNormal += contact.normal;
stickyNormal = avgNormal.normalized;
}
}
@@ -424,17 +409,9 @@ public class PlayerController : MonoBehaviour
public void PerformJump(float force)
{
Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
// Jump direction: surface normal when on sticky, otherwise up
Vector3 jumpDir = isOnGelViolet ? stickyNormal : Vector3.up;
rb.AddForce(jumpDir * force, ForceMode.Impulse);
}
else
{
Debug.LogWarning("Rigidbody component not found on PlayerController.");
}
if (_rb == null) return;
Vector3 jumpDir = isOnGelViolet ? stickyNormal : Vector3.up;
_rb.AddForce(jumpDir * force, ForceMode.Impulse);
}
private bool IsGrounded()
@@ -546,11 +523,8 @@ public class PlayerController : MonoBehaviour
// Add slight upward component so the ball lifts off the ground
dir = (dir + Vector3.up * 0.3f).normalized;
var rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.AddForce(dir * bumpForce, ForceMode.Impulse);
}
if (_rb != null)
_rb.AddForce(dir * bumpForce, ForceMode.Impulse);
}
void OnCollisionEnter(Collision collision)
@@ -742,10 +716,9 @@ public class PlayerController : MonoBehaviour
// ========================
// SPEED INDICATOR
// ========================
Rigidbody rbHud = GetComponent<Rigidbody>();
if (rbHud != null)
if (_rb != null)
{
float speed = rbHud.linearVelocity.magnitude;
float speed = _rb.linearVelocity.magnitude;
var speedStyle = new GUIStyle(GUI.skin.label)
{
alignment = TextAnchor.MiddleRight,

View File

@@ -1,573 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Builds a compact 80×80 tutorial arena at runtime with connected zones:
///
/// Zone A (South) — Movement basics + jump gaps
/// Zone B (Center-S) — Bounce (GelBleu) training
/// Zone C (West) — Speed (GelOrange) training
/// Zone D (NW) — Moving platforms
/// Zone E (NE) — Sticky walls (GelViolet) training
/// Zone F (North) — Combo challenge (all gels + platforms)
/// Zone G (Center) — Central tower (final challenge)
///
/// Spawn: (0, 2, -30) — south edge
///
/// Ball capabilities reference (from PlayerController):
/// Max jump height ≈ 5.1 units (JumpForce=10, gravity=9.81)
/// Horizontal jump range ≈ 6-8 units at cruising speed
/// GelBleu bounce = perfect elasticity (bounciness 1.0, combine=Max)
/// GelOrange speed = ×3.83 multiplier (~19 u/s)
/// GelViolet sticky = surface-relative movement, jump = surface normal
/// Ball radius = 0.5, min platform 2×2 for playability
///
/// Loads PhysicMaterials from Resources/ folder.
/// Attach to a persistent GameObject (e.g. NetworkManager).
/// </summary>
public class ArenaZoneBuilder : MonoBehaviour
{
private PhysicsMaterial _matGelBleu;
private PhysicsMaterial _matGelOrange;
private PhysicsMaterial _matGelViolet;
private PhysicsMaterial _matBouncy;
private PhysicsMaterial _matNormal;
private Material _baseMat;
private readonly List<MovingPlatform> _movingPlatforms = new List<MovingPlatform>();
// ── Color palette ──
private static readonly Color ColFloor = new Color(0.28f, 0.29f, 0.34f, 1f);
private static readonly Color ColBleu = new Color(0.2f, 0.5f, 1f, 0.85f);
private static readonly Color ColBleuLt = new Color(0.3f, 0.65f, 1f, 0.9f);
private static readonly Color ColOrange = new Color(1f, 0.55f, 0.1f, 0.85f);
private static readonly Color ColOrangeLt = new Color(1f, 0.65f, 0.2f, 0.9f);
private static readonly Color ColViolet = new Color(0.6f, 0.2f, 0.8f, 0.8f);
private static readonly Color ColVioletLt = new Color(0.7f, 0.35f, 0.9f, 0.85f);
private static readonly Color ColNormal = new Color(0.42f, 0.43f, 0.50f, 0.9f);
private static readonly Color ColNormalLt = new Color(0.52f, 0.53f, 0.60f, 0.95f);
private static readonly Color ColDark = new Color(0.22f, 0.22f, 0.28f, 0.95f);
private static readonly Color ColGold = new Color(1f, 0.84f, 0f, 0.95f);
private static readonly Color ColWall = new Color(0.35f, 0.35f, 0.42f, 0.95f);
private static readonly Color ColPath = new Color(0.38f, 0.40f, 0.48f, 0.9f);
private static readonly Color ColSignBleu = new Color(0.15f, 0.4f, 0.9f, 0.95f);
private static readonly Color ColSignOrange= new Color(0.9f, 0.45f, 0.05f, 0.95f);
private static readonly Color ColSignViolet= new Color(0.5f, 0.15f, 0.7f, 0.95f);
private static readonly Color ColSignGrey = new Color(0.5f, 0.5f, 0.55f, 0.95f);
private static readonly Color ColSignGold = new Color(0.9f, 0.75f, 0f, 0.95f);
private static readonly Color ColGuide = new Color(0.5f, 0.5f, 0.6f, 0.5f);
// ── Constants ──
private const float HALF = 40f; // Arena half-size
private const float WALL_H = 12f; // Perimeter wall height
private const float WALL_T = 1f; // Perimeter wall thickness
void Start()
{
LoadMaterials();
if (_baseMat == null) FindBaseMaterial();
BuildFloorAndWalls();
BuildPaths();
BuildZoneA_Movement();
BuildZoneB_Bounce();
BuildZoneC_Speed();
BuildZoneD_MovingPlatforms();
BuildZoneE_StickyWalls();
BuildZoneF_Combo();
BuildZoneG_CentralTower();
Debug.Log("[ArenaZoneBuilder] 80x80 tutorial arena built successfully.");
}
void Update()
{
for (int i = 0; i < _movingPlatforms.Count; i++)
{
var mp = _movingPlatforms[i];
if (mp.go == null) continue;
mp.t += Time.deltaTime * mp.speed;
float ping = Mathf.PingPong(mp.t, 1f);
mp.go.transform.position = Vector3.Lerp(mp.posA, mp.posB, ping);
}
}
void OnDestroy()
{
_movingPlatforms.Clear();
}
// ═══════════════════════════════════════════
// FLOOR, WALLS & PATHS
// ═══════════════════════════════════════════
private void BuildFloorAndWalls()
{
// Main floor
CreateZone("Arena_Floor", new Vector3(0, -0.25f, 0),
new Vector3(HALF * 2f, 0.5f, HALF * 2f), _matNormal, ColFloor);
// Perimeter walls (high to prevent escape)
float h2 = WALL_H / 2f;
float full = HALF * 2f + 2f;
CreateZone("Wall_N", new Vector3(0, h2, HALF), new Vector3(full, WALL_H, WALL_T), _matNormal, ColWall);
CreateZone("Wall_S", new Vector3(0, h2, -HALF), new Vector3(full, WALL_H, WALL_T), _matNormal, ColWall);
CreateZone("Wall_E", new Vector3(HALF, h2, 0), new Vector3(WALL_T, WALL_H, full), _matNormal, ColWall);
CreateZone("Wall_W", new Vector3(-HALF, h2, 0), new Vector3(WALL_T, WALL_H, full), _matNormal, ColWall);
}
/// <summary>Ground-level paths connecting all zones, with directional color markers.</summary>
private void BuildPaths()
{
float pathH = 0.06f;
float pathY = 0.03f;
// Spawn -> Zone A (already at spawn)
// Zone A -> Zone B (south to center-south)
CreateZone("Path_A_B", new Vector3(0, pathY, -22f), new Vector3(3f, pathH, 10f), _matNormal, ColPath);
// Zone B -> Zone G center
CreateZone("Path_B_G", new Vector3(0, pathY, -8f), new Vector3(3f, pathH, 10f), _matNormal, ColPath);
// Zone G -> Zone C (center to west)
CreateZone("Path_G_C", new Vector3(-10f, pathY, 0f), new Vector3(12f, pathH, 3f), _matNormal, ColPath);
// Zone C -> Zone D (west to northwest)
CreateZone("Path_C_D", new Vector3(-28f, pathY, 12f), new Vector3(3f, pathH, 16f), _matNormal, ColPath);
// Zone G -> Zone E (center to northeast)
CreateZone("Path_G_E", new Vector3(12f, pathY, 10f), new Vector3(16f, pathH, 3f), _matNormal, ColPath);
// Zone D/E -> Zone F (north)
CreateZone("Path_D_F", new Vector3(-12f, pathY, 28f), new Vector3(16f, pathH, 3f), _matNormal, ColPath);
CreateZone("Path_E_F", new Vector3(12f, pathY, 28f), new Vector3(16f, pathH, 3f), _matNormal, ColPath);
}
// ═══════════════════════════════════════════
// ZONE A — MOVEMENT BASICS + JUMP GAPS (South)
// Origin: (0, 0, -32)
// ═══════════════════════════════════════════
private void BuildZoneA_Movement()
{
// Sign
CreateSign("Sign_A", new Vector3(-4f, 1.5f, -35f), ColSignGrey);
// Spawn platform (raised)
CreateZone("A_Spawn", new Vector3(0, 0.4f, -32f), new Vector3(8f, 0.8f, 6f), _matNormal, ColNormalLt);
// Straight corridor with slight turns to learn rolling
CreateZone("A_Corr1", new Vector3(0, 0.15f, -27f), new Vector3(4f, 0.3f, 5f), _matNormal, ColNormal);
CreateZone("A_Corr2", new Vector3(3f, 0.15f, -23f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormal);
CreateZone("A_Corr3", new Vector3(0f, 0.15f, -19.5f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormal);
// Low guide walls for the corridor
CreateZone("A_GuideL1", new Vector3(-2.5f, 0.6f, -27f), new Vector3(0.3f, 0.9f, 5f), _matNormal, ColGuide);
CreateZone("A_GuideR1", new Vector3(2.5f, 0.6f, -27f), new Vector3(0.3f, 0.9f, 5f), _matNormal, ColGuide);
// Jump gaps: 3 gaps of increasing difficulty
// Gap 1: 2 units gap
CreateZone("A_Plat1", new Vector3(-3f, 0.15f, -16f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormalLt);
// (gap of 2 units)
CreateZone("A_Plat2", new Vector3(-3f, 0.15f, -10f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormalLt);
// Gap 2: 3 units gap
// (gap of 3 units)
CreateZone("A_Plat3", new Vector3(-3f, 0.15f, -3f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormalLt);
// Gap 3: 4.5 units gap (needs charged jump)
// (gap of 4.5 units)
CreateZone("A_Plat4", new Vector3(-3f, 0.15f, 5f), new Vector3(4f, 0.3f, 4f), _matNormal, ColNormalLt);
// Return ramp to ground
CreateRamp("A_Ramp", new Vector3(-3f, 1.0f, 9f), new Vector3(4f, 0.3f, 5f),
-12f, Vector3.right, _matNormal, ColNormal);
Debug.Log("[ArenaZoneBuilder] Zone A (Movement) built.");
}
// ═══════════════════════════════════════════
// ZONE B — BOUNCE TRAINING (Center-South)
// Origin: (10, 0, -15)
// ═══════════════════════════════════════════
private void BuildZoneB_Bounce()
{
Vector3 o = new Vector3(10f, 0f, -15f);
// Sign
CreateSign("Sign_B", o + new Vector3(-3f, 1.5f, -3f), ColSignBleu);
// Entry pad
CreateZone("B_Entry", o + new Vector3(0, 0.15f, 0), new Vector3(5f, 0.3f, 5f), _matNormal, ColNormalLt);
// Bounce pads with targets at increasing heights
// Pad 1 (small) -> target at 3m
CreateZone("B_Pad1", o + new Vector3(0, 0.2f, 5f), new Vector3(3f, 0.25f, 3f), _matGelBleu, ColBleu);
CreateZone("B_Tgt1", o + new Vector3(0, 3f, 9f), new Vector3(4f, 0.4f, 4f), _matNormal, ColNormalLt);
// Pad 2 (medium) -> target at 4.5m
CreateZone("B_Pad2", o + new Vector3(0, 3.2f, 9f), new Vector3(3.5f, 0.25f, 3.5f), _matGelBleu, ColBleuLt);
CreateZone("B_Tgt2", o + new Vector3(4f, 5.5f, 12f), new Vector3(4f, 0.4f, 4f), _matNormal, ColNormalLt);
// Pad 3 on target 2 -> top at 8m
CreateZone("B_Pad3", o + new Vector3(4f, 5.7f, 12f), new Vector3(3f, 0.2f, 3f), _matGelBleu, ColBleu);
CreateZone("B_Tgt3", o + new Vector3(0, 8f, 15f), new Vector3(5f, 0.4f, 5f), _matNormal, ColNormalLt);
// Bounce staircase: fall from 8m -> bounce up further
CreateZone("B_Stair_Bnc", o + new Vector3(-5f, 0.2f, 15f), new Vector3(4f, 0.25f, 4f), _matGelBleu, ColBleu);
CreateZone("B_Stair_Mid", o + new Vector3(-5f, 5f, 19f), new Vector3(4f, 0.4f, 4f), _matNormal, ColNormalLt);
CreateZone("B_Stair_Top", o + new Vector3(-5f, 8.5f, 23f), new Vector3(5f, 0.4f, 4f), _matNormal, ColNormalLt);
// Return to ground via gentle ramp
CreateRamp("B_Return", o + new Vector3(-5f, 4f, 27f), new Vector3(4f, 0.3f, 8f),
-20f, Vector3.right, _matNormal, ColNormal);
Debug.Log("[ArenaZoneBuilder] Zone B (Bounce) built.");
}
// ═══════════════════════════════════════════
// ZONE C — SPEED TRAINING (West)
// Origin: (-25, 0, -5)
// ═══════════════════════════════════════════
private void BuildZoneC_Speed()
{
Vector3 o = new Vector3(-25f, 0f, -5f);
// Sign
CreateSign("Sign_C", o + new Vector3(8f, 1.5f, -2f), ColSignOrange);
// Entry
CreateZone("C_Entry", o + new Vector3(0, 0.15f, 0), new Vector3(5f, 0.3f, 5f), _matNormal, ColNormalLt);
// Speed strip 1 -> short, straight, with ramp launch
CreateZone("C_Strip1", o + new Vector3(0, 0.12f, 5f), new Vector3(4f, 0.15f, 8f), _matGelOrange, ColOrange);
// Guide walls
CreateZone("C_GuideL1", new Vector3(o.x - 2.5f, 0.5f, o.z + 5f), new Vector3(0.3f, 1f, 8f), _matNormal, ColGuide);
CreateZone("C_GuideR1", new Vector3(o.x + 2.5f, 0.5f, o.z + 5f), new Vector3(0.3f, 1f, 8f), _matNormal, ColGuide);
// Ramp at end of strip 1
CreateRamp("C_Ramp1", o + new Vector3(0, 0.8f, 10.5f), new Vector3(4f, 0.25f, 4f),
20f, Vector3.right, _matGelOrange, ColOrangeLt);
// Landing platform (12m ahead, 2m up)
CreateZone("C_Land1", o + new Vector3(0, 2f, 18f), new Vector3(5f, 0.4f, 5f), _matNormal, ColNormalLt);
// Speed strip 2 -> longer with a curve
CreateZone("C_Strip2a", o + new Vector3(0, 2.1f, 22f), new Vector3(4f, 0.15f, 5f), _matGelOrange, ColOrange);
CreateZone("C_Strip2b", o + new Vector3(4f, 2.1f, 26f), new Vector3(5f, 0.15f, 4f), _matGelOrange, ColOrangeLt);
CreateZone("C_Strip2c", o + new Vector3(8f, 2.1f, 30f), new Vector3(4f, 0.15f, 5f), _matGelOrange, ColOrange);
// Guide walls for curve
CreateZone("C_GuideO", o + new Vector3(-2.5f, 2.6f, 22f), new Vector3(0.3f, 1f, 5f), _matNormal, ColGuide);
CreateZone("C_GuideI", o + new Vector3(10.5f, 2.6f, 30f), new Vector3(0.3f, 1f, 5f), _matNormal, ColGuide);
// Final landing with another ramp
CreateRamp("C_Ramp2", o + new Vector3(8f, 3f, 34f), new Vector3(4f, 0.25f, 4f),
25f, Vector3.right, _matGelOrange, ColOrangeLt);
CreateZone("C_Land2", o + new Vector3(8f, 4.5f, 38f), new Vector3(6f, 0.4f, 5f), _matNormal, ColNormalLt);
Debug.Log("[ArenaZoneBuilder] Zone C (Speed) built.");
}
// ═══════════════════════════════════════════
// ZONE D — MOVING PLATFORMS (Northwest)
// Origin: (-28, 0, 18)
// ═══════════════════════════════════════════
private void BuildZoneD_MovingPlatforms()
{
Vector3 o = new Vector3(-28f, 0f, 18f);
// Sign
CreateSign("Sign_D", o + new Vector3(4f, 1.5f, -2f), ColSignGrey);
// Entry platform
CreateZone("D_Entry", o + new Vector3(0, 0.2f, 0), new Vector3(6f, 0.4f, 6f), _matNormal, ColNormalLt);
// 3 horizontal sliders (oscillate along X, spaced along Z, rising)
for (int i = 0; i < 3; i++)
{
Vector3 a = o + new Vector3(-2f, 1.5f + i * 2f, 5f + i * 6f);
Vector3 b = a + new Vector3(8f, 0f, 0f);
var go = CreateZone("D_Slide" + i, a, new Vector3(4f, 0.5f, 4f), _matNormal, ColDark);
AddMovingPlatform(go, a, b, 0.3f + i * 0.1f);
}
// Mid-platform
CreateZone("D_Mid", o + new Vector3(2f, 7f, 20f), new Vector3(5f, 0.4f, 5f), _matNormal, ColNormalLt);
// 2 vertical lifts
for (int i = 0; i < 2; i++)
{
Vector3 a = o + new Vector3(-4f + i * 8f, 7.5f, 24f + i * 5f);
Vector3 b = a + new Vector3(0f, 5f, 0f);
var go = CreateZone("D_Lift" + i, a, new Vector3(4f, 0.5f, 4f), _matNormal, ColDark);
AddMovingPlatform(go, a, b, 0.22f + i * 0.1f);
}
// End platform (high, with view)
CreateZone("D_End", o + new Vector3(2f, 12f, 32f), new Vector3(6f, 0.4f, 6f), _matNormal, ColNormalLt);
// Bouncy descent pad at ground level
CreateZone("D_Desc", o + new Vector3(2f, 0.2f, 32f), new Vector3(4f, 0.25f, 4f), _matGelBleu, ColBleu);
Debug.Log("[ArenaZoneBuilder] Zone D (Moving Platforms) built.");
}
// ═══════════════════════════════════════════
// ZONE E — STICKY WALLS (Northeast)
// Origin: (24, 0, 15)
// ═══════════════════════════════════════════
private void BuildZoneE_StickyWalls()
{
Vector3 o = new Vector3(24f, 0f, 15f);
// Sign
CreateSign("Sign_E", o + new Vector3(-5f, 1.5f, -2f), ColSignViolet);
// Entry pad
CreateZone("E_Entry", o + new Vector3(0, 0.15f, 0), new Vector3(5f, 0.3f, 5f), _matNormal, ColNormalLt);
// === Intro wall: simple vertical climb (4m high) ===
// Wall face pointing -X (ball approaches from the left)
CreateZone("E_Wall1", o + new Vector3(3f, 3f, 3f), new Vector3(0.5f, 6f, 5f), _matGelViolet, ColViolet);
// Platform at top of wall
CreateZone("E_Top1", o + new Vector3(3f, 6.2f, 3f), new Vector3(4f, 0.4f, 5f), _matNormal, ColNormalLt);
// === L-shaped sticky: wall -> turn -> wall ===
// Vertical wall going up (face -Z)
CreateZone("E_Wall2a", o + new Vector3(0, 3.5f, 8f), new Vector3(4f, 7f, 0.5f), _matGelViolet, ColViolet);
// Ceiling connecting to second wall (face down, -Y)
CreateZone("E_Ceil", o + new Vector3(0, 7.1f, 10f), new Vector3(4f, 0.5f, 4f), _matGelViolet, ColVioletLt);
// Second wall going down (face +Z)
CreateZone("E_Wall2b", o + new Vector3(0, 3.5f, 12f), new Vector3(4f, 7f, 0.5f), _matGelViolet, ColViolet);
// Landing after L traverse
CreateZone("E_Land2", o + new Vector3(0, 0.2f, 14f), new Vector3(5f, 0.4f, 4f), _matNormal, ColNormalLt);
// === Vertical tunnel: two sticky walls face-to-face, zigzag up ===
// Left wall
CreateZone("E_Tun_L", o + new Vector3(-3.5f, 6f, 18f), new Vector3(0.5f, 12f, 4f), _matGelViolet, ColViolet);
// Right wall
CreateZone("E_Tun_R", o + new Vector3(3.5f, 6f, 18f), new Vector3(0.5f, 12f, 4f), _matGelViolet, ColVioletLt);
// Small ledges alternating sides to help zigzag
for (int i = 0; i < 4; i++)
{
float side = (i % 2 == 0) ? -2.5f : 2.5f;
float h = 2f + i * 2.8f;
CreateZone("E_Tun_Ledge" + i, o + new Vector3(side, h, 18f),
new Vector3(2f, 0.3f, 3f), _matNormal, ColNormalLt);
}
// Top of tunnel
CreateZone("E_TunTop", o + new Vector3(0, 12.2f, 18f), new Vector3(5f, 0.4f, 5f), _matNormal, ColNormalLt);
Debug.Log("[ArenaZoneBuilder] Zone E (Sticky Walls) built.");
}
// ═══════════════════════════════════════════
// ZONE F — COMBO CHALLENGE (North)
// Origin: (0, 0, 30)
// ═══════════════════════════════════════════
private void BuildZoneF_Combo()
{
Vector3 o = new Vector3(0f, 0f, 30f);
// Sign
CreateSign("Sign_F", o + new Vector3(-5f, 1.5f, -4f), ColSignGold);
// Entry
CreateZone("F_Entry", o + new Vector3(0, 0.15f, 0), new Vector3(5f, 0.3f, 5f), _matNormal, ColNormalLt);
// Step 1: Speed strip launch
CreateZone("F_Speed", o + new Vector3(0, 0.12f, 4f), new Vector3(3f, 0.15f, 6f), _matGelOrange, ColOrange);
CreateZone("F_GuidL", o + new Vector3(-2f, 0.5f, 4f), new Vector3(0.3f, 0.7f, 6f), _matNormal, ColGuide);
CreateZone("F_GuidR", o + new Vector3(2f, 0.5f, 4f), new Vector3(0.3f, 0.7f, 6f), _matNormal, ColGuide);
// Step 2: Ramp -> bounce pad
CreateRamp("F_Ramp", o + new Vector3(0, 0.6f, 8.5f), new Vector3(3f, 0.25f, 3f),
22f, Vector3.right, _matGelOrange, ColOrangeLt);
CreateZone("F_BncPad", o + new Vector3(0, 0.2f, 13f), new Vector3(4f, 0.25f, 4f), _matGelBleu, ColBleu);
// Step 3: High platform with sticky wall leading higher
CreateZone("F_MidPlat", o + new Vector3(0, 4.5f, 17f), new Vector3(5f, 0.4f, 4f), _matNormal, ColNormalLt);
CreateZone("F_StickyWall", o + new Vector3(-3f, 7f, 17f), new Vector3(0.5f, 5f, 4f), _matGelViolet, ColViolet);
// Step 4: Moving platform to final
Vector3 mpA = o + new Vector3(0, 9.5f, 17f);
Vector3 mpB = o + new Vector3(0, 9.5f, 23f);
var mp = CreateZone("F_MovPlat", mpA, new Vector3(4f, 0.5f, 4f), _matNormal, ColDark);
AddMovingPlatform(mp, mpA, mpB, 0.25f);
// Step 5: Gold finish platform
CreateZone("F_Finish", o + new Vector3(0, 10f, 27f), new Vector3(6f, 0.5f, 5f), _matNormal, ColGold);
CreateZone("F_FinBnc", o + new Vector3(0, 10.3f, 27f), new Vector3(3f, 0.2f, 3f), _matGelBleu, ColBleuLt);
Debug.Log("[ArenaZoneBuilder] Zone F (Combo) built.");
}
// ═══════════════════════════════════════════
// ZONE G — CENTRAL TOWER (Center)
// Origin: (0, 0, 5)
// ═══════════════════════════════════════════
private void BuildZoneG_CentralTower()
{
Vector3 o = new Vector3(0f, 0f, 5f);
// Base (accessible from ground)
CreateZone("G_Base", o + new Vector3(0, 0.25f, 0), new Vector3(10f, 0.5f, 10f), _matNormal, ColNormal);
// Access ramp from south
CreateRamp("G_Ramp", o + new Vector3(0, 0.8f, -6f), new Vector3(4f, 0.3f, 5f),
-10f, Vector3.right, _matNormal, ColNormal);
// Level 1: Bounce pad -> L2
CreateZone("G_L1_Bnc", o + new Vector3(0, 0.5f, 0), new Vector3(3.5f, 0.25f, 3.5f), _matGelBleu, ColBleu);
// Level 2 (3.5m): platform + speed strip
CreateZone("G_L2", o + new Vector3(0, 3.5f, 0), new Vector3(8f, 0.4f, 8f), _matNormal, ColNormalLt);
CreateZone("G_L2_Spd", o + new Vector3(0, 3.7f, 0), new Vector3(6f, 0.15f, 2f), _matGelOrange, ColOrange);
CreateRamp("G_L2_Ramp", o + new Vector3(3f, 4.5f, 3f), new Vector3(3f, 0.25f, 3f),
25f, Vector3.right, _matGelOrange, ColOrangeLt);
// Level 3 (7m): platform + moving platform to L4
CreateZone("G_L3", o + new Vector3(0, 7f, 0), new Vector3(7f, 0.4f, 7f), _matNormal, ColNormalLt);
CreateZone("G_L3_Bnc", o + new Vector3(2f, 7.25f, 2f), new Vector3(3f, 0.2f, 3f), _matGelBleu, ColBleuLt);
// Moving platform L3->L4
Vector3 mp3a = o + new Vector3(5f, 8f, 0);
Vector3 mp3b = o + new Vector3(0, 8f, 5f);
var m3 = CreateZone("G_MP3", mp3a, new Vector3(3.5f, 0.5f, 3.5f), _matNormal, ColDark);
AddMovingPlatform(m3, mp3a, mp3b, 0.2f);
// Level 4 (10.5m): platform + sticky wall to L5
CreateZone("G_L4", o + new Vector3(0, 10.5f, 0), new Vector3(7f, 0.4f, 7f), _matNormal, ColNormalLt);
CreateZone("G_L4_Sticky", o + new Vector3(-3.8f, 13f, 0), new Vector3(0.5f, 5f, 5f), _matGelViolet, ColViolet);
// Level 5 — SUMMIT (15.5m): gold platform
CreateZone("G_L5", o + new Vector3(0, 15.5f, 0), new Vector3(8f, 0.5f, 8f), _matNormal, ColGold);
CreateZone("G_L5_Bnc", o + new Vector3(0, 15.85f, 0), new Vector3(4f, 0.25f, 4f), _matGelBleu, ColBleuLt);
// Orbital moving platform for alternative L3->L4 access
Vector3 orb_a = o + new Vector3(-6f, 9f, 0);
Vector3 orb_b = o + new Vector3(0, 9f, -6f);
var orb = CreateZone("G_Orb", orb_a, new Vector3(3.5f, 0.5f, 3.5f), _matNormal, ColDark);
AddMovingPlatform(orb, orb_a, orb_b, 0.18f);
Debug.Log("[ArenaZoneBuilder] Zone G (Central Tower) built.");
}
// ═══════════════════════════════════════════
// SIGN HELPER (zone entrance markers)
// ═══════════════════════════════════════════
private void CreateSign(string name, Vector3 position, Color color)
{
// Tall thin panel as zone marker
CreateZone(name, position, new Vector3(0.3f, 2.5f, 0.3f), _matNormal, color);
// Colored cap on top
CreateZone(name + "_Cap", position + new Vector3(0, 1.45f, 0), new Vector3(0.8f, 0.4f, 0.8f), _matNormal, color);
}
// ═══════════════════════════════════════════
// MATERIAL LOADING
// ═══════════════════════════════════════════
private void LoadMaterials()
{
_matGelBleu = Resources.Load<PhysicsMaterial>("GelBleu");
_matGelOrange = Resources.Load<PhysicsMaterial>("GelOrange");
_matGelViolet = Resources.Load<PhysicsMaterial>("GelViolet");
_matBouncy = Resources.Load<PhysicsMaterial>("Bouncy");
_matNormal = Resources.Load<PhysicsMaterial>("Normal");
if (_matGelBleu == null) Debug.LogWarning("[ArenaZoneBuilder] GelBleu not found in Resources!");
if (_matGelOrange == null) Debug.LogWarning("[ArenaZoneBuilder] GelOrange not found in Resources!");
if (_matGelViolet == null) Debug.LogWarning("[ArenaZoneBuilder] GelViolet not found in Resources!");
if (_matBouncy == null) Debug.LogWarning("[ArenaZoneBuilder] Bouncy not found in Resources!");
if (_matNormal == null) Debug.LogWarning("[ArenaZoneBuilder] Normal not found in Resources!");
}
private void FindBaseMaterial()
{
var shader = Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard");
_baseMat = new Material(shader);
}
// ═══════════════════════════════════════════
// ZONE HELPERS
// ═══════════════════════════════════════════
private GameObject CreateZone(string name, Vector3 position, Vector3 size,
PhysicsMaterial physMat, Color color)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
go.name = name;
go.transform.position = position;
go.transform.localScale = size;
go.isStatic = true;
var col = go.GetComponent<Collider>();
if (col != null && physMat != null) col.material = physMat;
var rend = go.GetComponent<Renderer>();
if (rend != null)
{
var mat = new Material(_baseMat);
SetMatColor(mat, color);
if (color.a < 1f) SetMatTransparent(mat, color);
rend.material = mat;
}
return go;
}
private GameObject CreateRamp(string name, Vector3 position, Vector3 size,
float angle, Vector3 axis, PhysicsMaterial physMat, Color color)
{
var go = CreateZone(name, position, size, physMat, color);
go.transform.rotation = Quaternion.AngleAxis(angle, axis);
return go;
}
private void AddMovingPlatform(GameObject go, Vector3 posA, Vector3 posB, float speed)
{
go.isStatic = false;
_movingPlatforms.Add(new MovingPlatform { go = go, posA = posA, posB = posB, speed = speed });
}
// ═══════════════════════════════════════════
// MATERIAL HELPERS
// ═══════════════════════════════════════════
private static void SetMatColor(Material mat, Color color)
{
if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", color);
if (mat.HasProperty("_Color")) mat.color = color;
}
private static void SetMatTransparent(Material mat, Color color)
{
if (mat.HasProperty("_Surface"))
{
mat.SetFloat("_Surface", 1);
mat.SetFloat("_Blend", 0);
}
mat.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
mat.DisableKeyword("_SURFACE_TYPE_OPAQUE");
mat.EnableKeyword("_ALPHAPREMULTIPLY_ON");
mat.renderQueue = 3000;
if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", color);
if (mat.HasProperty("_Color")) mat.color = color;
}
// ═══════════════════════════════════════════
// MOVING PLATFORM DATA
// ═══════════════════════════════════════════
private class MovingPlatform
{
public GameObject go;
public Vector3 posA;
public Vector3 posB;
public float speed;
public float t;
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 31c6f8ef706b51448b461b2b027e2ea8

View File

@@ -27,32 +27,32 @@ public class GameManager : MonoBehaviour
DontDestroyOnLoad(gameObject);
}
void OnEnable()
void Start()
{
var nm = NetworkManager.Instance;
if (nm == null) return;
nm.OnPhaseChanged += HandlePhaseChanged;
nm.OnPhaseChanged += HandlePhaseChanged;
nm.OnCountdownChanged += HandleCountdownChanged;
nm.OnEliminated += HandleEliminated;
nm.OnQualified += HandleQualified;
nm.OnRoundStart += HandleRoundStart;
nm.OnRoundEnd += HandleRoundEnd;
nm.OnGameEnd += HandleGameEnd;
nm.OnDisconnected += HandleDisconnected;
nm.OnEliminated += HandleEliminated;
nm.OnQualified += HandleQualified;
nm.OnRoundStart += HandleRoundStart;
nm.OnRoundEnd += HandleRoundEnd;
nm.OnGameEnd += HandleGameEnd;
nm.OnDisconnected += HandleDisconnected;
}
void OnDisable()
void OnDestroy()
{
var nm = NetworkManager.Instance;
if (nm == null) return;
nm.OnPhaseChanged -= HandlePhaseChanged;
nm.OnPhaseChanged -= HandlePhaseChanged;
nm.OnCountdownChanged -= HandleCountdownChanged;
nm.OnEliminated -= HandleEliminated;
nm.OnQualified -= HandleQualified;
nm.OnRoundStart -= HandleRoundStart;
nm.OnRoundEnd -= HandleRoundEnd;
nm.OnGameEnd -= HandleGameEnd;
nm.OnDisconnected -= HandleDisconnected;
nm.OnEliminated -= HandleEliminated;
nm.OnQualified -= HandleQualified;
nm.OnRoundStart -= HandleRoundStart;
nm.OnRoundEnd -= HandleRoundEnd;
nm.OnGameEnd -= HandleGameEnd;
nm.OnDisconnected -= HandleDisconnected;
}
// ─── Event Handlers ───────────────────────────────────────────────────
@@ -106,11 +106,12 @@ public class GameManager : MonoBehaviour
}
}
void HandleRoundStart(int round, string mode)
void HandleRoundStart(int round, string mode, int totalRounds)
{
CurrentRound = round;
CurrentMode = mode;
gameHUD?.SetRoundInfo(round, mode);
gameHUD?.SetTotalRounds(totalRounds);
IsLocalEliminated = false;
}
@@ -140,7 +141,7 @@ public class GameManager : MonoBehaviour
switch (phase)
{
case GamePhase.Lobby:
SetPlayerActive(false);
SetPlayerActive(NetworkManager.Instance?.IsConnected ?? false);
SetSpectatorActive(false);
gameHUD?.SetPhase("lobby");
break;

View File

@@ -88,6 +88,7 @@ public class LobbyUI : MonoBehaviour
_lobbyActive = false;
_isConnecting = false;
_statusMessage = "";
CancelInvoke(nameof(CheckConnectionTimeout));
// --- Activate the player hierarchy ---
if (playerRoot != null)
@@ -113,6 +114,7 @@ public class LobbyUI : MonoBehaviour
rb.position = spawnPos;
}
pc.transform.position = spawnPos;
pc.SetSpawnPosition(spawnPos);
Debug.Log($"[Lobby] Player teleported to spawn: {spawnPos}");
}
pc.enabled = true;

View File

@@ -44,10 +44,9 @@ public class NetworkManager : MonoBehaviour
public event Action<float> OnCountdownChanged; // seconds remaining
public event Action<string, string> OnEliminated; // sessionId, reason
public event Action<string> OnQualified; // sessionId
public event Action<int, string> OnRoundStart; // roundNumber, mode
public event Action<int, string, int> OnRoundStart; // roundNumber, mode, totalRounds
public event Action<int> OnRoundEnd; // roundNumber
public event Action<string> OnGameEnd; // winnerName
public event Action<float> OnDeathZoneYChanged; // for survival mode
// --- Internals ---
private Client _client;
@@ -145,7 +144,6 @@ public class NetworkManager : MonoBehaviour
// Game state changes
_callbacks.Listen(state => state.phase, (newValue, prevValue) => _OnPhaseChanged(newValue));
_callbacks.Listen(state => state.countdown, (newValue, prevValue) => OnCountdownChanged?.Invoke(newValue));
_callbacks.Listen(state => state.deathZoneY, (newValue, prevValue) => OnDeathZoneYChanged?.Invoke(newValue));
// Server messages
_room.OnMessage<EliminatedMsg>("eliminated", msg =>
@@ -161,7 +159,7 @@ public class NetworkManager : MonoBehaviour
_room.OnMessage<RoundStartMsg>("roundStart", msg =>
{
Debug.Log($"[Network] Round {msg.round} started ({msg.mode})");
OnRoundStart?.Invoke(msg.round, msg.mode);
OnRoundStart?.Invoke(msg.round, msg.mode, msg.totalRounds);
});
_room.OnMessage<RoundEndMsg>("roundEnd", msg =>
{
@@ -208,18 +206,6 @@ public class NetworkManager : MonoBehaviour
await _room.Send("checkpointReached", new { index });
}
public async void SendDeathZoneHit()
{
if (_room != null && IsConnected)
await _room.Send("deathZoneHit", null);
}
public async void SendInZone(bool inZone)
{
if (_room != null && IsConnected)
await _room.Send("inZone", new { inZone });
}
// ─── State Callbacks ─────────────────────────────────────────────────
private void _OnPhaseChanged(string phase)
@@ -285,11 +271,6 @@ public class NetworkManager : MonoBehaviour
new Vector3(player.avx, player.avy, player.avz)
);
// Sync team color changes (for teams mode)
controller.UpdateTeamColor(player.team,
new Color(player.colorR, player.colorG, player.colorB));
// Hide/show eliminated remote players
controller.SetVisible(!player.isEliminated);
}
}

View File

@@ -25,8 +25,7 @@ public partial class NetworkPlayer : Schema
[Type(18, "boolean")] public bool isEliminated = false;
[Type(19, "boolean")] public bool isQualified = false;
[Type(20, "boolean")] public bool isReady = false;
[Type(21, "int8")] public int team = 0;
[Type(22, "int8")] public int checkpointIndex = 0;
[Type(21, "int8")] public int checkpointIndex = 0;
}
public partial class NetworkState : Schema
@@ -40,8 +39,5 @@ public partial class NetworkState : Schema
[Type(4, "int8")] public int totalRounds = 4;
[Type(5, "int8")] public int playersAlive = 0;
[Type(6, "string")] public string gameMode = "race";
[Type(7, "float32")] public float deathZoneY = -100;
[Type(8, "int16")] public int teamScoreRed = 0;
[Type(9, "int16")] public int teamScoreBlue = 0;
[Type(10, "string")] public string winnerName = "";
[Type(7, "string")] public string winnerName = "";
}

View File

@@ -80,11 +80,10 @@ public class CheckpointSystem : MonoBehaviour
{
_finished = true;
Debug.Log("[Checkpoint] FINISH LINE reached!");
StartCoroutine(FinishFlash());
}
else
{
StartCoroutine(FlashCheckpoint(index));
StartCoroutine(FlashCheckpoint(_localCheckpointIndex));
}
}
@@ -146,17 +145,6 @@ public class CheckpointSystem : MonoBehaviour
}
}
private IEnumerator FinishFlash()
{
float t = 0f;
while (t < 2f)
{
t += Time.deltaTime;
yield return null;
}
// Finish confirmed via network — GameManager/EliminationOverlay handles the "Qualifié!" overlay
}
private static void SetRendererColor(Renderer rend, Color c)
{
if (rend.material.HasProperty("_BaseColor")) rend.material.SetColor("_BaseColor", c);

View File

@@ -1,93 +0,0 @@
using UnityEngine;
/// <summary>
/// Survival mode: a rising death zone that climbs from below.
/// The server is authoritative on the Y position (broadcast via NetworkState.deathZoneY).
/// This component moves the visual/collider locally, and detects local player contact.
///
/// Setup: Create a large Plane or Cube GameObject, attach this script,
/// add a Box/Mesh Collider set to "Is Trigger".
/// The object will be positioned at deathZoneY each frame.
/// </summary>
public class DeathZone : MonoBehaviour
{
[Header("Visual")]
[Tooltip("Half-size of the death zone plane (X and Z)")]
public float halfExtent = 200f;
[Tooltip("Thickness of the zone collider")]
public float thickness = 2f;
[Header("Warning")]
[Tooltip("Distance above death zone where the red tint starts")]
public float warningDistance = 8f;
private bool _hitSent = false;
private float _targetY = -100f;
void Start()
{
// Scale the collider to cover the arena
transform.localScale = new Vector3(halfExtent * 2f, thickness, halfExtent * 2f);
// Subscribe to death zone Y changes from server
if (NetworkManager.Instance != null)
{
NetworkManager.Instance.OnDeathZoneYChanged += OnDeathZoneYChanged;
NetworkManager.Instance.OnPhaseChanged += OnPhaseChanged;
}
}
void OnDestroy()
{
if (NetworkManager.Instance != null)
{
NetworkManager.Instance.OnDeathZoneYChanged -= OnDeathZoneYChanged;
NetworkManager.Instance.OnPhaseChanged -= OnPhaseChanged;
}
}
void OnDeathZoneYChanged(float y)
{
_targetY = y;
}
void OnPhaseChanged(string phase)
{
if (phase == "playing" || phase == "lobby")
{
_hitSent = false; // reset for new round
}
}
void Update()
{
// Smooth follow of the server Y value
Vector3 pos = transform.position;
pos.y = Mathf.Lerp(pos.y, _targetY, Time.deltaTime * 3f);
transform.position = pos;
// Update warning tint based on local player proximity
var nm = NetworkManager.Instance;
if (nm != null && nm.IsConnected && GameHUD.Instance != null)
{
var localState = nm.GetLocalPlayerState();
if (localState != null)
{
float dist = localState.y - pos.y;
float intensity = Mathf.Clamp01(1f - dist / warningDistance);
GameHUD.Instance.SetDeathZoneWarning(intensity);
}
}
}
void OnTriggerEnter(Collider other)
{
if (_hitSent) return;
if (other.GetComponent<PlayerController>() == null) return;
_hitSent = true;
Debug.Log("[DeathZone] Local player hit the death zone!");
NetworkManager.Instance?.SendDeathZoneHit();
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 99fdfaa3e87a64d4e958f81014e6cdab

View File

@@ -1,125 +0,0 @@
using UnityEngine;
/// <summary>
/// Teams mode: a central capture zone. The local player sends "inZone" messages
/// to the server while inside the zone. The server tallies scores.
/// Visualizes zone control by tinting the zone's renderer.
///
/// Setup: Create a flat Box/Plane in the center of the arena.
/// Add a BoxCollider set to "Is Trigger". Attach this script.
/// </summary>
public class ZoneCapture : MonoBehaviour
{
public static ZoneCapture Instance { get; private set; }
[Header("Visual")]
[Tooltip("Neutral zone color")]
public Color neutralColor = new Color(0.5f, 0.5f, 0.6f, 0.5f);
[Tooltip("Red team controls color")]
public Color redColor = new Color(1f, 0.2f, 0.2f, 0.6f);
[Tooltip("Blue team controls color")]
public Color blueColor = new Color(0.2f, 0.5f, 1f, 0.6f);
[Header("Score Reporting")]
[Tooltip("How often (seconds) to send inZone=true to server while inside")]
public float reportInterval = 0.5f;
private bool _isLocalPlayerInZone = false;
private float _reportTimer = 0f;
private Renderer _renderer;
// Zone occupant counts (received via server state — approximated by remote player positions)
private int _redInZone = 0;
private int _blueInZone = 0;
void Awake()
{
Instance = this;
_renderer = GetComponent<Renderer>();
SetZoneColor(neutralColor);
}
void Start()
{
if (NetworkManager.Instance != null)
{
NetworkManager.Instance.OnPhaseChanged += OnPhaseChanged;
}
}
void OnDestroy()
{
if (NetworkManager.Instance != null)
NetworkManager.Instance.OnPhaseChanged -= OnPhaseChanged;
}
void OnPhaseChanged(string phase)
{
if (phase == "lobby" || phase == "roundEnd")
{
_isLocalPlayerInZone = false;
_redInZone = 0;
_blueInZone = 0;
SetZoneColor(neutralColor);
}
}
void Update()
{
if (!_isLocalPlayerInZone) return;
_reportTimer += Time.deltaTime;
if (_reportTimer >= reportInterval)
{
_reportTimer = 0f;
NetworkManager.Instance?.SendInZone(true);
}
// Update zone tint based on dominance
UpdateZoneColor();
}
void OnTriggerEnter(Collider other)
{
if (other.GetComponent<PlayerController>() != null)
{
_isLocalPlayerInZone = true;
_reportTimer = 0f;
NetworkManager.Instance?.SendInZone(true);
}
// Count remote players in zone
var remote = other.GetComponent<RemotePlayerController>();
if (remote != null)
{
// team info would need to be tracked — skip for now, server handles scoring
}
}
void OnTriggerExit(Collider other)
{
if (other.GetComponent<PlayerController>() != null)
{
_isLocalPlayerInZone = false;
NetworkManager.Instance?.SendInZone(false);
}
}
private void UpdateZoneColor()
{
// Read team scores from NetworkManager state for visual feedback
// For now use a pulsing neutral tint when local player is inside
float pulse = 0.7f + 0.3f * Mathf.Sin(Time.time * 3f);
Color c = neutralColor;
c.a = pulse * 0.6f;
SetZoneColor(c);
}
public void SetZoneColor(Color c)
{
if (_renderer == null) return;
var mat = _renderer.material;
if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", c);
else mat.color = c;
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 3e7c98b369c3ccf4aac0ad3ad2bcbbff

View File

@@ -91,6 +91,7 @@ public class GameHUD : MonoBehaviour
public void SetPhase(string phase) => _phase = phase;
public void SetCountdown(float v) => _countdown = v;
public void SetRoundInfo(int round, string mode) { _roundNumber = round; _gameMode = mode; }
public void SetTotalRounds(int n) => _totalRounds = n;
public void SetCheckpoint(int current, int total) { _checkpointsCurrent = current; _checkpointsTotal = total; }
public void SetLocalRaceActive(bool active)
@@ -168,15 +169,6 @@ public class GameHUD : MonoBehaviour
GUI.Label(new Rect(panelX + 8f, panelY + 32f, panelW - 16f, 24f), modeFull, modeStyle);
// ── Top-right: Players alive ──────────────────────────────────────
int alive = nm?.GetLocalPlayerState() != null
? (_room_playersAlive > 0 ? _room_playersAlive : 1)
: 0;
if (nm != null)
{
// read from room state if accessible
}
float prX = Screen.width - 180f;
GUI.color = new Color(0.08f, 0.08f, 0.12f, 0.85f);
GUI.DrawTexture(new Rect(prX, panelY, 168f, panelH), _bgTex);
@@ -239,44 +231,6 @@ public class GameHUD : MonoBehaviour
$"Checkpoint {_checkpointsCurrent} / {_checkpointsTotal}", cpStyle);
}
// ── Teams: score display (bottom center) ──────────────────────────
if (_gameMode == "teams" && _phase == "playing")
{
float tw = 260f;
float tx = (Screen.width - tw) / 2f;
float ty = Screen.height - 60f;
GUI.color = new Color(0.08f, 0.08f, 0.12f, 0.85f);
GUI.DrawTexture(new Rect(tx - 8f, ty - 8f, tw + 16f, 36f), _bgTex);
GUI.color = Color.white;
var teamStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontSize = 20, fontStyle = FontStyle.Bold };
// Red team score
teamStyle.normal.textColor = new Color(1f, 0.3f, 0.3f);
GUI.Label(new Rect(tx, ty - 2f, tw * 0.4f, 28f), $"{_cachedScoreRed}", teamStyle);
// Separator
var sepStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontSize = 16 };
sepStyle.normal.textColor = new Color(0.5f, 0.5f, 0.6f);
GUI.Label(new Rect(tx + tw * 0.4f, ty - 2f, tw * 0.2f, 28f), "vs", sepStyle);
// Blue team score
teamStyle.normal.textColor = new Color(0.3f, 0.6f, 1f);
GUI.Label(new Rect(tx + tw * 0.6f, ty - 2f, tw * 0.4f, 28f), $"{_cachedScoreBlue}", teamStyle);
}
// ── Survival: death zone warning ──────────────────────────────────
if (_gameMode == "survival" && _phase == "playing" && _deathZoneWarning > 0.01f)
{
GUI.color = new Color(1f, 0.3f, 0.1f, _deathZoneWarning * 0.4f);
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), _bgTex);
GUI.color = Color.white;
var warnStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontSize = 20, fontStyle = FontStyle.Bold };
warnStyle.normal.textColor = new Color(1f, 0.4f, 0.2f, _deathZoneWarning);
GUI.Label(new Rect(0, Screen.height * 0.8f, Screen.width, 36f), "⚠ ZONE DE MORT MONTE !", warnStyle);
}
}
// Static accessors for cross-script use
@@ -285,32 +239,7 @@ public class GameHUD : MonoBehaviour
// Cached values updated from NetworkManager state polling
private int _cachedPlayersAlive = 0;
private int _cachedScoreRed = 0;
private int _cachedScoreBlue = 0;
private float _deathZoneWarning = 0f;
private int _room_playersAlive = 0;
void LateUpdate()
{
// Poll NetworkManager for display values (avoids tight coupling via events for display-only data)
if (NetworkManager.Instance == null || !NetworkManager.Instance.IsConnected) return;
// Survival: check death zone proximity
if (_gameMode == "survival")
{
var localState = NetworkManager.Instance.GetLocalPlayerState();
if (localState != null)
{
// deathZoneY is synced via NetworkState — we read via a static accessor pattern
// For now, warn when player Y is within 5 units above death zone
// (actual deathZoneY is not directly accessible here without extra plumbing)
}
}
}
// Called by DeathZone.cs to update the warning
public void SetDeathZoneWarning(float intensity) => _deathZoneWarning = intensity;
public void SetTeamScores(int red, int blue) { _cachedScoreRed = red; _cachedScoreBlue = blue; }
public void SetPlayersAlive(int count) => _cachedPlayersAlive = count;
private static void EnsureTextures()