using Unity.Netcode; using UnityEngine; namespace Northbound { /// /// 적대 유닛 (적대세력 또는 몬스터) /// public class EnemyUnit : NetworkBehaviour, IDamageable, ITeamMember, IVisionProvider { [Header("Team Settings")] [Tooltip("이 유닛의 팀 (Hostile = 적대세력, Monster = 몬스터)")] public TeamType enemyTeam = TeamType.Hostile; [Header("Combat")] public int maxHealth = 100; public float visionRange = 10f; [Header("Visual")] public GameObject damageEffectPrefab; public GameObject destroyEffectPrefab; private NetworkVariable _currentHealth = new NetworkVariable( 0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server ); private NetworkVariable _team = new NetworkVariable( TeamType.Neutral, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server ); public override void OnNetworkSpawn() { base.OnNetworkSpawn(); if (IsServer) { _currentHealth.Value = maxHealth; _team.Value = enemyTeam; // FogOfWar 시스템에 등록 FogOfWarSystem.Instance?.RegisterVisionProvider(this); Debug.Log($"[EnemyUnit] {gameObject.name} 스폰됨 (팀: {TeamManager.GetTeamName(_team.Value)})"); } } public override void OnNetworkDespawn() { base.OnNetworkDespawn(); if (IsServer) { FogOfWarSystem.Instance?.UnregisterVisionProvider(this); } } #region IDamageable Implementation public void TakeDamage(int damage, ulong attackerId) { if (!IsServer) return; if (_currentHealth.Value <= 0) return; // 공격자의 팀 확인 if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(attackerId, out NetworkObject attackerObj)) { var attackerTeamMember = attackerObj.GetComponent(); if (attackerTeamMember != null) { if (!TeamManager.CanAttack(attackerTeamMember, this)) { Debug.Log($"[EnemyUnit] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다."); return; } } } int actualDamage = Mathf.Min(damage, _currentHealth.Value); _currentHealth.Value -= actualDamage; Debug.Log($"[EnemyUnit] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{maxHealth}"); // 데미지 이펙트 ShowDamageEffectClientRpc(); // 체력이 0이 되면 파괴 if (_currentHealth.Value <= 0) { DestroyUnit(attackerId); } } private void DestroyUnit(ulong attackerId) { if (!IsServer) return; Debug.Log($"[EnemyUnit] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) 파괴되었습니다! (공격자: {attackerId})"); // 파괴 이펙트 ShowDestroyEffectClientRpc(); // FogOfWar 시스템에서 제거 FogOfWarSystem.Instance?.UnregisterVisionProvider(this); // 네트워크 오브젝트 파괴 Invoke(nameof(DespawnUnit), 0.5f); } private void DespawnUnit() { if (IsServer && NetworkObject != null) { NetworkObject.Despawn(true); } } [ClientRpc] private void ShowDamageEffectClientRpc() { if (damageEffectPrefab != null) { GameObject effect = Instantiate(damageEffectPrefab, transform.position, Quaternion.identity); Destroy(effect, 2f); } } [ClientRpc] private void ShowDestroyEffectClientRpc() { if (destroyEffectPrefab != null) { GameObject effect = Instantiate(destroyEffectPrefab, transform.position, Quaternion.identity); Destroy(effect, 3f); } } #endregion #region ITeamMember Implementation public TeamType GetTeam() => _team.Value; public void SetTeam(TeamType team) { if (!IsServer) return; _team.Value = team; } #endregion #region IVisionProvider Implementation public ulong GetOwnerId() => OwnerClientId; public float GetVisionRange() => visionRange; public Transform GetTransform() => transform; public bool IsActive() => IsSpawned && _currentHealth.Value > 0; #endregion private void OnDrawGizmosSelected() { // 팀 색상으로 시야 범위 표시 Color teamColor = Application.isPlaying ? TeamManager.GetTeamColor(_team.Value) : TeamManager.GetTeamColor(enemyTeam); Gizmos.color = new Color(teamColor.r, teamColor.g, teamColor.b, 0.3f); Gizmos.DrawWireSphere(transform.position, visionRange); #if UNITY_EDITOR if (Application.isPlaying) { UnityEditor.Handles.Label(transform.position + Vector3.up * 2f, $"Team: {TeamManager.GetTeamName(_team.Value)}\nHP: {_currentHealth.Value}/{maxHealth}"); } else { UnityEditor.Handles.Label(transform.position + Vector3.up * 2f, $"Team: {TeamManager.GetTeamName(enemyTeam)}"); } #endif } } }