同步
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TrackGlobalColorChange_BM : AnimationBase_BM
|
||||
{
|
||||
public FlexibleFloat_BM colorR, colorG, colorB, colorA;
|
||||
|
||||
public TrackGlobalColorChange_BM()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TrackGlobalColorChange_BM(string elementName, Guid elementGuid, List<string> tags,
|
||||
GameElement_BM attachedElement, FlexibleFloat_BM colorR, FlexibleFloat_BM colorG, FlexibleFloat_BM colorB, FlexibleFloat_BM colorA)
|
||||
: base(elementName, elementGuid, tags, attachedElement)
|
||||
{
|
||||
this.colorR = colorR;
|
||||
this.colorG = colorG;
|
||||
this.colorB = colorB;
|
||||
this.colorA = colorA;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
{
|
||||
matchedElement = TrackGlobalColorChange.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid),
|
||||
colorR.ConvertToGameType(), colorG.ConvertToGameType(), colorB.ConvertToGameType(), colorA.ConvertToGameType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68d836b6038e9744a83ffc7d486c53f0
|
||||
@@ -3,7 +3,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class ColorSubmodule_BM : Submodule_BM
|
||||
public class ColorSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public Color originalBaseColor;
|
||||
public bool emissionEnabled;
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class EffectSubmodule_BM : Submodule_BM
|
||||
public class EffectSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public Dictionary<string, List<EffectBase_BM>> effectCollection;
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public abstract class Submodule_BM : BaseElement_BM
|
||||
public abstract class SubmoduleBase_BM : BaseElement_BM
|
||||
{
|
||||
[System.NonSerialized] protected GameElement attachedElement; //存档类对应的游戏物体
|
||||
|
||||
public Submodule_BM()
|
||||
public SubmoduleBase_BM()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Submodule_BM(GameElement attachedElement)
|
||||
public SubmoduleBase_BM(GameElement attachedElement)
|
||||
{
|
||||
this.attachedElement = attachedElement;
|
||||
attachedElementGuid = attachedElement.elementGuid;
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TimeDurationSubmodule_BM : Submodule_BM
|
||||
public class TimeDurationSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public bool isOverridingDuration;
|
||||
public float startTime, endTime;
|
||||
|
||||
@@ -2,7 +2,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TransformSubmodule_BM : Submodule_BM
|
||||
public class TransformSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public Vector3 originalPosition;
|
||||
public Vector3 originalEulerAngles;
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class NoteAudioSubmodule_BM : Submodule_BM
|
||||
public class NoteAudioSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public List<string> generalJudgeAudioList;
|
||||
public List<string> perfectAudioList;
|
||||
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class NoteJudgeSubmodule_BM : Submodule_BM
|
||||
public class NoteJudgeSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public List<NoteJudgeUnit_BM> judgeUnitList;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class NoteJudgeTriggerSubmodule_BM : Submodule_BM
|
||||
public class NoteJudgeTriggerSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public NoteJudgeTriggerSubmodule_BM()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TrackPathSubmodule_BM : Submodule_BM
|
||||
public class TrackPathSubmodule_BM : SubmoduleBase_BM
|
||||
{
|
||||
public Track.TrackSpaceType trackSpaceType;
|
||||
public Track.TrackSamplingType trackSamplingType;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using Dreamteck.Splines;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TrackRendererSubmoduleAutoOrient_BM : Submodule_BM
|
||||
public class TrackRendererSubmoduleAutoOrient_BM : SubmoduleBase_BM
|
||||
{
|
||||
public string materialThemeBundleName;
|
||||
public string materialName;
|
||||
@@ -12,6 +13,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
public bool zWrite;
|
||||
public Vector2 uvScale = Vector2.one;
|
||||
public Vector2 uvOffset = Vector2.zero;
|
||||
public string customTextureThemeBundleName = "None";
|
||||
public string customTextureName = "None";
|
||||
public MeshGenerator.UVMode uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
public float uvRotation = 0f;
|
||||
public float size = 1f;
|
||||
|
||||
public TrackRendererSubmoduleAutoOrient_BM() { }
|
||||
|
||||
@@ -25,6 +31,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
zWrite = trSubmodule.zWrite;
|
||||
uvScale = trSubmodule.uvScale;
|
||||
uvOffset = trSubmodule.uvOffset;
|
||||
customTextureThemeBundleName = trSubmodule.customTextureThemeBundleName;
|
||||
customTextureName = trSubmodule.customTextureName;
|
||||
uvMode = trSubmodule.uvMode;
|
||||
uvRotation = trSubmodule.uvRotation;
|
||||
size = trSubmodule.size;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
@@ -33,6 +44,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
if (attachedElement is Track track)
|
||||
{
|
||||
track.trackRendererSubmodule = new TrackRendererSubmoduleAutoOrient(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset);
|
||||
track.trackRendererSubmodule.customTextureThemeBundleName = customTextureThemeBundleName;
|
||||
track.trackRendererSubmodule.customTextureName = customTextureName;
|
||||
track.trackRendererSubmodule.uvMode = uvMode;
|
||||
track.trackRendererSubmodule.uvRotation = uvRotation;
|
||||
track.trackRendererSubmodule.size = size;
|
||||
if (materialName.Trim() != String.Empty)
|
||||
{
|
||||
track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName);
|
||||
@@ -41,7 +57,7 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackRendererSubmodulePathGenerator_BM : Submodule_BM
|
||||
public class TrackRendererSubmodulePathGenerator_BM : SubmoduleBase_BM
|
||||
{
|
||||
public string materialThemeBundleName;
|
||||
public string materialName;
|
||||
@@ -50,6 +66,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
public bool zWrite;
|
||||
public Vector2 uvScale = Vector2.one;
|
||||
public Vector2 uvOffset = Vector2.zero;
|
||||
public string customTextureThemeBundleName = "None";
|
||||
public string customTextureName = "None";
|
||||
public MeshGenerator.UVMode uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
public float uvRotation = 90f;
|
||||
public float size = 1f;
|
||||
|
||||
public TrackRendererSubmodulePathGenerator_BM() { }
|
||||
|
||||
@@ -63,6 +84,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
zWrite = trSubmodule.zWrite;
|
||||
uvScale = trSubmodule.uvScale;
|
||||
uvOffset = trSubmodule.uvOffset;
|
||||
customTextureThemeBundleName = trSubmodule.customTextureThemeBundleName;
|
||||
customTextureName = trSubmodule.customTextureName;
|
||||
uvMode = trSubmodule.uvMode;
|
||||
uvRotation = trSubmodule.uvRotation;
|
||||
size = trSubmodule.size;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
@@ -71,6 +97,11 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
if (attachedElement is Track track)
|
||||
{
|
||||
track.trackRendererSubmodule = new TrackRendererSubmodulePathGenerator(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset);
|
||||
track.trackRendererSubmodule.customTextureThemeBundleName = customTextureThemeBundleName;
|
||||
track.trackRendererSubmodule.customTextureName = customTextureName;
|
||||
track.trackRendererSubmodule.uvMode = uvMode;
|
||||
track.trackRendererSubmodule.uvRotation = uvRotation;
|
||||
track.trackRendererSubmodule.size = size;
|
||||
if (materialName.Trim() != String.Empty)
|
||||
{
|
||||
track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName);
|
||||
@@ -79,7 +110,7 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackRendererSubmoduleSurface_BM : Submodule_BM
|
||||
public class TrackRendererSubmoduleSurface_BM : SubmoduleBase_BM
|
||||
{
|
||||
public string materialThemeBundleName;
|
||||
public string materialName;
|
||||
@@ -88,6 +119,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
public bool zWrite;
|
||||
public Vector2 uvScale = Vector2.one;
|
||||
public Vector2 uvOffset = Vector2.zero;
|
||||
public MeshGenerator.UVMode uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
public float uvRotation = 0f;
|
||||
public float size = 1f;
|
||||
|
||||
public TrackRendererSubmoduleSurface_BM() { }
|
||||
|
||||
@@ -101,6 +135,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
zWrite = trSubmodule.zWrite;
|
||||
uvScale = trSubmodule.uvScale;
|
||||
uvOffset = trSubmodule.uvOffset;
|
||||
uvMode = trSubmodule.uvMode;
|
||||
uvRotation = trSubmodule.uvRotation;
|
||||
size = trSubmodule.size;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
@@ -109,6 +146,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
if (attachedElement is Track track)
|
||||
{
|
||||
track.trackRendererSubmodule = new TrackRendererSubmoduleSurface(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset);
|
||||
track.trackRendererSubmodule.uvMode = uvMode;
|
||||
track.trackRendererSubmodule.uvRotation = uvRotation;
|
||||
track.trackRendererSubmodule.size = size;
|
||||
if (materialName.Trim() != String.Empty)
|
||||
{
|
||||
track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName);
|
||||
@@ -117,7 +157,7 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackRendererSubmoduleTubeGenerator_BM : Submodule_BM
|
||||
public class TrackRendererSubmoduleTubeGenerator_BM : SubmoduleBase_BM
|
||||
{
|
||||
public string materialThemeBundleName;
|
||||
public string materialName;
|
||||
@@ -127,6 +167,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
public int sideCount;
|
||||
public Vector2 uvScale = Vector2.one;
|
||||
public Vector2 uvOffset = Vector2.zero;
|
||||
public MeshGenerator.UVMode uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
public float uvRotation = 0f;
|
||||
public float size = 1f;
|
||||
|
||||
public TrackRendererSubmoduleTubeGenerator_BM() { }
|
||||
|
||||
@@ -141,6 +184,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
zWrite = trSubmodule.zWrite;
|
||||
uvScale = trSubmodule.uvScale;
|
||||
uvOffset = trSubmodule.uvOffset;
|
||||
uvMode = trSubmodule.uvMode;
|
||||
uvRotation = trSubmodule.uvRotation;
|
||||
size = trSubmodule.size;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
@@ -149,6 +195,9 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
if (attachedElement is Track track)
|
||||
{
|
||||
track.trackRendererSubmodule = new TrackRendererSubmoduleTubeGenerator(track, enableEmission, emissionIntensity, zWrite, sideCount, uvScale, uvOffset);
|
||||
track.trackRendererSubmodule.uvMode = uvMode;
|
||||
track.trackRendererSubmodule.uvRotation = uvRotation;
|
||||
track.trackRendererSubmodule.size = size;
|
||||
if (materialName.Trim() != String.Empty)
|
||||
{
|
||||
track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TrackTimeSubmoduleMovable_BM : Submodule_BM
|
||||
public class TrackTimeSubmoduleMovable_BM : SubmoduleBase_BM
|
||||
{
|
||||
public float trackStartTime;
|
||||
public float trackEndTime;
|
||||
@@ -30,7 +30,7 @@ namespace Ichni.RhythmGame.Beatmap
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackTimeSubmoduleStatic_BM : Submodule_BM
|
||||
public class TrackTimeSubmoduleStatic_BM : SubmoduleBase_BM
|
||||
{
|
||||
public float trackTotalTime;
|
||||
public AnimationCurveType animationCurveType;
|
||||
|
||||
@@ -5,7 +5,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public abstract partial class AnimationBase : GameElement, IHaveTimeDurationSubmodule
|
||||
public abstract partial class AnimationBase : GameElement, IHaveTimeDurationSubmodule, IScheduledElement
|
||||
{
|
||||
#region [属性和相关对象] Attributes & Related Objects
|
||||
public GameElement animatedObject;
|
||||
@@ -27,8 +27,7 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
base.AfterInitialize();
|
||||
|
||||
// 【新增】受管家管控
|
||||
GameManager.Instance.animationManager.RegisterAnimation(this);
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Animation, this);
|
||||
float delay = GameManager.Instance.songInformation.delay;
|
||||
if (timeDurationSubmodule.CheckTimeInDuration(-delay))
|
||||
{
|
||||
@@ -36,6 +35,12 @@ namespace Ichni.RhythmGame
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Animation, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新动画
|
||||
/// </summary>
|
||||
@@ -51,10 +56,19 @@ namespace Ichni.RhythmGame
|
||||
|
||||
if (timeDurationSubmodule.CheckAfterEndTime(currentSongTime))
|
||||
{
|
||||
GameManager.Instance.animationManager.UnregisterAnimation(this);
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Animation, this);
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public virtual void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
ManualUpdate(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 施加时间偏移,即移动所有Flexible参数的时间
|
||||
/// </summary>
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Ichni.RhythmGame
|
||||
if (forceUpdate || fieldOfView.returnType == FlexibleReturnType.MiddleExecuting)
|
||||
{
|
||||
targetGameCamera.perspectiveAngle = fieldOfView.value;
|
||||
targetGameCamera.cam.fieldOfView = fieldOfView.value;
|
||||
targetGameCamera.RefreshFOV();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
@@ -89,32 +87,4 @@ namespace Ichni.RhythmGame
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
namespace Ichni.RhythmGame.Beatmap
|
||||
{
|
||||
public class TrackGlobalColorChange_BM : AnimationBase_BM
|
||||
{
|
||||
public FlexibleFloat_BM colorR, colorG, colorB, colorA;
|
||||
|
||||
public TrackGlobalColorChange_BM()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TrackGlobalColorChange_BM(string elementName, Guid elementGuid, List<string> tags,
|
||||
GameElement_BM attachedElement, FlexibleFloat_BM colorR, FlexibleFloat_BM colorG, FlexibleFloat_BM colorB, FlexibleFloat_BM colorA)
|
||||
: base(elementName, elementGuid, tags, attachedElement)
|
||||
{
|
||||
this.colorR = colorR;
|
||||
this.colorG = colorG;
|
||||
this.colorB = colorB;
|
||||
this.colorA = colorA;
|
||||
}
|
||||
|
||||
public override void ExecuteBM()
|
||||
{
|
||||
matchedElement = TrackGlobalColorChange.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid),
|
||||
colorR.ConvertToGameType(), colorG.ConvertToGameType(), colorB.ConvertToGameType(), colorA.ConvertToGameType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,16 +45,44 @@ namespace Ichni.RhythmGame
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
// 注册 Phase 7(Effect),确保在 Track/TrackFollower(Phase 4/5)和 Note(Phase 6)
|
||||
// 全部更新完毕后再计算旋转覆盖,避免因位置尚未就绪导致的旋转抖动
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
public override void ManualUpdate(float songTime, bool forceUpdate = false)
|
||||
{
|
||||
base.ManualUpdate(songTime, forceUpdate);
|
||||
// 动画结束时同步注销 Phase 7
|
||||
if (timeDurationSubmodule.CheckAfterEndTime(songTime))
|
||||
{
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Effect, this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [核心动画逻辑] Core Animation Logic
|
||||
void LateUpdate()
|
||||
public override void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
if (enabling.value)
|
||||
switch (phase)
|
||||
{
|
||||
(animatedObject as IHaveTransformSubmodule)?.UpdateLookAt(this);
|
||||
case UpdatePhase.Animation:
|
||||
base.ScheduledUpdate(phase, songTime);
|
||||
break;
|
||||
case UpdatePhase.Effect:
|
||||
// 在所有 Track/TrackFollower/Note 位置更新完毕后覆盖旋转
|
||||
if (enabling.value)
|
||||
{
|
||||
(animatedObject as IHaveTransformSubmodule)?.UpdateLookAt(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量级核心服务定位器,允许游戏与编辑器分别注册自己的的时间引擎
|
||||
/// 轻量级核心服务定位器,允许游戏本体与谱面编辑器分别注册自己的时间引擎。
|
||||
/// </summary>
|
||||
public static class CoreServices
|
||||
{
|
||||
public static ISongTimeProvider TimeProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 集中式元素更新调度器。
|
||||
/// 所有 GameElement 子类应通过此属性访问调度器进行 Register / Unregister,
|
||||
/// 而非直接引用 EditorManager 或 GameManager。
|
||||
/// </summary>
|
||||
public static ElementUpdateScheduler UpdateScheduler { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 全局时间供应器接口
|
||||
/// 无论是在游戏本体还是在谱面编辑器,一切与时间强相关的运作(特效、生成、判定)
|
||||
/// 全局时间供应器接口。
|
||||
/// 无论是在游戏本体还是在谱面编辑器,一切与时间强相关的运作(特效、动画、生成)
|
||||
/// 只能依赖此接口,严禁直接调用 GameManager!
|
||||
/// </summary>
|
||||
public interface ISongTimeProvider
|
||||
{
|
||||
/// <summary>当前音频的播放进度时间 (秒)</summary>
|
||||
/// <summary>当前音频的播放进度时间(秒),已扣除 offset</summary>
|
||||
float SongTime { get; }
|
||||
|
||||
|
||||
/// <summary>当前时间轴是否处于流转播放状态</summary>
|
||||
bool IsPlaying { get; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
22
Assets/Scripts/Game/Base/Interfaces/IScheduledElement.cs
Normal file
22
Assets/Scripts/Game/Base/Interfaces/IScheduledElement.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
/// <summary>
|
||||
/// 所有参与集中更新调度的元素需实现的接口。
|
||||
/// 同一元素可注册到多个 <see cref="UpdatePhase"/>,
|
||||
/// 通过 <paramref name="phase"/> 参数区分当前所处阶段并执行对应逻辑。
|
||||
/// </summary>
|
||||
public interface IScheduledElement
|
||||
{
|
||||
/// <summary>
|
||||
/// 由 <see cref="ElementUpdateScheduler"/> 在对应阶段调用。
|
||||
/// </summary>
|
||||
/// <param name="phase">当前执行的更新阶段</param>
|
||||
/// <param name="songTime">当前音频播放时间(秒)</param>
|
||||
void ScheduledUpdate(UpdatePhase phase, float songTime);
|
||||
|
||||
/// <summary>
|
||||
/// 元素是否处于活跃状态。调度器跳过非活跃元素以节省开销。
|
||||
/// </summary>
|
||||
bool IsScheduledActive { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b0489cd9b9f44b4f9a7c73cf2b07de2
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
@@ -21,7 +20,7 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
gameElementList = new List<GameElement>();
|
||||
lowPriorityActions = new List<UnityAction>();
|
||||
Observable.EveryUpdate().Subscribe(_ => ExecuteLowPriorityActions());
|
||||
// UniRx Observable.EveryUpdate 已移除:低优先级操作由 ElementUpdateScheduler Phase 8 在 TickLate 中驱动
|
||||
}
|
||||
|
||||
public void ExecuteLowPriorityActions()
|
||||
|
||||
38
Assets/Scripts/Game/Base/UpdatePhase.cs
Normal file
38
Assets/Scripts/Game/Base/UpdatePhase.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
/// <summary>
|
||||
/// 集中式更新调度器的阶段定义。
|
||||
/// 每帧按数值升序执行,保证严格的依赖顺序:
|
||||
/// 动画先于变换应用 → 变换先于 Spline 重建 → 轨道先于音符。
|
||||
/// 数值留有间隔,便于未来插入新阶段。
|
||||
/// </summary>
|
||||
public enum UpdatePhase
|
||||
{
|
||||
/// <summary>判定元素激活/隐藏状态</summary>
|
||||
TimeDuration = 0,
|
||||
|
||||
/// <summary>更新动画值,设置脏标记</summary>
|
||||
Animation = 10,
|
||||
|
||||
/// <summary>执行 DirtyRefresh + Transform + Color</summary>
|
||||
Apply = 20,
|
||||
|
||||
/// <summary>手动重建 Dreamteck SplineComputer;同时执行 LookAt 等 Transform 后处理覆盖</summary>
|
||||
SplineRebuild = 30,
|
||||
|
||||
/// <summary>更新轨道时间、裁剪区间</summary>
|
||||
TrackCore = 40,
|
||||
|
||||
/// <summary>更新轨道跟踪器(CrossTrackPoint / HeadPoint / PercentPoint 等)</summary>
|
||||
TrackFollower = 50,
|
||||
|
||||
/// <summary>音符可见性、轨道位置、判定、特效</summary>
|
||||
Note = 60,
|
||||
|
||||
/// <summary>ParticleEmitter / TimeEffectsCollection / ParticleTracker 等特效</summary>
|
||||
Effect = 70,
|
||||
|
||||
/// <summary>SkyboxSubsetter / LowPriorityActions 等杂项</summary>
|
||||
Misc = 80
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Game/Base/UpdatePhase.cs.meta
Normal file
2
Assets/Scripts/Game/Base/UpdatePhase.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b8c1372937779c4ab43eeddbce7cf29
|
||||
@@ -1,18 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Dodger : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class SkyboxSubsetter : GameElement
|
||||
public partial class SkyboxSubsetter : GameElement, IScheduledElement
|
||||
{
|
||||
#region [核心组件与天空盒列表] Core Components & Skybox Lists
|
||||
public SkyboxBlender skyboxBlender;
|
||||
@@ -46,6 +46,18 @@ namespace Ichni.RhythmGame
|
||||
skyboxSubsetter.selectedSkybox = String.Empty;
|
||||
return skyboxSubsetter;
|
||||
}
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Misc, this);
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Misc, this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [内部设置与工具] Internal Configs & Utils
|
||||
@@ -81,28 +93,31 @@ namespace Ichni.RhythmGame
|
||||
#endregion
|
||||
|
||||
#region [轮询更新] Main Update
|
||||
private void Update()
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
if (skyBoxThemeBundleList.Count > 1)
|
||||
{
|
||||
float songTime = CoreServices.TimeProvider.SongTime;
|
||||
float delay = GameManager.Instance.songInformation.delay;
|
||||
float finalTime = 32767; // 曲目长度
|
||||
const float finalTime = 32767f; // 曲目长度上限
|
||||
|
||||
for (var index = 0; index < blendTimeList.Count + 1; index++)
|
||||
{
|
||||
float startTime = index == 0 ? -delay : blendTimeList[index - 1];
|
||||
float endTime = index >= blendTimeList.Count ? finalTime : blendTimeList[index];
|
||||
if(songTime >= startTime && songTime < endTime && currentSkyboxIndex != index)
|
||||
if (songTime >= startTime && songTime < endTime && currentSkyboxIndex != index)
|
||||
{
|
||||
currentSkyboxIndex = index;
|
||||
if(currentSkyboxIndex != 0) skyboxBlender.blendSpeed = blendSpeedList[currentSkyboxIndex - 1];
|
||||
if (currentSkyboxIndex != 0) skyboxBlender.blendSpeed = blendSpeedList[currentSkyboxIndex - 1];
|
||||
skyboxBlender.Blend(currentSkyboxIndex, false);
|
||||
DynamicGI.UpdateEnvironment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace Ichni.RhythmGame
|
||||
public float orthographicSize;
|
||||
|
||||
public float perspectiveOffset;
|
||||
public float zoomOffset; // 用于效果(如CameraZoomEffect)的临时视野偏移
|
||||
|
||||
public void RefreshFOV()
|
||||
{
|
||||
cam.fieldOfView = perspectiveAngle + perspectiveOffset + zoomOffset;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [子模块接口与关联引用] Submodules & References
|
||||
@@ -54,14 +60,14 @@ namespace Ichni.RhythmGame
|
||||
float ratioDifference = UIManager.GetScreenRatio() - UIManager.StandardRatio;
|
||||
if (ratioDifference > 0)
|
||||
{
|
||||
gameCamera.perspectiveOffset = -22f * ratioDifference;
|
||||
//gameCamera.perspectiveOffset = 12.5f * ratioDifference;
|
||||
}
|
||||
else
|
||||
{
|
||||
//gameCamera.perspectiveOffset = 11f * ratioDifference;
|
||||
gameCamera.perspectiveOffset = -25f * ratioDifference;
|
||||
}
|
||||
|
||||
gameCamera.cam.fieldOfView = perspectiveAngle + gameCamera.perspectiveOffset;
|
||||
gameCamera.RefreshFOV();
|
||||
return gameCamera;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Object = UnityEngine.Object;
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
|
||||
public partial class ParticleEmitter : GameElement, IHaveParticles, IHaveTimeDurationSubmodule, IHaveTransformSubmodule, IHaveColorSubmodule
|
||||
public partial class ParticleEmitter : GameElement, IHaveParticles, IHaveTimeDurationSubmodule, IHaveTransformSubmodule, IHaveColorSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public string themeBundleName;
|
||||
@@ -85,15 +85,26 @@ namespace Ichni.RhythmGame
|
||||
transformSubmodule = new TransformSubmodule(this);
|
||||
colorSubmodule = new ColorSubmodule(this, Color.white, true, Color.white, 0);
|
||||
}
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Effect, this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region [轮询更新与交互重写] Main Update & Behavior Overrides
|
||||
public partial class ParticleEmitter
|
||||
{
|
||||
private void Update()
|
||||
private void UpdateParticlePlayState(float songTime)
|
||||
{
|
||||
float songTime = CoreServices.TimeProvider.SongTime;
|
||||
if (playTime > songTime || stopTime < songTime)
|
||||
{
|
||||
particle.Stop();
|
||||
@@ -107,6 +118,15 @@ namespace Ichni.RhythmGame
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
UpdateParticlePlayState(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
@@ -8,12 +8,18 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class TimeEffectsCollection : GameElement, IHaveTransformSubmodule, IHaveEffectSubmodule
|
||||
public partial class TimeEffectsCollection : GameElement, IHaveTransformSubmodule, IHaveEffectSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public float time; //触发效果的时间
|
||||
#endregion
|
||||
|
||||
#region [运行时缓存] Cached Effect Lists
|
||||
private List<EffectBase> _priorEffects;
|
||||
private List<EffectBase> _defaultEffects;
|
||||
private List<EffectBase> _lateEffects;
|
||||
#endregion
|
||||
|
||||
#region [子模块接口] Submodules
|
||||
public TransformSubmodule transformSubmodule { get; set; }
|
||||
public EffectSubmodule effectSubmodule { get; set; }
|
||||
@@ -34,31 +40,55 @@ namespace Ichni.RhythmGame
|
||||
transformSubmodule = new TransformSubmodule(this);
|
||||
effectSubmodule = new EffectSubmodule(this);
|
||||
}
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
CacheEffectLists();
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存 effectCollection 中的 Prior/Default/Late 列表引用,
|
||||
/// 避免 ScheduledUpdate 中每帧执行 Dictionary string key 查找。
|
||||
/// </summary>
|
||||
private void CacheEffectLists()
|
||||
{
|
||||
if (effectSubmodule?.effectCollection == null) return;
|
||||
effectSubmodule.effectCollection.TryGetValue("Prior", out _priorEffects);
|
||||
effectSubmodule.effectCollection.TryGetValue("Default", out _defaultEffects);
|
||||
effectSubmodule.effectCollection.TryGetValue("Late", out _lateEffects);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [轮询更新] Main Update
|
||||
private void Update()
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
if (!GameManager.Instance.songPlayer.isUpdating || effectSubmodule == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (effectSubmodule == null) return;
|
||||
|
||||
foreach (EffectBase effect in effectSubmodule.effectCollection["Prior"])
|
||||
{
|
||||
effect.UpdateEffect(time);
|
||||
}
|
||||
UpdateEffectList(_priorEffects, time);
|
||||
UpdateEffectList(_defaultEffects, time);
|
||||
UpdateEffectList(_lateEffects, time);
|
||||
}
|
||||
|
||||
foreach (EffectBase effect in effectSubmodule.effectCollection["Default"])
|
||||
private static void UpdateEffectList(List<EffectBase> effects, float effectTime)
|
||||
{
|
||||
if (effects == null) return;
|
||||
for (int i = 0; i < effects.Count; i++)
|
||||
{
|
||||
effect.UpdateEffect(time);
|
||||
}
|
||||
|
||||
foreach (EffectBase effect in effectSubmodule.effectCollection["Late"])
|
||||
{
|
||||
effect.UpdateEffect(time);
|
||||
effects[i].UpdateEffect(effectTime);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Ichni.RhythmGame
|
||||
public override bool CheckJudgeAvailability(InputUnit inputUnit)
|
||||
{
|
||||
Vector2 inputScreenPosition = inputUnit.inputPosition;
|
||||
Vector2 noteScreenPosition = note.GetScreenPosition();
|
||||
Vector2 noteScreenPosition = note.noteScreenPosition != Vector2.zero ? note.noteScreenPosition : note.GetScreenPosition();
|
||||
float scaledBaseRadius = areaRadius * CurrentScreenRatio;
|
||||
|
||||
float dx = Mathf.Abs(inputScreenPosition.x - noteScreenPosition.x) / ellipseXMultiplier;
|
||||
|
||||
@@ -295,6 +295,7 @@ namespace Ichni.RhythmGame
|
||||
protected virtual void ExecuteJudge(NoteJudgeType judgeType, float triggerTime)
|
||||
{
|
||||
isDuringJudging = false;
|
||||
isFirstJudged = true;
|
||||
judgedTriggerTime = triggerTime;
|
||||
judgedType = judgeType;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class Track : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule
|
||||
public partial class Track : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public GameObject trackRenderer;
|
||||
@@ -50,7 +50,11 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
base.AfterInitialize();
|
||||
|
||||
GameManager.Instance.trackManager.RegisterTrack(this);
|
||||
// 保留 TrackManager 注册以维持 ManualLateUpdate 的 refreshedThisFrame 清除逻辑
|
||||
GameManager.Instance.trackManager.RegisterTrack(this);
|
||||
// 注册调度器 Phase 4(TrackCore)驱动 ManualUpdate
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.TrackCore, this);
|
||||
CoreServices.UpdateScheduler.RegisterTrackSpline(this);
|
||||
|
||||
if (trackPathSubmodule != null && trackPathSubmodule.pathNodeList.Count > 3)
|
||||
{
|
||||
@@ -77,6 +81,15 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
if(trackPathSubmodule != null) trackPathSubmodule.refreshedThisFrame = false;
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
ManualUpdate(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region [行为重写] Behavior Overrides
|
||||
@@ -91,6 +104,8 @@ namespace Ichni.RhythmGame
|
||||
public override void OnDelete()
|
||||
{
|
||||
GameManager.Instance.trackManager.UnregisterTrack(this);
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackCore, this);
|
||||
CoreServices.UpdateScheduler.UnregisterTrackSpline(this);
|
||||
if (parentElement is ElementFolder folder) folder.trackList.Remove(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -9,7 +9,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class CrossTrackPoint : GameElement, IHaveTimeDurationSubmodule
|
||||
public partial class CrossTrackPoint : GameElement, IHaveTimeDurationSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public ElementFolder trackListFolder;
|
||||
@@ -45,10 +45,16 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
GameManager.Instance.trackManager.RegisterCrossPoint(this);
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.TrackFollower, this);
|
||||
base.AfterInitialize();
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
|
||||
public override void SetDefaultSubmodules()
|
||||
{
|
||||
timeDurationSubmodule = new TimeDurationSubmodule(this);
|
||||
@@ -68,11 +74,20 @@ namespace Ichni.RhythmGame
|
||||
trackPercent.returnType == FlexibleReturnType.After)
|
||||
{
|
||||
trackPositioner.SetPercent(1);
|
||||
GameManager.Instance.trackManager.UnregisterCrossPoint(this);
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
ManualUpdate(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
|
||||
private void SetPoint()
|
||||
{
|
||||
if (nowAttachedTrackIndex != trackSwitch.value &&
|
||||
@@ -82,6 +97,7 @@ namespace Ichni.RhythmGame
|
||||
nowAttachedTrack = trackListFolder.trackList[trackSwitch.value];
|
||||
nowAttachedTrackIndex = trackSwitch.value;
|
||||
trackPositioner.spline = trackListFolder.trackList[trackSwitch.value].trackPathSubmodule.path;
|
||||
trackPositioner.RebuildImmediate();
|
||||
}
|
||||
|
||||
trackPositioner.SetPercent(trackPercent.value);
|
||||
|
||||
@@ -8,7 +8,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class TrackHeadPoint : GameElement, IHaveTimeDurationSubmodule
|
||||
public partial class TrackHeadPoint : GameElement, IHaveTimeDurationSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public Track track;
|
||||
@@ -53,9 +53,15 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
GameManager.Instance.trackManager.RegisterHeadPoint(this);
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.TrackFollower, this);
|
||||
base.AfterInitialize();
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [轮询更新] Main Update
|
||||
@@ -68,9 +74,18 @@ namespace Ichni.RhythmGame
|
||||
|
||||
if(track.timeDurationSubmodule.CheckAfterEndTime(currentSongTime))
|
||||
{
|
||||
GameManager.Instance.trackManager.UnregisterHeadPoint(this);
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
ManualUpdate(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Ichni.RhythmGame
|
||||
/// <summary>
|
||||
/// 在轨道上根据百分比进行运动的点
|
||||
/// </summary>
|
||||
public partial class TrackPercentPoint : GameElement, IHaveTimeDurationSubmodule
|
||||
public partial class TrackPercentPoint : GameElement, IHaveTimeDurationSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public Track track;
|
||||
@@ -52,9 +52,15 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
GameManager.Instance.trackManager.RegisterPercentPoint(this);
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.TrackFollower, this);
|
||||
base.AfterInitialize();
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [轮询更新] Main Update
|
||||
@@ -73,10 +79,19 @@ namespace Ichni.RhythmGame
|
||||
if (trackPercent.returnType == FlexibleReturnType.After)
|
||||
{
|
||||
trackPositioner.SetPercent(1);
|
||||
GameManager.Instance.trackManager.UnregisterPercentPoint(this);
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.TrackFollower, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
ManualUpdate(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -65,14 +65,17 @@ namespace Ichni.RhythmGame
|
||||
path.type = Spline.Type.Linear;
|
||||
path.sampleRate = 1;
|
||||
}
|
||||
|
||||
if (isClosed)
|
||||
|
||||
if (pathNodeList.Count >= 3)
|
||||
{
|
||||
path.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
path.Break();
|
||||
if (isClosed)
|
||||
{
|
||||
path.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
path.Break();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public string materialThemeBundleName;
|
||||
public string materialName;
|
||||
public string customTextureThemeBundleName = "None";
|
||||
public string customTextureName = "None";
|
||||
public MeshGenerator.UVMode uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
public float uvRotation = 0f;
|
||||
public float size = 1f;
|
||||
public bool enableEmission;
|
||||
public float emissionIntensity;
|
||||
public bool zWrite;
|
||||
@@ -86,6 +91,9 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
meshGenerator.uvScale = uvScale;
|
||||
meshGenerator.uvOffset = uvOffset;
|
||||
meshGenerator.uvRotation = uvRotation;
|
||||
meshGenerator.uvMode = uvMode;
|
||||
meshGenerator.size = size;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -32,8 +32,8 @@ namespace Ichni.RhythmGame
|
||||
this.splineRenderer.updateMethod = SplineUser.UpdateMethod.Update;
|
||||
this.meshRenderer.material = renderMaterial;
|
||||
this.splineRenderer.color = Color.white;
|
||||
this.splineRenderer.uvRotation = 90;
|
||||
this.splineRenderer.uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
this.uvRotation = 0f;
|
||||
this.uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
|
||||
SetMesh();
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace Ichni.RhythmGame
|
||||
this.pathGenerator.updateMethod = SplineUser.UpdateMethod.Update;
|
||||
this.meshRenderer.material = renderMaterial;
|
||||
this.pathGenerator.color = Color.white;
|
||||
this.pathGenerator.uvRotation = 90;
|
||||
this.pathGenerator.uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
this.uvRotation = 90f;
|
||||
this.uvMode = MeshGenerator.UVMode.UniformClip;
|
||||
|
||||
SetMesh();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,10 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
track.trackRendererSubmodule.meshGenerator.clipFrom = tailPercent;
|
||||
track.trackRendererSubmodule.meshGenerator.clipTo = headPercent;
|
||||
track.trackRendererSubmodule.meshGenerator.RebuildImmediate();
|
||||
}
|
||||
|
||||
//track.trackPathSubmodule.path.Rebuild(true);
|
||||
}
|
||||
|
||||
public float GetTrackPercent(float songTimeInTime)
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class ObjectTracker : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public partial class ParticleTracker : GameElement, IHaveParticles, IHaveColorSubmodule
|
||||
public partial class ParticleTracker : GameElement, IHaveParticles, IHaveColorSubmodule, IScheduledElement
|
||||
{
|
||||
#region [暴露属性字段] Essential Configs
|
||||
public Track track;
|
||||
@@ -66,6 +66,18 @@ namespace Ichni.RhythmGame
|
||||
{
|
||||
colorSubmodule = new ColorSubmodule(this, Color.white, true, Color.white, 0);
|
||||
}
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
CoreServices.UpdateScheduler.Register(UpdatePhase.Effect, this);
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
{
|
||||
base.OnDelete();
|
||||
CoreServices.UpdateScheduler.Unregister(UpdatePhase.Effect, this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [运行时设置] Runtime Settings
|
||||
@@ -91,9 +103,8 @@ namespace Ichni.RhythmGame
|
||||
#region [轮询更新] Main Update
|
||||
public partial class ParticleTracker
|
||||
{
|
||||
private void Update()
|
||||
private void UpdateParticlePlayState(float songTime)
|
||||
{
|
||||
float songTime = CoreServices.TimeProvider.SongTime;
|
||||
if (playTime > songTime || stopTime < songTime)
|
||||
{
|
||||
particle.Stop();
|
||||
@@ -106,6 +117,15 @@ namespace Ichni.RhythmGame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region [IScheduledElement] Scheduler Interface
|
||||
public void ScheduledUpdate(UpdatePhase phase, float songTime)
|
||||
{
|
||||
UpdateParticlePlayState(songTime);
|
||||
}
|
||||
|
||||
public bool IsScheduledActive => isActiveAndEnabled;
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
266
Assets/Scripts/Manager/ElementUpdateScheduler.cs
Normal file
266
Assets/Scripts/Manager/ElementUpdateScheduler.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
/// <summary>
|
||||
/// 集中式元素更新调度器(ichni Official)。
|
||||
/// 将所有 GameElement 的帧更新按 9 个阶段有序执行,
|
||||
/// 替代原先分散在 GameManager.Update/LateUpdate()、各子管理器、MonoBehaviour.Update() 中的零散更新。
|
||||
///
|
||||
/// TickEarly (GameManager.Update)
|
||||
/// Phase 0 TimeDuration — 判定元素激活/隐藏(Legacy timeDurations 列表)
|
||||
/// Phase 1 Animation — 更新动画值,设置脏标记(AnimationBase 通过 IScheduledElement 注册)
|
||||
/// Phase 2 Apply — 执行 Color + Transform(Legacy activeColorSubmodules / activeTransformSubmodules)
|
||||
///
|
||||
/// --- Unity 执行其他脚本的 Update(SplineComputer 检测 transform.hasChanged,重新采样)---
|
||||
///
|
||||
/// TickLate (GameManager.LateUpdate)
|
||||
/// DirtyMark — ExecuteDeferredRefresh(Legacy,isStarting || isPlaying 条件)
|
||||
/// Phase 3 SplineRebuild — 占位(SplineComputer 已在 Update 中自行处理)
|
||||
/// Phase 4 TrackCore — 更新轨道时间、裁剪区间(Track 通过 IScheduledElement 注册)
|
||||
/// Phase 5 TrackFollower — CrossTrackPoint / HeadPoint / PercentPoint
|
||||
/// [TrackManager.ManualLateUpdate — 清除 refreshedThisFrame 标记]
|
||||
/// Phase 6 Note — NoteManager 内部驱动(激活 + 更新 + 重置)
|
||||
/// Phase 7 Effect — ParticleEmitter / TimeEffectsCollection / ParticleTracker / DTM 元素 / LookAt 旋转覆盖
|
||||
/// Phase 8 Misc — SkyboxSubsetter / JudgeTrigger / LowPriorityActions
|
||||
///
|
||||
/// 此分离设计消除了 Dreamteck Spline 的一帧延迟:
|
||||
/// Phase 2 (Update) 修改 Transform → SplineComputer.Update() 检测 hasChanged 并重采样
|
||||
/// → Phase 4/5 (LateUpdate) 调用 SetPercent() 时获取最新采样数据。
|
||||
/// </summary>
|
||||
public class ElementUpdateScheduler
|
||||
{
|
||||
#region [常量] Constants
|
||||
|
||||
/// <summary>阶段总数(TimeDuration..Misc,步长 10,共 9 个)</summary>
|
||||
private const int PhaseCount = 9;
|
||||
|
||||
/// <summary>阶段枚举值到数组索引的步长除数</summary>
|
||||
private const int PhaseStep = 10;
|
||||
|
||||
private static readonly UpdatePhase[] AllPhases = (UpdatePhase[])Enum.GetValues(typeof(UpdatePhase));
|
||||
|
||||
#endregion
|
||||
|
||||
#region [阶段元素列表] Phase Element Lists
|
||||
|
||||
/// <summary>
|
||||
/// 每个阶段的已注册 IScheduledElement 列表。
|
||||
/// 使用数组替代 Dictionary<UpdatePhase, ...>,
|
||||
/// 索引通过 (int)phase / 10 直接映射,消除哈希查找开销。
|
||||
/// </summary>
|
||||
private readonly List<IScheduledElement>[] _phaseElements;
|
||||
|
||||
/// <summary>将 UpdatePhase 枚举值转换为数组索引。</summary>
|
||||
private static int PhaseIndex(UpdatePhase phase) => (int)phase / PhaseStep;
|
||||
|
||||
#endregion
|
||||
|
||||
#region [轨道 Spline 列表] Track Spline List
|
||||
|
||||
/// <summary>Phase 3 专用:需要手动重建 SplineComputer 的 Track 列表(保留供后续优化)</summary>
|
||||
private readonly List<Track> _trackSplines = new List<Track>(50);
|
||||
|
||||
#endregion
|
||||
|
||||
#region [缓存] Cached State
|
||||
|
||||
/// <summary>TickEarly 中缓存的 songTime,供 TickLate 使用</summary>
|
||||
private float _cachedSongTime;
|
||||
|
||||
/// <summary>当前帧是否处于更新状态(由 songPlayer.isUpdating 决定)</summary>
|
||||
private bool _isUpdating;
|
||||
|
||||
#endregion
|
||||
|
||||
#region [构造函数] Constructor
|
||||
|
||||
public ElementUpdateScheduler()
|
||||
{
|
||||
_phaseElements = new List<IScheduledElement>[PhaseCount];
|
||||
for (int i = 0; i < PhaseCount; i++)
|
||||
{
|
||||
_phaseElements[i] = new List<IScheduledElement>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [注册与注销] Registration
|
||||
|
||||
/// <summary>将元素注册到指定的更新阶段。同一元素可注册到多个阶段。</summary>
|
||||
public void Register(UpdatePhase phase, IScheduledElement element)
|
||||
{
|
||||
var list = _phaseElements[PhaseIndex(phase)];
|
||||
if (!list.Contains(element))
|
||||
{
|
||||
list.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>将元素从指定阶段注销。</summary>
|
||||
public void Unregister(UpdatePhase phase, IScheduledElement element)
|
||||
{
|
||||
_phaseElements[PhaseIndex(phase)].Remove(element);
|
||||
}
|
||||
|
||||
/// <summary>注册 Track 的 SplineComputer 到 Phase 3(SplineRebuild,当前为占位)。</summary>
|
||||
public void RegisterTrackSpline(Track track)
|
||||
{
|
||||
if (!_trackSplines.Contains(track))
|
||||
{
|
||||
_trackSplines.Add(track);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>从 Phase 3 注销 Track 的 SplineComputer。</summary>
|
||||
public void UnregisterTrackSpline(Track track)
|
||||
{
|
||||
_trackSplines.Remove(track);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [主循环 - 早期阶段] TickEarly (Update)
|
||||
|
||||
/// <summary>
|
||||
/// 早期阶段调度,由 GameManager.Update() 调用。
|
||||
/// 执行 Phase 0(TimeDuration)→ Phase 1(Animation)→ Phase 2(Apply)。
|
||||
/// Phase 2 完成后 Transform 已更新;SplineComputer 将在后续的 Update [order 0] 中检测变化。
|
||||
/// </summary>
|
||||
public void TickEarly(float songTime)
|
||||
{
|
||||
_isUpdating = GameManager.Instance.songPlayer.isUpdating;
|
||||
if (!_isUpdating) return;
|
||||
|
||||
_cachedSongTime = songTime;
|
||||
|
||||
// ─── Phase 0: TimeDuration(Legacy timeDurations 列表)─────────────────
|
||||
TickTimeDurationLegacy(songTime);
|
||||
|
||||
// ─── Phase 1: Animation ──────────────────────────────────────────────
|
||||
TickPhase(UpdatePhase.Animation, songTime);
|
||||
|
||||
// ─── Phase 2: Apply(Color + Transform from active lists)────────────
|
||||
TickApplyLegacy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [主循环 - 晚期阶段] TickLate (LateUpdate)
|
||||
|
||||
/// <summary>
|
||||
/// 晚期阶段调度,由 GameManager.LateUpdate() 调用。
|
||||
/// 此时所有 Update() 已完成,SplineComputer 已检测 transform.hasChanged 并重新采样。
|
||||
/// </summary>
|
||||
public void TickLate()
|
||||
{
|
||||
// DirtyMark:保持原有 isStarting || isPlaying 条件,与 TickEarly 的 isUpdating 独立判断
|
||||
if (GameManager.Instance.songPlayer.isStarting || GameManager.Instance.songPlayer.isPlaying)
|
||||
{
|
||||
TickDirtyMarkLegacy();
|
||||
}
|
||||
|
||||
if (!_isUpdating) return;
|
||||
float songTime = _cachedSongTime;
|
||||
|
||||
// ─── Phase 3: SplineRebuild(占位)───────────────────────────────────
|
||||
// SplineComputer 使用默认 UpdateMode.Update,已在 Update 中自行处理。
|
||||
TickPhase(UpdatePhase.SplineRebuild, songTime);
|
||||
|
||||
// ─── Phase 4: TrackCore ──────────────────────────────────────────────
|
||||
TickPhase(UpdatePhase.TrackCore, songTime);
|
||||
|
||||
// ─── Phase 5: TrackFollower ──────────────────────────────────────────
|
||||
TickPhase(UpdatePhase.TrackFollower, songTime);
|
||||
|
||||
// 清除轨道 refreshedThisFrame 标记(TrackManager 保留此单一职责)
|
||||
GameManager.Instance.trackManager.ManualLateUpdate(songTime);
|
||||
|
||||
// ─── Phase 6: Note(由 NoteManager 内部驱动)─────────────────────────
|
||||
TickPhase(UpdatePhase.Note, songTime);
|
||||
GameManager.Instance.noteManager.ManualUpdate(songTime);
|
||||
GameManager.Instance.noteManager.ManualLateUpdate(songTime);
|
||||
|
||||
// ─── Phase 7: Effect ─────────────────────────────────────────────────
|
||||
TickPhase(UpdatePhase.Effect, songTime);
|
||||
|
||||
// ─── Phase 8: Misc ───────────────────────────────────────────────────
|
||||
TickPhase(UpdatePhase.Misc, songTime);
|
||||
GameManager.Instance.beatmapContainer?.ExecuteLowPriorityActions();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [阶段执行] Phase Execution
|
||||
|
||||
/// <summary>
|
||||
/// 执行指定阶段的所有已注册 IScheduledElement。
|
||||
/// 倒序遍历防止更新途中元素自行销毁导致越界。
|
||||
/// </summary>
|
||||
private void TickPhase(UpdatePhase phase, float songTime)
|
||||
{
|
||||
var list = _phaseElements[PhaseIndex(phase)];
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var element = list[i];
|
||||
if (element != null && element.IsScheduledActive)
|
||||
{
|
||||
element.ScheduledUpdate(phase, songTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [Legacy] 过渡期内联逻辑
|
||||
|
||||
/// <summary>
|
||||
/// [Legacy] Phase 0 内联逻辑:遍历 GameManager.timeDurations 执行激活/隐藏检测。
|
||||
/// </summary>
|
||||
private void TickTimeDurationLegacy(float songTime)
|
||||
{
|
||||
var timeDurations = GameManager.Instance.timeDurations;
|
||||
for (int i = 0; i < timeDurations.Count; i++)
|
||||
{
|
||||
timeDurations[i]?.DetectAndSwitchActiveState(songTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Legacy] Phase 2 内联逻辑:从 GameManager 活跃列表执行 Color + Transform 更新。
|
||||
/// 在 Update 阶段完成 Transform 修改,以便 SplineComputer 在后续 Update 中检测 hasChanged。
|
||||
/// </summary>
|
||||
private void TickApplyLegacy()
|
||||
{
|
||||
var activeColorSubmodules = GameManager.Instance.activeColorSubmodules;
|
||||
var activeTransformSubmodules = GameManager.Instance.activeTransformSubmodules;
|
||||
|
||||
for (int i = 0; i < activeColorSubmodules.Count; i++)
|
||||
{
|
||||
activeColorSubmodules[i].UpdateColor(true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < activeTransformSubmodules.Count; i++)
|
||||
{
|
||||
activeTransformSubmodules[i].UpdateTransform(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Legacy] TickLate DirtyMark 内联逻辑:保持原有 isStarting || isPlaying 条件。
|
||||
/// </summary>
|
||||
private void TickDirtyMarkLegacy()
|
||||
{
|
||||
var activeDirtyMarkSubmodules = GameManager.Instance.activeDirtyMarkSubmodules;
|
||||
for (int i = 0; i < activeDirtyMarkSubmodules.Count; i++)
|
||||
{
|
||||
activeDirtyMarkSubmodules[i]?.dirtyMarkSubmodule?.ExecuteDeferredRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Manager/ElementUpdateScheduler.cs.meta
Normal file
2
Assets/Scripts/Manager/ElementUpdateScheduler.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cd8f0f4ccc613a4cb262664c1cdf328
|
||||
@@ -44,6 +44,8 @@ namespace Ichni
|
||||
public List<IHaveTransformSubmodule> activeTransformSubmodules = new List<IHaveTransformSubmodule>();
|
||||
public List<IHaveColorSubmodule> activeColorSubmodules = new List<IHaveColorSubmodule>();
|
||||
public List<IHaveDirtyMarkSubmodule> activeDirtyMarkSubmodules = new List<IHaveDirtyMarkSubmodule>();
|
||||
|
||||
public ElementUpdateScheduler updateScheduler;
|
||||
|
||||
[Title("UI")]
|
||||
public Canvas judgeHintCanvas;
|
||||
@@ -64,6 +66,8 @@ namespace Ichni
|
||||
CoreServices.TimeProvider = this;
|
||||
timeDurations = new List<TimeDurationSubmodule>();
|
||||
playingRecorder = new PlayingRecorder();
|
||||
updateScheduler = new ElementUpdateScheduler();
|
||||
CoreServices.UpdateScheduler = updateScheduler;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -74,43 +78,12 @@ namespace Ichni
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!songPlayer.isUpdating) return;
|
||||
|
||||
foreach (var timeDuration in timeDurations)
|
||||
{
|
||||
timeDuration?.DetectAndSwitchActiveState(SongTime);
|
||||
}
|
||||
|
||||
animationManager.ManualUpdate(SongTime);
|
||||
trackManager.ManualUpdate(SongTime);
|
||||
noteManager.ManualUpdate(SongTime);
|
||||
updateScheduler.TickEarly(SongTime);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (songPlayer.isUpdating)
|
||||
{
|
||||
trackManager.ManualLateUpdate(SongTime);
|
||||
noteManager.ManualLateUpdate(SongTime);
|
||||
|
||||
for (int i = 0; i < activeColorSubmodules.Count; i++)
|
||||
{
|
||||
activeColorSubmodules[i].UpdateColor(true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < activeTransformSubmodules.Count; i++)
|
||||
{
|
||||
activeTransformSubmodules[i].UpdateTransform(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (songPlayer.isStarting || songPlayer.isPlaying)
|
||||
{
|
||||
for (int i = 0; i < activeDirtyMarkSubmodules.Count; i++)
|
||||
{
|
||||
activeDirtyMarkSubmodules[i]?.dirtyMarkSubmodule?.ExecuteDeferredRefresh();
|
||||
}
|
||||
}
|
||||
updateScheduler.TickLate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,18 +162,18 @@ namespace Ichni
|
||||
|
||||
if (closestFlick != null)
|
||||
{
|
||||
if (closestFlick.exactJudgeTime < closestHold.exactJudgeTime)
|
||||
if (closestFlick.exactJudgeTime < closestHold.exactJudgeTime - 0.05f)
|
||||
{
|
||||
holdBlockedByFlick = true;
|
||||
}
|
||||
|
||||
if (closestFlick.exactJudgeTime < closestTap.exactJudgeTime)
|
||||
if (closestFlick.exactJudgeTime < closestTap.exactJudgeTime - 0.05f)
|
||||
{
|
||||
tapBlockedByFlick = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!holdBlockedByFlick && closestHold.exactJudgeTime < closestTap.exactJudgeTime)
|
||||
if (!holdBlockedByFlick && closestHold.exactJudgeTime < closestTap.exactJudgeTime - 0.05f)
|
||||
{
|
||||
if (_minHoldsBuffer.Count == 1)
|
||||
{
|
||||
@@ -199,7 +199,7 @@ namespace Ichni
|
||||
else if (haveHold)
|
||||
{
|
||||
GetAllMinNotes(_availableHoldsBuffer, _minHoldsBuffer);
|
||||
bool holdBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minHoldsBuffer[0].exactJudgeTime;
|
||||
bool holdBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minHoldsBuffer[0].exactJudgeTime - 0.05f;
|
||||
if (!holdBlockedByFlick)
|
||||
{
|
||||
if (_minHoldsBuffer.Count == 1)
|
||||
@@ -215,7 +215,7 @@ namespace Ichni
|
||||
else if (haveTap)
|
||||
{
|
||||
GetAllMinNotes(_availableTapsBuffer, _minTapsBuffer);
|
||||
bool tapBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minTapsBuffer[0].exactJudgeTime;
|
||||
bool tapBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minTapsBuffer[0].exactJudgeTime - 0.05f;
|
||||
if (!tapBlockedByFlick)
|
||||
{
|
||||
if (_minTapsBuffer.Count == 1)
|
||||
|
||||
Reference in New Issue
Block a user