185 lines
5.4 KiB
C#
185 lines
5.4 KiB
C#
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 적대 유닛 (적대세력 또는 몬스터)
|
|
/// </summary>
|
|
[RequireComponent(typeof(Collider))]
|
|
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<int> _currentHealth = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
private NetworkVariable<TeamType> _team = new NetworkVariable<TeamType>(
|
|
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);
|
|
}
|
|
}
|
|
|
|
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<ITeamMember>();
|
|
if (attackerTeamMember != null)
|
|
{
|
|
if (!TeamManager.CanAttack(attackerTeamMember, this))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
|
_currentHealth.Value -= actualDamage;
|
|
|
|
// 데미지 이펙트
|
|
ShowDamageEffectClientRpc();
|
|
|
|
// 체력이 0이 되면 파괴
|
|
if (_currentHealth.Value <= 0)
|
|
{
|
|
DestroyUnit(attackerId);
|
|
}
|
|
}
|
|
|
|
private void DestroyUnit(ulong attackerId)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
// 파괴 이펙트
|
|
ShowDestroyEffectClientRpc();
|
|
|
|
// FogOfWar 시스템에서 제거
|
|
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
|
|
|
// 네트워크 오브젝트 파괴
|
|
Invoke(nameof(DespawnUnit), 0.5f);
|
|
}
|
|
|
|
private void DespawnUnit()
|
|
{
|
|
if (IsServer && NetworkObject != null)
|
|
{
|
|
NetworkObject.Despawn(true);
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
private void ShowDamageEffectClientRpc()
|
|
{
|
|
if (damageEffectPrefab != null)
|
|
{
|
|
GameObject effect = Instantiate(damageEffectPrefab, transform.position, Quaternion.identity);
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
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
|
|
}
|
|
}
|
|
} |