2026-04-08 스킬시스템 진행중, 폴더구조 변경등
This commit is contained in:
@@ -16,11 +16,26 @@ public class SkillModule : MonoBehaviour
|
||||
private int _chargingSlot = -1;
|
||||
private float _chargeTimer = 0f;
|
||||
|
||||
// 범위 지정
|
||||
// 캐스팅
|
||||
private int _castingSlot = -1;
|
||||
private float _castTimer = 0f;
|
||||
|
||||
// 채널링
|
||||
private int _channelingSlot = -1;
|
||||
private float _channelTimer = 0f;
|
||||
private float _channelTickAccumulator = 0f;
|
||||
|
||||
// 범위 지정 (Remote)
|
||||
private int _areaSelectSlot = -1;
|
||||
private GameObject _areaIndicator;
|
||||
|
||||
// Remote 타겟 위치
|
||||
private Vector3 _remoteTargetPos;
|
||||
private bool _hasRemoteTarget;
|
||||
|
||||
public bool IsAreaSelecting => _areaSelectSlot >= 0;
|
||||
public bool IsCasting => _castingSlot >= 0;
|
||||
public bool IsChanneling => _channelingSlot >= 0;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -36,6 +51,8 @@ private void Update()
|
||||
{
|
||||
TickCooldowns();
|
||||
TickCharge();
|
||||
TickCast();
|
||||
TickChannel();
|
||||
TickAreaSelect();
|
||||
}
|
||||
|
||||
@@ -67,6 +84,7 @@ private ISkillEffect ResolveEffect(TargetType targetType)
|
||||
TargetType.Single => GetComponent<DamageEffect>(),
|
||||
TargetType.Area => GetComponent<AreaEffect>(),
|
||||
TargetType.Projectile => GetComponent<ProjectileEffect>(),
|
||||
TargetType.Zone => GetComponent<ZoneEffect>(),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -92,18 +110,16 @@ public void SkillInput(int slotIndex, InputState inputState)
|
||||
|
||||
if (!CanUseSkill(skill)) return;
|
||||
|
||||
if (skill.Data.ActivationType == ActivationType.Instant)
|
||||
{
|
||||
ExecuteSkill(slotIndex);
|
||||
}
|
||||
else if (skill.Data.ActivationType == ActivationType.Charge)
|
||||
{
|
||||
StartCharge(slotIndex);
|
||||
}
|
||||
else if (skill.Data.ActivationType == ActivationType.AreaSelect)
|
||||
// Remote 스킬: 먼저 범위 지정 모드 진입
|
||||
if (skill.Data.CastMethod == CastMethod.Remote)
|
||||
{
|
||||
StartAreaSelect(slotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Self 스킬: 바로 ActivationType 흐름
|
||||
BeginActivation(slotIndex);
|
||||
}
|
||||
}
|
||||
else if (inputState == InputState.Canceled)
|
||||
{
|
||||
@@ -111,6 +127,10 @@ public void SkillInput(int slotIndex, InputState inputState)
|
||||
{
|
||||
ReleaseCharge();
|
||||
}
|
||||
else if (_channelingSlot == slotIndex)
|
||||
{
|
||||
CancelChannel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +148,32 @@ public void AreaConfirmInput(InputState inputState)
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 발동 분기
|
||||
/// <summary>
|
||||
/// ActivationType에 따라 적절한 흐름을 시작
|
||||
/// </summary>
|
||||
private void BeginActivation(int slotIndex)
|
||||
{
|
||||
SkillInstance skill = _equippedSkills[slotIndex];
|
||||
|
||||
switch (skill.Data.ActivationType)
|
||||
{
|
||||
case ActivationType.Instant:
|
||||
ExecuteSkill(slotIndex);
|
||||
break;
|
||||
case ActivationType.Charge:
|
||||
StartCharge(slotIndex);
|
||||
break;
|
||||
case ActivationType.Cast:
|
||||
StartCast(slotIndex);
|
||||
break;
|
||||
case ActivationType.Channel:
|
||||
StartChannel(slotIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 실행
|
||||
private bool CanUseSkill(SkillInstance skill)
|
||||
{
|
||||
@@ -136,7 +182,8 @@ private bool CanUseSkill(SkillInstance skill)
|
||||
PlayerState state = _stateMachine.CurrentState;
|
||||
if (state == PlayerState.Dead || state == PlayerState.Hit
|
||||
|| state == PlayerState.Dodge || state == PlayerState.Trans
|
||||
|| state == PlayerState.Action)
|
||||
|| state == PlayerState.Action || state == PlayerState.Cast
|
||||
|| state == PlayerState.Channel)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -159,12 +206,26 @@ private void ExecuteSkill(int slotIndex)
|
||||
chargeRatio = maxCharge > 0 ? Mathf.Clamp01(_chargeTimer / maxCharge) : 1f;
|
||||
}
|
||||
|
||||
effect?.Execute(skill, _transform, chargeRatio);
|
||||
// CastMethod에 따라 실행 방식 분기
|
||||
if (_hasRemoteTarget)
|
||||
{
|
||||
effect?.ExecuteAtPosition(skill, _transform, _remoteTargetPos, chargeRatio);
|
||||
ClearRemoteTarget();
|
||||
}
|
||||
else
|
||||
{
|
||||
effect?.Execute(skill, _transform, chargeRatio);
|
||||
}
|
||||
|
||||
skill.StartCooldown();
|
||||
_chargeTimer = 0f;
|
||||
_chargingSlot = -1;
|
||||
}
|
||||
|
||||
private void ClearRemoteTarget()
|
||||
{
|
||||
_hasRemoteTarget = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 차지
|
||||
@@ -200,7 +261,150 @@ private void TickCharge()
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 범위 지정
|
||||
#region 캐스팅
|
||||
private void StartCast(int slotIndex)
|
||||
{
|
||||
_castingSlot = slotIndex;
|
||||
_castTimer = 0f;
|
||||
|
||||
_stateMachine.ChangeState(PlayerState.Cast);
|
||||
|
||||
SkillInstance skill = _equippedSkills[slotIndex];
|
||||
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
|
||||
_anim.SetTrigger(skill.Data.AnimTrigger);
|
||||
|
||||
_stateMachine.OnStateChanged += OnCastInterrupted;
|
||||
}
|
||||
|
||||
private void TickCast()
|
||||
{
|
||||
if (_castingSlot < 0) return;
|
||||
|
||||
_castTimer += Time.deltaTime;
|
||||
|
||||
SkillInstance skill = _equippedSkills[_castingSlot];
|
||||
if (_castTimer >= skill.CurrentLevelData.CastTime)
|
||||
{
|
||||
CompleteCast();
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteCast()
|
||||
{
|
||||
if (_castingSlot < 0) return;
|
||||
|
||||
_stateMachine.OnStateChanged -= OnCastInterrupted;
|
||||
ExecuteSkill(_castingSlot);
|
||||
_castingSlot = -1;
|
||||
_castTimer = 0f;
|
||||
}
|
||||
|
||||
private void CancelCast()
|
||||
{
|
||||
_stateMachine.OnStateChanged -= OnCastInterrupted;
|
||||
_castingSlot = -1;
|
||||
_castTimer = 0f;
|
||||
ClearRemoteTarget();
|
||||
}
|
||||
|
||||
private void OnCastInterrupted(PlayerState newState)
|
||||
{
|
||||
if (newState == PlayerState.Hit || newState == PlayerState.Dead
|
||||
|| newState == PlayerState.Dodge)
|
||||
{
|
||||
CancelCast();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 채널링
|
||||
private void StartChannel(int slotIndex)
|
||||
{
|
||||
_channelingSlot = slotIndex;
|
||||
_channelTimer = 0f;
|
||||
_channelTickAccumulator = 0f;
|
||||
|
||||
_stateMachine.ChangeState(PlayerState.Channel);
|
||||
|
||||
SkillInstance skill = _equippedSkills[slotIndex];
|
||||
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
|
||||
_anim.SetTrigger(skill.Data.AnimTrigger);
|
||||
|
||||
// 첫 틱 즉시 실행
|
||||
ISkillEffect effect = _skillEffects[slotIndex];
|
||||
ExecuteChannelTick(skill, effect);
|
||||
|
||||
// 쿨다운은 채널 시작 시점에 시작
|
||||
skill.StartCooldown();
|
||||
|
||||
_stateMachine.OnStateChanged += OnChannelInterrupted;
|
||||
}
|
||||
|
||||
private void TickChannel()
|
||||
{
|
||||
if (_channelingSlot < 0) return;
|
||||
|
||||
SkillInstance skill = _equippedSkills[_channelingSlot];
|
||||
SkillLevelData data = skill.CurrentLevelData;
|
||||
|
||||
_channelTimer += Time.deltaTime;
|
||||
_channelTickAccumulator += Time.deltaTime;
|
||||
|
||||
if (_channelTickAccumulator >= data.TickInterval)
|
||||
{
|
||||
_channelTickAccumulator -= data.TickInterval;
|
||||
ISkillEffect effect = _skillEffects[_channelingSlot];
|
||||
ExecuteChannelTick(skill, effect);
|
||||
}
|
||||
|
||||
if (_channelTimer >= data.ChannelDuration)
|
||||
{
|
||||
EndChannel();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteChannelTick(SkillInstance skill, ISkillEffect effect)
|
||||
{
|
||||
if (_hasRemoteTarget)
|
||||
{
|
||||
effect?.ExecuteAtPosition(skill, _transform, _remoteTargetPos, 1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
effect?.Execute(skill, _transform, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
private void EndChannel()
|
||||
{
|
||||
_stateMachine.OnStateChanged -= OnChannelInterrupted;
|
||||
_stateMachine.ChangeState(PlayerState.Idle);
|
||||
_channelingSlot = -1;
|
||||
_channelTimer = 0f;
|
||||
_channelTickAccumulator = 0f;
|
||||
ClearRemoteTarget();
|
||||
}
|
||||
|
||||
private void CancelChannel()
|
||||
{
|
||||
_stateMachine.OnStateChanged -= OnChannelInterrupted;
|
||||
_channelingSlot = -1;
|
||||
_channelTimer = 0f;
|
||||
_channelTickAccumulator = 0f;
|
||||
ClearRemoteTarget();
|
||||
}
|
||||
|
||||
private void OnChannelInterrupted(PlayerState newState)
|
||||
{
|
||||
if (newState == PlayerState.Hit || newState == PlayerState.Dead
|
||||
|| newState == PlayerState.Dodge)
|
||||
{
|
||||
CancelChannel();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 범위 지정 (Remote)
|
||||
private void StartAreaSelect(int slotIndex)
|
||||
{
|
||||
_areaSelectSlot = slotIndex;
|
||||
@@ -229,26 +433,19 @@ private void ConfirmAreaSelect()
|
||||
{
|
||||
if (_areaSelectSlot < 0) return;
|
||||
|
||||
SkillInstance skill = _equippedSkills[_areaSelectSlot];
|
||||
ISkillEffect effect = _skillEffects[_areaSelectSlot];
|
||||
int slotIndex = _areaSelectSlot;
|
||||
|
||||
_stateMachine.ChangeState(PlayerState.Attack);
|
||||
|
||||
if (!string.IsNullOrEmpty(skill.Data.AnimTrigger))
|
||||
_anim.SetTrigger(skill.Data.AnimTrigger);
|
||||
|
||||
Vector3 targetPos = _areaIndicator.transform.position;
|
||||
|
||||
if (effect is AreaEffect areaEffect)
|
||||
{
|
||||
areaEffect.ExecuteAtPosition(skill, _transform, targetPos);
|
||||
}
|
||||
|
||||
skill.StartCooldown();
|
||||
// 타겟 위치 저장
|
||||
_remoteTargetPos = _areaIndicator.transform.position;
|
||||
_hasRemoteTarget = true;
|
||||
|
||||
// 인디케이터 정리
|
||||
Destroy(_areaIndicator);
|
||||
_areaIndicator = null;
|
||||
_areaSelectSlot = -1;
|
||||
|
||||
// ActivationType에 따라 흐름 시작
|
||||
BeginActivation(slotIndex);
|
||||
}
|
||||
|
||||
public void CancelAreaSelect()
|
||||
|
||||
Reference in New Issue
Block a user