1.新增了SplineAnchor的旋转跟随2.修复了IInteraction交互物体的乱序错误

This commit is contained in:
2025-10-08 21:47:18 +08:00
parent 0c11c917c4
commit 943c86d5f5
55 changed files with 329 additions and 555 deletions

View File

@@ -19,46 +19,96 @@ namespace Demo.Game
item.SetupDuration(new(VisibleDuration.x, VisibleDuration.y), GetTimelineItemColor());
}
[Serializable]
public enum DurationStats
{
BeforeBegin = 0,
BeforeEnd,
After,
Judgement
}
public const float IdentifiableJudgementTerminal = 0.16f;
[Content, SerializeField, Header("Cache")]
public float MyCacheUpdateTick = 0;
[Header(nameof(VisibleDuration))]
[Content, SerializeField] private Vector2 VisibleDuration;
public static float DefaultVisibleLength = IdentifiableJudgementTerminal * 15;
[Content, SerializeField] public UnityEvent VisibleDurationBeforeEvent = new(),
VisibleDurationBeginEvent = new(), VisibleDurationEndEvent = new();
[Content, SerializeField] private DurationStats VisibleDurationStats = default;
[Header(nameof(InteractiveDuration))]
[Content, SerializeField] private Vector2 InteractiveDuration;
public static float DefaultInteractiveLength = IdentifiableJudgementTerminal * 9;
[Content, SerializeField] public UnityEvent InteractiveDurationBeforeEvent = new(),
InteractiveDurationBeginEvent = new(), InteractiveDurationEndEvent = new();
[Content, SerializeField] private DurationStats InteractiveDurationStats = default;
[Header(nameof(InteractableScoreInterval))]
[Content, SerializeField] private Vector2 InteractableScoreInterval;
public static float DefaultInteractableScoreIntervalLength = IdentifiableJudgementTerminal * 6;
[Content, SerializeField] public UnityEvent InteractableScoreIntervalBeforeEvent = new(),
InteractableScoreIntervalBeginEvent = new(), InteractableScoreIntervalEndEvent = new();
[Content, SerializeField] private DurationStats InteractableScoreIntervalStats = default;
[Header(nameof(InteractableIntervalThatCanScoreBest))]
[Content, SerializeField] private Vector2 InteractableIntervalThatCanScoreBest;
public static float DefaultInteractableIntervalLengthThatCanScoreBest = IdentifiableJudgementTerminal * 3;
[Content, SerializeField] public UnityEvent InteractableIntervalThatCanScoreBestBeforeEvent = new(),
InteractableIntervalThatCanScoreBestBeginEvent = new(), InteractableIntervalThatCanScoreBestEndEvent = new();
[Content, SerializeField] private DurationStats InteractableIntervalThatCanScoreBestStats = default;
[Flags,Serializable]
public enum UpdatePhaseComponent
{
OutsideVisibleDuration = 0b00000000,
//
InsideVisibleDuration = 0b00000001,
InsideInteractiveDuration = 0b00000010,
InsideInteractableScoreInterval = 0b00000100,
InsideInteractableIntervalThatCanScoreBest = 0b00001000,
//
EnterVisibleDuration = 0b00010000,
EnterInteractiveDuration = 0b00100000,
EnterInteractableScoreInterval = 0b01000000,
EnterInteractableIntervalThatCanScoreBest = 0b10000000,
}
[Flags, Serializable]
public enum UpdatePhase
{
// 进入可视区间之前
BeforeVisibleDuration
= UpdatePhaseComponent.OutsideVisibleDuration,
// 进入不可判定但可视的3级判定区间
EnterVisibleDuration
= UpdatePhaseComponent.InsideVisibleDuration | UpdatePhaseComponent.EnterVisibleDuration,
// 进入可判定为失误的2级判定区间
EnterInteractiveDuration
= UpdatePhaseComponent.InsideInteractiveDuration | UpdatePhaseComponent.EnterInteractiveDuration | UpdatePhase.EnterVisibleDuration,
// 进入可得分的1级判定区间
EnterInteractableScoreInterval
= UpdatePhaseComponent.InsideInteractableScoreInterval | UpdatePhaseComponent.EnterInteractableScoreInterval | UpdatePhase.EnterInteractiveDuration,
// 进入可得满分的0级判定区间
EnterInteractableIntervalThatCanScoreBest
= UpdatePhaseComponent.InsideInteractableIntervalThatCanScoreBest| UpdatePhaseComponent.EnterInteractableIntervalThatCanScoreBest
| UpdatePhase.EnterInteractableScoreInterval,
// 退出0级判定区间
AfterInteractableIntervalThatCanScoreBest
= UpdatePhase.EnterInteractableIntervalThatCanScoreBest&(~UpdatePhaseComponent.InsideInteractableIntervalThatCanScoreBest),
// 退出1级判定区间
AfterInteractableScoreInterval
= UpdatePhase.AfterInteractableIntervalThatCanScoreBest & (~UpdatePhaseComponent.InsideInteractableScoreInterval),
// 退出2级判断区间
AfterInteractiveDuration
= UpdatePhase.AfterInteractableScoreInterval & (~UpdatePhaseComponent.InsideInteractiveDuration),
// 退出3级判断区间, 已不可视
AfterVisibleDuration
= UpdatePhase.AfterInteractiveDuration & (~UpdatePhaseComponent.InsideVisibleDuration)
}
public enum JudgementLevel
{
None = -2,//No Judge
Default = -1,
BestLevel = 0,//Level0
ScoreLevel = 1,//Level1
Bad = 128,
}
[Header("Judgement")]
[Content, SerializeField] private JudgementLevel MyJudgementLevel = JudgementLevel.None;
[Content, SerializeField] private UpdatePhase MyUpdatePhase = default;
[Content, SerializeField] private float BestJudgementTimePoint = -1;
[Content, SerializeField] public UnityEvent<JudgementLevel> JudgementEvent = new();
@@ -67,14 +117,14 @@ namespace Demo.Game
return BestJudgementTimePoint;
}
public bool IsInInteractiveDuration()
{
return InteractiveDurationStats == DurationStats.BeforeEnd;
}
public bool IsInInteractiveDuration(float time)
{
return time > InteractiveDuration.x && time < InteractiveDuration.y;
}
public bool IsInInteractiveDuration()
{
return IsInInteractiveDuration(MyCacheUpdateTick);
}
public const int JudgementLevelCount = 3;
@@ -105,92 +155,126 @@ namespace Demo.Game
{
if (level == JudgementLevel.None)
return;
// 进入已判定状态
MyJudgementLevel = level;
JudgementEvent.Invoke(level);
VisibleDurationStats = DurationStats.Judgement;
VisibleDurationEndEvent.Invoke();
InteractiveDurationStats = DurationStats.Judgement;
InteractableScoreIntervalStats = DurationStats.Judgement;
InteractableIntervalThatCanScoreBestStats = DurationStats.Judgement;
InteractiveDurationEndEvent.Invoke();
InteractableScoreIntervalEndEvent.Invoke();
InteractableIntervalThatCanScoreBestEndEvent.Invoke();
}
private static DurationStats UpdateDurationStats(float begin, UnityEvent beforeEvent, UnityEvent beginEvent, float end, UnityEvent endEvent,
float current, DurationStats currentStats)
{
if (currentStats == DurationStats.After)
return DurationStats.After;
else if (current < begin)
// 触发还未触发的退出事件
{
if (currentStats != DurationStats.BeforeBegin)
beforeEvent.Invoke();
return DurationStats.BeforeBegin;
if (((int)MyUpdatePhase & (int)UpdatePhaseComponent.EnterInteractableIntervalThatCanScoreBest) != 0)
{
InteractableIntervalThatCanScoreBestEndEvent.Invoke();
}
if (((int)MyUpdatePhase & (int)UpdatePhaseComponent.EnterInteractableScoreInterval) != 0)
{
InteractableScoreIntervalEndEvent.Invoke();
}
if (((int)MyUpdatePhase & (int)UpdatePhaseComponent.EnterInteractiveDuration) != 0)
{
InteractiveDurationEndEvent.Invoke();
}
if (((int)MyUpdatePhase & (int)UpdatePhaseComponent.EnterVisibleDuration) != 0)
{
VisibleDurationEndEvent.Invoke();
}
}
else if (current < end)
{
if (currentStats == DurationStats.BeforeBegin)
beginEvent.Invoke();
return DurationStats.BeforeEnd;
}
else if (currentStats == DurationStats.BeforeEnd)
{
endEvent.Invoke();
}
return DurationStats.After;
}
public enum JudgementLevel
{
None = -2,//No Judge
Default = -1,
BestLevel = 0,//Level0
ScoreLevel = 1,//Level1
Bad = 128,
}
public abstract JudgementLevel JudgementBehaviour(float timePoint);
#region Update Judgement Stats
private void DoUpdateJudgementStats(float currentTime)
{
// 可见区间
VisibleDurationStats = UpdateDurationStats(VisibleDuration.x, VisibleDurationBeforeEvent,
VisibleDurationBeginEvent, VisibleDuration.y, VisibleDurationEndEvent,
currentTime, VisibleDurationStats);
if (VisibleDurationStats != DurationStats.BeforeEnd)
return;
// 可判定区间
InteractiveDurationStats = UpdateDurationStats(InteractiveDuration.x, InteractiveDurationBeforeEvent,
InteractiveDurationBeginEvent, InteractiveDuration.y, InteractiveDurationEndEvent,
currentTime, InteractiveDurationStats);
if (InteractiveDurationStats != DurationStats.BeforeEnd)
return;
// 1级判定区间
InteractableScoreIntervalStats = UpdateDurationStats(InteractableScoreInterval.x, InteractableScoreIntervalBeforeEvent,
InteractableScoreIntervalBeginEvent, InteractableScoreInterval.y, InteractableScoreIntervalEndEvent,
currentTime, InteractableScoreIntervalStats);
if (InteractableScoreIntervalStats != DurationStats.BeforeEnd)
return;
// 0级判定区间
InteractableIntervalThatCanScoreBestStats = UpdateDurationStats(InteractableIntervalThatCanScoreBest.x, InteractableIntervalThatCanScoreBestBeforeEvent,
InteractableIntervalThatCanScoreBestBeginEvent, InteractableIntervalThatCanScoreBest.y, InteractableIntervalThatCanScoreBestEndEvent,
currentTime, InteractableIntervalThatCanScoreBestStats);
// 重置
if (MyCacheUpdateTick > currentTime)
{
MyUpdatePhase = UpdatePhase.BeforeVisibleDuration;
VisibleDurationBeforeEvent.Invoke();
InteractiveDurationBeforeEvent.Invoke();
InteractableScoreIntervalBeforeEvent.Invoke();
InteractableIntervalThatCanScoreBestBeforeEvent.Invoke();
MyJudgementLevel = JudgementLevel.None;
}
// 状态更新
{
// 在当前区间进行判断, 当前时间落入下一个区间时, 就进入下一个区间
switch (MyUpdatePhase)
{
case UpdatePhase.BeforeVisibleDuration:
if (currentTime > VisibleDuration.x)
{
MyUpdatePhase = UpdatePhase.EnterVisibleDuration;
VisibleDurationBeginEvent.Invoke();
}
break;
case UpdatePhase.EnterVisibleDuration:
if (currentTime > InteractiveDuration.x)
{
MyUpdatePhase = UpdatePhase.EnterInteractiveDuration;
InteractiveDurationBeginEvent.Invoke();
}
break;
case UpdatePhase.EnterInteractiveDuration:
if (currentTime > InteractableScoreInterval.x)
{
MyUpdatePhase = UpdatePhase.EnterInteractableScoreInterval;
InteractableScoreIntervalBeginEvent.Invoke();
}
break;
case UpdatePhase.EnterInteractableScoreInterval:
if (currentTime > InteractableIntervalThatCanScoreBest.x)
{
MyUpdatePhase = UpdatePhase.EnterInteractableIntervalThatCanScoreBest;
InteractableIntervalThatCanScoreBestBeginEvent.Invoke();
}
break;
// ---------------------------------------------------------
case UpdatePhase.EnterInteractableIntervalThatCanScoreBest:
if (currentTime > InteractableIntervalThatCanScoreBest.y)
{
MyUpdatePhase = UpdatePhase.AfterInteractableIntervalThatCanScoreBest;
InteractableIntervalThatCanScoreBestEndEvent.Invoke();
}
break;
case UpdatePhase.AfterInteractableIntervalThatCanScoreBest:
if (currentTime > InteractableScoreInterval.y)
{
MyUpdatePhase = UpdatePhase.AfterInteractableScoreInterval;
InteractableScoreIntervalEndEvent.Invoke();
}
break;
case UpdatePhase.AfterInteractableScoreInterval:
if (currentTime > InteractiveDuration.y)
{
MyUpdatePhase = UpdatePhase.AfterInteractiveDuration;
InteractiveDurationEndEvent.Invoke();
}
break;
case UpdatePhase.AfterInteractiveDuration:
if (currentTime > VisibleDuration.y)
{
MyUpdatePhase = UpdatePhase.AfterVisibleDuration;
VisibleDurationEndEvent.Invoke();
}
break;
default:
break;
}
}
// 记录
MyCacheUpdateTick = currentTime;
}
public override void ResetEnterGameStatus()
{
base.ResetEnterGameStatus();
DoUpdateJudgementStats(Mathf.NegativeInfinity);
}
#endregion
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
base.UpdateTicks(currentTime, deltaTime, tickType);
// 预判断
if (VisibleDurationStats == DurationStats.Judgement)
return;
// 检定
InvokeJudgement(JudgementBehaviour(currentTime));
if (MyJudgementLevel == JudgementLevel.None)
{
InvokeJudgement(JudgementBehaviour(currentTime));
}
// 更新状态
DoUpdateJudgementStats(currentTime);
}