Files
Convention-Unity-Demo/Assets/Scripts/Framework/Updatement.cs

250 lines
8.9 KiB
C#

using Convention;
using Demo.Editor.UI;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Demo.Game
{
public abstract class Updatement<DataType> : 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 readonly List<UpdatementEntry> Entries = new();
protected abstract void UpdateData(DataType data);
protected abstract DataType Lerp(DataType begin, DataType end, float t);
/// <summary>
/// 添加数据
/// </summary>
/// <param name="time"></param>
/// <param name="position"></param>
/// <param name="curveType"></param>
public void ManualAddEntry(float time, DataType position, MathExtension.EaseCurveType curveType)
{
Entries.Add(new()
{
TimePoint = 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)));
}
protected override IEnumerator DoSomethingDuringApplyScript()
{
yield return base.DoSomethingDuringApplyScript();
Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint));
if (UpdateTarget == null)
{
UpdateTarget = Parent.gameObject;
}
}
public override void ResetEnterGameStatus()
{
base.ResetEnterGameStatus();
UpdateEntry(0, 0);
}
public override IEnumerator UnloadScript()
{
Content = 0;
Entries.Clear();
yield return base.UnloadScript();
}
protected override void UpdateTicks(float currentTime, float deltaTime, TickType tickType)
{
using (Profiler.BeginZone($"Updatement<{typeof(DataType).Name}>.UpdateTicks"))
{
base.UpdateTicks(currentTime, deltaTime, tickType);
float GetPercentValue()
{
return (currentTime - Entries[Content].TimePoint) / (Entries[Content + 1].TimePoint - Entries[Content].TimePoint);
}
if (Entries.Count <= 1)
return;
switch (tickType)
{
case TickType.Reset:
case TickType.Start:
{
Content = 0;
while (Content + 1 < Entries.Count && Entries[Content + 1].TimePoint < currentTime)
Content++;
UpdateEntry(Content, GetPercentValue());
}
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, GetPercentValue());
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;
/// <summary>
/// 设置更新对象
/// </summary>
[Convention.RScript.Variable.Attr.Method]
public void SetUpdateTarget(ScriptableObject target)
{
UpdateTarget = target.gameObject;
}
/// <summary>
///
/// </summary>
/// <param name="item">实例在父类中控制</param>
protected override void SetupTimelineItem(TimelineItem item)
{
if (Entries.Count == 0)
return;
item.SetupDuration(new(Entries[0].TimePoint, Entries[^1].TimePoint), GetTimelineItemColor());
}
}
public interface ILocalUpdatement<DataType>: IScriptableObject
{
int Content { get; set; }
public List<ILocalUpdatementExtension.UpdatementEntry<DataType>> Entries { get; set; }
void UpdateData(DataType data);
DataType Lerp(DataType begin, DataType end, float t);
}
public static class ILocalUpdatementExtension
{
[Serializable]
public class UpdatementEntry<DataType>
{
public float TimePoint = 0;
public DataType Position = default;
public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear;
}
public static void AddEntry<DataType>(this ILocalUpdatement<DataType> self, float time, DataType position, MathExtension.EaseCurveType curveType)
{
self.Entries.Add(new()
{
TimePoint = time,
Position = position,
easeCurveType = curveType
});
}
public static void UpdateEntry<DataType>(this ILocalUpdatement<DataType> self, int start, float percent)
{
UpdatementEntry<DataType> 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<DataType>(this ILocalUpdatement<DataType> 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<DataType>(this ILocalUpdatement<DataType> self)
{
self.Content = 0;
self.Entries = new();
}
public static void UpdateTicks<DataType>(this ILocalUpdatement<DataType> 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;
}
}
}
}