using Convention; using Demo.Editor.UI; using Demo.Game.ConfigType; using System; using System.Collections; using System.Collections.Generic; using System.IO; using Unity.Collections; using UnityEngine; namespace Demo.Game { namespace ConfigType { public abstract class UpdatementConfig: ScriptLoadableConfig where DataType : struct { public NativeArray TimePoints; public NativeArray Positions; public NativeArray EaseCurveTypes; protected abstract void DeserializePositions(BinaryReader reader, ref NativeArray positions); protected abstract void SerializePositions(BinaryWriter writer, in NativeArray positions); public override void Deserialize(BinaryReader reader) { BinarySerializeUtility.DeserializeNativeArray(reader, ref TimePoints); DeserializePositions(reader, ref Positions); { NativeArray temp = new(EaseCurveTypes.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory); BinarySerializeUtility.DeserializeNativeArray(reader, ref temp); for (int i = 0, e = EaseCurveTypes.Length; i < e; i++) { EaseCurveTypes[i] = (MathExtension.EaseCurveType)temp[i]; } temp.Dispose(); } base.Deserialize(reader); } public override void Serialize(BinaryWriter writer) { BinarySerializeUtility.SerializeNativeArray(writer, TimePoints); SerializePositions(writer, Positions); { NativeArray temp = new(EaseCurveTypes.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (int i = 0, e = EaseCurveTypes.Length; i < e; i++) { temp[i] = (int)EaseCurveTypes[i]; } BinarySerializeUtility.SerializeNativeArray(writer, temp); temp.Dispose(); } base.Serialize(writer); } } public class UpdatementIntConfig : UpdatementConfig { protected override sealed void DeserializePositions(BinaryReader reader, ref NativeArray positions) { BinarySerializeUtility.DeserializeNativeArray(reader, ref positions); } protected override sealed void SerializePositions(BinaryWriter writer, in NativeArray positions) { BinarySerializeUtility.SerializeNativeArray(writer, positions); } } public class UpdatementFloatConfig : UpdatementConfig { protected override sealed void DeserializePositions(BinaryReader reader, ref NativeArray positions) { BinarySerializeUtility.DeserializeNativeArray(reader, ref positions); } protected override sealed void SerializePositions(BinaryWriter writer, in NativeArray positions) { BinarySerializeUtility.SerializeNativeArray(writer, positions); } } public class UpdatementVec2Config : UpdatementConfig { protected override sealed void DeserializePositions(BinaryReader reader, ref NativeArray positions) { BinarySerializeUtility.DeserializeNativeArray(reader, ref positions); } protected override sealed void SerializePositions(BinaryWriter writer, in NativeArray positions) { BinarySerializeUtility.SerializeNativeArray(writer, positions); } } public class UpdatementVec3Config : UpdatementConfig { protected override sealed void DeserializePositions(BinaryReader reader, ref NativeArray positions) { BinarySerializeUtility.DeserializeNativeArray(reader, ref positions); } protected override sealed void SerializePositions(BinaryWriter writer, in NativeArray positions) { BinarySerializeUtility.SerializeNativeArray(writer, positions); } } } 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; } public int Content = 0; private readonly List Entries = new(); 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)); GetConfig>().TimePoints = new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); GetConfig>().Positions = new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); GetConfig>().EaseCurveTypes = new NativeArray(Entries.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); int index = 0; foreach (var item in Entries) { GetConfig>().TimePoints[index] = item.TimePoint; GetConfig>().Positions[index] = item.Position; GetConfig>().EaseCurveTypes[index] = item.easeCurveType; index++; } Entries.Clear(); } private void UpdateEntry(int start, float percent) { int head = start; int tail = Mathf.Min(start + 1, GetConfig>().TimePoints.Length - 1); UpdateData(Lerp(GetConfig>().Positions[start], GetConfig>().Positions[tail], MathExtension.Evaluate(Mathf.Clamp01(percent), GetConfig>().EaseCurveTypes[head]))); } protected override IEnumerator DoSomethingDuringApplyScript() { yield return base.DoSomethingDuringApplyScript(); BuildupCompiledEntriesAndReleaseEntries(); if (UpdateTarget == null) { UpdateTarget = Parent.gameObject; } } public override void ResetEnterGameStatus() { base.ResetEnterGameStatus(); if (GetConfig>().TimePoints.Length <= 1) return; UpdateEntry(0, 0); } public override IEnumerator UnloadScript() { Content = 0; GetConfig>().TimePoints.Dispose(); GetConfig>().Positions.Dispose(); GetConfig>().EaseCurveTypes.Dispose(); yield return base.UnloadScript(); } protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType) { base.UpdateTicks(currentTime, deltaTime, tickType); float GetPercentValue() { if (Content + 1 == GetConfig>().TimePoints.Length) return 1; return (currentTime - GetConfig>().TimePoints[Content]) / (GetConfig>().TimePoints[Content + 1] - GetConfig>().TimePoints[Content]); } if (GetConfig>().TimePoints.Length <= 1) return; switch (tickType) { case TickType.Reset: case TickType.Start: { Content = 0; while (Content + 1 < GetConfig>().TimePoints.Length && GetConfig>().TimePoints[Content + 1] < currentTime) Content++; UpdateEntry(Content, GetPercentValue()); } break; default: if (GetConfig>().TimePoints[0] > currentTime) return; if (Content + 1 < GetConfig>().TimePoints.Length && GetConfig>().TimePoints[Content + 1] < currentTime) Content++; if (Content + 1 > GetConfig>().TimePoints.Length) return; UpdateEntry(Content, GetPercentValue()); break; } } public DataType Evaluate(float time) { if (GetConfig>().TimePoints.Length == 0) return default; if (GetConfig>().TimePoints.Length == 1) return GetConfig>().Positions[0]; if (time < GetConfig>().TimePoints[0]) return GetConfig>().Positions[0]; for (int i = 1; i < GetConfig>().TimePoints.Length; i++) { if (GetConfig>().TimePoints[i - 1] <= time && GetConfig>().TimePoints[i] > time) { return Lerp(GetConfig>().Positions[i - 1], GetConfig>().Positions[i], (time - GetConfig>().TimePoints[i - 1]) / (GetConfig>().TimePoints[i] - GetConfig>().TimePoints[i - 1])); } } return GetConfig>().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 (GetConfig>().TimePoints.Length == 0) return; item.SetupDuration(new(GetConfig>().TimePoints[0], GetConfig>().TimePoints[^1]), GetTimelineItemColor()); } private void OnDestroy() { if (GetConfig>().TimePoints.IsCreated) GetConfig>().TimePoints.Dispose(); if (GetConfig>().Positions.IsCreated) GetConfig>().Positions.Dispose(); if (GetConfig>().EaseCurveTypes.IsCreated) GetConfig>().EaseCurveTypes.Dispose(); } } }