feat: 방어 시스템과 드로그 검증 경로 정리
- 애니메이션 이벤트 기반 방어/유지/해제 흐름과 HUD 피드백, 방어 디버그 로그를 추가했다. - 드로그 기본기1 테스트 패턴을 정리하고 공격 판정을 OnEffect 기반으로 옮기며 드로그 범위 효과의 타겟 레이어를 정상화했다. - 플레이어 퀵슬롯 테스트 세팅과 적-플레이어 겹침 방지 로직을 조정해 충돌 시 적이 수평 이동을 멈추고 최소 분리만 수행하게 했다.
This commit is contained in:
171
Assets/_Game/Scripts/Player/PlayerDefenseSustainController.cs
Normal file
171
Assets/_Game/Scripts/Player/PlayerDefenseSustainController.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using UnityEngine;
|
||||
|
||||
using Colosseum.Passives;
|
||||
using Colosseum.Skills;
|
||||
using Colosseum.Weapons;
|
||||
|
||||
namespace Colosseum.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어의 방어 유지 자원 소모를 관리합니다.
|
||||
/// 방어 판정과 분리되어 있으며, 애니메이션 이벤트로 시작/종료됩니다.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class PlayerDefenseSustainController : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[Tooltip("플레이어 자원 관리자")]
|
||||
[SerializeField] private PlayerNetworkController networkController;
|
||||
|
||||
[Tooltip("현재 실행 중인 스킬 정보 참조용")]
|
||||
[SerializeField] private SkillController skillController;
|
||||
|
||||
[Tooltip("무기 마나 배율 참조용")]
|
||||
[SerializeField] private WeaponEquipment weaponEquipment;
|
||||
|
||||
[Header("Sustain Settings")]
|
||||
[Tooltip("방어 유지의 초당 기본 마나 소모량입니다.")]
|
||||
[Min(0f)] [SerializeField] private float sustainManaPerSecond = 4f;
|
||||
|
||||
[Tooltip("방어 유지 시간이 길어질수록 추가되는 초당 마나 소모량입니다.")]
|
||||
[Min(0f)] [SerializeField] private float sustainManaRampPerSecond = 4f;
|
||||
|
||||
[Tooltip("완벽한 방어 성공 시 환급할 시작 마나 비율입니다.")]
|
||||
[Range(0f, 1f)] [SerializeField] private float perfectGuardStartManaRefundRatio = 1f;
|
||||
|
||||
[Tooltip("방어 유지 중 이동 속도 배율입니다.")]
|
||||
[Range(0f, 1f)] [SerializeField] private float sustainMoveSpeedMultiplier = 0.45f;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("현재 방어 유지 활성 여부")]
|
||||
[SerializeField] private bool isSustainActive;
|
||||
|
||||
[Tooltip("현재 방어 유지 시간")]
|
||||
[Min(0f)] [SerializeField] private float sustainElapsed;
|
||||
|
||||
[Tooltip("현재 프레임에 계산된 초당 유지 마나")]
|
||||
[Min(0f)] [SerializeField] private float currentSustainManaPerSecond;
|
||||
|
||||
[Tooltip("마지막으로 캡처한 시작 마나 소모량")]
|
||||
[Min(0f)] [SerializeField] private float capturedStartManaCost;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 방어 유지 활성 여부입니다.
|
||||
/// </summary>
|
||||
public bool IsSustainActive => isSustainActive;
|
||||
|
||||
/// <summary>
|
||||
/// 방어 유지 중 이동 속도 배율입니다.
|
||||
/// </summary>
|
||||
public float MoveSpeedMultiplier => isSustainActive ? sustainMoveSpeedMultiplier : 1f;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
EnsureReferences();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ClearSustainState();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!isSustainActive)
|
||||
return;
|
||||
|
||||
sustainElapsed += Time.deltaTime;
|
||||
currentSustainManaPerSecond = sustainManaPerSecond + (sustainManaRampPerSecond * sustainElapsed);
|
||||
|
||||
if (networkController == null || !networkController.IsServer)
|
||||
return;
|
||||
|
||||
float requiredMana = currentSustainManaPerSecond * Time.deltaTime;
|
||||
float actualSpentMana = networkController.SpendMana(requiredMana);
|
||||
if (actualSpentMana + 0.001f < requiredMana)
|
||||
{
|
||||
skillController?.CancelSkillFromServer(SkillCancelReason.ResourceExhausted);
|
||||
ClearSustainState();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 애니메이션 이벤트로 방어 유지 자원 소모를 시작합니다.
|
||||
/// </summary>
|
||||
public void BeginSustain()
|
||||
{
|
||||
EnsureReferences();
|
||||
|
||||
isSustainActive = true;
|
||||
sustainElapsed = 0f;
|
||||
currentSustainManaPerSecond = sustainManaPerSecond;
|
||||
capturedStartManaCost = ResolveCurrentSkillManaCost();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 애니메이션 이벤트로 방어 유지 자원 소모를 종료합니다.
|
||||
/// </summary>
|
||||
public void EndSustain()
|
||||
{
|
||||
ClearSustainState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스킬 종료/취소 시 방어 유지 상태를 정리합니다.
|
||||
/// </summary>
|
||||
public void HandleSkillExecutionEnded()
|
||||
{
|
||||
ClearSustainState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 완벽한 방어 성공 시 시작 마나를 환급합니다.
|
||||
/// </summary>
|
||||
public void RefundStartManaOnPerfectGuard()
|
||||
{
|
||||
EnsureReferences();
|
||||
|
||||
if (!isSustainActive || networkController == null || !networkController.IsServer)
|
||||
return;
|
||||
|
||||
float refundAmount = capturedStartManaCost * perfectGuardStartManaRefundRatio;
|
||||
if (refundAmount <= 0f)
|
||||
return;
|
||||
|
||||
networkController.RestoreMana(refundAmount);
|
||||
}
|
||||
|
||||
private void EnsureReferences()
|
||||
{
|
||||
if (networkController == null)
|
||||
networkController = GetComponent<PlayerNetworkController>();
|
||||
|
||||
if (skillController == null)
|
||||
skillController = GetComponent<SkillController>();
|
||||
|
||||
if (weaponEquipment == null)
|
||||
weaponEquipment = GetComponent<WeaponEquipment>();
|
||||
}
|
||||
|
||||
private float ResolveCurrentSkillManaCost()
|
||||
{
|
||||
SkillLoadoutEntry loadoutEntry = skillController != null ? skillController.CurrentLoadoutEntry : null;
|
||||
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : skillController != null ? skillController.CurrentSkill : null;
|
||||
if (skill == null)
|
||||
return 0f;
|
||||
|
||||
float baseManaCost = loadoutEntry != null ? loadoutEntry.GetResolvedManaCost() : skill.ManaCost;
|
||||
float weaponMultiplier = weaponEquipment != null ? weaponEquipment.ManaCostMultiplier : 1f;
|
||||
float passiveMultiplier = PassiveRuntimeModifierUtility.GetManaCostMultiplier(gameObject, skill);
|
||||
return baseManaCost * weaponMultiplier * passiveMultiplier;
|
||||
}
|
||||
|
||||
private void ClearSustainState()
|
||||
{
|
||||
isSustainActive = false;
|
||||
sustainElapsed = 0f;
|
||||
currentSustainManaPerSecond = 0f;
|
||||
capturedStartManaCost = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user