132 lines
3.9 KiB
C#
132 lines
3.9 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using Ashwild.Player;
|
|
|
|
namespace Ashwild.UI
|
|
{
|
|
public class DeathScreen : MonoBehaviour
|
|
{
|
|
[Header("Timing")]
|
|
[SerializeField] private float fadeDelay = 0.8f;
|
|
[SerializeField] private float fadeDuration = 1.5f;
|
|
[SerializeField] private float respawnDelay = 1f;
|
|
|
|
[Header("UI")]
|
|
[SerializeField] private Image fadeOverlay;
|
|
|
|
private PlayerLifecycle playerLifecycle;
|
|
private bool isDying;
|
|
private float timer;
|
|
private enum Phase { Wait, Fade, Black, Done }
|
|
private Phase phase;
|
|
private bool bound;
|
|
|
|
/// <summary>
|
|
/// Waits for the networked local player to spawn before hooking onto its death event.
|
|
/// </summary>
|
|
private void OnEnable()
|
|
{
|
|
PlayerEvents.LocalPlayerSpawned += HandleLocalPlayerSpawned;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsubscribes — mirrors OnEnable exactly.
|
|
/// </summary>
|
|
private void OnDisable()
|
|
{
|
|
PlayerEvents.LocalPlayerSpawned -= HandleLocalPlayerSpawned;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (fadeOverlay != null)
|
|
{
|
|
fadeOverlay.color = Color.clear;
|
|
fadeOverlay.raycastTarget = false;
|
|
}
|
|
|
|
// The player may already exist (late UI init); otherwise we wait for the spawn event.
|
|
if (PlayerStats.Instance != null)
|
|
BindToPlayer();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Binds to the local player as soon as it spawns on the network.
|
|
/// </summary>
|
|
private void HandleLocalPlayerSpawned() => BindToPlayer();
|
|
|
|
/// <summary>
|
|
/// Caches the lifecycle and listens for the local player's death; runs once.
|
|
/// </summary>
|
|
private void BindToPlayer()
|
|
{
|
|
if (bound) return;
|
|
if (PlayerStats.Instance == null) return;
|
|
bound = true;
|
|
|
|
// Take the lifecycle from the LOCAL player only. A global Find would also match remote
|
|
// players (their root GameObject stays active; only the Behaviour is disabled), and could
|
|
// respawn the wrong player in multiplayer.
|
|
playerLifecycle = PlayerStats.Instance.GetComponent<PlayerLifecycle>();
|
|
PlayerStats.Instance.onDeath.AddListener(OnDeath);
|
|
}
|
|
|
|
private void OnDeath()
|
|
{
|
|
if (isDying) return;
|
|
isDying = true;
|
|
timer = 0f;
|
|
phase = Phase.Wait;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!isDying) return;
|
|
|
|
timer += Time.deltaTime;
|
|
|
|
switch (phase)
|
|
{
|
|
case Phase.Wait:
|
|
if (timer >= fadeDelay)
|
|
{
|
|
phase = Phase.Fade;
|
|
timer = 0f;
|
|
}
|
|
break;
|
|
|
|
case Phase.Fade:
|
|
float fadeT = Mathf.Clamp01(timer / fadeDuration);
|
|
if (fadeOverlay != null)
|
|
fadeOverlay.color = new Color(0f, 0f, 0f, fadeT);
|
|
|
|
if (fadeT >= 1f)
|
|
{
|
|
phase = Phase.Black;
|
|
timer = 0f;
|
|
}
|
|
break;
|
|
|
|
case Phase.Black:
|
|
if (timer >= respawnDelay)
|
|
{
|
|
playerLifecycle.Respawn();
|
|
|
|
if (fadeOverlay != null)
|
|
fadeOverlay.color = Color.clear;
|
|
|
|
isDying = false;
|
|
phase = Phase.Done;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (PlayerStats.Instance != null)
|
|
PlayerStats.Instance.onDeath.RemoveListener(OnDeath);
|
|
}
|
|
}
|
|
}
|