액션 및 인터랙션 시 장비를 착용할 수 있도록 함. 코드 개선 추가

This commit is contained in:
2026-01-28 16:08:12 +09:00
parent 42f5462b54
commit 2539b0f4ba
22 changed files with 323 additions and 206 deletions

View File

@@ -4,7 +4,7 @@ using UnityEngine;
namespace Northbound
{
/// <summary>
/// 액션 - 공격 (팀 시스템 적용)
/// 액션 - 공격 (팀 시스템 + 장비 시스템 적용)
/// </summary>
public class AttackAction : NetworkBehaviour, IAction
{
@@ -16,6 +16,12 @@ namespace Northbound
[Header("Animation")]
public string attackAnimationTrigger = "Attack";
public bool useAnimationEvents = true;
public bool blockDuringAnimation = true;
[Header("Equipment")]
public bool useEquipment = true;
public EquipmentData equipmentData;
[Header("Visual")]
public GameObject attackEffectPrefab;
@@ -24,15 +30,22 @@ namespace Northbound
private float _lastAttackTime;
private Animator _animator;
private ITeamMember _teamMember;
private EquipmentSocket _equipmentSocket;
private bool _isAttacking = false;
private bool _isWeaponEquipped = false;
private void Awake()
{
_animator = GetComponent<Animator>();
_teamMember = GetComponent<ITeamMember>();
_equipmentSocket = GetComponent<EquipmentSocket>();
}
public bool CanExecute(ulong playerId)
{
if (blockDuringAnimation && _isAttacking)
return false;
return Time.time - _lastAttackTime >= attackCooldown;
}
@@ -42,27 +55,43 @@ namespace Northbound
return;
_lastAttackTime = Time.time;
_isAttacking = true;
// 장비 장착 (애니메이션 이벤트 사용 안 할 경우)
if (!useAnimationEvents && useEquipment && !_isWeaponEquipped)
{
if (equipmentData != null && equipmentData.attachOnStart)
{
AttachWeapon();
}
}
// 애니메이션 재생
PlayAttackAnimation();
// 범위 내 적 검색
// 애니메이션이 없으면 즉시 공격 실행
if (_animator == null || string.IsNullOrEmpty(attackAnimationTrigger))
{
PerformAttack();
_isAttacking = false;
}
}
private void PerformAttack()
{
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<IDamageable>();
var targetTeamMember = hit.GetComponent<ITeamMember>();
if (targetDamageable != null)
{
// 팀 확인 - 적대 관계인 경우에만 공격
if (_teamMember != null && targetTeamMember != null)
{
if (!TeamManager.CanAttack(_teamMember, targetTeamMember))
@@ -92,13 +121,11 @@ namespace Northbound
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);
@@ -106,6 +133,108 @@ namespace Northbound
}
}
// ========================================
// Animation Event 함수들
// ========================================
public void OnEquipWeapon()
{
if (!useAnimationEvents || !useEquipment) return;
AttachWeapon();
}
public void OnEquipWeapon(string socketName)
{
if (!useAnimationEvents || !useEquipment) return;
AttachWeapon(socketName);
}
public void OnUnequipWeapon()
{
if (!useAnimationEvents || !useEquipment) return;
DetachWeapon();
}
public void OnUnequipWeapon(string socketName)
{
if (!useAnimationEvents || !useEquipment) return;
DetachWeapon(socketName);
}
public void OnAttackHit()
{
PerformAttack();
}
public void OnAttackComplete()
{
_isAttacking = false;
if (useEquipment && equipmentData != null && equipmentData.detachOnEnd && !equipmentData.keepEquipped)
{
DetachWeapon();
}
Debug.Log("[AttackAction] 공격 완료");
}
// ========================================
// 장비 관리 함수들
// ========================================
private void AttachWeapon(string socketName = null)
{
if (_equipmentSocket == null || equipmentData == null)
{
Debug.LogWarning("[AttackAction] EquipmentSocket 또는 EquipmentData가 없습니다.");
return;
}
if (equipmentData.equipmentPrefab == null)
{
Debug.LogWarning("[AttackAction] 무기 프리팹이 설정되지 않았습니다.");
return;
}
string socket = socketName ?? equipmentData.socketName;
_equipmentSocket.AttachToSocket(socket, equipmentData.equipmentPrefab);
_isWeaponEquipped = true;
Debug.Log($"[AttackAction] 무기 장착: {socket}");
}
private void DetachWeapon(string socketName = null)
{
if (_equipmentSocket == null)
return;
string socket = socketName ?? equipmentData?.socketName;
if (!string.IsNullOrEmpty(socket))
{
_equipmentSocket.DetachFromSocket(socket);
_isWeaponEquipped = false;
Debug.Log($"[AttackAction] 무기 해제: {socket}");
}
}
public void EquipWeapon()
{
if (!_isWeaponEquipped && useEquipment)
{
AttachWeapon();
}
}
public void UnequipWeapon()
{
if (_isWeaponEquipped)
{
DetachWeapon();
}
}
public string GetActionName()
{
return "Attack";
@@ -116,11 +245,29 @@ namespace Northbound
return attackAnimationTrigger;
}
public EquipmentData GetEquipmentData()
{
return equipmentData;
}
private void OnDrawGizmosSelected()
{
Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(attackOrigin, attackRange);
}
public override void OnDestroy()
{
// 무기 정리
if (_isWeaponEquipped)
{
DetachWeapon();
}
base.OnDestroy();
}
public bool IsAttacking => _isAttacking;
}
}