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 void Awake() { _animator = 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 enemy = hit.GetComponent(); if (enemy != null) { var netObj = hit.GetComponent(); if (netObj != null) { AttackServerRpc(playerId, netObj.NetworkObjectId); } } } Debug.Log($"플레이어 {playerId} 공격! (적중: {hits.Length}개)"); } [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] private void AttackServerRpc(ulong playerId, ulong targetNetworkId) { if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetNetworkId, out NetworkObject targetObj)) { var damageable = targetObj.GetComponent(); damageable?.TakeDamage(attackDamage, playerId); } } 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); } } }