Files
Convention-Unity-Demo/Assets/Scripts/Interaction/IInteraction.cs

362 lines
15 KiB
C#
Raw Normal View History

2025-09-25 19:04:05 +08:00
using System.Collections;
using System.Collections.Generic;
using Convention;
using Demo.Editor.UI;
using UnityEngine;
using UnityEngine.Events;
namespace Demo.Game
{
public interface IHookInteraction
{
}
public abstract class IInteraction : TimelineScriptObject
{
protected override void SetupTimelineItem(TimelineItem item)
{
item.SetupDuration(new(VisibleDuration.x, VisibleDuration.y), GetTimelineItemColor());
}
public enum DurationStats
{
BeforeBegin = 0,
BeforeEnd,
After,
Judgement
}
[Header(nameof(VisibleDuration))]
[Content, SerializeField] private Vector2 VisibleDuration;
[Content, SerializeField] public UnityEvent VisibleDurationBeginEvent = new(), VisibleDurationEndEvent = new();
[Content, SerializeField] private DurationStats VisibleDurationStats = default;
[Header(nameof(InteractiveDuration))]
[Content, SerializeField] private Vector2 InteractiveDuration;
[Content, SerializeField] public UnityEvent InteractiveDurationBeginEvent = new(), InteractiveDurationEndEvent = new();
[Content, SerializeField] private DurationStats InteractiveDurationStats = default;
[Header(nameof(InteractableScoreInterval))]
[Content, SerializeField] private Vector2 InteractableScoreInterval;
[Content, SerializeField] public UnityEvent InteractableScoreIntervalBeginEvent = new(), InteractableScoreIntervalEndEvent = new();
[Content, SerializeField] private DurationStats InteractableScoreIntervalStats = default;
[Header(nameof(InteractableIntervalThatCanScoreBest))]
[Content, SerializeField] private Vector2 InteractableIntervalThatCanScoreBest;
[Content, SerializeField] public UnityEvent InteractableIntervalThatCanScoreBestBeginEvent = new(), InteractableIntervalThatCanScoreBestEndEvent = new();
[Content, SerializeField] private DurationStats InteractableIntervalThatCanScoreBestStats = default;
[Header("Judgement")]
[Content, SerializeField] private float BestJudgementTimePoint = -1;
[Content, SerializeField] public UnityEvent<int> JudgementEvent = new();
public float GetBestJudgementTimePoint()
{
return BestJudgementTimePoint;
}
public bool IsInInteractiveDuration()
{
return InteractiveDurationStats == DurationStats.BeforeEnd;
}
public bool IsInInteractiveDuration(float time)
{
return time > InteractiveDuration.x && time < InteractiveDuration.y;
}
public const int JudgementLevelCount = 3;
public override IEnumerator LoadScript(string script)
{
yield return base.LoadScript(script);
if (BestJudgementTimePoint < 0)
{
BestJudgementTimePoint = Mathf.Lerp(InteractableIntervalThatCanScoreBest.x, InteractableIntervalThatCanScoreBest.y, 0.5f);
}
}
public override IEnumerator UnloadScript()
{
yield return base.UnloadScript();
VisibleDurationBeginEvent = new();
VisibleDurationEndEvent = new();
InteractiveDurationBeginEvent = new();
InteractiveDurationEndEvent = new();
InteractableScoreIntervalBeginEvent = new();
InteractableScoreIntervalEndEvent = new();
InteractableIntervalThatCanScoreBestBeginEvent = new();
InteractableIntervalThatCanScoreBestEndEvent = new();
BestJudgementTimePoint = -1;
}
public void InvokeJudgement()
{
int judgeStats = JudgementLevelCount;
if (InteractiveDurationStats == DurationStats.BeforeEnd)
{
judgeStats--;
if (InteractableScoreIntervalStats == DurationStats.BeforeEnd)
{
judgeStats--;
if (InteractableIntervalThatCanScoreBestStats == DurationStats.BeforeEnd)
{
judgeStats--;
}
}
}
if (judgeStats == JudgementLevelCount)
return;
JudgementEvent.Invoke(judgeStats);
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 beginEvent, float end, UnityEvent endEvent,
float current, DurationStats currentStats)
{
if (currentStats == DurationStats.After)
return DurationStats.After;
else if (current < begin)
return DurationStats.BeforeBegin;
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);
2025-09-25 19:04:05 +08:00
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
if (tickType == TickType.Start || tickType == TickType.Reset)
{
VisibleDurationStats = DurationStats.BeforeBegin;
InteractiveDurationStats = DurationStats.BeforeBegin;
InteractableScoreIntervalStats = DurationStats.BeforeBegin;
InteractableIntervalThatCanScoreBestStats = DurationStats.BeforeBegin;
}
//预判断
if (VisibleDurationStats == DurationStats.Judgement)
return;
//检定
if (JudgementBehaviour(currentTime))
InvokeJudgement();
if (tickType == TickType.Update)
{
// 可见区间
VisibleDurationStats = UpdateDurationStats(VisibleDuration.x, VisibleDurationBeginEvent, VisibleDuration.y, VisibleDurationEndEvent,
currentTime, VisibleDurationStats);
if (VisibleDurationStats != DurationStats.BeforeEnd)
return;
// 可判定区间
InteractiveDurationStats = UpdateDurationStats(InteractiveDuration.x, InteractiveDurationBeginEvent, InteractiveDuration.y, InteractiveDurationEndEvent,
currentTime, InteractiveDurationStats);
if (InteractiveDurationStats != DurationStats.BeforeEnd)
return;
// 1级判定区间
InteractableScoreIntervalStats = UpdateDurationStats(InteractableScoreInterval.x, InteractableScoreIntervalBeginEvent, InteractableScoreInterval.y, InteractableScoreIntervalEndEvent,
currentTime, InteractableScoreIntervalStats);
if (InteractableScoreIntervalStats != DurationStats.BeforeEnd)
return;
// 0级判定区间
InteractableIntervalThatCanScoreBestStats = UpdateDurationStats(InteractableIntervalThatCanScoreBest.x, InteractableIntervalThatCanScoreBestBeginEvent, InteractableIntervalThatCanScoreBest.y, InteractableIntervalThatCanScoreBestEndEvent,
currentTime, InteractableIntervalThatCanScoreBestStats);
}
}
/// <summary>
/// 通过传递对称区间进行初始化
/// </summary>
/// <param name="bestJudgementTimePoint">最佳判定点</param>
/// <param name="interactableIntervalThatCanScoreBest">区间时长,最终结果为
/// (bestJudgementTimePoint-interactableIntervalThatCanScoreBest/2,bestJudgementTimePoint+interactableIntervalThatCanScoreBest/2)</param>
/// <param name="interactableScoreInterval">区间时长,最终结果为
/// (bestJudgementTimePoint-interactableScoreInterval/2,bestJudgementTimePoint+interactableScoreInterval/2)</param>
/// <param name="interactiveDuration">区间时长,最终结果为
/// (bestJudgementTimePoint-interactiveDuration/2,bestJudgementTimePoint+interactiveDuration/2)</param>
/// <param name="visibleDuration">区间时长,最终结果为
/// (bestJudgementTimePoint-visibleDuration/2,bestJudgementTimePoint+visibleDuration/2)</param>
[ScriptableCall(@"
<summary>
</summary>
<param name=""bestJudgementTimePoint""></param>
<param name=""interactableIntervalThatCanScoreBest"">
(bestJudgementTimePoint-interactableIntervalThatCanScoreBest/2,bestJudgementTimePoint+interactableIntervalThatCanScoreBest/2)</param>
<param name=""interactableScoreInterval"">
(bestJudgementTimePoint-interactableScoreInterval/2,bestJudgementTimePoint+interactableScoreInterval/2)</param>
<param name=""interactiveDuration"">
(bestJudgementTimePoint-interactiveDuration/2,bestJudgementTimePoint+interactiveDuration/2)</param>
<param name=""visibleDuration"">
(bestJudgementTimePoint-visibleDuration/2,bestJudgementTimePoint+visibleDuration/2)</param>
")]
public void SetupJudgementLevels(
string bestJudgementTimePoint,
string interactableIntervalThatCanScoreBest,
string interactableScoreInterval,
string interactiveDuration,
string visibleDuration)
{
var bestJudgementTimePointValue = Parse(bestJudgementTimePoint);
// InteractableIntervalThatCanScoreBest
var interactableIntervalThatCanScoreBestValue = Parse(interactableIntervalThatCanScoreBest) * 0.5f;
InteractableIntervalThatCanScoreBest = new(bestJudgementTimePointValue - interactableIntervalThatCanScoreBestValue,
bestJudgementTimePointValue + interactableIntervalThatCanScoreBestValue);
// InteractableScoreInterval
var interactableScoreIntervalValue = Parse(interactableScoreInterval) * 0.5f;
InteractableScoreInterval = new(bestJudgementTimePointValue - interactableScoreIntervalValue,
bestJudgementTimePointValue + interactableScoreIntervalValue);
// InteractiveDuration
var interactiveDurationValue = Parse(interactiveDuration) * 0.5f;
InteractiveDuration = new(bestJudgementTimePointValue - interactiveDurationValue,
bestJudgementTimePointValue + interactiveDurationValue);
// InteractableScoreInterval
var visibleDurationValue = Parse(visibleDuration) * 0.5f;
VisibleDuration = new(bestJudgementTimePointValue - visibleDurationValue,
bestJudgementTimePointValue + visibleDurationValue);
}
/// <summary>
/// 设置可见区间显现但不可判定3级判定区间开始时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
3
</summary>
<param name=""value""></param>
")]
public void SetVisibleDurationBegin(string value)
{
VisibleDuration.x = Parse(value);
}
/// <summary>
/// 设置可见区间显现但不可判定3级判定区间结束时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
3
</summary>
<param name=""value""></param>
")]
public void SetVisibleDurationEnd(string value)
{
VisibleDuration.y = Parse(value);
}
/// <summary>
/// 设置2级判定区间可判定但错误的开始时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
2
</summary>
<param name=""value""></param>
")]
public void SetInteractiveDurationBegin(string value)
{
InteractiveDuration.x = Parse(value);
}
/// <summary>
/// 设置2级判定区间可判定但错误的结束时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
2
</summary>
<param name=""value""></param>
")]
public void SetInteractiveDurationEnd(string value)
{
InteractiveDuration.y = Parse(value);
}
/// <summary>
/// 设置1级判定区间可判定的开始时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
1
</summary>
<param name=""value""></param>
")]
public void SetInteractableScoreIntervalBegin(string value)
{
InteractableScoreInterval.x = Parse(value);
}
/// <summary>
/// 设置1级判定区间可判定的结束时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
1
</summary>
<param name=""value""></param>
")]
public void SetInteractableScoreIntervalEnd(string value)
{
InteractableScoreInterval.y = Parse(value);
}
/// <summary>
/// 设置0级判定区间最佳判定开始时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
0
</summary>
<param name=""value""></param>
")]
public void SetInteractableIntervalThatCanScoreBestBegin(string value)
{
InteractableIntervalThatCanScoreBest.x = Parse(value);
}
/// <summary>
/// 设置0级判定区间最佳判定结束时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
0
</summary>
<param name=""value""></param>
")]
public void SetInteractableIntervalThatCanScoreBestEnd(string value)
{
InteractableIntervalThatCanScoreBest.y = Parse(value);
}
}
}