using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using Convention; using Demo.Editor.UI; using UnityEngine; namespace Demo.Game { public abstract class Updatement : TimelineScriptObject { [Serializable] public class UpdatementEntry { public float TimePoint = 0; public DataType Position = default; public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear; } public int Content = 0; public List Entries = new(); protected abstract void UpdateData(DataType data); protected abstract DataType Lerp(DataType begin, DataType end, float t); /// /// 添加数据 /// /// /// /// public void ManualAddEntry(string time, DataType position, MathExtension.EaseCurveType curveType) { Entries.Add(new() { TimePoint = Parse(time), Position = position, easeCurveType = curveType }); } private void UpdateEntry(int start, float percent) { UpdatementEntry head = Entries[start], tail = Entries[Mathf.Min(start + 1, Entries.Count - 1)]; UpdateData(Lerp(head.Position, tail.Position, MathExtension.Evaluate(Mathf.Clamp01(percent), head.easeCurveType))); } public override IEnumerator LoadScript(string script) { yield return base.LoadScript(script); Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint)); if (UpdateTarget == null) { UpdateTarget = transform.parent.gameObject; } } public override IEnumerator UnloadScript() { Content = 0; Entries = new(); yield return base.UnloadScript(); } protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType) { base.UpdateTicks(currentTime, deltaTime, tickType); if (Entries.Count == 0) return; if (Entries.Count == 1) { UpdateEntry(0, 0); return; } if (Entries[0].TimePoint <= 0 && tickType == TickType.Reset) { UpdateEntry(0, 0); } switch (tickType) { case TickType.Pause: //case TickType.LateUpdate: break; case TickType.Reset: case TickType.Start: { Content = 0; while (Content + 1 < Entries.Count && Entries[Content + 1].TimePoint < currentTime) Content++; UpdateEntry(Content, 0); } break; default: if (Entries[0].TimePoint > currentTime) return; if (Content + 1 >= Entries.Count) return; if (Entries[Content + 1].TimePoint < currentTime) Content++; if (Content + 1 >= Entries.Count) UpdateEntry(Content, 1); else UpdateEntry(Content, (currentTime - Entries[Content].TimePoint) / (Entries[Content + 1].TimePoint - Entries[Content].TimePoint)); break; } } public DataType Evaluate(float time) { if (Entries.Count == 0) return default; if (Entries.Count == 1) return Entries[0].Position; if (time < Entries[0].TimePoint) return Entries[0].Position; for (int i = 1; i < Entries.Count; i++) { if (Entries[i - 1].TimePoint <= time && Entries[i].TimePoint > time) { return Lerp(Entries[i - 1].Position, Entries[i].Position, (time - Entries[i - 1].TimePoint) / (Entries[i].TimePoint - Entries[i - 1].TimePoint)); } } return Entries[^1].Position; } [Content] public GameObject UpdateTarget; /// /// 设置更新对象 /// /// 脚本的相对路径 [ScriptableCall(@" 设置更新对象 脚本的相对路径 ")] public void SetUpdateTarget(string path) { var temp = FindWithPath(path); if (temp != null) UpdateTarget = temp.gameObject; else Debug.LogWarning($"{path}' is not found", this); } /// /// /// /// 实例在父类中控制 protected override void SetupTimelineItem(TimelineItem item) { if (Entries.Count == 0) return; item.SetupDuration(new(Entries[0].TimePoint, Entries[^1].TimePoint), GetTimelineItemColor()); } } public interface ILocalUpdatement: IScriptableObject { int Content { get; set; } public List> Entries { get; set; } void UpdateData(DataType data); DataType Lerp(DataType begin, DataType end, float t); } public static class ILocalUpdatementExtension { [Serializable] public class UpdatementEntry { public float TimePoint = 0; public DataType Position = default; public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear; } public static void AddEntry(this ILocalUpdatement self, string time, DataType position, MathExtension.EaseCurveType curveType) { self.Entries.Add(new() { TimePoint = self.SharedInterfaceScriptObject.Parse(time), Position = position, easeCurveType = curveType }); } public static void UpdateEntry(this ILocalUpdatement self, int start, float percent) { UpdatementEntry head = self.Entries[start], tail = self.Entries[Mathf.Min(start + 1, self.Entries.Count - 1)]; self.UpdateData(self.Lerp(head.Position, tail.Position, MathExtension.Evaluate(Mathf.Clamp01(percent), head.easeCurveType))); } public static void LoadScriptHelper(this ILocalUpdatement self) { if (self.Entries.Count < 2) { self.Entries = new() { new() { TimePoint=0, Position=default, }, new() { TimePoint = float.MaxValue, Position=default, } }; } else { self.Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint)); } } public static void UnloadScriptHelper(this ILocalUpdatement self) { self.Content = 0; self.Entries = new(); } public static void UpdateTicks(this ILocalUpdatement self, float currentTime, ScriptableObject.TickType tickType) { switch (tickType) { case ScriptableObject.TickType.Pause: //case TickType.LateUpdate: break; case ScriptableObject.TickType.Reset: case ScriptableObject.TickType.Start: { self.Content = 0; while (self.Entries[self.Content + 1].TimePoint < currentTime) self.Content++; UpdateEntry(self, self.Content, 0); } break; default: if (self.Entries[0].TimePoint > currentTime) return; if (self.Content + 1 >= self.Entries.Count) return; if (self.Entries[self.Content + 1].TimePoint < currentTime) self.Content++; if (self.Content + 1 >= self.Entries.Count) UpdateEntry(self, self.Content, 1); else UpdateEntry(self, self.Content, (currentTime - self.Entries[self.Content].TimePoint) / (self.Entries[self.Content + 1].TimePoint - self.Entries[self.Content].TimePoint)); break; } } } }