179 lines
6.7 KiB
C#
179 lines
6.7 KiB
C#
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 자동으로 적을 탐지하고 공격하는 시스템
|
|
/// </summary>
|
|
public class AutoTargetSystem : NetworkBehaviour
|
|
{
|
|
[Header("Targeting")]
|
|
[Tooltip("탐지할 레이어")]
|
|
public LayerMask targetLayer = ~0;
|
|
|
|
[Header("Debug")]
|
|
[Tooltip("디버그 정보 표시")]
|
|
public bool showDebugInfo = true;
|
|
|
|
private Building _building;
|
|
private ITeamMember _teamMember;
|
|
private float _lastAttackTime;
|
|
|
|
private void Awake()
|
|
{
|
|
_building = GetComponent<Building>();
|
|
_teamMember = GetComponent<ITeamMember>();
|
|
|
|
if (_building == null)
|
|
{
|
|
Debug.LogError($"<color=red>[AutoTargetSystem] {gameObject.name}에 Building 컴포넌트가 없습니다!</color>");
|
|
}
|
|
|
|
if (_teamMember == null)
|
|
{
|
|
Debug.LogError($"<color=red>[AutoTargetSystem] {gameObject.name}에 ITeamMember 컴포넌트가 없습니다!</color>");
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!IsServer) return;
|
|
if (_building == null || _teamMember == null) return;
|
|
|
|
if (Time.time - _lastAttackTime >= _building.buildingData.atkIntervalSec)
|
|
{
|
|
FindAndAttackEnemy();
|
|
}
|
|
}
|
|
|
|
private void FindAndAttackEnemy()
|
|
{
|
|
float detectionRange = _building.buildingData.atkRange;
|
|
float attackRange = _building.buildingData.atkRange;
|
|
int attackDamage = _building.buildingData.atkDamage;
|
|
|
|
// 범위 내 모든 콜라이더 탐지
|
|
Collider[] colliders = Physics.OverlapSphere(transform.position, detectionRange, targetLayer);
|
|
|
|
if (showDebugInfo && colliders.Length > 0)
|
|
{
|
|
Debug.Log($"<color=cyan>[AutoTarget] {gameObject.name}이(가) {colliders.Length}개의 오브젝트를 감지했습니다.</color>");
|
|
}
|
|
|
|
GameObject closestEnemy = null;
|
|
float closestDistance = float.MaxValue;
|
|
|
|
foreach (Collider col in colliders)
|
|
{
|
|
// 자기 자신 제외
|
|
if (col.transform.root == transform.root)
|
|
continue;
|
|
|
|
// 팀 확인
|
|
ITeamMember targetTeam = col.GetComponent<ITeamMember>();
|
|
|
|
if (targetTeam == null)
|
|
{
|
|
// 부모나 자식에서 찾기
|
|
targetTeam = col.GetComponentInParent<ITeamMember>();
|
|
if (targetTeam == null)
|
|
{
|
|
targetTeam = col.GetComponentInChildren<ITeamMember>();
|
|
}
|
|
}
|
|
|
|
if (targetTeam == null)
|
|
{
|
|
if (showDebugInfo)
|
|
{
|
|
Debug.Log($"<color=yellow>[AutoTarget] {col.gameObject.name}에 ITeamMember가 없습니다.</color>");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// 적대 관계 확인
|
|
bool canAttack = TeamManager.CanAttack(_teamMember, targetTeam);
|
|
|
|
if (showDebugInfo)
|
|
{
|
|
Debug.Log($"<color=yellow>[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())}) → {col.gameObject.name} ({TeamManager.GetTeamName(targetTeam.GetTeam())}): 공격가능={canAttack}</color>");
|
|
}
|
|
|
|
if (!canAttack)
|
|
continue;
|
|
|
|
// 가장 가까운 적 찾기
|
|
float distance = Vector3.Distance(transform.position, col.transform.position);
|
|
if (distance < closestDistance && distance <= attackRange)
|
|
{
|
|
closestDistance = distance;
|
|
closestEnemy = col.gameObject;
|
|
}
|
|
}
|
|
|
|
// 공격
|
|
if (closestEnemy != null)
|
|
{
|
|
IDamageable damageable = closestEnemy.GetComponent<IDamageable>();
|
|
|
|
if (damageable == null)
|
|
{
|
|
damageable = closestEnemy.GetComponentInParent<IDamageable>();
|
|
if (damageable == null)
|
|
{
|
|
damageable = closestEnemy.GetComponentInChildren<IDamageable>();
|
|
}
|
|
}
|
|
|
|
if (damageable != null)
|
|
{
|
|
damageable.TakeDamage(attackDamage, NetworkObjectId);
|
|
_lastAttackTime = Time.time;
|
|
|
|
var targetTeam = closestEnemy.GetComponent<ITeamMember>() ??
|
|
closestEnemy.GetComponentInParent<ITeamMember>() ??
|
|
closestEnemy.GetComponentInChildren<ITeamMember>();
|
|
|
|
Debug.Log($"<color=red>[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())})이(가) {closestEnemy.name} ({TeamManager.GetTeamName(targetTeam?.GetTeam() ?? TeamType.Neutral)})을(를) 공격! (거리: {closestDistance:F2}m, 데미지: {attackDamage})</color>");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"<color=orange>[AutoTarget] {closestEnemy.name}에 IDamageable이 없습니다.</color>");
|
|
}
|
|
}
|
|
else if (showDebugInfo && colliders.Length > 0)
|
|
{
|
|
Debug.Log($"<color=yellow>[AutoTarget] {gameObject.name}이(가) 공격 가능한 적을 찾지 못했습니다.</color>");
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
if (_building == null || _building.buildingData == null) return;
|
|
|
|
float range = _building.buildingData.atkRange;
|
|
|
|
// 탐지 범위 (노란색)
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawWireSphere(transform.position, range);
|
|
|
|
// 공격 범위 (빨간색)
|
|
Gizmos.color = Color.red;
|
|
Gizmos.DrawWireSphere(transform.position, range);
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
OnDrawGizmos();
|
|
|
|
#if UNITY_EDITOR
|
|
if (_teamMember != null && _building != null && _building.buildingData != null && Application.isPlaying)
|
|
{
|
|
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
|
$"Auto Target\nTeam: {TeamManager.GetTeamName(_teamMember.GetTeam())}\nRange: {_building.buildingData.atkRange}m\nDamage: {_building.buildingData.atkDamage}");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} |