플레이어/적/몬스터 팀 시스템 생성
몬스터 및 적 AI 구현
This commit is contained in:
177
Assets/Scripts/AutoTargetSystem.cs
Normal file
177
Assets/Scripts/AutoTargetSystem.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 자동으로 적을 탐지하고 공격하는 시스템
|
||||
/// </summary>
|
||||
public class AutoTargetSystem : NetworkBehaviour
|
||||
{
|
||||
[Header("Targeting")]
|
||||
[Tooltip("적을 감지하는 범위")]
|
||||
public float detectionRange = 15f;
|
||||
|
||||
[Tooltip("공격 가능한 범위")]
|
||||
public float attackRange = 10f;
|
||||
|
||||
[Tooltip("공격 간격 (초)")]
|
||||
public float attackInterval = 1f;
|
||||
|
||||
[Tooltip("탐지할 레이어")]
|
||||
public LayerMask targetLayer = ~0;
|
||||
|
||||
[Header("Combat")]
|
||||
[Tooltip("공격 데미지")]
|
||||
public int attackDamage = 10;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("디버그 정보 표시")]
|
||||
public bool showDebugInfo = true;
|
||||
|
||||
private ITeamMember _teamMember;
|
||||
private float _lastAttackTime;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_teamMember = GetComponent<ITeamMember>();
|
||||
|
||||
if (_teamMember == null)
|
||||
{
|
||||
Debug.LogError($"<color=red>[AutoTargetSystem] {gameObject.name}에 ITeamMember 컴포넌트가 없습니다!</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (_teamMember == null) return;
|
||||
|
||||
if (Time.time - _lastAttackTime >= attackInterval)
|
||||
{
|
||||
FindAndAttackEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindAndAttackEnemy()
|
||||
{
|
||||
// 범위 내 모든 콜라이더 탐지
|
||||
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()
|
||||
{
|
||||
// 탐지 범위 (노란색)
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, detectionRange);
|
||||
|
||||
// 공격 범위 (빨간색)
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, attackRange);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
OnDrawGizmos();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (_teamMember != null && Application.isPlaying)
|
||||
{
|
||||
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
||||
$"Auto Target\nTeam: {TeamManager.GetTeamName(_teamMember.GetTeam())}\nDetection: {detectionRange}m\nAttack: {attackRange}m");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user