15 Commits

Author SHA1 Message Date
8d31372c24 refactor: universe and objects now use fast list 2025-10-13 12:40:43 +03:00
a2e704916e feat: fast list now implements IList 2025-10-13 12:39:49 +03:00
c7d170fad9 perf: significant performance optimizations on ordered behaviour collectors by using a sorted dictionary 2025-10-13 09:58:58 +03:00
9ccf7b754d perf: ordered behaviour collectors now use linked lists for performance 2025-10-11 16:07:26 +03:00
e3d4899112 refactor: renamed behaviour collectors from sorted to ordered 2025-10-11 16:05:47 +03:00
566c16d09c refactor: active behaviour collector base added 2025-10-11 15:36:58 +03:00
ae9d4f02ef chore: moved behaviour collectors into subdirectory 2025-10-11 15:36:06 +03:00
e77772cbc2 refactor: behaviour collector base added 2025-10-11 15:08:02 +03:00
4c542df401 perf: implemented fast list with index mapping 2025-10-10 14:58:40 +03:00
28ca343b43 perf: improved pool return method by using a hashset for searching if the returning item is already queued 2025-10-10 14:21:54 +03:00
651b0614c4 fix: index check on triangle batch flush 2025-10-10 11:43:04 +03:00
f47488c6f1 fix: registering/unregistering objects during universe enter/exit causing stack overflows 2025-10-10 10:59:39 +03:00
6d159330a1 refactor: moved client and server interfaces into their files 2025-08-31 23:09:02 +03:00
8e314f3269 feat: networking type hasher added 2025-08-19 21:17:47 +03:00
f5a7077570 perf: improved garbage created by tweens slightly
They still do generate a lot of garbage but with boxed value pools I made the boxes reusable, it still does generate garbage through the delegate creation, gotta find a solution for them later
2025-08-14 20:31:46 +03:00
41 changed files with 829 additions and 378 deletions

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private IComparer<T>? _sortBy = null;
public IComparer<T>? SortBy
{
get => _sortBy;
set
{
_sortBy = value;
if (value is not null)
activeBehaviours.Sort(value);
}
}
protected override void AddBehaviour(T behaviour)
{
if (SortBy is null)
{
activeBehaviours.Add(behaviour);
return;
}
int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy);
if (insertionIndex < 0)
insertionIndex = ~insertionIndex;
activeBehaviours.Insert(insertionIndex, behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
}
protected override void OnBehaviourRemove(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
}
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
T behaviour = (T)sender;
activeBehaviours.Remove(behaviour);
AddBehaviour(behaviour);
}
public ActiveBehaviourCollectorSorted()
{
delegateOnPriorityChanged = OnPriorityChanged;
}
public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
SortBy = Comparer<T>.Create(sortBy);
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private IComparer<T>? _sortBy = null;
public IComparer<T>? SortBy
{
get => _sortBy;
set
{
_sortBy = value;
if (value is not null)
behaviours.Sort(value);
}
}
protected override void AddBehaviour(T behaviour)
{
if (SortBy is null)
{
behaviours.Add(behaviour);
return;
}
int insertionIndex = behaviours.BinarySearch(behaviour, SortBy);
if (insertionIndex < 0)
insertionIndex = ~insertionIndex;
behaviours.Insert(insertionIndex, behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
}
protected override void OnBehaviourRemove(IBehaviour behaviour)
{
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
}
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
T behaviour = (T)sender;
behaviours.Remove(behaviour);
AddBehaviour(behaviour);
}
public BehaviourCollectorSorted()
{
delegateOnPriorityChanged = OnPriorityChanged;
}
public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
SortBy = Comparer<T>.Create(sortBy);
}
}

View File

@@ -10,7 +10,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
private readonly List<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
private readonly FastList<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
private IUniverseObject _universeObject = null!;

View File

@@ -0,0 +1,16 @@
using System;
namespace Engine.Core;
public class ActiveBehaviourCollector<T> : ActiveBehaviourCollectorBase<T> where T : class, IBehaviour
{
protected readonly FastList<T> activeBehaviours = new(32);
public override T this[Index index] => activeBehaviours[index];
public override int Count => activeBehaviours.Count;
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IUniverse universe) : base(universe) { }
protected override void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
protected override bool RemoveBehaviour(T tBehaviour) => activeBehaviours.Remove(tBehaviour);
}

View File

@@ -3,12 +3,10 @@ using System.Collections.Generic;
namespace Engine.Core;
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
public abstract class ActiveBehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class, IBehaviour
{
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
protected readonly FastList<T> monitoringBehaviours = new(32);
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
@@ -16,80 +14,16 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
private readonly List<T> monitoringBehaviours = new(32);
protected readonly List<T> activeBehaviours = new(32);
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
public abstract int Count { get; }
public abstract T this[Index index] { get; }
public IUniverse Universe { get; private set; } = null!;
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
}
protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
AddBehaviour(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, new(behaviour));
}
else if (activeBehaviours.Remove(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, new(behaviour));
}
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, new(tBehaviour));
}
}
public bool Assign(IUniverse universe)
{
if (Universe is not null)
@@ -123,10 +57,75 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return true;
}
public int Count => activeBehaviours.Count;
public T this[Index index] => activeBehaviours[index];
protected abstract void AddBehaviour(T behaviour);
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
public ActiveBehaviourCollector()
monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
}
protected abstract bool RemoveBehaviour(T behaviour);
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
if (!RemoveBehaviour(tBehaviour))
return;
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, new(tBehaviour));
}
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
AddBehaviour(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, new(behaviour));
}
else if (RemoveBehaviour(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, new(behaviour));
}
}
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
public ActiveBehaviourCollectorBase()
{
delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved;
@@ -135,7 +134,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
}
public ActiveBehaviourCollector(IUniverse universe)
public ActiveBehaviourCollectorBase(IUniverse universe)
{
delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved;

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCollector<TItem> where TItem : class, IBehaviour where TIndex : IComparable
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
private readonly Func<TItem, TIndex> getIndexFunc = null!;
private readonly IComparer<TIndex> sortBy = null!;
private int count = 0;
public override int Count => count;
public override TItem this[Index index]
{
get
{
int actualIndex = index.IsFromEnd
? count - index.Value
: index.Value;
if (actualIndex < 0 || actualIndex >= count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
foreach ((TIndex i, FastList<TItem> list) in behaviours)
{
if (leftIndex < list.Count)
return list[leftIndex];
leftIndex -= list.Count;
}
throw new IndexOutOfRangeException();
}
}
protected override bool RemoveBehaviour(TItem tBehaviour)
{
TIndex index = getIndexFunc(tBehaviour);
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
if (!list.Remove(tBehaviour))
return false;
count--;
return true;
}
protected override void AddBehaviour(TItem behaviour)
{
TIndex key = getIndexFunc(behaviour);
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
behaviours[key] = list = [];
count++;
list.Add(behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
TItem behaviour = (TItem)sender;
RemoveBehaviour(behaviour);
AddBehaviour(behaviour);
}
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Engine.Core;
public class BehaviourCollector<T> : BehaviourCollectorBase<T> where T : class
{
protected readonly FastList<T> behaviours = new(32);
public override T this[Index index] => behaviours[index];
public override int Count => behaviours.Count;
protected override void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
protected override bool RemoveBehaviour(T tBehaviour) => behaviours.Remove(tBehaviour);
public BehaviourCollector() { }
public BehaviourCollector(IUniverse universe) : base(universe) { }
}

View File

@@ -1,72 +1,25 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
public abstract class BehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class
{
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
protected readonly List<T> behaviours = new(32);
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
public Event<IAssignable>? OnUnassigned { get; } = new();
public IUniverse Universe { get; private set; } = null!;
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
public abstract int Count { get; }
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
public abstract T this[Index index] { get; }
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
protected virtual void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
AddBehaviour(tBehaviour);
OnBehaviourAdd(args.BehaviourAdded);
OnCollected?.Invoke(this, new(tBehaviour));
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!behaviours.Remove(tBehaviour))
return;
OnBehaviourRemove(args.BehaviourRemoved);
OnRemoved?.Invoke(this, new(tBehaviour));
}
protected virtual void OnAssign(IUniverse universe) { }
public bool Assign(IUniverse universe)
{
if (Universe is not null)
@@ -101,10 +54,57 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return true;
}
public int Count => behaviours.Count;
public T this[Index index] => behaviours[index];
protected virtual void OnAssign(IUniverse universe) { }
public BehaviourCollector()
protected abstract void AddBehaviour(T behaviour);
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
{
if (args.BehaviourAdded is not T tBehaviour)
return;
AddBehaviour(tBehaviour);
OnBehaviourAdd(args.BehaviourAdded);
OnCollected?.Invoke(this, new(tBehaviour));
}
protected abstract bool RemoveBehaviour(T tBehaviour);
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
{
if (args.BehaviourRemoved is not T tBehaviour)
return;
if (!RemoveBehaviour(tBehaviour))
return;
OnBehaviourRemove(args.BehaviourRemoved);
OnRemoved?.Invoke(this, new(tBehaviour));
}
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{
IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
}
public BehaviourCollectorBase()
{
delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved;
@@ -112,7 +112,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
}
public BehaviourCollector(IUniverse universe)
public BehaviourCollectorBase(IUniverse universe)
{
delegateOnBehaviourAdded = OnBehaviourAdded;
delegateOnBehaviourRemoved = OnBehaviourRemoved;

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<TItem> where TItem : class where TIndex : IComparable
{
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
private readonly Func<TItem, TIndex> getIndexFunc = null!;
private readonly IComparer<TIndex> sortBy = null!;
private int count = 0;
public override int Count => count;
public override TItem this[Index index]
{
get
{
int actualIndex = index.IsFromEnd
? count - index.Value
: index.Value;
if (actualIndex < 0 || actualIndex >= count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
foreach ((TIndex i, FastList<TItem> list) in behaviours)
{
if (leftIndex < list.Count)
return list[leftIndex];
leftIndex -= list.Count;
}
throw new IndexOutOfRangeException();
}
}
protected override bool RemoveBehaviour(TItem tBehaviour)
{
TIndex index = getIndexFunc(tBehaviour);
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
if (!list.Remove(tBehaviour))
return false;
count--;
return true;
}
protected override void AddBehaviour(TItem behaviour)
{
TIndex key = getIndexFunc(behaviour);
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
behaviours[key] = list = [];
count++;
list.Add(behaviour);
}
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
{
TItem behaviour = (TItem)sender;
RemoveBehaviour(behaviour);
AddBehaviour(behaviour);
}
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer<TIndex>.Create(sortBy);
behaviours = new(this.sortBy);
}
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
{
this.getIndexFunc = getIndexFunc;
delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
{
delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
}

View File

@@ -0,0 +1,78 @@
using System.Collections;
using System.Collections.Generic;
namespace Engine.Core;
public class FastList<T> : IList<T>, IReadOnlyList<T>, IEnumerable<T> where T : notnull
{
private readonly List<T> items = [];
private readonly Dictionary<T, int> indexMap = [];
public int Count => items.Count;
public bool IsReadOnly { get; set; } = false;
public T this[int index] { get => items[index]; set => items[index] = value; }
public void Add(T item)
{
indexMap[item] = items.Count;
items.Add(item);
}
public void RemoveAt(int i) => Remove(items[i], i);
public bool Remove(T item)
{
if (!indexMap.TryGetValue(item, out int index))
return false;
Remove(item, index);
return true;
}
private void Remove(T item, int index)
{
int lastIndex = items.Count - 1;
T lastItem = items[lastIndex];
items[index] = lastItem;
indexMap[lastItem] = index;
items.RemoveAt(lastIndex);
indexMap.Remove(item);
}
public void Insert(int index, T item)
{
items.Insert(index, item);
for (int i = index; i < items.Count; i++)
indexMap[items[i]] = i;
}
public void Clear()
{
items.Clear();
indexMap.Clear();
}
public bool Contains(T item) => indexMap.ContainsKey(item);
public int BinarySearch(T item, IComparer<T>? comparer = null) => items.BinarySearch(item, comparer);
public void Sort(IComparer<T> comparer)
{
items.Sort(comparer);
for (int i = 0; i < items.Count; i++)
indexMap[items[i]] = i;
}
public int IndexOf(T item) => items.IndexOf(item);
public void CopyTo(T[] array, int arrayIndex) => items.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public FastList() { }
public FastList(int count) { items.Capacity = count; }
}

View File

@@ -10,22 +10,25 @@ public class Pool<T> : IPool<T>
private readonly Func<T> generator = null!;
private readonly Queue<T> queue = new();
private readonly HashSet<T> queuedHashes = [];
public T Get()
{
if (!queue.TryDequeue(out T? result))
result = generator();
queuedHashes.Remove(result);
OnRemoved?.Invoke(this, result);
return result;
}
public void Return(T item)
{
if (queue.Contains(item))
if (queuedHashes.Contains(item))
return;
queue.Enqueue(item);
queuedHashes.Add(item);
OnReturned?.Invoke(this, item);
}

View File

@@ -5,11 +5,12 @@ namespace Engine.Core;
public class DrawManager : Behaviour
{
// We use Descending order because draw calls are running from last to first
private static Comparer<IBehaviour> SortByDescendingPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
private static Comparer<int> SortByDescendingPriority() => Comparer<int>.Create((x, y) => y.CompareTo(x));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private readonly ActiveBehaviourCollectorSorted<IPreDraw> preDrawEntities = new() { SortBy = SortByDescendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IDraw> drawEntities = new() { SortBy = SortByDescendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IPostDraw> postDrawEntities = new() { SortBy = SortByDescendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IPreDraw> preDrawEntities = new(GetPriority(), SortByDescendingPriority());
private readonly ActiveBehaviourCollectorOrdered<int, IDraw> drawEntities = new(GetPriority(), SortByDescendingPriority());
private readonly ActiveBehaviourCollectorOrdered<int, IPostDraw> postDrawEntities = new(GetPriority(), SortByDescendingPriority());
private void OnPreDraw(IUniverse sender)
{

View File

@@ -5,10 +5,11 @@ namespace Engine.Core;
public class UniverseEntranceManager : Behaviour
{
// We use Ascending order because we are using reverse for loop to call them
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private readonly ActiveBehaviourCollectorSorted<IEnterUniverse> enterUniverses = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IExitUniverse> exitUniverses = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IEnterUniverse> enterUniverses = new(GetPriority(), SortByAscendingPriority());
private readonly ActiveBehaviourCollectorOrdered<int, IExitUniverse> exitUniverses = new(GetPriority(), SortByAscendingPriority());
private readonly List<IEnterUniverse> toCallEnterUniverses = new(32);
private readonly List<IExitUniverse> toCallExitUniverses = new(32);
@@ -39,8 +40,9 @@ public class UniverseEntranceManager : Behaviour
{
for (int i = toCallExitUniverses.Count - 1; i >= 0; i--)
{
toCallExitUniverses[i].ExitUniverse(Universe);
IExitUniverse exitUniverse = toCallExitUniverses[i];
toCallExitUniverses.RemoveAt(i);
exitUniverse.ExitUniverse(Universe);
}
}
@@ -48,8 +50,9 @@ public class UniverseEntranceManager : Behaviour
{
for (int i = toCallEnterUniverses.Count - 1; i >= 0; i--)
{
toCallEnterUniverses[i].EnterUniverse(Universe);
IEnterUniverse enterUniverse = toCallEnterUniverses[i];
toCallEnterUniverses.RemoveAt(i);
enterUniverse.EnterUniverse(Universe);
}
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
namespace Engine.Core;
@@ -6,13 +5,14 @@ namespace Engine.Core;
public class UpdateManager : Behaviour
{
// We use Ascending order because we are using reverse for loop to call them
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private readonly ActiveBehaviourCollectorSorted<IFirstFrameUpdate> firstFrameUpdates = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IFirstFrameUpdate> firstFrameUpdates = new(GetPriority(), SortByAscendingPriority());
private readonly ActiveBehaviourCollector<ILastFrameUpdate> lastFrameUpdates = new();
private readonly ActiveBehaviourCollectorSorted<IPreUpdate> preUpdateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IUpdate> updateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IPostUpdate> postUpdateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IPreUpdate> preUpdateEntities = new(GetPriority(), SortByAscendingPriority());
private readonly ActiveBehaviourCollectorOrdered<int, IUpdate> updateEntities = new(GetPriority(), SortByAscendingPriority());
private readonly ActiveBehaviourCollectorOrdered<int, IPostUpdate> postUpdateEntities = new(GetPriority(), SortByAscendingPriority());
private readonly List<IFirstFrameUpdate> toCallFirstFrameUpdates = new(32);

View File

@@ -22,7 +22,7 @@ public class Universe : BaseEntity, IUniverse
private readonly Event<IInitializable>.EventHandler delegateOnUniverseObjectFinalize = null!;
private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateOnUniverseObjectExitedUniverse = null!;
private readonly List<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
private readonly FastList<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
private float _timeScale = 1f;
public Universe()

View File

@@ -19,7 +19,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
private IUniverse _universe = null!;
private IBehaviourController _behaviourController = null!;
private bool _isActive = false;
private readonly List<IUniverseObject> _children = [];
private readonly FastList<IUniverseObject> _children = [];
private IUniverseObject? _parent = null;
public IReadOnlyList<IUniverseObject> Children => _children;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Engine.Core;
namespace Engine.Integration.MonoGame;
@@ -6,9 +7,10 @@ namespace Engine.Integration.MonoGame;
public class LoadContentManager : Behaviour, IFirstFrameUpdate
{
// We use Ascending order because we are using reverse for loop to call them
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private readonly ActiveBehaviourCollectorSorted<ILoadContent> loadContents = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, ILoadContent> loadContents = new(GetPriority(), SortByAscendingPriority());
private readonly List<ILoadContent> toCallLoadContents = new(32);
private MonoGameWindowContainer monoGameWindowContainer = null!;

View File

@@ -6,12 +6,13 @@ namespace Engine.Integration.MonoGame;
public class SpriteBatcher : BehaviourBase, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
private static Comparer<int> SortByPriority() => Comparer<int>.Create((x, y) => y.CompareTo(x));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private ISpriteBatch spriteBatch = null!;
private MonoGameCamera2D camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableSprite> drawableSprites = new() { SortBy = SortByPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IDrawableSprite> drawableSprites = new(GetPriority(), SortByPriority());
public void FirstActiveFrame()
{

View File

@@ -8,11 +8,13 @@ namespace Engine.Integration.MonoGame;
public class TriangleBatcher : BehaviourBase, ITriangleBatch, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
private TriangleBatch triangleBatch = null!;
private MonoGameCamera2D camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableTriangle> drawableShapes = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorOrdered<int, IDrawableTriangle> drawableShapes = new(GetPriority(), SortByAscendingPriority());
public void FirstActiveFrame()
{

View File

@@ -58,6 +58,9 @@ public class TriangleBatch : ITriangleBatch
private void Flush()
{
if (verticesIndex == 0)
return;
graphicsDevice.RasterizerState = rasterizerState;
basicEffect.Projection = _projection;
basicEffect.View = _view;

View File

@@ -17,11 +17,12 @@ public class PhysicsEngine2D : Behaviour, IPreUpdate, IPhysicsEngine2D
protected readonly ICollisionResolver2D collisionResolver = null!;
protected readonly IRaycastResolver2D raycastResolver = null!;
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
protected ActiveBehaviourCollectorSorted<IPrePhysicsUpdate> physicsPreUpdateCollector = new() { SortBy = SortByPriority() };
protected ActiveBehaviourCollectorSorted<IPhysicsUpdate> physicsUpdateCollector = new() { SortBy = SortByPriority() };
protected ActiveBehaviourCollectorSorted<IPhysicsIteration> physicsIterationCollector = new() { SortBy = SortByPriority() };
protected ActiveBehaviourCollectorSorted<IPostPhysicsUpdate> physicsPostUpdateCollector = new() { SortBy = SortByPriority() };
private static Comparer<int> SortByPriority() => Comparer<int>.Create((x, y) => y.CompareTo(x));
private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
protected ActiveBehaviourCollectorOrdered<int, IPrePhysicsUpdate> physicsPreUpdateCollector = new(GetPriority(), SortByPriority());
protected ActiveBehaviourCollectorOrdered<int, IPhysicsUpdate> physicsUpdateCollector = new(GetPriority(), SortByPriority());
protected ActiveBehaviourCollectorOrdered<int, IPhysicsIteration> physicsIterationCollector = new(GetPriority(), SortByPriority());
protected ActiveBehaviourCollectorOrdered<int, IPostPhysicsUpdate> physicsPostUpdateCollector = new(GetPriority(), SortByPriority());
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
protected BehaviourCollector<ICollider2D> colliderCollector = new();

Submodule Engine.Serializers/YamlDotNet added at 62048d7abe

View File

@@ -16,22 +16,3 @@ public interface INetworkCommunicator
INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback);
INetworkCommunicator UnsubscribeFromPackets<T>(Event<IConnection, T>.EventHandler callback);
}
public interface INetworkCommunicatorClient : INetworkCommunicator
{
INetworkCommunicatorClient Connect(string address, int port, string? password = null);
INetworkCommunicatorClient SendToServer<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
}
public interface INetworkCommunicatorServer : INetworkCommunicator
{
string Password { get; }
int MaxConnectionCount { get; }
int Port { get; }
INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null);
INetworkCommunicatorServer SendToClient<T>(IConnection connection, T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
INetworkCommunicatorServer SendToAll<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
}

View File

@@ -0,0 +1,8 @@
namespace Engine.Systems.Network;
public interface INetworkCommunicatorClient : INetworkCommunicator
{
INetworkCommunicatorClient Connect(string address, int port, string? password = null);
INetworkCommunicatorClient SendToServer<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
}

View File

@@ -0,0 +1,13 @@
namespace Engine.Systems.Network;
public interface INetworkCommunicatorServer : INetworkCommunicator
{
string Password { get; }
int MaxConnectionCount { get; }
int Port { get; }
INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null);
INetworkCommunicatorServer SendToClient<T>(IConnection connection, T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
INetworkCommunicatorServer SendToAll<T>(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new();
}

View File

@@ -0,0 +1,27 @@
namespace Engine.Systems.Network;
public static class TypeHasher<T>
{
private static long _fnv1a = 0;
public static long FNV1a
{
get
{
if (_fnv1a == 0)
unchecked
{
const long fnvPrime = 1099511628211;
_fnv1a = 1469598103934665603;
string typeName = typeof(T).FullName ?? typeof(T).Name;
foreach (char c in typeName)
{
_fnv1a ^= c;
_fnv1a *= fnvPrime;
}
}
return _fnv1a;
}
}
}

View File

@@ -1,10 +1,24 @@
using System;
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenAABBExtensions
{
public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, Action<AABB> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB(initialAABB.LowerBoundary.Lerp(targetAABB.LowerBoundary, t), initialAABB.UpperBoundary.Lerp(targetAABB.UpperBoundary, t))));
private static readonly BoxedPool<AABB> boxedAABBPool = new(2);
public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, System.Action<AABB> setMethod)
{
Boxed<AABB> boxedInitial = boxedAABBPool.Get(initialAABB);
Boxed<AABB> boxedTarget = boxedAABBPool.Get(targetAABB);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB(boxedInitial.Value.LowerBoundary.Lerp(boxedTarget.Value.LowerBoundary, t), boxedInitial.Value.UpperBoundary.Lerp(boxedTarget.Value.UpperBoundary, t))));
tween.OnComplete(() =>
{
boxedAABBPool.Return(boxedInitial);
boxedAABBPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,9 +4,21 @@ namespace Engine.Systems.Tween;
public static class TweenCamera2DExtensions
{
private static readonly BoxedPool<float> boxedFloatPool = new(2);
public static ITween TweenZoom(this ICamera2D camera2D, ITweenManager tweenManager, float duration, float targetZoom)
{
float initialZoom = camera2D.Zoom;
return tweenManager.StartTween(duration, t => camera2D.Zoom = initialZoom.Lerp(targetZoom, t));
Boxed<float> boxedInitial = boxedFloatPool.Get(camera2D.Zoom);
Boxed<float> boxedTarget = boxedFloatPool.Get(targetZoom);
ITween tween = tweenManager.StartTween(duration, t => camera2D.Zoom = boxedInitial.Value.Lerp(boxedTarget.Value, t));
tween.OnComplete(() =>
{
boxedFloatPool.Return(boxedInitial);
boxedFloatPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,13 +4,28 @@ namespace Engine.Systems.Tween;
public static class TweenCircleExtensions
{
private static readonly BoxedPool<Circle> boxedCirclePool = new(2);
public static ITween TweenCircle(this Circle initialCircle, ITweenManager tweenManager, float duration, Circle targetCircle, System.Action<Circle> setMethod)
=> tweenManager.StartTween(duration,
{
Boxed<Circle> boxedInitial = boxedCirclePool.Get(initialCircle);
Boxed<Circle> boxedTarget = boxedCirclePool.Get(targetCircle);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Circle(
initialCircle.Center.Lerp(targetCircle.Center, t),
initialCircle.Diameter.Lerp(targetCircle.Diameter, t)
boxedInitial.Value.Center.Lerp(boxedTarget.Value.Center, t),
boxedInitial.Value.Diameter.Lerp(boxedTarget.Value.Diameter, t)
)
)
);
tween.OnComplete(() =>
{
boxedCirclePool.Return(boxedInitial);
boxedCirclePool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,6 +4,9 @@ namespace Engine.Systems.Tween;
public static class TweenColorExtensions
{
private static readonly BoxedPool<ColorHSV> boxedColorHSVPool = new(2);
private static readonly BoxedPool<ColorHSVA> boxedColorHSVAPool = new(2);
public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action<ColorRGB> setMethod)
=> TweenColor((ColorHSV)initialColorRGB, tweenManager, duration, (ColorHSV)targetColorRGB, color => setMethod?.Invoke(color));
@@ -11,8 +14,34 @@ public static class TweenColorExtensions
=> TweenColor((ColorHSVA)initialColorRGBA, tweenManager, duration, (ColorHSVA)targetColorRGBA, color => setMethod?.Invoke(color));
public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action<ColorHSV> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSV.Lerp(targetColorHSV, t)));
{
Boxed<ColorHSV> boxedInitial = boxedColorHSVPool.Get(initialColorHSV);
Boxed<ColorHSV> boxedTarget = boxedColorHSVPool.Get(targetColorHSV);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedColorHSVPool.Return(boxedInitial);
boxedColorHSVPool.Return(boxedTarget);
});
return tween;
}
public static ITween TweenColor(this ColorHSVA initialColorHSVA, ITweenManager tweenManager, float duration, ColorHSVA targetColorHSVA, System.Action<ColorHSVA> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSVA.Lerp(targetColorHSVA, t)));
{
Boxed<ColorHSVA> boxedInitial = boxedColorHSVAPool.Get(initialColorHSVA);
Boxed<ColorHSVA> boxedTarget = boxedColorHSVAPool.Get(targetColorHSVA);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedColorHSVAPool.Return(boxedInitial);
boxedColorHSVAPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,13 +4,28 @@ namespace Engine.Systems.Tween;
public static class TweenLine2DEquationExtensions
{
private static readonly BoxedPool<Line2DEquation> boxedLine2DEquationPool = new(2);
public static ITween TweenLine2DEquation(this Line2DEquation initialLine2DEquation, ITweenManager tweenManager, float duration, Line2DEquation targetLine2DEquation, System.Action<Line2DEquation> setMethod)
=> tweenManager.StartTween(duration,
{
Boxed<Line2DEquation> boxedInitial = boxedLine2DEquationPool.Get(initialLine2DEquation);
Boxed<Line2DEquation> boxedTarget = boxedLine2DEquationPool.Get(targetLine2DEquation);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Line2DEquation(
initialLine2DEquation.Slope.Lerp(targetLine2DEquation.Slope, t),
initialLine2DEquation.OffsetY.Lerp(targetLine2DEquation.OffsetY, t)
boxedInitial.Value.Slope.Lerp(boxedTarget.Value.Slope, t),
boxedInitial.Value.OffsetY.Lerp(boxedTarget.Value.OffsetY, t)
)
)
);
tween.OnComplete(() =>
{
boxedLine2DEquationPool.Return(boxedInitial);
boxedLine2DEquationPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,13 +4,28 @@ namespace Engine.Systems.Tween;
public static class TweenLine2DExtensions
{
private static readonly BoxedPool<Line2D> boxedLine2DPool = new(2);
public static ITween TweenLine2D(this Line2D initialLine2D, ITweenManager tweenManager, float duration, Line2D targetLine2D, System.Action<Line2D> setMethod)
=> tweenManager.StartTween(duration,
{
Boxed<Line2D> boxedInitial = boxedLine2DPool.Get(initialLine2D);
Boxed<Line2D> boxedTarget = boxedLine2DPool.Get(targetLine2D);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Line2D(
initialLine2D.From.Lerp(targetLine2D.From, t),
initialLine2D.To.Lerp(targetLine2D.To, t)
boxedInitial.Value.From.Lerp(boxedTarget.Value.From, t),
boxedInitial.Value.To.Lerp(boxedTarget.Value.To, t)
)
)
);
tween.OnComplete(() =>
{
boxedLine2DPool.Return(boxedInitial);
boxedLine2DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -0,0 +1,41 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenPrimitiveExtensions
{
private static readonly BoxedPool<float> boxedFloatPool = new(2);
private static readonly BoxedPool<int> boxedIntPool = new(2);
public static ITween TweenFloat(this float initialFloat, ITweenManager tweenManager, float duration, float targetFloat, System.Action<float> setMethod)
{
Boxed<float> boxedInitial = boxedFloatPool.Get(initialFloat);
Boxed<float> boxedTarget = boxedFloatPool.Get(targetFloat);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedFloatPool.Return(boxedInitial);
boxedFloatPool.Return(boxedTarget);
});
return tween;
}
public static ITween TweenInt(this int initialInt, ITweenManager tweenManager, float duration, int targetInt, System.Action<float> setMethod)
{
Boxed<int> boxedInitial = boxedIntPool.Get(initialInt);
Boxed<int> boxedTarget = boxedIntPool.Get(targetInt);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value + (boxedTarget.Value - boxedInitial.Value) * t));
tween.OnComplete(() =>
{
boxedIntPool.Return(boxedInitial);
boxedIntPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -1,17 +1,31 @@
using System;
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenProjection1DExtensions
{
public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, Action<Projection1D> setMethod)
=> tweenManager.StartTween(duration,
private static readonly BoxedPool<Projection1D> boxedProjection1DPool = new(2);
public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, System.Action<Projection1D> setMethod)
{
Boxed<Projection1D> boxedInitial = boxedProjection1DPool.Get(initialProjection1D);
Boxed<Projection1D> boxedTarget = boxedProjection1DPool.Get(targetProjection1D);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Projection1D(
initialProjection1D.Min.Lerp(targetProjection1D.Min, t),
initialProjection1D.Max.Lerp(targetProjection1D.Max, t)
boxedInitial.Value.Min.Lerp(boxedTarget.Value.Min, t),
boxedInitial.Value.Max.Lerp(boxedTarget.Value.Max, t)
)
)
);
tween.OnComplete(() =>
{
boxedProjection1DPool.Return(boxedInitial);
boxedProjection1DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -1,10 +1,24 @@
using System;
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenQuaternionExtensions
{
public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, Action<Quaternion> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialQuaternion.SLerp(targetQuaternion, t)));
private static readonly BoxedPool<Quaternion> boxedQuaternionPool = new(2);
public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, System.Action<Quaternion> setMethod)
{
Boxed<Quaternion> boxedInitial = boxedQuaternionPool.Get(initialQuaternion);
Boxed<Quaternion> boxedTarget = boxedQuaternionPool.Get(targetQuaternion);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.SLerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedQuaternionPool.Return(boxedInitial);
boxedQuaternionPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -5,40 +5,23 @@ namespace Engine.Systems.Tween;
public static class TweenTransform2DExtensions
{
public static ITween TweenPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetPosition)
{
Vector2D initialPosition = transform2D.Position;
return tweenManager.StartTween(duration, t => transform2D.Position = initialPosition.Lerp(targetPosition, t));
}
=> transform2D.Position.TweenVector2D(tweenManager, duration, targetPosition, x => transform2D.Position = x);
public static ITween TweenScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetScale)
{
Vector2D initialScale = transform2D.Scale;
return tweenManager.StartTween(duration, t => transform2D.Scale = initialScale.Lerp(targetScale, t));
}
=> transform2D.Scale.TweenVector2D(tweenManager, duration, targetScale, x => transform2D.Scale = x);
public static ITween TweenRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetRotation)
{
float initialRotation = transform2D.Rotation;
return tweenManager.StartTween(duration, t => transform2D.Rotation = initialRotation.Lerp(targetRotation, t));
}
=> transform2D.Rotation.TweenFloat(tweenManager, duration, targetRotation, x => transform2D.Rotation = x);
public static ITween TweenLocalPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalPosition)
{
Vector2D initialLocalPosition = transform2D.LocalPosition;
return tweenManager.StartTween(duration, t => transform2D.LocalPosition = initialLocalPosition.Lerp(targetLocalPosition, t));
}
=> transform2D.LocalPosition.TweenVector2D(tweenManager, duration, targetLocalPosition, x => transform2D.LocalPosition = x);
public static ITween TweenLocalScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalScale)
{
Vector2D initialLocalScale = transform2D.LocalScale;
return tweenManager.StartTween(duration, t => transform2D.LocalScale = initialLocalScale.Lerp(targetLocalScale, t));
}
=> transform2D.LocalScale.TweenVector2D(tweenManager, duration, targetLocalScale, x => transform2D.LocalScale = x);
public static ITween TweenLocalRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetLocalRotation)
{
float initialLocalRotation = transform2D.LocalRotation;
return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t));
}
=> transform2D.LocalRotation.TweenFloat(tweenManager, duration, targetLocalRotation, x => transform2D.LocalRotation = x);
public static ITween TweenPositionAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additivePosition)
{
Vector2D progressedPosition = Vector2D.Zero;

View File

@@ -1,18 +1,31 @@
using System;
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenTriangleExtensions
{
public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, Action<Triangle> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.Invoke(
private static readonly BoxedPool<Triangle> boxedTrianglePool = new(2);
public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, System.Action<Triangle> setMethod)
{
Boxed<Triangle> boxedInitial = boxedTrianglePool.Get(initialTriangle);
Boxed<Triangle> boxedTarget = boxedTrianglePool.Get(targetTriangle);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(
new Triangle(
initialTriangle.A.Lerp(targetTriangle.A, t),
initialTriangle.B.Lerp(targetTriangle.B, t),
initialTriangle.C.Lerp(targetTriangle.C, t)
boxedInitial.Value.A.Lerp(boxedTarget.Value.A, t),
boxedInitial.Value.B.Lerp(boxedTarget.Value.B, t),
boxedInitial.Value.C.Lerp(boxedTarget.Value.C, t)
)
)
);
tween.OnComplete(() =>
{
boxedTrianglePool.Return(boxedInitial);
boxedTrianglePool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,6 +4,21 @@ namespace Engine.Systems.Tween;
public static class TweenVector2DExtensions
{
private static readonly BoxedPool<Vector2D> boxedVector2DPool = new(2);
public static ITween TweenVector2D(this Vector2D initialVector2D, ITweenManager tweenManager, float duration, Vector2D targetVector2D, System.Action<Vector2D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialVector2D.Lerp(targetVector2D, t)));
{
Boxed<Vector2D> boxedInitial = boxedVector2DPool.Get(initialVector2D);
Boxed<Vector2D> boxedTarget = boxedVector2DPool.Get(targetVector2D);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedVector2DPool.Return(boxedInitial);
boxedVector2DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -4,6 +4,21 @@ namespace Engine.Systems.Tween;
public static class TweenVector3DExtensions
{
private static readonly BoxedPool<Vector3D> boxedVector3DPool = new(2);
public static ITween TweenVector3D(this Vector3D initialVector3D, ITweenManager tweenManager, float duration, Vector3D targetVector3D, System.Action<Vector3D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialVector3D.Lerp(targetVector3D, t)));
{
Boxed<Vector3D> boxedInitial = boxedVector3DPool.Get(initialVector3D);
Boxed<Vector3D> boxedTarget = boxedVector3DPool.Get(targetVector3D);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedVector3DPool.Return(boxedInitial);
boxedVector3DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -0,0 +1,12 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public class Boxed<T> where T : struct
{
public Event<Boxed<T>, BoxedValueChangedArguments> OnValueChanged { get; } = new();
public T Value { get; set; } = default;
public readonly record struct BoxedValueChangedArguments(T PreviousValue, T CurrentValue);
}

View File

@@ -0,0 +1,14 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public class BoxedPool<T>(int initialCapacity = 1) : Pool<Boxed<T>>(() => new(), initialCapacity) where T : struct;
public static class BoxedPoolExtensions
{
public static Boxed<T> Get<T>(this BoxedPool<T> boxedPool, T value) where T : struct
{
Boxed<T> boxed = boxedPool.Get();
boxed.Value = value;
return boxed;
}
}