using System; namespace Ichni.RhythmGame { public abstract class EffectBase : IBaseElement { #region [枚举定义] Enums public enum EffectState { Before = -1, Middle = 0, After = 1, Error = 100 } #endregion #region [核心属性] Properties public Beatmap.BaseElement_BM matchedBM { get; set; } public GameElement attachedGameElement; /// /// 效果的持续时间,如果为0则表示瞬间效果 /// public float effectTime; /// /// 是否是瞬间效果 /// public bool isInstantEffect => effectTime <= 0; /// /// 效果的进度百分比 /// public float effectProgressPercent; /// /// 效果当前的状态 /// public EffectState nowEffectState; #endregion #region [初始化] Initialization protected EffectBase() { this.effectTime = 0; this.nowEffectState = EffectState.Before; this.effectProgressPercent = 0; } protected EffectBase(float effectTime) { this.effectTime = effectTime; this.nowEffectState = EffectState.Before; this.effectProgressPercent = 0; } #endregion #region [基础逻辑] Main Update Logic public virtual void UpdateEffect(float judgeTime) { float songTime = CoreServices.TimeProvider.SongTime; // 零时长特效必须在到达触发时间的当帧执行。 // 若继续进入 Middle 分支,不仅会把 Adjust() 延迟到下一帧,还会产生 0 / 0 的 NaN 进度。 if (isInstantEffect) { UpdateInstantEffect(judgeTime, songTime); return; } EffectState state = CheckEffectState(judgeTime); if (state == EffectState.Before && nowEffectState != EffectState.Before) { nowEffectState = EffectState.Before; effectProgressPercent = 0; Recover(); } else if (state == EffectState.Middle) { if (nowEffectState == EffectState.Before) { PreExecute(); } nowEffectState = EffectState.Middle; effectProgressPercent = (songTime - judgeTime) / effectTime; Execute(); } else if (state == EffectState.After && nowEffectState != EffectState.After) { nowEffectState = EffectState.After; effectProgressPercent = 1; Adjust(); } } private void UpdateInstantEffect(float triggerTime, float songTime) { if (songTime < triggerTime) { if (nowEffectState != EffectState.Before) { nowEffectState = EffectState.Before; effectProgressPercent = 0; Recover(); } return; } if (nowEffectState == EffectState.After) return; nowEffectState = EffectState.After; effectProgressPercent = 1; Adjust(); } protected virtual EffectState CheckEffectState(float triggerTime) { float songTime = CoreServices.TimeProvider.SongTime; if (songTime < triggerTime) { return EffectState.Before; } if (songTime >= triggerTime && songTime <= triggerTime + effectTime) { return EffectState.Middle; } if (songTime > triggerTime + effectTime) { return EffectState.After; } return EffectState.Error; } #endregion #region [效果重写] Effect Overrides /// /// 当从Before状态进入Middle状态时,仅在效果的开始时触发一次方法 /// public virtual void PreExecute() { } /// /// 在效果的持续时间内,触发这个方法 /// public virtual void Execute() { } /// /// 如果是非瞬间效果,在效果完成后,触发这个方法; /// 如果是瞬间效果,则此方法即为Execute。原有的Execute方法不被调用。 /// public virtual void Adjust() { } /// /// 如果时间轴回退到效果的触发时间之前,则触发这个方法 /// public virtual void Recover() { } /// /// 如果效果被打断,则触发这个方法 /// public virtual void Disrupt() { } public void Refresh() { } #endregion } }