using Unity.Netcode; using UnityEngine; namespace Northbound { /// /// 액션 - 공격 (팀 시스템 적용) /// public class AttackAction : NetworkBehaviour, IAction { [Header("Attack Settings")] public float attackRange = 2f; public int attackDamage = 10; public float attackCooldown = 0.5f; public LayerMask attackableLayer = ~0; [Header("Animation")] public string attackAnimationTrigger = "Attack"; [Header("Visual")] public GameObject attackEffectPrefab; public Transform attackPoint; private float _lastAttackTime; private Animator _animator; private ITeamMember _teamMember; private void Awake() { _animator = GetComponent(); _teamMember = GetComponent(); } public bool CanExecute(ulong playerId) { return Time.time - _lastAttackTime >= attackCooldown; } public void Execute(ulong playerId) { if (!CanExecute(playerId)) return; _lastAttackTime = Time.time; // 애니메이션 재생 PlayAttackAnimation(); // 범위 내 적 검색 Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position; Collider[] hits = Physics.OverlapSphere(attackOrigin, attackRange, attackableLayer); foreach (Collider hit in hits) { // 자기 자신은 제외 if (hit.transform.root == transform.root) continue; // 대상 확인 var targetDamageable = hit.GetComponent(); var targetTeamMember = hit.GetComponent(); if (targetDamageable != null) { // 팀 확인 - 적대 관계인 경우에만 공격 if (_teamMember != null && targetTeamMember != null) { if (!TeamManager.CanAttack(_teamMember, targetTeamMember)) { continue; } } var netObj = hit.GetComponent(); if (netObj != null) { AttackServerRpc(NetworkObjectId, netObj.NetworkObjectId); } } } } [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] private void AttackServerRpc(ulong attackerNetworkId, ulong targetNetworkId) { if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetNetworkId, out NetworkObject targetObj)) { var damageable = targetObj.GetComponent(); damageable?.TakeDamage(attackDamage, attackerNetworkId); } } private void PlayAttackAnimation() { // 애니메이션 트리거 if (_animator != null && !string.IsNullOrEmpty(attackAnimationTrigger)) { _animator.SetTrigger(attackAnimationTrigger); } // 이펙트 생성 if (attackEffectPrefab != null && attackPoint != null) { GameObject effect = Instantiate(attackEffectPrefab, attackPoint.position, attackPoint.rotation); Destroy(effect, 1f); } } public string GetActionName() { return "Attack"; } public string GetActionAnimation() { return attackAnimationTrigger; } private void OnDrawGizmosSelected() { Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position; Gizmos.color = Color.red; Gizmos.DrawWireSphere(attackOrigin, attackRange); } } }