Files
Cielonos/Assets/Scripts/MainGame/AttackArea/Submodules/TimeSubmodule.cs
SoulliesOfficial ddd387ef35 做不出来
2026-06-30 01:48:58 -04:00

204 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using Lean.Pool;
using UnityEngine;
namespace Cielonos.MainGame
{
public partial class TimeSubmodule : AttackAreaSubmoduleBase
{
public float lifeTime;
public float delayTime;
public float enablingTimer;
public float remainingEnableTime;
public float remainingLifeTime;
private bool hasInvokedEnableAction;
public Action enableAction;
public Action timeOutAction;
public List<ScheduledAction> scheduledActions;
private List<ScheduledAction> toBeExecutedScheduledActions = new List<ScheduledAction>();
/// <summary>
/// enable 阶段开始前允许反应的提前量(秒),默认 0 表示无提前 grace window。
/// </summary>
public float reactionGraceBefore;
public TimeSubmodule(AttackAreaBase attackArea, float lifeTime, Action timeOutAction = null) : base(attackArea)
{
this.isEnabling = true;
this.lifeTime = lifeTime;
this.remainingLifeTime = lifeTime;
this.enablingTimer = 0;
this.scheduledActions = new List<ScheduledAction>();
if (attackArea is NormalArea)
{
this.remainingEnableTime = 0.04f;
}
else if (attackArea is Projectile)
{
this.remainingEnableTime = lifeTime;
}
else
{
this.remainingEnableTime = lifeTime;
}
this.timeOutAction = timeOutAction ?? (() =>
{
if (attackArea is Projectile projectile)
{
projectile.Explode(projectile.transform.position);
}
else
{
LeanPool.Despawn(attackArea.topParent.gameObject);
}
});
this.reactionGraceBefore = 0f;
}
public TimeSubmodule(AttackAreaBase attackArea, float lifeTime, float delayTime, float enableTime,
Action enableAction, Action timeOutAction, float graceBefore = 0f) : base(attackArea)
{
this.isEnabling = true;
this.lifeTime = lifeTime;
this.delayTime = delayTime;
this.attackArea.isEnabling = delayTime <= 0;
this.scheduledActions = new List<ScheduledAction>();
this.timeOutAction = timeOutAction ?? (() =>
{
if (attackArea is Projectile projectile)
{
projectile.Explode(projectile.transform.position);
}
else
{
LeanPool.Despawn(attackArea.topParent.gameObject);
}
});
if (this.attackArea.isEnabling)
{
enableAction?.Invoke();
hasInvokedEnableAction = true;
}
this.remainingLifeTime = lifeTime;
this.enablingTimer = 0;
this.remainingEnableTime = enableTime;
this.enableAction = enableAction;
this.reactionGraceBefore = graceBefore;
}
public TimeSubmodule AddScheduleAction(Action action, float delay)
{
scheduledActions.Add(new ScheduledAction(action, delay));
return this;
}
/// <summary>
/// 判断当前时刻是否处于反应窗口内(包含 grace 区间和 enable 阶段本身)。
/// before grace: delay 阶段末尾的 reactionGraceBefore 秒内。
/// after grace: enable 结束后的 reactionGraceAfter 秒内。
/// </summary>
public bool IsReactionActive()
{
// 在 enable 阶段本身,反应始终可用
if (attackArea.isEnabling)
{
return true;
}
// before grace: delay 尚未结束,但已进入 grace 窗口
if (delayTime > 0f && reactionGraceBefore > 0f && delayTime <= reactionGraceBefore)
{
return true;
}
// 过渡帧桥接delay 刚结束delayTime 被扣至 <=0但 enable 阶段尚未在下一帧
// 的 timeSm.Update() 中激活。此帧 isEnabling 为 false 且 delayTime 不再 >0
// 若不补此条件会导致 1 帧的反应窗口真空,玩家的完美闪避/格挡可能被跳过。
if (reactionGraceBefore > 0f && delayTime <= 0f && !hasInvokedEnableAction)
{
return true;
}
return false;
}
}
public partial class TimeSubmodule
{
public void Update()
{
if (!isEnabling) return;
if (delayTime > 0)
{
delayTime -= attackArea.creator.selfTimeSm.DeltaTime;
return;
}
if (!hasInvokedEnableAction)
{
attackArea.isEnabling = true;
enableAction?.Invoke();
hasInvokedEnableAction = true;
}
toBeExecutedScheduledActions.Clear();
foreach (var scheduledAction in scheduledActions)
{
scheduledAction.delay -= attackArea.creator.selfTimeSm.DeltaTime;
if (scheduledAction.delay <= 0)
{
toBeExecutedScheduledActions.Add(scheduledAction);
}
}
foreach (var scheduledAction in toBeExecutedScheduledActions)
{
scheduledAction.action.Invoke();
scheduledActions.Remove(scheduledAction);
}
if (remainingLifeTime <= 0)
{
this.isEnabling = false;
timeOutAction?.Invoke();
}
if (remainingEnableTime <= 0)
{
attackArea.isEnabling = false;
}
enablingTimer += attackArea.creator.selfTimeSm.DeltaTime;
remainingLifeTime -= attackArea.creator.selfTimeSm.DeltaTime;
remainingEnableTime -= attackArea.creator.selfTimeSm.DeltaTime;
}
public void ModifyLifeTime(float modifyValue)
{
lifeTime += modifyValue;
remainingLifeTime += modifyValue;
}
}
public partial class TimeSubmodule
{
public class ScheduledAction
{
public Action action;
public float delay;
public ScheduledAction(Action action, float delay)
{
this.action = action;
this.delay = delay;
}
}
}
}