From 047c115f9599377e9faf0ee2d15243d35a9d2a51 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Mon, 16 Feb 2026 00:13:25 +0900 Subject: [PATCH] =?UTF-8?q?Enemy=EC=9D=98=20=EC=82=AC=EB=A7=9D=20=EC=95=A0?= =?UTF-8?q?=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 네트워크 상에서의 동작 확인 완료 --- .../MonsterAnimationController.controller | 6 +-- Assets/Scripts/EnemyAIController.cs | 33 ++++++++++++++++ Assets/Scripts/EnemyAIState.cs | 3 +- Assets/Scripts/EnemyPortal.cs | 5 ++- Assets/Scripts/EnemyUnit.cs | 10 ++++- Assets/Scripts/MonsterAnimationController.cs | 38 +++++++++++++++++++ 6 files changed, 89 insertions(+), 6 deletions(-) diff --git a/Assets/Animations/MonsterAnimationController.controller b/Assets/Animations/MonsterAnimationController.controller index 6f8a4a9..f226037 100644 --- a/Assets/Animations/MonsterAnimationController.controller +++ b/Assets/Animations/MonsterAnimationController.controller @@ -80,8 +80,8 @@ AnimatorController: m_DefaultInt: 0 m_DefaultBool: 0 m_Controller: {fileID: 9100000} - - m_Name: bIsDeath - m_Type: 4 + - m_Name: Die + m_Type: 9 m_DefaultFloat: 0 m_DefaultInt: 0 m_DefaultBool: 0 @@ -242,7 +242,7 @@ AnimatorStateTransition: m_Name: m_Conditions: - m_ConditionMode: 1 - m_ConditionEvent: bIsDeath + m_ConditionEvent: Die m_EventTreshold: 0 m_DstStateMachine: {fileID: 0} m_DstState: {fileID: 3895323774234557799} diff --git a/Assets/Scripts/EnemyAIController.cs b/Assets/Scripts/EnemyAIController.cs index bce4ccb..b21e3c6 100644 --- a/Assets/Scripts/EnemyAIController.cs +++ b/Assets/Scripts/EnemyAIController.cs @@ -119,9 +119,24 @@ namespace Northbound { TransitionToState(EnemyAIState.Idle); } + + // 사망 이벤트 구독 + if (_enemyUnit != null) + { + _enemyUnit.OnDeath += HandleDeath; + } } } + public override void OnNetworkDespawn() + { + if (_enemyUnit != null) + { + _enemyUnit.OnDeath -= HandleDeath; + } + base.OnNetworkDespawn(); + } + private void Update() { if (!IsServer) return; @@ -134,6 +149,7 @@ namespace Northbound case EnemyAIState.ChasePlayer: UpdateChasePlayer(); break; case EnemyAIState.Attack: UpdateAttack(); break; case EnemyAIState.ReturnToOrigin: UpdateReturnToOrigin(); break; + case EnemyAIState.Dead: break; // 사망 상태에서는 아무것도 하지 않음 } } @@ -471,11 +487,28 @@ namespace Northbound if (state == EnemyAIState.ChasePlayer) _chaseStartPosition = transform.position; if (state == EnemyAIState.ReturnToOrigin) _agent.SetDestination(_originPosition); break; + case EnemyAIState.Dead: + _agent.isStopped = true; + _agent.ResetPath(); + _agent.enabled = false; // NavMeshAgent 비활성화 + break; } } private void OnExitState(EnemyAIState state) { } + private void HandleDeath(ulong killerId) + { + if (!IsServer) return; + + // 사망 상태로 전환 + TransitionToState(EnemyAIState.Dead); + ClearTargetPlayer(); + + if (showDebugInfo) + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 사망했습니다. (killer: {killerId})"); + } + private void OnLostTarget() { ClearTargetPlayer(); diff --git a/Assets/Scripts/EnemyAIState.cs b/Assets/Scripts/EnemyAIState.cs index 9c346f2..e946d15 100644 --- a/Assets/Scripts/EnemyAIState.cs +++ b/Assets/Scripts/EnemyAIState.cs @@ -9,6 +9,7 @@ namespace Northbound MoveToCore, // 코어로 이동 (몬스터 기본 상태) ChasePlayer, // 플레이어 추적 Attack, // 공격 - ReturnToOrigin // 원래 위치로 복귀 (적대 세력) + ReturnToOrigin, // 원래 위치로 복귀 (적대 세력) + Dead // 사망 (아무것도 하지 않음) } } \ No newline at end of file diff --git a/Assets/Scripts/EnemyPortal.cs b/Assets/Scripts/EnemyPortal.cs index 347647d..e0abcfe 100644 --- a/Assets/Scripts/EnemyPortal.cs +++ b/Assets/Scripts/EnemyPortal.cs @@ -153,7 +153,10 @@ using UnityEngine; visibility.updateInterval = 0.2f; } - enemy.GetComponent().SpawnWithOwnership(NetworkManager.Singleton.LocalClientId); + var netObj = enemy.GetComponent(); + netObj.Spawn(true); + + Debug.Log($"[EnemyPortal] {enemy.name} 스폰됨 - OwnerClientId: {netObj.OwnerClientId}, IsServer: {IsServer}"); } private void IncreaseCost() diff --git a/Assets/Scripts/EnemyUnit.cs b/Assets/Scripts/EnemyUnit.cs index 1319744..e3354d0 100644 --- a/Assets/Scripts/EnemyUnit.cs +++ b/Assets/Scripts/EnemyUnit.cs @@ -32,6 +32,11 @@ namespace Northbound NetworkVariableWritePermission.Server ); + /// + /// 사망 시 발생하는 이벤트 (매개변수: killerId) + /// + public event System.Action OnDeath; + public override void OnNetworkSpawn() { base.OnNetworkSpawn(); @@ -85,11 +90,14 @@ namespace Northbound { if (!IsServer) return; + // 사망 이벤트 발생 (애니메이션 등) + OnDeath?.Invoke(attackerId); + // 파괴 이펙트 ShowDestroyEffectClientRpc(); // 네트워크 오브젝트 파괴 - Invoke(nameof(DespawnUnit), 0.5f); + Invoke(nameof(DespawnUnit), 3.0f); } private void DespawnUnit() diff --git a/Assets/Scripts/MonsterAnimationController.cs b/Assets/Scripts/MonsterAnimationController.cs index db595c3..2cb67bb 100644 --- a/Assets/Scripts/MonsterAnimationController.cs +++ b/Assets/Scripts/MonsterAnimationController.cs @@ -18,6 +18,9 @@ namespace Northbound [Tooltip("IsMoving bool parameter name in Animator")] public string isMovingParam = "IsMoving"; + [Tooltip("Death trigger parameter name in Animator")] + public string dieTriggerParam = "Die"; + [Header("Settings")] [Tooltip("Auto-load animator controller from MonsterData")] public bool autoLoadFromMonsterData = true; @@ -27,6 +30,7 @@ namespace Northbound private Animator _animator; private EnemyAIController _aiController; + private EnemyUnit _enemyUnit; private NavMeshAgent _agent; private NetworkVariable _networkSpeed = new NetworkVariable( @@ -47,9 +51,14 @@ namespace Northbound _animator = GetComponent(); _aiController = GetComponent(); + _enemyUnit = GetComponent(); _agent = GetComponent(); _aiController.OnAttackPerformed += HandleAttackPerformed; + if (_enemyUnit != null) + { + _enemyUnit.OnDeath += HandleDeath; + } if (autoLoadFromMonsterData) { @@ -63,6 +72,10 @@ namespace Northbound { _aiController.OnAttackPerformed -= HandleAttackPerformed; } + if (_enemyUnit != null) + { + _enemyUnit.OnDeath -= HandleDeath; + } base.OnNetworkDespawn(); } @@ -74,6 +87,14 @@ namespace Northbound Debug.Log($"[MonsterAnimationController] Triggered attack animation for {target.name}", this); } + private void HandleDeath(ulong killerId) + { + if (!IsServer) return; + TriggerDeathClientRpc(); + if (debugLogging) + Debug.Log($"[MonsterAnimationController] Triggered death animation (killer: {killerId})", this); + } + private void LoadAnimatorController() { var monsterDataComponent = GetComponent(); @@ -111,6 +132,14 @@ namespace Northbound private void UpdateServerSide() { + // 사망 상태면 이동 애니메이션 중지 + if (_aiController != null && _aiController.GetCurrentState() == EnemyAIState.Dead) + { + _networkSpeed.Value = 0f; + _networkIsMoving.Value = false; + return; + } + if (_agent == null) return; float currentSpeed = _agent.velocity.magnitude; @@ -137,6 +166,15 @@ namespace Northbound } } + [Rpc(SendTo.ClientsAndHost)] + private void TriggerDeathClientRpc() + { + if (_animator != null) + { + _animator.SetTrigger(dieTriggerParam); + } + } + public void ResetAttackTrigger() { if (_animator != null)