using System.Collections;
using Unity.Netcode;
using UnityEngine;
namespace Northbound
{
///
/// 자동으로 적을 탐지하고 공격하는 시스템
///
public class AutoTargetSystem : NetworkBehaviour
{
[Header("Targeting")]
[Tooltip("탐지할 레이어")]
public LayerMask targetLayer = ~0;
[Header("Beam Effect")]
[Tooltip("빔 효과 LineRenderer 프리팹 (없으면 자동 생성)")]
public LineRenderer beamPrefab;
[Tooltip("빔 색상")]
public Color beamColor = Color.red;
[Tooltip("빔 시작 너비")]
public float beamStartWidth = 0.1f;
[Tooltip("빔 끝 너비")]
public float beamEndWidth = 0.05f;
[Tooltip("빔 표시 시간")]
public float beamDuration = 0.15f;
[Tooltip("빔 발사 위치 (null이면 건물 중심)")]
public Transform firePoint;
[Header("Debug")]
[Tooltip("디버그 정보 표시")]
public bool showDebugInfo = true;
private Building _building;
private ITeamMember _teamMember;
private float _lastAttackTime;
private LineRenderer _beamRenderer;
private Coroutine _beamCoroutine;
private void Awake()
{
_building = GetComponent();
_teamMember = GetComponent();
if (_building == null)
{
Debug.LogError($"[AutoTargetSystem] {gameObject.name}에 Building 컴포넌트가 없습니다!");
}
if (_teamMember == null)
{
Debug.LogError($"[AutoTargetSystem] {gameObject.name}에 ITeamMember 컴포넌트가 없습니다!");
}
}
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($"[AutoTarget] {gameObject.name}이(가) {colliders.Length}개의 오브젝트를 감지했습니다.");
}
GameObject closestEnemy = null;
float closestDistance = float.MaxValue;
foreach (Collider col in colliders)
{
// 자기 자신 제외
if (col.transform.root == transform.root)
continue;
// 팀 확인
ITeamMember targetTeam = col.GetComponent();
if (targetTeam == null)
{
// 부모나 자식에서 찾기
targetTeam = col.GetComponentInParent();
if (targetTeam == null)
{
targetTeam = col.GetComponentInChildren();
}
}
if (targetTeam == null)
{
if (showDebugInfo)
{
Debug.Log($"[AutoTarget] {col.gameObject.name}에 ITeamMember가 없습니다.");
}
continue;
}
// 적대 관계 확인
bool canAttack = TeamManager.CanAttack(_teamMember, targetTeam);
if (showDebugInfo)
{
Debug.Log($"[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())}) → {col.gameObject.name} ({TeamManager.GetTeamName(targetTeam.GetTeam())}): 공격가능={canAttack}");
}
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();
if (damageable == null)
{
damageable = closestEnemy.GetComponentInParent();
if (damageable == null)
{
damageable = closestEnemy.GetComponentInChildren();
}
}
if (damageable != null)
{
// 빔 시작점 계산
Vector3 beamStart = firePoint != null ? firePoint.position : transform.position + Vector3.up * 2f;
Vector3 beamEnd = closestEnemy.transform.position + Vector3.up * 1f; // 타겟 중앙
damageable.TakeDamage(attackDamage, NetworkObjectId);
_lastAttackTime = Time.time;
// 모든 클라이언트에 빔 효과 표시
ShowAttackBeamClientRpc(beamStart, beamEnd);
var targetTeam = closestEnemy.GetComponent() ??
closestEnemy.GetComponentInParent() ??
closestEnemy.GetComponentInChildren();
Debug.Log($"[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())})이(가) {closestEnemy.name} ({TeamManager.GetTeamName(targetTeam?.GetTeam() ?? TeamType.Neutral)})을(를) 공격! (거리: {closestDistance:F2}m, 데미지: {attackDamage})");
}
else
{
Debug.LogWarning($"[AutoTarget] {closestEnemy.name}에 IDamageable이 없습니다.");
}
}
else if (showDebugInfo && colliders.Length > 0)
{
Debug.Log($"[AutoTarget] {gameObject.name}이(가) 공격 가능한 적을 찾지 못했습니다.");
}
}
#region Beam Effect
///
/// 빔 효과를 모든 클라이언트에 표시
///
[ClientRpc]
private void ShowAttackBeamClientRpc(Vector3 start, Vector3 end)
{
if (_beamCoroutine != null)
{
StopCoroutine(_beamCoroutine);
}
_beamCoroutine = StartCoroutine(ShowBeamCoroutine(start, end));
}
private IEnumerator ShowBeamCoroutine(Vector3 start, Vector3 end)
{
// LineRenderer 초기화
if (_beamRenderer == null)
{
InitializeBeamRenderer();
}
if (_beamRenderer != null)
{
_beamRenderer.enabled = true;
_beamRenderer.SetPosition(0, start);
_beamRenderer.SetPosition(1, end);
yield return new WaitForSeconds(beamDuration);
_beamRenderer.enabled = false;
}
}
private void InitializeBeamRenderer()
{
if (beamPrefab != null)
{
// 프리팹 사용
GameObject beamObj = Instantiate(beamPrefab.gameObject, transform);
_beamRenderer = beamObj.GetComponent();
}
else
{
// 자동 생성
GameObject beamObj = new GameObject("AttackBeam");
beamObj.transform.SetParent(transform);
_beamRenderer = beamObj.AddComponent();
// 기본 설정
_beamRenderer.positionCount = 2;
_beamRenderer.startWidth = beamStartWidth;
_beamRenderer.endWidth = beamEndWidth;
_beamRenderer.material = new Material(Shader.Find("Sprites/Default"));
_beamRenderer.startColor = beamColor;
_beamRenderer.endColor = beamColor;
_beamRenderer.enabled = false;
}
}
#endregion
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
}
}
}