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 scheduledActions; private List toBeExecutedScheduledActions = new List(); /// /// enable 阶段开始前允许反应的提前量(秒),默认 0 表示无提前 grace window。 /// 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(); 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(); 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; } /// /// 判断当前时刻是否处于反应窗口内(包含 grace 区间和 enable 阶段本身)。 /// before grace: delay 阶段末尾的 reactionGraceBefore 秒内。 /// after grace: enable 结束后的 reactionGraceAfter 秒内。 /// 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; } } } }