From 62306a34a2474473d59165628809405dcc88584a Mon Sep 17 00:00:00 2001 From: dal4segno Date: Sat, 14 Mar 2026 15:08:03 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=EC=82=AC=EB=A7=9D=20=EB=B0=8F=20=EB=A6=AC=EC=8A=A4=ED=8F=B0=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - isDead 네트워크 변수 추가로 사망 상태 동기화 - 사망 시 이동/스킬 비활성화 및 사망 애니메이션 재생 - Respawn() 메서드로 체력/마나 회복 및 컴포넌트 재활성화 - 스킬 입력에서 사망 상태 체크 추가 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../Scripts/Player/PlayerNetworkController.cs | 90 +++++++++++++++++-- Assets/Scripts/Player/PlayerSkillInput.cs | 8 ++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/Player/PlayerNetworkController.cs b/Assets/Scripts/Player/PlayerNetworkController.cs index 6dcce4b0..22a6da79 100644 --- a/Assets/Scripts/Player/PlayerNetworkController.cs +++ b/Assets/Scripts/Player/PlayerNetworkController.cs @@ -18,6 +18,7 @@ namespace Colosseum.Player // 네트워크 동기화 변수 private NetworkVariable currentHealth = new NetworkVariable(100f); private NetworkVariable currentMana = new NetworkVariable(50f); + private NetworkVariable isDead = new NetworkVariable(false); public float Health => currentHealth.Value; public float Mana => currentMana.Value; @@ -29,9 +30,13 @@ namespace Colosseum.Player public event Action OnHealthChanged; // (oldValue, newValue) public event Action OnManaChanged; // (oldValue, newValue) + // 사망 이벤트 + public event Action OnDeath; + public event Action OnDeathStateChanged; // (isDead) + // IDamageable 구현 public float CurrentHealth => currentHealth.Value; - public bool IsDead => currentHealth.Value <= 0f; + public bool IsDead => isDead.Value; public override void OnNetworkSpawn() { @@ -44,12 +49,14 @@ namespace Colosseum.Player // 네트워크 변수 변경 콜백 등록 currentHealth.OnValueChanged += HandleHealthChanged; currentMana.OnValueChanged += HandleManaChanged; + isDead.OnValueChanged += HandleDeathStateChanged; // 초기화 if (IsServer) { currentHealth.Value = MaxHealth; currentMana.Value = MaxMana; + isDead.Value = false; } } @@ -58,6 +65,7 @@ namespace Colosseum.Player // 콜백 해제 currentHealth.OnValueChanged -= HandleHealthChanged; currentMana.OnValueChanged -= HandleManaChanged; + isDead.OnValueChanged -= HandleDeathStateChanged; } private void HandleHealthChanged(float oldValue, float newValue) @@ -70,12 +78,19 @@ namespace Colosseum.Player OnManaChanged?.Invoke(oldValue, newValue); } + private void HandleDeathStateChanged(bool oldValue, bool newValue) + { + OnDeathStateChanged?.Invoke(newValue); + } + /// /// 대미지 적용 (서버에서만 실행) /// [Rpc(SendTo.Server)] public void TakeDamageRpc(float damage) { + if (isDead.Value) return; + currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage); if (currentHealth.Value <= 0f) @@ -111,10 +126,75 @@ namespace Colosseum.Player currentMana.Value = Mathf.Min(MaxMana, currentMana.Value + amount); } + /// + /// 사망 처리 (서버에서만 실행) + /// private void HandleDeath() { - // TODO: 사망 처리 로직 - Debug.Log($"Player {OwnerClientId} died!"); + if (isDead.Value) return; + + isDead.Value = true; + + // 이동 비활성화 + var movement = GetComponent(); + if (movement != null) + { + movement.enabled = false; + } + + // 스킬 입력 비활성화 + var skillInput = GetComponent(); + if (skillInput != null) + { + skillInput.enabled = false; + } + + // 사망 애니메이션 재생 + var animator = GetComponentInChildren(); + if (animator != null) + { + animator.SetTrigger("Die"); + } + + // 사망 이벤트 발생 + OnDeath?.Invoke(this); + + Debug.Log($"[Player] Player {OwnerClientId} died!"); + } + + /// + /// 리스폰 (서버에서만 실행) + /// + public void Respawn() + { + if (!IsServer) return; + + isDead.Value = false; + currentHealth.Value = MaxHealth; + currentMana.Value = MaxMana; + + // 이동 재활성화 + var movement = GetComponent(); + if (movement != null) + { + movement.enabled = true; + } + + // 스킬 입력 재활성화 + var skillInput = GetComponent(); + if (skillInput != null) + { + skillInput.enabled = true; + } + + // 애니메이션 리셋 + var animator = GetComponentInChildren(); + if (animator != null) + { + animator.Rebind(); + } + + Debug.Log($"[Player] Player {OwnerClientId} respawned!"); } #region IDamageable @@ -123,7 +203,7 @@ namespace Colosseum.Player /// public float TakeDamage(float damage, object source = null) { - if (!IsServer) return 0f; + if (!IsServer || isDead.Value) return 0f; float actualDamage = Mathf.Min(damage, currentHealth.Value); currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage); @@ -141,7 +221,7 @@ namespace Colosseum.Player /// public float Heal(float amount) { - if (!IsServer) return 0f; + if (!IsServer || isDead.Value) return 0f; float actualHeal = Mathf.Min(amount, MaxHealth - currentHealth.Value); currentHealth.Value = Mathf.Min(MaxHealth, currentHealth.Value + amount); diff --git a/Assets/Scripts/Player/PlayerSkillInput.cs b/Assets/Scripts/Player/PlayerSkillInput.cs index c69f2798..d5574d30 100644 --- a/Assets/Scripts/Player/PlayerSkillInput.cs +++ b/Assets/Scripts/Player/PlayerSkillInput.cs @@ -107,6 +107,10 @@ namespace Colosseum.Player return; } + // 사망 상태 체크 + if (networkController != null && networkController.IsDead) + return; + // 로컬 체크 (빠른 피드백용) if (skillController.IsExecutingSkill) { @@ -145,6 +149,10 @@ namespace Colosseum.Player if (skill == null) return; // 서버에서 다시 검증 + // 사망 상태 체크 + if (networkController != null && networkController.IsDead) + return; + if (skillController.IsExecutingSkill || skillController.IsOnCooldown(skill)) return;