Updatement数据优化, 性能优化

This commit is contained in:
2025-12-02 15:26:41 +08:00
parent 716a23fad2
commit f3f13f7051
4 changed files with 122 additions and 145 deletions

View File

@@ -1,40 +1,46 @@
using System.Collections; using System.Collections;
using System.Collections.Generic;
using Convention; using Convention;
using UnityEngine; using UnityEngine;
namespace Demo.Game namespace Demo.Game
{ {
public class SkyUpdatement : Updatement<Material>, IAssetBundleLoader public class SkyUpdatement : Updatement<int>, IAssetBundleLoader
{ {
public static SkyUpdatement Make() public static SkyUpdatement Make()
{ {
return new GameObject().AddComponent<SkyUpdatement>(); return new GameObject().AddComponent<SkyUpdatement>();
} }
private readonly Dictionary<string, int> NameCache = new();
private readonly Dictionary<int, Material> MaterialCache = new();
public string SkyAssetBundlePath; public string SkyAssetBundlePath;
public AssetBundle SkyAssetBundle; public AssetBundle SkyAssetBundle;
protected override Material Lerp(Material begin, Material end, float t) protected override int Lerp(int begin, int end, float t)
{ {
return begin; return begin;
} }
[Content, SerializeField] private Material Cache; [Content, SerializeField] private int Cache;
protected override void UpdateData(Material data) protected override void UpdateData(int data)
{ {
if (string.IsNullOrEmpty(SkyAssetBundlePath)) if (string.IsNullOrEmpty(SkyAssetBundlePath))
return; return;
if (Cache < 0)
return;
if (Cache != data) if (Cache != data)
{ {
RenderSettings.skybox = data; RenderSettings.skybox = MaterialCache[data];
Cache = data; Cache = data;
} }
} }
public override IEnumerator UnloadScript() public override IEnumerator UnloadScript()
{ {
Cache = null; Cache = -1;
if (string.IsNullOrEmpty(SkyAssetBundlePath) == false) if (string.IsNullOrEmpty(SkyAssetBundlePath) == false)
yield return this.UnloadAssetBundle(SkyAssetBundlePath); yield return this.UnloadAssetBundle(SkyAssetBundlePath);
SkyAssetBundlePath = ""; SkyAssetBundlePath = "";
@@ -67,7 +73,13 @@ namespace Demo.Game
ir.completed += delegate ir.completed += delegate
{ {
var mat = ir.asset as Material; var mat = ir.asset as Material;
ManualAddEntry(time, mat, default); if (NameCache.TryGetValue(sky, out int id) == false)
{
id = NameCache.Count;
NameCache[sky] = id;
}
MaterialCache[id] = mat;
ManualAddEntry(time, id, default);
}; };
yield return ir; yield return ir;
} }

View File

@@ -3,11 +3,12 @@ using Demo.Editor.UI;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Unity.Collections;
using UnityEngine; using UnityEngine;
namespace Demo.Game namespace Demo.Game
{ {
public abstract class Updatement<DataType> : TimelineScriptObject public abstract class Updatement<DataType> : TimelineScriptObject where DataType : struct
{ {
[Serializable] [Serializable]
public class UpdatementEntry public class UpdatementEntry
@@ -16,9 +17,29 @@ namespace Demo.Game
public DataType Position = default; public DataType Position = default;
public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear; public MathExtension.EaseCurveType easeCurveType = MathExtension.EaseCurveType.Linear;
} }
[Serializable]
public struct UpdatementCompiledEntries
{
public NativeArray<float> TimePoints;
public NativeArray<DataType> Positions;
public NativeArray<MathExtension.EaseCurveType> EaseCurveTypes;
public readonly int Count;
public UpdatementCompiledEntries(NativeArray<float> timePoints,
NativeArray<DataType> positions,
NativeArray<MathExtension.EaseCurveType> easeCurveTypes,
int count)
{
TimePoints = timePoints;
Positions = positions;
EaseCurveTypes = easeCurveTypes;
Count = count;
}
}
public int Content = 0; public int Content = 0;
public readonly List<UpdatementEntry> Entries = new(); private List<UpdatementEntry> Entries = new();
public UpdatementCompiledEntries CompiledEntries;
protected abstract void UpdateData(DataType data); protected abstract void UpdateData(DataType data);
protected abstract DataType Lerp(DataType begin, DataType end, float t); protected abstract DataType Lerp(DataType begin, DataType end, float t);
@@ -38,16 +59,40 @@ namespace Demo.Game
}); });
} }
private void BuildupCompiledEntriesAndReleaseEntries()
{
Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint));
CompiledEntries = new(
new NativeArray<float>(Entries.Count, Allocator.Persistent),
new NativeArray<DataType>(Entries.Count, Allocator.Persistent),
new NativeArray<MathExtension.EaseCurveType>(Entries.Count, Allocator.Persistent),
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();
Entries = null;
}
private void UpdateEntry(int start, float percent) private void UpdateEntry(int start, float percent)
{ {
UpdatementEntry head = Entries[start], tail = Entries[Mathf.Min(start + 1, Entries.Count - 1)]; int head = start;
UpdateData(Lerp(head.Position, tail.Position, MathExtension.Evaluate(Mathf.Clamp01(percent), head.easeCurveType))); 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() protected override IEnumerator DoSomethingDuringApplyScript()
{ {
yield return base.DoSomethingDuringApplyScript(); yield return base.DoSomethingDuringApplyScript();
Entries.Sort((x, y) => x.TimePoint.CompareTo(y.TimePoint)); BuildupCompiledEntriesAndReleaseEntries();
if (UpdateTarget == null) if (UpdateTarget == null)
{ {
UpdateTarget = Parent.gameObject; UpdateTarget = Parent.gameObject;
@@ -63,7 +108,7 @@ namespace Demo.Game
public override IEnumerator UnloadScript() public override IEnumerator UnloadScript()
{ {
Content = 0; Content = 0;
Entries.Clear(); CompiledEntries = default;
yield return base.UnloadScript(); yield return base.UnloadScript();
} }
@@ -73,10 +118,10 @@ namespace Demo.Game
float GetPercentValue() float GetPercentValue()
{ {
return (currentTime - Entries[Content].TimePoint) / (Entries[Content + 1].TimePoint - Entries[Content].TimePoint); return (currentTime - CompiledEntries.TimePoints[Content]) / (CompiledEntries.TimePoints[Content + 1] - CompiledEntries.TimePoints[Content]);
} }
if (Entries.Count <= 1) if (CompiledEntries.Count <= 1)
return; return;
switch (tickType) switch (tickType)
{ {
@@ -84,19 +129,19 @@ namespace Demo.Game
case TickType.Start: case TickType.Start:
{ {
Content = 0; Content = 0;
while (Content + 1 < Entries.Count && Entries[Content + 1].TimePoint < currentTime) while (Content + 1 < CompiledEntries.Count && CompiledEntries.TimePoints[Content + 1] < currentTime)
Content++; Content++;
UpdateEntry(Content, GetPercentValue()); UpdateEntry(Content, GetPercentValue());
} }
break; break;
default: default:
if (Entries[0].TimePoint > currentTime) if (CompiledEntries.TimePoints[0] > currentTime)
return; return;
if (Content + 1 >= Entries.Count) if (Content + 1 >= CompiledEntries.Count)
return; return;
if (Entries[Content + 1].TimePoint < currentTime) if (CompiledEntries.TimePoints[Content + 1] < currentTime)
Content++; Content++;
if (Content + 1 >= Entries.Count) if (Content + 1 >= CompiledEntries.Count)
UpdateEntry(Content, 1); UpdateEntry(Content, 1);
else else
UpdateEntry(Content, GetPercentValue()); UpdateEntry(Content, GetPercentValue());
@@ -106,21 +151,21 @@ namespace Demo.Game
public DataType Evaluate(float time) public DataType Evaluate(float time)
{ {
if (Entries.Count == 0) if (CompiledEntries.Count == 0)
return default; return default;
if (Entries.Count == 1) if (CompiledEntries.Count == 1)
return Entries[0].Position; return CompiledEntries.Positions[0];
if (time < Entries[0].TimePoint) if (time < CompiledEntries.TimePoints[0])
return Entries[0].Position; return CompiledEntries.Positions[0];
for (int i = 1; i < Entries.Count; i++) for (int i = 1; i < CompiledEntries.Count; i++)
{ {
if (Entries[i - 1].TimePoint <= time && Entries[i].TimePoint > time) if (CompiledEntries.TimePoints[i - 1] <= time && CompiledEntries.TimePoints[i] > time)
{ {
return Lerp(Entries[i - 1].Position, Entries[i].Position, return Lerp(CompiledEntries.Positions[i - 1], CompiledEntries.Positions[i],
(time - Entries[i - 1].TimePoint) / (Entries[i].TimePoint - Entries[i - 1].TimePoint)); (time - CompiledEntries.TimePoints[i - 1]) / (CompiledEntries.TimePoints[i] - CompiledEntries.TimePoints[i - 1]));
} }
} }
return Entries[^1].Position; return CompiledEntries.Positions[^1];
} }
[Content] public GameObject UpdateTarget; [Content] public GameObject UpdateTarget;
@@ -140,107 +185,10 @@ namespace Demo.Game
/// <param name="item">实例在父类中控制</param> /// <param name="item">实例在父类中控制</param>
protected override void SetupTimelineItem(TimelineItem item) protected override void SetupTimelineItem(TimelineItem item)
{ {
if (Entries.Count == 0) if (CompiledEntries.Count == 0)
return; return;
item.SetupDuration(new(Entries[0].TimePoint, Entries[^1].TimePoint), GetTimelineItemColor()); item.SetupDuration(new(CompiledEntries.TimePoints[0], CompiledEntries.TimePoints[^1]), 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;
}
} }
} }
} }

View File

@@ -5,32 +5,36 @@ using UnityEngine;
namespace Demo.Game namespace Demo.Game
{ {
public class LookAtAnchor : Updatement<ScriptableObject> public class LookAtAnchor : Updatement<int>
{ {
public static LookAtAnchor Make() public static LookAtAnchor Make()
{ {
return new GameObject().AddComponent<LookAtAnchor>(); return new GameObject().AddComponent<LookAtAnchor>();
} }
protected override ScriptableObject Lerp(ScriptableObject begin, ScriptableObject end, float t) protected override int Lerp(int begin, int end, float t)
{ {
return begin; return begin;
} }
[Content] public ScriptableObject LookAtObject; private readonly List<ScriptableObject> LookAtCache = new();
[Content] public int LookAtObject;
[Content] public bool IsEnableUpdateEveryTick = false; [Content] public bool IsEnableUpdateEveryTick = false;
protected override void UpdateData(ScriptableObject data) protected override void UpdateData(int data)
{ {
ScriptableObject target = LookAtCache[LookAtObject];
if (data != LookAtObject) if (data != LookAtObject)
{ {
LookAtObject = data; LookAtObject = data;
transform.LookAt(LookAtObject.transform); if (target != null)
transform.LookAt(target.transform);
} }
else if (IsEnableUpdateEveryTick) else if (IsEnableUpdateEveryTick)
{ {
if (LookAtObject != null) if (target != null)
transform.LookAt(LookAtObject.transform); transform.LookAt(target.transform);
} }
} }
@@ -40,7 +44,8 @@ namespace Demo.Game
[Convention.RScript.Variable.Attr.Method] [Convention.RScript.Variable.Attr.Method]
public void Add(float time, ScriptableObject target) public void Add(float time, ScriptableObject target)
{ {
ManualAddEntry(time, target, default); LookAtCache.Add(target);
ManualAddEntry(time, LookAtCache.Count - 1, default);
} }
/// <summary> /// <summary>

View File

@@ -1,41 +1,47 @@
using Convention;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Convention;
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering.LookDev;
namespace Demo.Game namespace Demo.Game
{ {
public class MaterialUpdatement : Updatement<Material>, IAssetBundleLoader public class MaterialUpdatement : Updatement<int>, IAssetBundleLoader
{ {
public static MaterialUpdatement Make() public static MaterialUpdatement Make()
{ {
return new GameObject().AddComponent<MaterialUpdatement>(); return new GameObject().AddComponent<MaterialUpdatement>();
} }
private readonly Dictionary<string, int> NameCache = new();
private readonly Dictionary<int, Material> MaterialCache = new();
public string MaterialAssetBundlePath = null; public string MaterialAssetBundlePath = null;
public AssetBundle MaterialAssetBundle = null; public AssetBundle MaterialAssetBundle = null;
protected override Material Lerp(Material begin, Material end, float t) protected override int Lerp(int begin, int end, float t)
{ {
return begin; return begin;
} }
[Content, SerializeField] private Material Cache; [Content, SerializeField] private int Cache;
protected override void UpdateData(Material data) protected override void UpdateData(int data)
{ {
if (string.IsNullOrEmpty(MaterialAssetBundlePath)) if (string.IsNullOrEmpty(MaterialAssetBundlePath))
return; return;
if (Cache < 0)
return;
if (Cache != data && Parent.TryGetComponent<MeshRenderer>(out var meshRenderer)) if (Cache != data && Parent.TryGetComponent<MeshRenderer>(out var meshRenderer))
{ {
meshRenderer.material = data; meshRenderer.material = MaterialCache[data];
Cache = data; Cache = data;
} }
} }
public override IEnumerator UnloadScript() public override IEnumerator UnloadScript()
{ {
Cache = null; Cache = -1;
if (string.IsNullOrEmpty(MaterialAssetBundlePath) == false) if (string.IsNullOrEmpty(MaterialAssetBundlePath) == false)
yield return this.UnloadAssetBundle(MaterialAssetBundlePath); yield return this.UnloadAssetBundle(MaterialAssetBundlePath);
MaterialAssetBundlePath = null; MaterialAssetBundlePath = null;
@@ -66,7 +72,13 @@ namespace Demo.Game
ir.completed += x => ir.completed += x =>
{ {
var mat = ir.asset as Material; var mat = ir.asset as Material;
ManualAddEntry(time, mat, default); if (NameCache.TryGetValue(material, out int id) == false)
{
id = NameCache.Count;
NameCache[material] = id;
}
MaterialCache[id] = mat;
ManualAddEntry(time, id, default);
}; };
yield return ir; yield return ir;
} }