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 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 abstract bool JudgementBehaviour(float timePoint); 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); } } /// /// 通过传递对称区间进行初始化 /// /// 最佳判定点 /// 区间时长,最终结果为 /// (bestJudgementTimePoint-interactableIntervalThatCanScoreBest/2,bestJudgementTimePoint+interactableIntervalThatCanScoreBest/2) /// 区间时长,最终结果为 /// (bestJudgementTimePoint-interactableScoreInterval/2,bestJudgementTimePoint+interactableScoreInterval/2) /// 区间时长,最终结果为 /// (bestJudgementTimePoint-interactiveDuration/2,bestJudgementTimePoint+interactiveDuration/2) /// 区间时长,最终结果为 /// (bestJudgementTimePoint-visibleDuration/2,bestJudgementTimePoint+visibleDuration/2) [ScriptableCall(@" 通过传递对称区间进行初始化 最佳判定点 区间时长,最终结果为 (bestJudgementTimePoint-interactableIntervalThatCanScoreBest/2,bestJudgementTimePoint+interactableIntervalThatCanScoreBest/2) 区间时长,最终结果为 (bestJudgementTimePoint-interactableScoreInterval/2,bestJudgementTimePoint+interactableScoreInterval/2) 区间时长,最终结果为 (bestJudgementTimePoint-interactiveDuration/2,bestJudgementTimePoint+interactiveDuration/2) 区间时长,最终结果为 (bestJudgementTimePoint-visibleDuration/2,bestJudgementTimePoint+visibleDuration/2) ")] 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); } /// /// 设置可见区间(显现但不可判定,3级判定区间)开始时间 /// /// [ScriptableCall(@" 设置可见区间(显现但不可判定,3级判定区间)开始时间 ")] public void SetVisibleDurationBegin(string value) { VisibleDuration.x = Parse(value); } /// /// 设置可见区间(显现但不可判定,3级判定区间)结束时间 /// /// [ScriptableCall(@" 设置可见区间(显现但不可判定,3级判定区间)结束时间 ")] public void SetVisibleDurationEnd(string value) { VisibleDuration.y = Parse(value); } /// /// 设置2级判定区间(可判定但错误的)开始时间 /// /// [ScriptableCall(@" 设置2级判定区间(可判定但错误的)开始时间 ")] public void SetInteractiveDurationBegin(string value) { InteractiveDuration.x = Parse(value); } /// /// 设置2级判定区间(可判定但错误的)结束时间 /// /// [ScriptableCall(@" 设置2级判定区间(可判定但错误的)结束时间 ")] public void SetInteractiveDurationEnd(string value) { InteractiveDuration.y = Parse(value); } /// /// 设置1级判定区间(可判定的)开始时间 /// /// [ScriptableCall(@" 设置1级判定区间(可判定的)开始时间 ")] public void SetInteractableScoreIntervalBegin(string value) { InteractableScoreInterval.x = Parse(value); } /// /// 设置1级判定区间(可判定的)结束时间 /// /// [ScriptableCall(@" 设置1级判定区间(可判定的)结束时间 ")] public void SetInteractableScoreIntervalEnd(string value) { InteractableScoreInterval.y = Parse(value); } /// /// 设置0级判定区间(最佳判定)开始时间 /// /// [ScriptableCall(@" 设置0级判定区间(最佳判定)开始时间 ")] public void SetInteractableIntervalThatCanScoreBestBegin(string value) { InteractableIntervalThatCanScoreBest.x = Parse(value); } /// /// 设置0级判定区间(最佳判定)结束时间 /// /// [ScriptableCall(@" 设置0级判定区间(最佳判定)结束时间 ")] public void SetInteractableIntervalThatCanScoreBestEnd(string value) { InteractableIntervalThatCanScoreBest.y = Parse(value); } } }