using Convention; using Demo.Editor.UI; using System; using System.Collections; using System.Collections.Generic; using Unity.Collections; using UnityEngine; namespace Demo.Game { public abstract class Updatement : TimelineScriptObject where DataType : struct { [Serializable] public class UpdatementEntry { public float TimePoint = 0; public DataType Position = default; public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear; } [Serializable] public struct UpdatementCompiledEntries: IDisposable { public NativeArray TimePoints; public NativeArray Positions; public NativeArray EaseCurveTypes; public readonly int Count; public UpdatementCompiledEntries(NativeArray timePoints, NativeArray positions, NativeArray easeCurveTypes, int count) { TimePoints = timePoints; Positions = positions; EaseCurveTypes = easeCurveTypes; Count = count; } public void Dispose() { if (TimePoints.IsCreated) TimePoints.Dispose(); if (Positions.IsCreated) Positions.Dispose(); if (EaseCurveTypes.IsCreated) EaseCurveTypes.Dispose(); } } public int Content = 0; private readonly List Entries = new(); public UpdatementCompiledEntries CompiledEntries; protected abstract void UpdateData(DataType data); protected abstract DataType Lerp(DataType begin, DataType end, float t); /// /// 添加数据 /// /// /// /// public void ManualAddEntry(float time, DataType position, MathExtension.EaseCurveType curveType) { Entries.Add(new() { TimePoint = time, Position = position, easeCurveType = curveType }); } private void BuildupCompiledEntriesAndReleaseEntries() { Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint)); CompiledEntries = new( new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory), Entries.Count ); int index = 0; foreach (var item in Entries) { CompiledEntries.TimePoints[index] = item.TimePoint; CompiledEntries.Positions[index] = item.Position; CompiledEntries.EaseCurveTypes[index] = item.easeCurveType; index++; } Entries.Clear(); } private void UpdateEntry(int start, float percent) { int head = start; int tail = Mathf.Min(start + 1, CompiledEntries.Count - 1); UpdateData(Lerp(CompiledEntries.Positions[start], CompiledEntries.Positions[tail], MathExtension.Evaluate(Mathf.Clamp01(percent), CompiledEntries.EaseCurveTypes[head]))); } protected override IEnumerator DoSomethingDuringApplyScript() { yield return base.DoSomethingDuringApplyScript(); BuildupCompiledEntriesAndReleaseEntries(); if (UpdateTarget == null) { UpdateTarget = Parent.gameObject; } } public override void ResetEnterGameStatus() { base.ResetEnterGameStatus(); if (CompiledEntries.Count <= 1) return; UpdateEntry(0, 0); } public override IEnumerator UnloadScript() { Content = 0; CompiledEntries = default; yield return base.UnloadScript(); } protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType) { base.UpdateTicks(currentTime, deltaTime, tickType); float GetPercentValue() { if (Content + 1 == CompiledEntries.Count) return 1; return (currentTime - CompiledEntries.TimePoints[Content]) / (CompiledEntries.TimePoints[Content + 1] - CompiledEntries.TimePoints[Content]); } if (CompiledEntries.Count <= 1) return; switch (tickType) { case TickType.Reset: case TickType.Start: { Content = 0; while (Content + 1 < CompiledEntries.Count && CompiledEntries.TimePoints[Content + 1] < currentTime) Content++; UpdateEntry(Content, GetPercentValue()); } break; default: if (CompiledEntries.TimePoints[0] > currentTime) return; if (Content + 1 < CompiledEntries.Count && CompiledEntries.TimePoints[Content + 1] < currentTime) Content++; if (Content + 1 > CompiledEntries.Count) return; UpdateEntry(Content, GetPercentValue()); break; } } public DataType Evaluate(float time) { if (CompiledEntries.Count == 0) return default; if (CompiledEntries.Count == 1) return CompiledEntries.Positions[0]; if (time < CompiledEntries.TimePoints[0]) return CompiledEntries.Positions[0]; for (int i = 1; i < CompiledEntries.Count; i++) { if (CompiledEntries.TimePoints[i - 1] <= time && CompiledEntries.TimePoints[i] > time) { return Lerp(CompiledEntries.Positions[i - 1], CompiledEntries.Positions[i], (time - CompiledEntries.TimePoints[i - 1]) / (CompiledEntries.TimePoints[i] - CompiledEntries.TimePoints[i - 1])); } } return CompiledEntries.Positions[^1]; } [Content] public GameObject UpdateTarget; /// /// 设置更新对象 /// [Convention.RScript.Variable.Attr.Method] public void SetUpdateTarget(ScriptableObject target) { UpdateTarget = target.gameObject; } /// /// /// /// 实例在父类中控制 protected override void SetupTimelineItem(TimelineItem item) { if (CompiledEntries.Count == 0) return; item.SetupDuration(new(CompiledEntries.TimePoints[0], CompiledEntries.TimePoints[^1]), GetTimelineItemColor()); } private void OnDestroy() { CompiledEntries.Dispose(); } } }