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

503 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
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 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();
[Header(nameof(InteractiveDuration))]
[Content, SerializeField] private Vector2 InteractiveDuration;
public static float DefaultInteractiveLength = IdentifiableJudgementTerminal * 9;
[Content, SerializeField] public UnityEvent InteractiveDurationBeforeEvent = new(),
InteractiveDurationBeginEvent = new(), InteractiveDurationEndEvent = new();
[Header(nameof(InteractableScoreInterval))]
[Content, SerializeField] private Vector2 InteractableScoreInterval;
public static float DefaultInteractableScoreIntervalLength = IdentifiableJudgementTerminal * 6;
[Content, SerializeField] public UnityEvent InteractableScoreIntervalBeforeEvent = new(),
InteractableScoreIntervalBeginEvent = new(), InteractableScoreIntervalEndEvent = new();
[Header(nameof(InteractableIntervalThatCanScoreBest))]
[Content, SerializeField] private Vector2 InteractableIntervalThatCanScoreBest;
public static float DefaultInteractableIntervalLengthThatCanScoreBest = IdentifiableJudgementTerminal * 3;
[Content, SerializeField] public UnityEvent InteractableIntervalThatCanScoreBestBeforeEvent = new(),
InteractableIntervalThatCanScoreBestBeginEvent = new(), InteractableIntervalThatCanScoreBestEndEvent = new();
[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();
public float GetBestJudgementTimePoint()
{
return BestJudgementTimePoint;
}
public bool IsInInteractiveDuration(float time)
{
return time > InteractiveDuration.x && time < InteractiveDuration.y;
}
public bool IsInInteractiveDuration()
{
return IsInInteractiveDuration(MyCacheUpdateTick);
}
public const int JudgementLevelCount = 3;
public override IEnumerator LoadScript(string script)
{
yield return base.LoadScript(script);
if (BestJudgementTimePoint <= 0)
{
DoSetupJudgement(0);
}
}
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(JudgementLevel level)
{
if (level == JudgementLevel.None)
return;
// 进入已判定状态
MyJudgementLevel = level;
JudgementEvent.Invoke(level);
// 触发还未触发的退出事件
{
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();
}
}
}
public abstract JudgementLevel JudgementBehaviour(float timePoint);
#region Update Judgement Stats
private void DoUpdateJudgementStats(float currentTime)
{
// 重置
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;
}
#endregion
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
base.UpdateTicks(currentTime, deltaTime, tickType);
// 检定
if (MyJudgementLevel == JudgementLevel.None)
{
InvokeJudgement(JudgementBehaviour(currentTime));
}
// 更新状态
DoUpdateJudgementStats(currentTime);
}
public void DoSetupJudgementLevels(
float bestJudgementTimePoint,
float interactableIntervalThatCanScoreBest,
float interactableScoreInterval,
float interactiveDuration,
float visibleDuration)
{
this.BestJudgementTimePoint = bestJudgementTimePoint;
var bestJudgementTimePointValue = bestJudgementTimePoint;
// InteractableIntervalThatCanScoreBest
var interactableIntervalThatCanScoreBestValue = interactableIntervalThatCanScoreBest * 0.5f;
InteractableIntervalThatCanScoreBest = new(bestJudgementTimePointValue - interactableIntervalThatCanScoreBestValue,
bestJudgementTimePointValue + interactableIntervalThatCanScoreBestValue);
// InteractableScoreInterval
var interactableScoreIntervalValue = interactableScoreInterval * 0.5f;
InteractableScoreInterval = new(bestJudgementTimePointValue - interactableScoreIntervalValue,
bestJudgementTimePointValue + interactableScoreIntervalValue);
// InteractiveDuration
var interactiveDurationValue = interactiveDuration * 0.5f;
InteractiveDuration = new(bestJudgementTimePointValue - interactiveDurationValue,
bestJudgementTimePointValue + interactiveDurationValue);
// InteractableScoreInterval
var visibleDurationValue = visibleDuration * 0.5f;
VisibleDuration = new(bestJudgementTimePointValue - visibleDurationValue,
bestJudgementTimePointValue + visibleDurationValue);
}
public void DoSetupJudgement(float bestJudgementTimePoint)
{
DoSetupJudgementLevels(
bestJudgementTimePoint,
DefaultInteractableIntervalLengthThatCanScoreBest,
DefaultInteractableScoreIntervalLength,
DefaultInteractiveLength,
DefaultVisibleLength);
}
/// <summary>
/// 使用配置中的区间长度设置以最佳判定点为中心的各级区间
/// </summary>
/// <param name="bestJudgementTimePoint">最佳判定点</param>
[ScriptableCall(@"
<summary>
使用配置中的区间长度设置以最佳判定点为中心的各级区间
</summary>
<param name=""bestJudgementTimePoint"">最佳判定点</param>
")]
public void SetupJudgement(string bestJudgementTimePoint)
{
DoSetupJudgement(Parse(bestJudgementTimePoint));
}
/// <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)
{
DoSetupJudgementLevels(
Parse(bestJudgementTimePoint),
Parse(interactableIntervalThatCanScoreBest),
Parse(interactableScoreInterval),
Parse(interactiveDuration),
Parse(visibleDuration));
}
/// <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);
if (BestJudgementTimePoint < 0)
{
BestJudgementTimePoint = Mathf.Lerp(InteractableIntervalThatCanScoreBest.x, InteractableIntervalThatCanScoreBest.y, 0.5f);
}
}
/// <summary>
/// 设置0级判定区间最佳判定结束时间
/// </summary>
/// <param name="value"></param>
[ScriptableCall(@"
<summary>
设置0级判定区间最佳判定结束时间
</summary>
<param name=""value""></param>
")]
public void SetInteractableIntervalThatCanScoreBestEnd(string value)
{
InteractableIntervalThatCanScoreBest.y = Parse(value);
if (BestJudgementTimePoint < 0)
{
BestJudgementTimePoint = Mathf.Lerp(InteractableIntervalThatCanScoreBest.x, InteractableIntervalThatCanScoreBest.y, 0.5f);
}
}
}
}