From 5238b65dc2600d3aa33cb20dd6d775ad756ce42f Mon Sep 17 00:00:00 2001 From: dal4segno Date: Mon, 16 Mar 2026 09:46:56 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A0=81=20=EC=82=AC=EB=A7=9D=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EnemyBase: PlayDeathAnimationRpc로 모든 클라이언트에 사망 애니메이션 동기화 - EnemyAnimationController: 사망 상태에서 애니메이션 업데이트 및 트리거 중단 - BossEnemy: HandleDeath에서 AI 정지 순서 개선 (enabled=false 먼저) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- Assets/Scripts/Enemy/BossEnemy.cs | 10 ++-- .../Scripts/Enemy/EnemyAnimationController.cs | 43 +++++++++++----- Assets/Scripts/Enemy/EnemyBase.cs | 49 ++++++++++++++----- 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/Assets/Scripts/Enemy/BossEnemy.cs b/Assets/Scripts/Enemy/BossEnemy.cs index b3540ea4..e4f4003a 100644 --- a/Assets/Scripts/Enemy/BossEnemy.cs +++ b/Assets/Scripts/Enemy/BossEnemy.cs @@ -11,6 +11,7 @@ namespace Colosseum.Enemy /// 보스 캐릭터. 페이즈 시스템과 동적 AI 전환을 지원합니다. /// Unity Behavior 패키지를 사용하여 Behavior Tree 기반 AI를 구현합니다. /// + public class BossEnemy : EnemyBase { [Header("Boss Settings")] @@ -274,13 +275,16 @@ namespace Colosseum.Enemy return; } - base.HandleDeath(); - - // AI 정지 + // AI 완전 중단 (순서 중요: enabled=false를 먼저 호출하여 Update() 차단) if (behaviorAgent != null) { + behaviorAgent.enabled = false; // 가장 먼저: Update() 호출 방지 behaviorAgent.End(); + behaviorAgent.Graph = null; } + behaviorAgent = null; + + base.HandleDeath(); } #region Debug diff --git a/Assets/Scripts/Enemy/EnemyAnimationController.cs b/Assets/Scripts/Enemy/EnemyAnimationController.cs index 0f260111..faa6b803 100644 --- a/Assets/Scripts/Enemy/EnemyAnimationController.cs +++ b/Assets/Scripts/Enemy/EnemyAnimationController.cs @@ -20,6 +20,7 @@ namespace Colosseum.Enemy private Animator animator; private UnityEngine.AI.NavMeshAgent navMeshAgent; + private EnemyBase enemyBase; private float currentSpeed; private float speedVelocity; @@ -27,6 +28,7 @@ namespace Colosseum.Enemy { animator = GetComponent(); navMeshAgent = GetComponent(); + enemyBase = GetComponent(); } public override void OnNetworkSpawn() @@ -49,6 +51,10 @@ namespace Colosseum.Enemy /// private void UpdateAnimationParameters() { + // 사망 상태에서는 애니메이션 파라미터 업데이트 중단 + if (enemyBase != null && enemyBase.IsDead) + return; + if (animator == null || navMeshAgent == null) return; @@ -71,20 +77,29 @@ namespace Colosseum.Enemy /// public void PlayAttack() { - if (IsServer && animator != null) - { - animator.SetTrigger(attackTriggerParam); - } + if (!IsServer || animator == null) + return; + + // 사망 상태에서는 공격 애니메이션 재생하지 않음 + if (enemyBase != null && enemyBase.IsDead) + return; + + animator.SetTrigger(attackTriggerParam); } + /// /// 스킬 애니메이션 트리거 (외부에서 호출) /// public void PlaySkill() { - if (IsServer && animator != null) - { - animator.SetTrigger(skillTriggerParam); - } + if (!IsServer || animator == null) + return; + + // 사망 상태에서는 스킬 애니메이션 재생하지 않음 + if (enemyBase != null && enemyBase.IsDead) + return; + + animator.SetTrigger(skillTriggerParam); } /// @@ -93,10 +108,14 @@ namespace Colosseum.Enemy /// 트리거 파라미터 이름 public void PlayTrigger(string triggerName) { - if (IsServer && animator != null) - { - animator.SetTrigger(triggerName); - } + if (!IsServer || animator == null) + return; + + // 사망 상태에서는 일반 애니메이션 재생하지 않음 + if (enemyBase != null && enemyBase.IsDead) + return; + + animator.SetTrigger(triggerName); } /// diff --git a/Assets/Scripts/Enemy/EnemyBase.cs b/Assets/Scripts/Enemy/EnemyBase.cs index 03b504d3..ae60dec4 100644 --- a/Assets/Scripts/Enemy/EnemyBase.cs +++ b/Assets/Scripts/Enemy/EnemyBase.cs @@ -23,6 +23,8 @@ namespace Colosseum.Enemy [Header("Data")] [SerializeField] protected EnemyData enemyData; + + // 네트워크 동기화 변수 protected NetworkVariable currentHealth = new NetworkVariable(100f); protected NetworkVariable currentMana = new NetworkVariable(50f); @@ -90,16 +92,6 @@ namespace Colosseum.Enemy isDead.Value = false; } - - - - - - - - - - /// /// 대미지 적용 (서버에서 실행) /// @@ -150,6 +142,34 @@ namespace Colosseum.Enemy return actualHeal; } + /// + /// 사망 애니메이션 재생 (모든 클라이언트에서 실행) + /// + [Rpc(SendTo.Everyone)] + private void PlayDeathAnimationRpc() + { + if (animator != null) + { + // EnemyAnimationController 비활성화 (더 이상 애니메이션 제어하지 않음) + var animController = GetComponent(); + if (animController != null) + { + animController.enabled = false; + } + + // 모든 트리거 리셋 + animator.ResetTrigger("Attack"); + animator.ResetTrigger("Skill"); + animator.ResetTrigger("Hit"); + animator.ResetTrigger("Jump"); + animator.ResetTrigger("Land"); + animator.ResetTrigger("Die"); + + // 즉시 Die 상태로 전환 (다른 애니메이션 중단) + animator.Play("Die", 0, 0f); + } + } + /// /// 사망 처리 (서버에서 실행) /// @@ -157,11 +177,16 @@ namespace Colosseum.Enemy { isDead.Value = true; - if (animator != null) + // 실행 중인 스킬 즉시 취소 + var skillController = GetComponent(); + if (skillController != null) { - animator.SetTrigger("Die"); + skillController.CancelSkill(); } + // 모든 클라이언트에서 사망 애니메이션 재생 + PlayDeathAnimationRpc(); + if (navMeshAgent != null) { navMeshAgent.isStopped = true;