Compare commits
195 Commits
9e4c74ed1d
...
tests
Author | SHA1 | Date | |
---|---|---|---|
22b3a342a8 | |||
d0aee7fe16 | |||
be15cba9e1 | |||
ac0eac3fbb | |||
3c23ac7f1e | |||
57807c2a62 | |||
3452194941 | |||
11612ff0db | |||
63bc94c7a6 | |||
e00319d7ff | |||
11719440dc | |||
f246d68aa7 | |||
6e87c67096 | |||
b8217f2106 | |||
9824980cbf | |||
93a79cd075 | |||
f6e52abcc1 | |||
03232f72e8 | |||
37aca44e45 | |||
9f4d95a57b | |||
65eac57fce | |||
08311acc9a | |||
f8fbae6130 | |||
df06e8d134 | |||
ad365dc722 | |||
200e8ae7da | |||
65cfaf1b4a | |||
83b155fc5e | |||
7db56e7f3e | |||
42064875a0 | |||
41245c0c1c | |||
0e5cc8f898 | |||
c8bb991865 | |||
bc1c76d746 | |||
8f03628bd6 | |||
a1feb0bad3 | |||
978cba96c8 | |||
7212094a3d | |||
14843ddeba | |||
5315db0077 | |||
026f343d43 | |||
da5f31f9d7 | |||
fa1614f238 | |||
0c096d39db | |||
dae6549bad | |||
767fc28488 | |||
c3be8f60b7 | |||
33cb44bf36 | |||
4c1018ddec | |||
cf7061fd58 | |||
e6b7b9953f | |||
4a3775a0de | |||
4d353662a1 | |||
ca0b2de917 | |||
2335c3ec62 | |||
30ccab1b93 | |||
f56d6a7fc8 | |||
29a7f5880f | |||
eee3056614 | |||
152b0e93db | |||
3f914fe46f | |||
62b54ee836 | |||
6a41407005 | |||
adfa6c6ba0 | |||
a53766f472 | |||
40735c713a | |||
2054ae3a35 | |||
9066e11c12 | |||
f16a7e55c9 | |||
e3b32b3c4a | |||
a02584f3b6 | |||
45524e474e | |||
fbdea47dc7 | |||
f5fbd4e5ef | |||
c7f63dc638 | |||
beecefec1c | |||
24d1a1d764 | |||
9edf3b0aa6 | |||
8d49fb467c | |||
2caa042317 | |||
fe8bde855d | |||
ac620264b1 | |||
f31b84f519 | |||
efb7cc7452 | |||
7a3202a053 | |||
86c9ed2ba9 | |||
56321864fb | |||
6adc002f1a | |||
1acc8bdb8f | |||
61e2761580 | |||
996e61d0ad | |||
b1b5af94d3 | |||
b0f8b0dad6 | |||
67d7f401b8 | |||
9bf17cc191 | |||
bf8fbebae3 | |||
1b0f25e854 | |||
61a7f685c1 | |||
feb2a05aa3 | |||
cd30047e4a | |||
a3b03efd47 | |||
4213b3f8b5 | |||
d3fb612904 | |||
8f8558a262 | |||
2df41e1881 | |||
114fa82b9d | |||
bcce427376 | |||
6a750f8ce0 | |||
3e02ee7b6f | |||
6b9020bd24 | |||
832514ba7d | |||
877a004a13 | |||
b1970d93f9 | |||
e7bd924494 | |||
37b87f0f85 | |||
3b6a93d37a | |||
0bf38234c6 | |||
ed6969c16a | |||
b0b421151f | |||
41c5def097 | |||
fbbdfb07fa | |||
bf283d804c | |||
063ea08707 | |||
fd11a94ddf | |||
be2295b92d | |||
a93e55619c | |||
48ae24af47 | |||
1366a417f1 | |||
4bfe98852c | |||
98edbe1af5 | |||
3725a3b0fd | |||
f43ab36742 | |||
c7aafd85bc | |||
5de08b8fe4 | |||
16e4077d40 | |||
fc3c1ed1f9 | |||
b100b5c2fe | |||
5e28ba8814 | |||
4c235e3230 | |||
131203d578 | |||
bd5eb432b7 | |||
d2ca85568f | |||
4c41870732 | |||
f77afa3632 | |||
eb61598489 | |||
efe51b491d | |||
fa3a4d1e0d | |||
6e7a0993f5 | |||
d70bee2c6b | |||
5812f43117 | |||
d102c5471d | |||
fb363970fc | |||
791349686b | |||
3a0942ff46 | |||
b002dd469a | |||
f92f36442c | |||
bb934b59f3 | |||
c704173183 | |||
c3876add1e | |||
35a75d993b | |||
2637f99456 | |||
9581f5aa54 | |||
82cc25a9ef | |||
336e7e16e7 | |||
a3a8fb4e84 | |||
35f6c3850e | |||
f51d5f342e | |||
9c129cefe2 | |||
a254bb721b | |||
5fa7420c04 | |||
5bcc256777 | |||
680d718957 | |||
20bc6a1adb | |||
eb454a471c | |||
c205e710bc | |||
cddb30c631 | |||
29f6c83bf0 | |||
c20f210b29 | |||
1ea1844677 | |||
5b2c13f8bf | |||
c39ee44442 | |||
4623b4861a | |||
0a868b82e5 | |||
d92d16cfad | |||
0184d1758c | |||
6e5b805803 | |||
8293c58f9f | |||
94d01521d4 | |||
5c1c025fe3 | |||
1d292a104e | |||
70c884acfe | |||
a9f5974568 | |||
dae72b11c5 | |||
58eb373c79 | |||
00f7b1aaab |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "Engine.Serializers/YamlDotNet"]
|
||||||
|
path = Engine.Serializers/YamlDotNet
|
||||||
|
url = git@github.com:Syntriax/YamlDotNet.git
|
2
Engine.Core/.gitignore
vendored
2
Engine.Core/.gitignore
vendored
@@ -482,3 +482,5 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# Vim temporary swap files
|
# Vim temporary swap files
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
!Debug
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.
|
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.
|
||||||
@@ -8,7 +8,7 @@ public interface IAssignable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event UnassignEventHandler? OnUnassigned;
|
Event<IAssignable>? OnUnassigned { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
|
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
|
||||||
@@ -17,6 +17,4 @@ public interface IAssignable
|
|||||||
/// <see cref="true"/>, if the fields are unsigned successfully, <see cref="false"/> if not.
|
/// <see cref="true"/>, if the fields are unsigned successfully, <see cref="false"/> if not.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool Unassign();
|
bool Unassign();
|
||||||
|
|
||||||
delegate void UnassignEventHandler(IAssignable sender);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
||||||
@@ -8,7 +8,7 @@ public interface IHasBehaviourController : IAssignable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
|
Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBehaviourController" />
|
/// <inheritdoc cref="IBehaviourController" />
|
||||||
IBehaviourController BehaviourController { get; }
|
IBehaviourController BehaviourController { get; }
|
||||||
@@ -21,6 +21,4 @@ public interface IHasBehaviourController : IAssignable
|
|||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool Assign(IBehaviourController behaviourController);
|
bool Assign(IBehaviourController behaviourController);
|
||||||
|
|
||||||
delegate void BehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
||||||
@@ -8,7 +8,7 @@ public interface IHasEntity : IAssignable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EntityAssignedEventHandler? OnEntityAssigned;
|
Event<IHasEntity> OnEntityAssigned { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IEntity" />
|
/// <inheritdoc cref="IEntity" />
|
||||||
IEntity Entity { get; }
|
IEntity Entity { get; }
|
||||||
@@ -21,6 +21,4 @@ public interface IHasEntity : IAssignable
|
|||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool Assign(IEntity entity);
|
bool Assign(IEntity entity);
|
||||||
|
|
||||||
delegate void EntityAssignedEventHandler(IHasEntity sender);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform2D"/> field.
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasGameManager : IAssignable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
|
|
||||||
/// </summary>
|
|
||||||
event GameManagerAssignedEventHandler? OnGameManagerAssigned;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IGameManager" />
|
|
||||||
IGameManager GameManager { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assign a value to the <see cref="IGameManager"/> field of this object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="gameManager">New <see cref="IGameManager"/> to assign.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
|
||||||
/// </returns>
|
|
||||||
bool Assign(IGameManager gameManager);
|
|
||||||
|
|
||||||
delegate void GameManagerAssignedEventHandler(IHasGameManager sender);
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IHierarchyObject"/> field.
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasHierarchyObject : IAssignable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when the <see cref="IHierarchyObject"/> value has has been assigned a new value.
|
|
||||||
/// </summary>
|
|
||||||
event HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IHierarchyObject" />
|
|
||||||
IHierarchyObject HierarchyObject { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assign a value to the <see cref="IHierarchyObject"/> field of this object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">New <see cref="IHierarchyObject"/> to assign.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
|
||||||
/// </returns>
|
|
||||||
bool Assign(IHierarchyObject hierarchyObject);
|
|
||||||
|
|
||||||
delegate void HierarchyObjectAssignedEventHandler(IHasHierarchyObject sender);
|
|
||||||
}
|
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
||||||
@@ -8,7 +8,7 @@ public interface IHasStateEnable : IAssignable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event StateEnableAssignedEventHandler? OnStateEnableAssigned;
|
Event<IHasStateEnable> OnStateEnableAssigned { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IStateEnable" />
|
/// <inheritdoc cref="IStateEnable" />
|
||||||
IStateEnable StateEnable { get; }
|
IStateEnable StateEnable { get; }
|
||||||
@@ -21,6 +21,4 @@ public interface IHasStateEnable : IAssignable
|
|||||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool Assign(IStateEnable stateEnable);
|
bool Assign(IStateEnable stateEnable);
|
||||||
|
|
||||||
delegate void StateEnableAssignedEventHandler(IHasStateEnable sender);
|
|
||||||
}
|
}
|
||||||
|
24
Engine.Core/Abstract/Assignable/IHasUniverse.cs
Normal file
24
Engine.Core/Abstract/Assignable/IHasUniverse.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasUniverse : IAssignable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="IUniverse"/> value has has been assigned a new value.
|
||||||
|
/// </summary>
|
||||||
|
Event<IHasUniverse> OnUniverseAssigned { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUniverse" />
|
||||||
|
IUniverse Universe { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assign a value to the <see cref="IUniverse"/> field of this object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universe">New <see cref="IUniverse"/> to assign.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||||
|
/// </returns>
|
||||||
|
bool Assign(IUniverse universe);
|
||||||
|
}
|
24
Engine.Core/Abstract/Assignable/IHasUniverseObject.cs
Normal file
24
Engine.Core/Abstract/Assignable/IHasUniverseObject.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasUniverseObject : IAssignable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="IUniverseObject"/> value has has been assigned a new value.
|
||||||
|
/// </summary>
|
||||||
|
Event<IHasUniverseObject> OnUniverseObjectAssigned { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUniverseObject" />
|
||||||
|
IUniverseObject UniverseObject { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assign a value to the <see cref="IUniverseObject"/> field of this object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeObject">New <see cref="IUniverseObject"/> to assign.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||||
|
/// </returns>
|
||||||
|
bool Assign(IUniverseObject universeObject);
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity which can be active or not.
|
/// Represents an entity which can be active or not.
|
||||||
@@ -8,12 +8,12 @@ public interface IActive
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IsActive"/> state of the <see cref="IActive"/> changes.
|
/// Event triggered when the <see cref="IsActive"/> state of the <see cref="IActive"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ActiveChangedEventHandler? OnActiveChanged;
|
Event<IActive, ActiveChangedArguments> OnActiveChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value indicating whether the <see cref="IActive"/> is enabled.
|
/// The value indicating whether the <see cref="IActive"/> is enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsActive { get; }
|
bool IsActive { get; }
|
||||||
|
|
||||||
delegate void ActiveChangedEventHandler(IActive sender, bool previousState);
|
readonly record struct ActiveChangedArguments(bool PreviousState);
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
|
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
|
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event PriorityChangedEventHandler? OnPriorityChanged;
|
Event<IBehaviour, PriorityChangedArguments> OnPriorityChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The priority of the <see cref="IBehaviour"/>.
|
/// The priority of the <see cref="IBehaviour"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Priority { get; set; }
|
int Priority { get; set; }
|
||||||
|
|
||||||
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
|
readonly record struct PriorityChangedArguments(int PreviousPriority);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public interface IBehaviour2D : IBehaviour
|
public interface IBehaviour2D : IBehaviour
|
||||||
{
|
{
|
||||||
|
@@ -1,35 +1,42 @@
|
|||||||
using System.Collections.Generic;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a collector for the class type of <typeparamref name="T"/>.
|
/// Represents a collector for the class type of <typeparamref name="T"/>.
|
||||||
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IGameManager"/>.
|
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IUniverse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
|
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
|
||||||
public interface IBehaviourCollector<T> : IHasGameManager, IEnumerable<T> where T : class
|
public interface IBehaviourCollector<T> : IHasUniverse where T : class
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when an object of type <typeparamref name="T"/> is added to the collector.
|
/// Event triggered when an object of type <typeparamref name="T"/> is added to the collector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event CollectedEventHandler? OnCollected;
|
Event<IBehaviourCollector<T>, BehaviourCollectedArguments> OnCollected { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when an object of type <typeparamref name="T"/> is removed from the collector.
|
/// Event triggered when an object of type <typeparamref name="T"/> is removed from the collector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event RemovedEventHandler? OnRemoved;
|
Event<IBehaviourCollector<T>, BehaviourRemovedArguments> OnRemoved { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount of <typeparamref name="T"/> collected.
|
||||||
|
/// </summary>
|
||||||
|
int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a <typeparamref name="T"/> collected by it's index.
|
||||||
|
/// </summary>
|
||||||
|
T this[System.Index index] { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for handling the <see cref="OnCollected"/> event.
|
/// Delegate for handling the <see cref="OnCollected"/> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
|
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
|
||||||
/// <param name="behaviourCollected">The object of type <typeparamref name="T"/> that was added to the collector.</param>
|
/// <param name="behaviourCollected">The object of type <typeparamref name="T"/> that was added to the collector.</param>
|
||||||
delegate void CollectedEventHandler(IBehaviourCollector<T> sender, T behaviourCollected);
|
readonly record struct BehaviourCollectedArguments(T BehaviourCollected);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for handling the <see cref="OnRemoved"/> event.
|
/// Delegate for handling the <see cref="OnRemoved"/> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
|
/// <param name="BehaviourRemoved">The object of type <typeparamref name="T"/> that was removed from the collector.</param>
|
||||||
/// <param name="behaviourRemoved">The object of type <typeparamref name="T"/> that was removed from the collector.</param>
|
readonly record struct BehaviourRemovedArguments(T BehaviourRemoved);
|
||||||
delegate void RemovedEventHandler(IBehaviourCollector<T> sender, T behaviourRemoved);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,31 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IHierarchyObject"/>.
|
/// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEnumerable<IBehaviour>
|
public interface IBehaviourController : IEntity, IHasUniverseObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Event triggered before the update of <see cref="IBehaviour"/>s.
|
|
||||||
/// </summary>
|
|
||||||
event PreUpdateEventHandler? OnPreUpdate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered during the update of <see cref="IBehaviour"/>s.
|
|
||||||
/// </summary>
|
|
||||||
event UpdateEventHandler? OnUpdate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered before the drawing phase.
|
|
||||||
/// </summary>
|
|
||||||
event PreDrawEventHandler? OnPreDraw;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event BehaviourAddedEventHandler? OnBehaviourAdded;
|
Event<IBehaviourController, BehaviourAddedArguments> OnBehaviourAdded { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event BehaviourRemovedEventHandler? OnBehaviourRemoved;
|
Event<IBehaviourController, BehaviourRemovedArguments> OnBehaviourRemoved { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount of <see cref="IBehaviour"/> collected.
|
||||||
|
/// </summary>
|
||||||
|
int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a <see cref="IBehaviour"/> collected by it's index.
|
||||||
|
/// </summary>
|
||||||
|
IBehaviour this[System.Index index] { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
||||||
@@ -83,20 +78,6 @@ public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEn
|
|||||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
|
/// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
|
||||||
void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||||
|
|
||||||
/// <summary>
|
readonly record struct BehaviourAddedArguments(IBehaviour BehaviourAdded);
|
||||||
/// Updates all <see cref="IBehaviour"/>s in the <see cref="IBehaviourController"/>.
|
readonly record struct BehaviourRemovedArguments(IBehaviour BehaviourRemoved);
|
||||||
/// </summary>
|
|
||||||
void Update();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs pre-draw operations.
|
|
||||||
/// </summary>
|
|
||||||
void UpdatePreDraw();
|
|
||||||
|
|
||||||
delegate void PreUpdateEventHandler(IBehaviourController sender);
|
|
||||||
delegate void UpdateEventHandler(IBehaviourController sender);
|
|
||||||
delegate void PreDrawEventHandler(IBehaviourController sender);
|
|
||||||
delegate void BehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
|
|
||||||
delegate void BehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a 2D camera in the engine.
|
/// Represents a 2D camera in the engine.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public interface ICoroutineYield
|
public interface ICoroutineYield
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a basic entity in the engine.
|
/// Represents a basic entity in the engine.
|
||||||
@@ -9,12 +9,12 @@ public interface IEntity : IInitializable, IHasStateEnable
|
|||||||
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
||||||
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event IdChangedEventHandler? OnIdChanged;
|
Event<IEntity, IdChangedArguments> OnIdChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the <see cref="IEntity"/>.
|
/// The ID of the <see cref="IEntity"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Id { get; set; }
|
string Id { get; set; }
|
||||||
|
|
||||||
delegate void IdChangedEventHandler(IEntity sender, string previousId);
|
readonly record struct IdChangedArguments(string PreviousId);
|
||||||
}
|
}
|
||||||
|
@@ -1,91 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a game world responsible for managing <see cref="IHierarchyObject"/>s.
|
|
||||||
/// </summary>
|
|
||||||
public interface IGameManager : IEntity, IEnumerable<IHierarchyObject>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when <see cref="Update(EngineTime)"/> is about to be called called on the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
event UpdateEventHandler? OnPreUpdate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when <see cref="Update(EngineTime)"/> is called on the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
event UpdateEventHandler? OnUpdate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
event PreDrawEventHandler? OnPreDraw;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when a <see cref="IHierarchyObject"/> is registered to the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
event HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when a <see cref="IHierarchyObject"/> is unregistered from the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
event HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Current time scale the <see cref="IGameManager"/> operates on.
|
|
||||||
/// </summary>
|
|
||||||
float TimeScale { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contains time data related to this <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
EngineTime Time { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contains unscaled time data related to this <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
EngineTime UnscaledTime { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a read-only list of <see cref="IHierarchyObject"/>s managed by the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyList<IHierarchyObject> HierarchyObjects { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers an <see cref="IHierarchyObject"/> to the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to register.</param>
|
|
||||||
void Register(IHierarchyObject hierarchyObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a <see cref="IHierarchyObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of <see cref="IHierarchyObject"/> to instantiate.</typeparam>
|
|
||||||
/// <param name="args">Constructor parameters for the given type of <see cref="IHierarchyObject"/>.</param>
|
|
||||||
/// <returns>The instantiated <see cref="IHierarchyObject"/>.</returns>
|
|
||||||
T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes an <see cref="IHierarchyObject"/> from the <see cref="IGameManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to remove.</param>
|
|
||||||
void Remove(IHierarchyObject hierarchyObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the <see cref="IGameManager"/> with the given delta time.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="engineTime">Delta time.</param>
|
|
||||||
void Update(EngineTime engineTime);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs operations that should be done before the draw calls.
|
|
||||||
/// </summary>
|
|
||||||
void PreDraw();
|
|
||||||
|
|
||||||
delegate void UpdateEventHandler(IGameManager sender, EngineTime engineTime);
|
|
||||||
delegate void PreDrawEventHandler(IGameManager sender);
|
|
||||||
|
|
||||||
delegate void HierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
|
|
||||||
delegate void HierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
|
|
||||||
}
|
|
@@ -1,131 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an <see cref="IEntity"/> that can enter and exit a hierarchy within the <see cref="IGameManager"/> system.
|
|
||||||
/// This interface allows for tracking the object's presence in the hierarchy and provides events
|
|
||||||
/// for notifying when the see enters or exits the hierarchy.
|
|
||||||
/// </summary>
|
|
||||||
public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy.
|
|
||||||
/// </summary>
|
|
||||||
event EnteredHierarchyEventHandler? OnEnteredHierarchy;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy.
|
|
||||||
/// </summary>
|
|
||||||
event ExitedHierarchyEventHandler? OnExitedHierarchy;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when the <see cref="Parent"/> of the <see cref="IHierarchyObject"/> changes. The second parameter is the old <see cref="IHierarchyObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
event ParentChangedEventHandler? OnParentChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when a new <see cref="IHierarchyObject"/> is added to the <see cref="Children"/>.
|
|
||||||
/// </summary>
|
|
||||||
event ChildrenAddedEventHandler? OnChildrenAdded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event triggered when an <see cref="IHierarchyObject"/> is removed from the <see cref="Children"/>.
|
|
||||||
/// </summary>
|
|
||||||
event ChildrenRemovedEventHandler? OnChildrenRemoved;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="IGameManager"/> this <see cref="IHierarchyObject"/> is connected to, if any.
|
|
||||||
/// </summary>
|
|
||||||
IGameManager GameManager { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the <see cref="IHierarchyObject"/> is currently in the hierarchy.
|
|
||||||
/// </summary>
|
|
||||||
bool IsInHierarchy { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parent <see cref="IHierarchyObject"/> of the <see cref="IHierarchyObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
IHierarchyObject? Parent { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="IHierarchyObject"/>s that have this <see cref="IHierarchyObject"/> as their <see cref="Parent"/>.
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyList<IHierarchyObject> Children { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal method to handle entering the hierarchy.
|
|
||||||
/// This should be called by the system to properly manage hierarchy states.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="gameManager">The <see cref="IGameManager"/> that is managing this hierarchy.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully entered the hierarchy;
|
|
||||||
/// <see cref="false"/> if it failed to do so.
|
|
||||||
/// </returns>
|
|
||||||
internal bool EnterHierarchy(IGameManager gameManager);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal method to handle exiting the hierarchy.
|
|
||||||
/// This should be called by the system to properly manage hierarchy states.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully exited the hierarchy;
|
|
||||||
/// <see cref="false"/> if it failed to do so.
|
|
||||||
/// </returns>
|
|
||||||
internal bool ExitHierarchy();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the parent <see cref="IHierarchyObject"/> of this <see cref="IHierarchyObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">The parent <see cref="IHierarchyObject"/> to set.</param>
|
|
||||||
void SetParent(IHierarchyObject? hierarchyObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a child <see cref="IHierarchyObject"/> to this <see cref="IHierarchyObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to add.</param>
|
|
||||||
void AddChild(IHierarchyObject hierarchyObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a child <see cref="IHierarchyObject"/> from this <see cref="IHierarchyObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to remove.</param>
|
|
||||||
void RemoveChild(IHierarchyObject hierarchyObject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy of a <see cref="IGameManager">.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that entered the hierarchy.</param>
|
|
||||||
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has entered it's hierarchy.</param>
|
|
||||||
delegate void EnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy of a <see cref="IGameManager">.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that exited the hierarchy.</param>
|
|
||||||
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has exited it's hierarchy.</param>
|
|
||||||
delegate void ExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delegate for the event triggered when the <see cref="IHierarchyObject"/>'s parent changes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that the parent has changed.</param>
|
|
||||||
/// <param name="previousParent">The previous <see cref="IHierarchyObject"/> the sender was a child of.</param>
|
|
||||||
/// <param name="newParent">The new and current <see cref="IHierarchyObject"/> the sender is a child of.</param>
|
|
||||||
delegate void ParentChangedEventHandler(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> added as a child.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
|
|
||||||
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
|
|
||||||
delegate void ChildrenAddedEventHandler(IHierarchyObject sender, IHierarchyObject childrenAdded);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> removed from being a child.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
|
|
||||||
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
|
|
||||||
delegate void ChildrenRemovedEventHandler(IHierarchyObject sender, IHierarchyObject childrenRemoved);
|
|
||||||
}
|
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
||||||
@@ -8,12 +8,12 @@ public interface IInitializable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event InitializedEventHandler? OnInitialized;
|
Event<IInitializable> OnInitialized { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
|
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event FinalizedEventHandler? OnFinalized;
|
Event<IInitializable> OnFinalized { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value indicating whether the entity has been initialized.
|
/// The value indicating whether the entity has been initialized.
|
||||||
@@ -31,7 +31,4 @@ public interface IInitializable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
||||||
bool Finalize();
|
bool Finalize();
|
||||||
|
|
||||||
delegate void InitializedEventHandler(IInitializable sender);
|
|
||||||
delegate void FinalizedEventHandler(IInitializable sender);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity with a name.
|
/// Represents an entity with a name.
|
||||||
@@ -8,12 +8,12 @@ public interface INameable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the name of the entity changes.
|
/// Event triggered when the name of the entity changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event NameChangedEventHandler? OnNameChanged;
|
Event<INameable, NameChangedArguments> OnNameChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the entity.
|
/// The name of the entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Name { get; set; }
|
string Name { get; set; }
|
||||||
|
|
||||||
delegate void NameChangedEventHandler(INameable sender, string previousName);
|
readonly record struct NameChangedArguments(string PreviousName);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity with an enable state that can be toggled.
|
/// Represents an entity with an enable state that can be toggled.
|
||||||
@@ -8,12 +8,12 @@ public interface IStateEnable : IHasEntity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EnabledChangedEventHandler? OnEnabledChanged;
|
Event<IStateEnable, EnabledChangedArguments> OnEnabledChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Enabled { get; set; }
|
bool Enabled { get; set; }
|
||||||
|
|
||||||
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
|
readonly record struct EnabledChangedArguments(bool PreviousState);
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.
|
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.
|
||||||
@@ -8,17 +8,37 @@ public interface ITransform2D : IBehaviour
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform2D"/> changes.
|
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform2D"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event PositionChangedEventHandler? OnPositionChanged;
|
Event<ITransform2D, PositionChangedArguments> OnPositionChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform2D"/> changes.
|
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform2D"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ScaleChangedEventHandler? OnScaleChanged;
|
Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event RotationChangedEventHandler? OnRotationChanged;
|
Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D Up { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D Down { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D Left { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D Right { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The world position of the <see cref="ITransform2D"/> in 2D space.
|
/// The world position of the <see cref="ITransform2D"/> in 2D space.
|
||||||
@@ -51,23 +71,20 @@ public interface ITransform2D : IBehaviour
|
|||||||
float LocalRotation { get; set; }
|
float LocalRotation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
/// Arguments for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
|
/// <param name="PreviousPosition">The previous <see cref="Position"/> of the <see cref="ITransform2D"/>.</param>
|
||||||
/// <param name="previousPosition">The previous <see cref="Position"/> of the <see cref="ITransform2D"/>.</param>
|
readonly record struct PositionChangedArguments(Vector2D PreviousPosition);
|
||||||
delegate void PositionChangedEventHandler(ITransform2D sender, Vector2D previousPosition);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
/// Arguments for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
|
/// <param name="PreviousScale">The previous <see cref="Scale"/> of the <see cref="ITransform2D"/>.</param>
|
||||||
/// <param name="previousScale">The previous <see cref="Scale"/> of the <see cref="ITransform2D"/>.</param>
|
readonly record struct ScaleChangedArguments(Vector2D PreviousScale);
|
||||||
delegate void ScaleChangedEventHandler(ITransform2D sender, Vector2D previousScale);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
/// Arguments for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
|
/// <param name="PreviousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform2D"/>.</param>
|
||||||
/// <param name="previousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform2D"/>.</param>
|
readonly record struct RotationChangedArguments(float PreviousRotation);
|
||||||
delegate void RotationChangedEventHandler(ITransform2D sender, float previousRotation);
|
|
||||||
}
|
}
|
||||||
|
120
Engine.Core/Abstract/IUniverse.cs
Normal file
120
Engine.Core/Abstract/IUniverse.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when <see cref="Update(UniverseTime)"/> is about to be called called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UpdateArguments> OnPreUpdate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UpdateArguments> OnUpdate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered after <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UpdateArguments> OnPostUpdate { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when <see cref="Draw"/> is about to be called called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse> OnPreDraw { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when <see cref="Draw"/> is called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse> OnDraw { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered after <see cref="Draw"/> is called on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse> OnPostDraw { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is about to be registered to the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectRegisteredArguments> OnPreUniverseObjectRegistered { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is about to be unregistered from the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectUnRegisteredArguments> OnPreUniverseObjectUnRegistered { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectUnRegisteredArguments> OnUniverseObjectUnRegistered { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when <see cref="TimeScale"/> is changed on the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, TimeScaleChangedArguments> OnTimeScaleChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current time scale the <see cref="IUniverse"/> operates on.
|
||||||
|
/// </summary>
|
||||||
|
float TimeScale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains time data related to this <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
UniverseTime Time { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains unscaled time data related to this <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
UniverseTime UnscaledTime { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only list of <see cref="IUniverseObject"/>s managed by the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<IUniverseObject> UniverseObjects { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an <see cref="IUniverseObject"/> to the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to register.</param>
|
||||||
|
void Register(IUniverseObject universeObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a <see cref="IUniverseObject"/> of type T with the given arguments and registers it to the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to instantiate.</typeparam>
|
||||||
|
/// <param name="args">Constructor parameters for the given type of <see cref="IUniverseObject"/>.</param>
|
||||||
|
/// <returns>The instantiated <see cref="IUniverseObject"/>.</returns>
|
||||||
|
T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an <see cref="IUniverseObject"/> from the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to remove.</param>
|
||||||
|
void Remove(IUniverseObject universeObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the <see cref="IUniverse"/> with the given delta time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeTime">Delta time.</param>
|
||||||
|
void Update(UniverseTime universeTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs operations that should be done to the draw.
|
||||||
|
/// </summary>
|
||||||
|
void Draw();
|
||||||
|
|
||||||
|
readonly record struct TimeScaleChangedArguments(float PreviousTimeScale);
|
||||||
|
readonly record struct UpdateArguments(UniverseTime EngineTime);
|
||||||
|
readonly record struct UniverseObjectRegisteredArguments(IUniverseObject UniverseObjectRegistered);
|
||||||
|
readonly record struct UniverseObjectUnRegisteredArguments(IUniverseObject UniverseObjectUnregistered);
|
||||||
|
}
|
125
Engine.Core/Abstract/IUniverseObject.cs
Normal file
125
Engine.Core/Abstract/IUniverseObject.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
|
||||||
|
/// This interface allows for tracking the object's presence in the universe and provides events
|
||||||
|
/// for notifying when the see enters or exits the universe.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverseObject, EnteredUniverseArguments> OnEnteredUniverse { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="IUniverseObject"/> exits the universe.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverseObject, ExitedUniverseArguments> OnExitedUniverse { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Parent"/> of the <see cref="IUniverseObject"/> changes. The second parameter is the old <see cref="IUniverseObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverseObject, ParentChangedArguments> OnParentChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a new <see cref="IUniverseObject"/> is added to the <see cref="Children"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverseObject, ChildrenAddedArguments> OnChildrenAdded { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when an <see cref="IUniverseObject"/> is removed from the <see cref="Children"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverseObject, ChildrenRemovedArguments> OnChildrenRemoved { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IUniverse"/> this <see cref="IUniverseObject"/> is connected to, if any.
|
||||||
|
/// </summary>
|
||||||
|
IUniverse Universe { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the <see cref="IUniverseObject"/> is currently in the universe.
|
||||||
|
/// </summary>
|
||||||
|
bool IsInUniverse { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
IUniverseObject? Parent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<IUniverseObject> Children { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal method to handle entering the universe.
|
||||||
|
/// This should be called by the system to properly manage universe states.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universe">The <see cref="IUniverse"/> that is managing this universe.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully entered the universe;
|
||||||
|
/// <see cref="false"/> if it failed to do so.
|
||||||
|
/// </returns>
|
||||||
|
internal bool EnterUniverse(IUniverse universe);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal method to handle exiting the universe.
|
||||||
|
/// This should be called by the system to properly manage universe states.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully exited the universe;
|
||||||
|
/// <see cref="false"/> if it failed to do so.
|
||||||
|
/// </returns>
|
||||||
|
internal bool ExitUniverse();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to add.</param>
|
||||||
|
void AddChild(IUniverseObject universeObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a child <see cref="IUniverseObject"/> from this <see cref="IUniverseObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to remove.</param>
|
||||||
|
void RemoveChild(IUniverseObject universeObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="IUniverseObject"/> enters the universe of a <see cref="IUniverse">.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The <see cref="IUniverseObject"/> that entered the universe.</param>
|
||||||
|
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has entered it's universe.</param>
|
||||||
|
readonly record struct EnteredUniverseArguments(IUniverse Universe);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="IUniverseObject"/> exits the universe of a <see cref="IUniverse">.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The <see cref="IUniverseObject"/> that exited the universe.</param>
|
||||||
|
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has exited it's universe.</param>
|
||||||
|
readonly record struct ExitedUniverseArguments(IUniverse Universe);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="IUniverseObject"/>'s parent changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The <see cref="IUniverseObject"/> that the parent has changed.</param>
|
||||||
|
/// <param name="previousParent">The previous <see cref="IUniverseObject"/> the sender was a child of.</param>
|
||||||
|
/// <param name="newParent">The new and current <see cref="IUniverseObject"/> the sender is a child of.</param>
|
||||||
|
readonly record struct ParentChangedArguments(IUniverseObject? PreviousParent, IUniverseObject? CurrentParent);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when a new <see cref="IUniverseObject"/> added as a child.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
|
||||||
|
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
|
||||||
|
readonly record struct ChildrenAddedArguments(IUniverseObject ChildrenAdded);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> removed from being a child.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
|
||||||
|
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
|
||||||
|
readonly record struct ChildrenRemovedArguments(IUniverseObject ChildrenRemoved);
|
||||||
|
}
|
@@ -1,127 +1,148 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
|
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
||||||
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
||||||
|
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
||||||
|
public Event<IAssignable>? OnUnassigned { get; } = new();
|
||||||
|
|
||||||
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
||||||
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
|
||||||
|
private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnBehaviourStateChanged = null!;
|
||||||
|
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);
|
private readonly List<T> monitoringBehaviours = new(32);
|
||||||
protected readonly List<T> activeBehaviours = new(32);
|
protected readonly List<T> activeBehaviours = new(32);
|
||||||
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
|
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
|
||||||
|
|
||||||
public IReadOnlyList<T> Behaviours => activeBehaviours;
|
public IUniverse Universe { get; private set; } = null!;
|
||||||
public IGameManager GameManager { get; private set; } = null!;
|
|
||||||
|
|
||||||
public T this[Index index] => activeBehaviours[index];
|
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
||||||
|
|
||||||
public ActiveBehaviourCollector() { }
|
|
||||||
public ActiveBehaviourCollector(IGameManager gameManager) => Assign(gameManager);
|
|
||||||
|
|
||||||
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
|
|
||||||
{
|
{
|
||||||
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
||||||
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
|
||||||
|
|
||||||
foreach (IBehaviour item in hierarchyObject.BehaviourController)
|
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
||||||
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
|
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
|
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
||||||
{
|
{
|
||||||
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
||||||
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
|
||||||
|
|
||||||
foreach (IBehaviour item in hierarchyObject.BehaviourController)
|
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
||||||
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
|
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) { }
|
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
||||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
||||||
{
|
{
|
||||||
if (behaviour is not T tBehaviour)
|
if (args.BehaviourAdded is not T tBehaviour)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
monitoringBehaviours.Add(tBehaviour);
|
monitoringBehaviours.Add(tBehaviour);
|
||||||
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
|
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
|
||||||
tBehaviour.OnActiveChanged += OnBehaviourStateChanged;
|
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
|
||||||
OnBehaviourStateChanged(tBehaviour, !tBehaviour.IsActive);
|
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBehaviourStateChanged(IActive sender, bool previousState)
|
protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
|
||||||
|
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
|
||||||
{
|
{
|
||||||
T behaviour = monitoringActiveToBehaviour[sender];
|
T behaviour = monitoringActiveToBehaviour[sender];
|
||||||
if (sender.IsActive)
|
if (sender.IsActive)
|
||||||
{
|
{
|
||||||
activeBehaviours.Add(behaviour);
|
AddBehaviour(behaviour);
|
||||||
OnBehaviourAdd(behaviour);
|
OnBehaviourAdd(behaviour);
|
||||||
OnCollected?.Invoke(this, behaviour);
|
OnCollected?.Invoke(this, new(behaviour));
|
||||||
}
|
}
|
||||||
else if (activeBehaviours.Remove(behaviour))
|
else if (activeBehaviours.Remove(behaviour))
|
||||||
{
|
{
|
||||||
OnBehaviourRemove(behaviour);
|
OnBehaviourRemove(behaviour);
|
||||||
OnRemoved?.Invoke(this, behaviour);
|
OnRemoved?.Invoke(this, new(behaviour));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
||||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
||||||
{
|
{
|
||||||
if (behaviour is not T tBehaviour)
|
if (args.BehaviourRemoved is not T tBehaviour)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
|
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tBehaviour.OnActiveChanged -= OnBehaviourStateChanged;
|
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
|
||||||
if (activeBehaviours.Remove(tBehaviour))
|
if (activeBehaviours.Remove(tBehaviour))
|
||||||
{
|
{
|
||||||
OnBehaviourRemove(tBehaviour);
|
OnBehaviourRemove(tBehaviour);
|
||||||
OnRemoved?.Invoke(this, tBehaviour);
|
OnRemoved?.Invoke(this, new(tBehaviour));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Assign(IGameManager gameManager)
|
public bool Assign(IUniverse universe)
|
||||||
{
|
{
|
||||||
if (GameManager is not null)
|
if (Universe is not null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
|
foreach (IUniverseObject universeObject in universe.UniverseObjects)
|
||||||
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
|
OnUniverseObjectRegistered(universe, new(universeObject));
|
||||||
|
|
||||||
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
|
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
|
||||||
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
|
universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
GameManager = gameManager;
|
Universe = universe;
|
||||||
OnGameManagerAssigned?.Invoke(this);
|
OnUniverseAssigned?.Invoke(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Unassign()
|
public bool Unassign()
|
||||||
{
|
{
|
||||||
if (GameManager is null)
|
if (Universe is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
|
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||||
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
|
OnUniverseObjectUnregistered(Universe, new(universeObject));
|
||||||
|
|
||||||
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
|
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
|
||||||
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
|
Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
GameManager = null!;
|
Universe = null!;
|
||||||
OnUnassigned?.Invoke(this);
|
OnUnassigned?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() => activeBehaviours.GetEnumerator();
|
public int Count => activeBehaviours.Count;
|
||||||
IEnumerator IEnumerable.GetEnumerator() => activeBehaviours.GetEnumerator();
|
public T this[Index index] => activeBehaviours[index];
|
||||||
|
|
||||||
|
public ActiveBehaviourCollector()
|
||||||
|
{
|
||||||
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
|
delegateOnBehaviourStateChanged = OnBehaviourStateChanged;
|
||||||
|
delegateOnUniverseObjectRegistered = OnUniverseObjectRegistered;
|
||||||
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollector(IUniverse universe)
|
||||||
|
{
|
||||||
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
|
delegateOnBehaviourStateChanged = OnBehaviourStateChanged;
|
||||||
|
delegateOnUniverseObjectRegistered = OnUniverseObjectRegistered;
|
||||||
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
|
|
||||||
|
Assign(universe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
|
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
private Comparison<T>? _sortBy = null;
|
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
||||||
public Comparison<T>? SortBy
|
|
||||||
|
private IComparer<T>? _sortBy = null;
|
||||||
|
public IComparer<T>? SortBy
|
||||||
{
|
{
|
||||||
get => _sortBy;
|
get => _sortBy;
|
||||||
set
|
set
|
||||||
@@ -19,12 +20,48 @@ public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> whe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
protected override void AddBehaviour(T behaviour)
|
||||||
{
|
{
|
||||||
if (SortBy is not null)
|
if (SortBy is null)
|
||||||
activeBehaviours.Sort(SortBy);
|
{
|
||||||
|
activeBehaviours.Add(behaviour);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActiveBehaviourCollectorSorted() { }
|
int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy);
|
||||||
public ActiveBehaviourCollectorSorted(IGameManager gameManager, Comparison<T> sortBy) : base(gameManager) => SortBy = 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public abstract class BaseEntity : IEntity
|
public abstract class BaseEntity : IEntity
|
||||||
{
|
{
|
||||||
public event IEntity.IdChangedEventHandler? OnIdChanged = null;
|
public Event<IEntity, IEntity.IdChangedArguments> OnIdChanged { get; } = new();
|
||||||
|
public Event<IInitializable> OnInitialized { get; } = new();
|
||||||
public event IInitializable.InitializedEventHandler? OnInitialized = null;
|
public Event<IInitializable> OnFinalized { get; } = new();
|
||||||
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
|
public Event<IHasStateEnable> OnStateEnableAssigned { get; } = new();
|
||||||
|
public Event<IAssignable> OnUnassigned { get; } = new();
|
||||||
public event IHasStateEnable.StateEnableAssignedEventHandler? OnStateEnableAssigned = null;
|
|
||||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
|
||||||
|
|
||||||
private IStateEnable _stateEnable = null!;
|
private IStateEnable _stateEnable = null!;
|
||||||
|
|
||||||
@@ -33,7 +31,7 @@ public abstract class BaseEntity : IEntity
|
|||||||
string previousId = _id;
|
string previousId = _id;
|
||||||
|
|
||||||
_id = value;
|
_id = value;
|
||||||
OnIdChanged?.Invoke(this, previousId);
|
OnIdChanged?.Invoke(this, new(previousId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,112 +1,50 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Helpers;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public abstract class Behaviour : BehaviourBase
|
public abstract class Behaviour : BehaviourBase
|
||||||
{
|
{
|
||||||
private bool isInitializedThisFrame = false;
|
private readonly Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments>.EventHandler delegateEnteredUniverse = null!;
|
||||||
|
private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateExitedUniverse = null!;
|
||||||
protected IGameManager GameManager => BehaviourController.HierarchyObject.GameManager;
|
|
||||||
protected IHierarchyObject HierarchyObject => BehaviourController.HierarchyObject;
|
|
||||||
|
|
||||||
public Behaviour()
|
public Behaviour()
|
||||||
{
|
{
|
||||||
OnInitialized += OnInitialize;
|
OnInitialized.AddListener(OnInitialize);
|
||||||
OnFinalized += OnFinalize;
|
OnFinalized.AddListener(OnFinalize);
|
||||||
OnUnassigned += OnUnassign;
|
OnUnassigned.AddListener(OnUnassign);
|
||||||
|
|
||||||
|
delegateEnteredUniverse = EnteredUniverse;
|
||||||
|
delegateExitedUniverse = ExitedUniverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnUnassign() { }
|
protected virtual void OnUnassign() { }
|
||||||
protected virtual void OnUnassign(IAssignable assignable) => OnUnassign();
|
protected void OnUnassign(IAssignable assignable) => OnUnassign();
|
||||||
|
|
||||||
protected virtual void OnInitialize() { }
|
protected virtual void OnInitialize() { }
|
||||||
protected virtual void OnInitialize(IInitializable _)
|
protected void OnInitialize(IInitializable _)
|
||||||
{
|
{
|
||||||
isInitializedThisFrame = true;
|
BehaviourController.UniverseObject.OnEnteredUniverse.AddListener(delegateEnteredUniverse);
|
||||||
|
BehaviourController.UniverseObject.OnExitedUniverse.AddListener(delegateExitedUniverse);
|
||||||
BehaviourController.OnPreUpdate += PreUpdate;
|
|
||||||
BehaviourController.OnPreDraw += PreDraw;
|
|
||||||
BehaviourController.OnUpdate += Update;
|
|
||||||
BehaviourController.HierarchyObject.OnEnteredHierarchy += EnteredHierarchy;
|
|
||||||
BehaviourController.HierarchyObject.OnExitedHierarchy += ExitedHierarchy;
|
|
||||||
|
|
||||||
OnInitialize();
|
OnInitialize();
|
||||||
|
|
||||||
if (HierarchyObject.IsInHierarchy)
|
if (UniverseObject.IsInUniverse)
|
||||||
EnteredHierarchy(HierarchyObject, GameManager);
|
EnteredUniverse(UniverseObject, new(Universe));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnFinalize() { }
|
protected virtual void OnFinalize() { }
|
||||||
protected virtual void OnFinalize(IInitializable _)
|
protected void OnFinalize(IInitializable _)
|
||||||
{
|
{
|
||||||
BehaviourController.OnPreUpdate -= PreUpdate;
|
BehaviourController.UniverseObject.OnEnteredUniverse.RemoveListener(delegateEnteredUniverse);
|
||||||
BehaviourController.OnPreDraw -= PreDraw;
|
BehaviourController.UniverseObject.OnExitedUniverse.RemoveListener(delegateExitedUniverse);
|
||||||
BehaviourController.OnUpdate -= Update;
|
|
||||||
BehaviourController.HierarchyObject.OnEnteredHierarchy -= EnteredHierarchy;
|
|
||||||
BehaviourController.HierarchyObject.OnExitedHierarchy -= ExitedHierarchy;
|
|
||||||
|
|
||||||
OnFinalize();
|
OnFinalize();
|
||||||
|
|
||||||
if (HierarchyObject.IsInHierarchy)
|
if (UniverseObject.IsInUniverse)
|
||||||
ExitedHierarchy(HierarchyObject, GameManager);
|
ExitedUniverse(UniverseObject, new(Universe));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
protected virtual void OnEnteredUniverse(IUniverse universe) { }
|
||||||
protected virtual void OnPreUpdate() { }
|
protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
|
||||||
protected virtual void PreUpdate(IBehaviourController _)
|
|
||||||
{
|
|
||||||
AssertHelpers.AssertInitialized(this);
|
|
||||||
|
|
||||||
OnPreUpdatePreActiveCheck();
|
protected virtual void OnExitedUniverse(IUniverse universe) { }
|
||||||
|
protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
|
||||||
if (!IsActive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (isInitializedThisFrame)
|
|
||||||
FirstActiveFrame();
|
|
||||||
|
|
||||||
OnPreUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnFirstActiveFrame() { }
|
|
||||||
protected virtual void FirstActiveFrame()
|
|
||||||
{
|
|
||||||
OnFirstActiveFrame();
|
|
||||||
isInitializedThisFrame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnUpdatePreActiveCheck() { }
|
|
||||||
protected virtual void OnUpdate() { }
|
|
||||||
protected virtual void Update(IBehaviourController _)
|
|
||||||
{
|
|
||||||
AssertHelpers.AssertInitialized(this);
|
|
||||||
|
|
||||||
OnUpdatePreActiveCheck();
|
|
||||||
|
|
||||||
if (!IsActive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
|
||||||
protected virtual void OnPreDraw() { }
|
|
||||||
protected virtual void PreDraw(IBehaviourController _)
|
|
||||||
{
|
|
||||||
AssertHelpers.AssertInitialized(this);
|
|
||||||
|
|
||||||
OnPreDrawPreActiveCheck();
|
|
||||||
|
|
||||||
if (!StateEnable.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnPreDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnEnteredHierarchy(IGameManager gameManager) { }
|
|
||||||
protected virtual void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnEnteredHierarchy(gameManager);
|
|
||||||
|
|
||||||
protected virtual void OnExitedHierarchy(IGameManager gameManager) { }
|
|
||||||
protected virtual void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnExitedHierarchy(gameManager);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +1,9 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public abstract class Behaviour2D : Behaviour, IBehaviour2D
|
public abstract class Behaviour2D : Behaviour, IBehaviour2D
|
||||||
{
|
{
|
||||||
public ITransform2D Transform { get; private set; } = null!;
|
public ITransform2D Transform { get; private set; } = null!;
|
||||||
|
|
||||||
protected sealed override void OnInitialize(IInitializable _)
|
protected override void OnInitialize() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
||||||
{
|
protected override void OnFinalize() => Transform = null!;
|
||||||
Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
|
||||||
base.OnInitialize(_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override void OnFinalize(IInitializable _)
|
|
||||||
{
|
|
||||||
Transform = null!;
|
|
||||||
base.OnFinalize(_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override void OnUnassign(IAssignable assignable) => base.OnUnassign(assignable);
|
|
||||||
protected sealed override void PreUpdate(IBehaviourController behaviourController) => base.PreUpdate(behaviourController);
|
|
||||||
protected sealed override void FirstActiveFrame() => base.FirstActiveFrame();
|
|
||||||
protected sealed override void Update(IBehaviourController behaviourController) => base.Update(behaviourController);
|
|
||||||
protected sealed override void PreDraw(IBehaviourController behaviourController) => base.PreDraw(behaviourController);
|
|
||||||
protected sealed override void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.EnteredHierarchy(sender, gameManager);
|
|
||||||
protected sealed override void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.ExitedHierarchy(sender, gameManager);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,18 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
|
||||||
using Syntriax.Engine.Core.Helpers;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||||
public abstract class BehaviourBase : BaseEntity, IBehaviour
|
public abstract class BehaviourBase : BaseEntity, IBehaviour
|
||||||
{
|
{
|
||||||
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
|
public Event<IBehaviour, IBehaviour.PriorityChangedArguments> OnPriorityChanged { get; } = new();
|
||||||
public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null;
|
public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
|
||||||
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
|
public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
|
||||||
|
|
||||||
|
private readonly Event<IHasUniverseObject>.EventHandler delegateOnUniverseObjectAssigned = null!;
|
||||||
|
private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnUniverseObjectActiveChanged = null!;
|
||||||
|
private readonly Event<IStateEnable, IStateEnable.EnabledChangedArguments>.EventHandler delegateOnStateEnabledChanged = null!;
|
||||||
|
|
||||||
|
public IUniverse Universe => BehaviourController.UniverseObject.Universe;
|
||||||
|
public IUniverseObject UniverseObject => BehaviourController.UniverseObject;
|
||||||
|
|
||||||
private IBehaviourController _behaviourController = null!;
|
private IBehaviourController _behaviourController = null!;
|
||||||
public IBehaviourController BehaviourController => _behaviourController;
|
public IBehaviourController BehaviourController => _behaviourController;
|
||||||
@@ -25,7 +28,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
|
|
||||||
int previousPriority = _priority;
|
int previousPriority = _priority;
|
||||||
_priority = value;
|
_priority = value;
|
||||||
OnPriorityChanged?.Invoke(this, previousPriority);
|
OnPriorityChanged?.Invoke(this, new(previousPriority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +43,17 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
|
|
||||||
_behaviourController = behaviourController;
|
_behaviourController = behaviourController;
|
||||||
OnAssign(behaviourController);
|
OnAssign(behaviourController);
|
||||||
behaviourController.OnHierarchyObjectAssigned += OnHierarchyObjectAssigned;
|
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
|
||||||
if (behaviourController.HierarchyObject is not null)
|
behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
||||||
OnHierarchyObjectAssigned(behaviourController);
|
if (behaviourController.UniverseObject is not null)
|
||||||
|
OnUniverseObjectAssigned(behaviourController);
|
||||||
OnBehaviourControllerAssigned?.Invoke(this);
|
OnBehaviourControllerAssigned?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHierarchyObjectAssigned(IHasHierarchyObject sender)
|
private void OnUniverseObjectAssigned(IHasUniverseObject sender)
|
||||||
{
|
{
|
||||||
sender.HierarchyObject.OnActiveChanged += OnHierarchyObjectActiveChanged;
|
sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
|
||||||
UpdateActive();
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,32 +61,43 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
{
|
{
|
||||||
base.OnAssign(stateEnable);
|
base.OnAssign(stateEnable);
|
||||||
|
|
||||||
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
|
stateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UnassignInternal()
|
protected override void UnassignInternal()
|
||||||
{
|
{
|
||||||
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
|
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
|
||||||
BehaviourController.OnHierarchyObjectAssigned -= OnHierarchyObjectAssigned;
|
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
|
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
|
||||||
|
BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
base.UnassignInternal();
|
base.UnassignInternal();
|
||||||
_behaviourController = null!;
|
_behaviourController = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitializeInternal()
|
protected override void InitializeInternal()
|
||||||
{
|
{
|
||||||
AssertHelpers.AssertBehaviourControllerAssigned(this);
|
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
||||||
AssertHelpers.AssertStateEnableAssigned(this);
|
Debug.Assert.AssertStateEnableAssigned(this);
|
||||||
|
|
||||||
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
|
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
|
||||||
private void OnHierarchyObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
|
private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
|
||||||
|
|
||||||
private void UpdateActive()
|
private void UpdateActive()
|
||||||
{
|
{
|
||||||
bool previousActive = IsActive;
|
bool previousActive = IsActive;
|
||||||
_isActive = StateEnable.Enabled && _behaviourController.HierarchyObject.IsActive;
|
_isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
||||||
|
|
||||||
if (previousActive != IsActive)
|
if (previousActive != IsActive)
|
||||||
OnActiveChanged?.Invoke(this, previousActive);
|
OnActiveChanged?.Invoke(this, new(previousActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BehaviourBase()
|
||||||
|
{
|
||||||
|
delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
|
||||||
|
delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
|
||||||
|
delegateOnStateEnabledChanged = OnStateEnabledChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,106 +1,124 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
||||||
{
|
{
|
||||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
||||||
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
||||||
|
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
||||||
|
public Event<IAssignable>? OnUnassigned { get; } = new();
|
||||||
|
|
||||||
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
||||||
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = 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);
|
protected readonly List<T> behaviours = new(32);
|
||||||
|
|
||||||
public IReadOnlyList<T> Behaviours => behaviours;
|
public IUniverse Universe { get; private set; } = null!;
|
||||||
public IGameManager GameManager { get; private set; } = null!;
|
|
||||||
|
|
||||||
public T this[Index index] => behaviours[index];
|
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
||||||
|
|
||||||
public BehaviourCollector() { }
|
|
||||||
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
|
|
||||||
|
|
||||||
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
|
|
||||||
{
|
{
|
||||||
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
||||||
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
|
||||||
|
|
||||||
foreach (IBehaviour item in hierarchyObject.BehaviourController)
|
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
||||||
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
|
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
|
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
||||||
{
|
{
|
||||||
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
||||||
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
|
||||||
|
|
||||||
foreach (IBehaviour item in hierarchyObject.BehaviourController)
|
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
||||||
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
|
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) { }
|
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
||||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
||||||
{
|
{
|
||||||
if (behaviour is not T tBehaviour)
|
if (args.BehaviourAdded is not T tBehaviour)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
behaviours.Add(tBehaviour);
|
AddBehaviour(tBehaviour);
|
||||||
OnBehaviourAdd(behaviour);
|
OnBehaviourAdd(args.BehaviourAdded);
|
||||||
OnCollected?.Invoke(this, tBehaviour);
|
OnCollected?.Invoke(this, new(tBehaviour));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
||||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
||||||
{
|
{
|
||||||
if (behaviour is not T tBehaviour)
|
if (args.BehaviourRemoved is not T tBehaviour)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!behaviours.Remove(tBehaviour))
|
if (!behaviours.Remove(tBehaviour))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnBehaviourRemove(behaviour);
|
OnBehaviourRemove(args.BehaviourRemoved);
|
||||||
OnRemoved?.Invoke(this, tBehaviour);
|
OnRemoved?.Invoke(this, new(tBehaviour));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnAssign(IGameManager gameManager) { }
|
protected virtual void OnAssign(IUniverse universe) { }
|
||||||
public bool Assign(IGameManager gameManager)
|
public bool Assign(IUniverse universe)
|
||||||
{
|
{
|
||||||
if (GameManager is not null)
|
if (Universe is not null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
|
foreach (IUniverseObject universeObject in universe.UniverseObjects)
|
||||||
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
|
OnUniverseObjectRegistered(universe, new(universeObject));
|
||||||
|
|
||||||
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
|
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
|
||||||
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
|
universe.OnPreUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
GameManager = gameManager;
|
Universe = universe;
|
||||||
OnAssign(gameManager);
|
OnAssign(universe);
|
||||||
OnGameManagerAssigned?.Invoke(this);
|
OnUniverseAssigned?.Invoke(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Unassign()
|
public bool Unassign()
|
||||||
{
|
{
|
||||||
if (GameManager is null)
|
if (Universe is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
|
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||||
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
|
OnUniverseObjectUnregistered(Universe, new(universeObject));
|
||||||
|
|
||||||
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
|
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
|
||||||
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
|
Universe.OnPreUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
GameManager = null!;
|
Universe = null!;
|
||||||
OnUnassigned?.Invoke(this);
|
OnUnassigned?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() => behaviours.GetEnumerator();
|
public int Count => behaviours.Count;
|
||||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
public T this[Index index] => behaviours[index];
|
||||||
|
|
||||||
|
public BehaviourCollector()
|
||||||
|
{
|
||||||
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
|
delegateOnUniverseObjectRegistered = OnUniverseObjectRegistered;
|
||||||
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollector(IUniverse universe)
|
||||||
|
{
|
||||||
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
|
delegateOnUniverseObjectRegistered = OnUniverseObjectRegistered;
|
||||||
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
|
|
||||||
|
Assign(universe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
||||||
{
|
{
|
||||||
private Comparison<T>? _sortBy = null;
|
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
||||||
public Comparison<T>? SortBy
|
|
||||||
|
private IComparer<T>? _sortBy = null;
|
||||||
|
public IComparer<T>? SortBy
|
||||||
{
|
{
|
||||||
get => _sortBy;
|
get => _sortBy;
|
||||||
set
|
set
|
||||||
@@ -19,12 +20,48 @@ public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
protected override void AddBehaviour(T behaviour)
|
||||||
{
|
{
|
||||||
if (SortBy is not null)
|
if (SortBy is null)
|
||||||
behaviours.Sort(SortBy);
|
{
|
||||||
|
behaviours.Add(behaviour);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BehaviourCollectorSorted() { }
|
int insertionIndex = behaviours.BinarySearch(behaviour, SortBy);
|
||||||
public BehaviourCollectorSorted(IGameManager gameManager, Comparison<T> sortBy) : base(gameManager) => SortBy = 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,67 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Helpers;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||||
public class BehaviourController : IBehaviourController
|
public class BehaviourController : BaseEntity, IBehaviourController
|
||||||
{
|
{
|
||||||
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
|
public Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments> OnBehaviourAdded { get; } = new();
|
||||||
public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
|
public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
|
||||||
public event IBehaviourController.PreDrawEventHandler? OnPreDraw = null;
|
public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
|
||||||
|
|
||||||
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
|
private readonly List<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
|
||||||
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
|
|
||||||
public event IHasHierarchyObject.HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
|
|
||||||
|
|
||||||
public event IInitializable.InitializedEventHandler? OnInitialized = null;
|
private IUniverseObject _universeObject = null!;
|
||||||
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
|
|
||||||
|
|
||||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
public IUniverseObject UniverseObject => _universeObject;
|
||||||
|
public int Count => behaviours.Count;
|
||||||
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
|
public IBehaviour this[Index index] => behaviours[index];
|
||||||
|
|
||||||
private IHierarchyObject _hierarchyObject = null!;
|
|
||||||
private bool _initialized = false;
|
|
||||||
|
|
||||||
public IHierarchyObject HierarchyObject => _hierarchyObject;
|
|
||||||
|
|
||||||
public bool IsInitialized
|
|
||||||
{
|
|
||||||
get => _initialized;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
if (value == _initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_initialized = value;
|
|
||||||
if (value)
|
|
||||||
OnInitialized?.Invoke(this);
|
|
||||||
else
|
|
||||||
OnFinalized?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
|
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
InsertBehaviourByPriority(behaviour);
|
InsertBehaviourByPriority(behaviour);
|
||||||
|
|
||||||
behaviour.Assign(this);
|
behaviour.Assign(this);
|
||||||
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
|
|
||||||
|
|
||||||
|
if (IsInitialized)
|
||||||
behaviour.Initialize();
|
behaviour.Initialize();
|
||||||
behaviour.OnPriorityChanged += OnPriorityChange;
|
|
||||||
OnBehaviourAdded?.Invoke(this, behaviour);
|
behaviour.OnPriorityChanged.AddListener(OnPriorityChange);
|
||||||
|
OnBehaviourAdded?.Invoke(this, new(behaviour));
|
||||||
return behaviour;
|
return behaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
||||||
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_hierarchyObject, args));
|
{
|
||||||
|
T behaviour = Factory.BehaviourFactory.Instantiate<T>(args);
|
||||||
|
return AddBehaviour(behaviour);
|
||||||
|
}
|
||||||
|
|
||||||
public T? GetBehaviour<T>()
|
public T? GetBehaviour<T>()
|
||||||
{
|
{
|
||||||
@@ -74,17 +49,13 @@ public class BehaviourController : IBehaviourController
|
|||||||
|
|
||||||
public IReadOnlyList<T> GetBehaviours<T>()
|
public IReadOnlyList<T> GetBehaviours<T>()
|
||||||
{
|
{
|
||||||
List<T>? behaviours = null;
|
List<T> behaviours = [];
|
||||||
|
|
||||||
foreach (IBehaviour behaviourItem in this.behaviours)
|
foreach (IBehaviour behaviourItem in this.behaviours)
|
||||||
{
|
if (behaviourItem is T behaviour)
|
||||||
if (behaviourItem is not T behaviour)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
behaviours ??= [];
|
|
||||||
behaviours.Add(behaviour);
|
behaviours.Add(behaviour);
|
||||||
}
|
|
||||||
|
|
||||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
return behaviours;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetBehaviours<T>(IList<T> results)
|
public void GetBehaviours<T>(IList<T> results)
|
||||||
@@ -101,7 +72,7 @@ public class BehaviourController : IBehaviourController
|
|||||||
|
|
||||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
for (int i = behaviours.Count; i >= 0; i--)
|
for (int i = behaviours.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (behaviours[i] is not T behaviour)
|
if (behaviours[i] is not T behaviour)
|
||||||
continue;
|
continue;
|
||||||
@@ -116,81 +87,42 @@ public class BehaviourController : IBehaviourController
|
|||||||
public void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour
|
public void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
if (!behaviours.Contains(behaviour))
|
if (!behaviours.Contains(behaviour))
|
||||||
throw new Exception($"{behaviour.GetType().Name} does not exist in {HierarchyObject.Name}'s {nameof(IBehaviourController)}.");
|
throw new Exception($"{behaviour.GetType().Name} does not exist in {UniverseObject.Name}'s {nameof(IBehaviourController)}.");
|
||||||
|
|
||||||
behaviour.OnPriorityChanged -= OnPriorityChange;
|
behaviour.OnPriorityChanged.RemoveListener(OnPriorityChange);
|
||||||
behaviour.Finalize();
|
behaviour.Finalize();
|
||||||
behaviours.Remove(behaviour);
|
behaviours.Remove(behaviour);
|
||||||
OnBehaviourRemoved?.Invoke(this, behaviour);
|
OnBehaviourRemoved?.Invoke(this, new(behaviour));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
|
protected virtual void OnAssign(IUniverseObject universeObject) { }
|
||||||
public bool Assign(IHierarchyObject hierarchyObject)
|
public bool Assign(IUniverseObject universeObject)
|
||||||
{
|
{
|
||||||
if (HierarchyObject is not null && HierarchyObject.IsInitialized)
|
if (UniverseObject is not null && UniverseObject.IsInitialized)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_hierarchyObject = hierarchyObject;
|
_universeObject = universeObject;
|
||||||
OnAssign(hierarchyObject);
|
OnAssign(universeObject);
|
||||||
OnHierarchyObjectAssigned?.Invoke(this);
|
OnUniverseObjectAssigned?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Initialize()
|
protected override void InitializeInternal()
|
||||||
{
|
{
|
||||||
if (IsInitialized)
|
Debug.Assert.AssertUniverseObjectAssigned(this);
|
||||||
return false;
|
|
||||||
|
|
||||||
AssertHelpers.AssertHierarchyObjectAssigned(this);
|
|
||||||
|
|
||||||
foreach (IBehaviour behaviour in behaviours)
|
foreach (IBehaviour behaviour in behaviours)
|
||||||
behaviour.Initialize();
|
behaviour.Initialize();
|
||||||
|
|
||||||
IsInitialized = true;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Finalize()
|
protected override void FinalizeInternal()
|
||||||
{
|
{
|
||||||
if (!IsInitialized)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach (IBehaviour behaviour in behaviours)
|
foreach (IBehaviour behaviour in behaviours)
|
||||||
behaviour.Finalize();
|
behaviour.Finalize();
|
||||||
|
|
||||||
IsInitialized = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Unassign()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_hierarchyObject = null!;
|
|
||||||
OnUnassigned?.Invoke(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (!HierarchyObject.StateEnable.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnPreUpdate?.Invoke(this);
|
|
||||||
OnUpdate?.Invoke(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdatePreDraw()
|
|
||||||
{
|
|
||||||
if (!HierarchyObject.StateEnable.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnPreDraw?.Invoke(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BehaviourController() { }
|
public BehaviourController() { }
|
||||||
public BehaviourController(IHierarchyObject hierarchyObject) => Assign(hierarchyObject);
|
public BehaviourController(IUniverseObject universeObject) => Assign(universeObject);
|
||||||
|
|
||||||
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
|
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
@@ -209,12 +141,9 @@ public class BehaviourController : IBehaviourController
|
|||||||
behaviours.Add(behaviour);
|
behaviours.Add(behaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPriorityChange(IBehaviour sender, int previousPriority)
|
private void OnPriorityChange(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
|
||||||
{
|
{
|
||||||
behaviours.Remove(sender);
|
behaviours.Remove(sender);
|
||||||
InsertBehaviourByPriority(sender);
|
InsertBehaviourByPriority(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class CoroutineManager : HierarchyObject
|
public class CoroutineManager : Behaviour, IUpdate
|
||||||
{
|
{
|
||||||
private readonly List<IEnumerator> enumerators = [];
|
private readonly List<IEnumerator> enumerators = [];
|
||||||
|
|
||||||
@@ -20,17 +18,7 @@ public class CoroutineManager : HierarchyObject
|
|||||||
enumerators.Remove(enumerator);
|
enumerators.Remove(enumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnteringHierarchy(IGameManager gameManager)
|
void IUpdate.Update()
|
||||||
{
|
|
||||||
gameManager.OnUpdate += OnUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnExitingHierarchy(IGameManager gameManager)
|
|
||||||
{
|
|
||||||
gameManager.OnUpdate -= OnUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUpdate(IGameManager sender, EngineTime time)
|
|
||||||
{
|
{
|
||||||
for (int i = enumerators.Count - 1; i >= 0; i--)
|
for (int i = enumerators.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
@@ -41,4 +29,6 @@ public class CoroutineManager : HierarchyObject
|
|||||||
enumerators.RemoveAt(i);
|
enumerators.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CoroutineManager() => Priority = int.MinValue;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class CoroutineYield(Func<bool> condition) : ICoroutineYield
|
public class CoroutineYield(Func<bool> condition) : ICoroutineYield
|
||||||
|
30
Engine.Core/Debug/AssertHelpers.cs
Normal file
30
Engine.Core/Debug/AssertHelpers.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public static class Assert
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertInitialized(IInitializable initializable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IBehaviourController)}");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertEntityAssigned(IHasEntity assignable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IEntity)}");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertUniverseAssigned(IHasUniverse assignable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverse)}");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverseObject)}");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
|
||||||
|
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IStateEnable)}");
|
||||||
|
}
|
8
Engine.Core/Debug/ConsoleLogger.cs
Normal file
8
Engine.Core/Debug/ConsoleLogger.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class ConsoleLogger : LoggerBase
|
||||||
|
{
|
||||||
|
protected override void Write(string message) => Console.WriteLine(message);
|
||||||
|
}
|
29
Engine.Core/Debug/FileLogger.cs
Normal file
29
Engine.Core/Debug/FileLogger.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class FileLogger : LoggerBase
|
||||||
|
{
|
||||||
|
public readonly string FilePath;
|
||||||
|
|
||||||
|
protected override void Write(string message)
|
||||||
|
{
|
||||||
|
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileLogger(string filePath)
|
||||||
|
{
|
||||||
|
FilePath = filePath;
|
||||||
|
|
||||||
|
bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0;
|
||||||
|
|
||||||
|
if (isRelativePath)
|
||||||
|
FilePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath));
|
||||||
|
|
||||||
|
if (Path.GetDirectoryName(FilePath) is string directoryPath)
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
|
||||||
|
File.Open(FilePath, FileMode.Create).Close();
|
||||||
|
}
|
||||||
|
}
|
18
Engine.Core/Debug/ILogger.cs
Normal file
18
Engine.Core/Debug/ILogger.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public interface ILogger
|
||||||
|
{
|
||||||
|
static ILogger Shared { get; set; } = new ConsoleLogger();
|
||||||
|
|
||||||
|
Level FilterLevel { get; set; }
|
||||||
|
|
||||||
|
void Log(string message, Level level = Level.Info, bool force = false);
|
||||||
|
|
||||||
|
enum Level
|
||||||
|
{
|
||||||
|
Trace,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
}
|
20
Engine.Core/Debug/LoggerBase.cs
Normal file
20
Engine.Core/Debug/LoggerBase.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public abstract class LoggerBase : ILogger
|
||||||
|
{
|
||||||
|
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Trace;
|
||||||
|
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||||
|
{
|
||||||
|
if (!force && level < FilterLevel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string timestamp = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss tt");
|
||||||
|
|
||||||
|
Write($"[{timestamp}] [{level}] \t{message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void Write(string message);
|
||||||
|
}
|
9
Engine.Core/Debug/LoggerContainer.cs
Normal file
9
Engine.Core/Debug/LoggerContainer.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class LoggerContainer : Behaviour, ILogger
|
||||||
|
{
|
||||||
|
public ILogger Logger { get; set; } = ILogger.Shared;
|
||||||
|
|
||||||
|
public ILogger.Level FilterLevel { get => Logger.FilterLevel; set => Logger.FilterLevel = value; }
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => Logger.Log(message, level, force);
|
||||||
|
}
|
36
Engine.Core/Debug/LoggerExtensions.cs
Normal file
36
Engine.Core/Debug/LoggerExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public static class LoggerExtensions
|
||||||
|
{
|
||||||
|
public static void Log<T>(this ILogger logger, T caller, string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||||
|
{
|
||||||
|
string body = $"{caller?.GetType().Name ?? typeof(T).Name}: {message}";
|
||||||
|
logger.Log(body, level, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogWarning<T>(this ILogger logger, T caller, string message, bool force = false) => Log(logger, caller, message, ILogger.Level.Info, force);
|
||||||
|
|
||||||
|
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
|
||||||
|
{
|
||||||
|
Log(logger, caller, message, ILogger.Level.Error, force);
|
||||||
|
LogTrace(logger, caller, new StackTrace(), force);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
|
||||||
|
{
|
||||||
|
Log(logger, caller, $"Exception of type {exception.GetType().Name} occured", ILogger.Level.Error, force);
|
||||||
|
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
|
||||||
|
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
|
||||||
|
|
||||||
|
// Not using LogTrace because exception.StackTrace is a type of string
|
||||||
|
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}", ILogger.Level.Trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogTrace<T>(this ILogger logger, T caller, StackTrace? stackTrace = null, bool force = false)
|
||||||
|
{
|
||||||
|
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{stackTrace ?? new()}", ILogger.Level.Trace, force);
|
||||||
|
}
|
||||||
|
}
|
23
Engine.Core/Debug/LoggerWrapper.cs
Normal file
23
Engine.Core/Debug/LoggerWrapper.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger
|
||||||
|
{
|
||||||
|
private readonly ILogger firstLogger = firstLogger;
|
||||||
|
private readonly ILogger secondLogger = secondLogger;
|
||||||
|
|
||||||
|
public ILogger.Level FilterLevel
|
||||||
|
{
|
||||||
|
get => firstLogger.FilterLevel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
firstLogger.FilterLevel = value;
|
||||||
|
secondLogger.FilterLevel = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||||
|
{
|
||||||
|
firstLogger.Log(message, level, force);
|
||||||
|
secondLogger.Log(message, level, force);
|
||||||
|
}
|
||||||
|
}
|
6
Engine.Core/Debug/LoggerWrapperExtensions.cs
Normal file
6
Engine.Core/Debug/LoggerWrapperExtensions.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public static class LoggerWrapperExtensions
|
||||||
|
{
|
||||||
|
public static ILogger WrapWith(this ILogger thisLogger, ILogger logger) => new LoggerWrapper(thisLogger, logger);
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>false</ImplicitUsings>
|
<ImplicitUsings>false</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
|
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
|
||||||
|
@@ -6,41 +6,41 @@ skinparam nodesep 100
|
|||||||
|
|
||||||
title Core Engine Relations
|
title Core Engine Relations
|
||||||
|
|
||||||
interface Engine.Core.Abstract.IEntity extends Engine.Core.Abstract.IInitializable {}
|
interface Engine.Core.IEntity extends Engine.Core.IInitializable {}
|
||||||
interface Engine.Core.Abstract.IHierarchyObject extends Engine.Core.Abstract.IEntity, Engine.Core.Abstract.INameable {}
|
interface Engine.Core.IUniverseObject extends Engine.Core.IEntity, Engine.Core.INameable {}
|
||||||
|
|
||||||
interface Engine.Core.Abstract.INameable {}
|
interface Engine.Core.INameable {}
|
||||||
|
|
||||||
Engine.Core.Abstract.IHierarchyObject --> Engine.Core.Abstract.IBehaviourController: has
|
Engine.Core.IUniverseObject --> Engine.Core.IBehaviourController: has
|
||||||
Engine.Core.Abstract.IBehaviourController "1" --> "0..*" Engine.Core.Abstract.IBehaviour: has
|
Engine.Core.IBehaviourController "1" --> "0..*" Engine.Core.IBehaviour: has
|
||||||
|
|
||||||
interface Engine.Core.Abstract.IBehaviourController {}
|
interface Engine.Core.IBehaviourController {}
|
||||||
interface Engine.Core.Abstract.IBehaviour {}
|
interface Engine.Core.IBehaviour {}
|
||||||
interface Engine.Core.Abstract.IBehaviour2D extends Engine.Core.Abstract.IBehaviour {}
|
interface Engine.Core.IBehaviour2D extends Engine.Core.IBehaviour {}
|
||||||
interface Engine.Core.Abstract.IBehaviour3D extends Engine.Core.Abstract.IBehaviour {}
|
interface Engine.Core.IBehaviour3D extends Engine.Core.IBehaviour {}
|
||||||
|
|
||||||
interface Engine.Core.Abstract.IGameManager {}
|
interface Engine.Core.IUniverse {}
|
||||||
Engine.Core.Abstract.IGameManager "1" -r-> "0..*" Engine.Core.Abstract.IHierarchyObject: has
|
Engine.Core.IUniverse "1" -r-> "0..*" Engine.Core.IUniverseObject: has
|
||||||
|
|
||||||
' together {
|
' together {
|
||||||
' interface Engine.Core.Abstract.IAssignable {}
|
' interface Engine.Core.IAssignable {}
|
||||||
' interface Engine.Core.Abstract.IHasStateEnable extends Engine.Core.Abstract.IAssignable {}
|
' interface Engine.Core.IHasStateEnable extends Engine.Core.IAssignable {}
|
||||||
' interface Engine.Core.Abstract.IHasGameManager extends Engine.Core.Abstract.IAssignable {}
|
' interface Engine.Core.IHasUniverse extends Engine.Core.IAssignable {}
|
||||||
' interface Engine.Core.Abstract.IHasHierarchyObject extends Engine.Core.Abstract.IAssignable {}
|
' interface Engine.Core.IHasUniverseObject extends Engine.Core.IAssignable {}
|
||||||
' interface Engine.Core.Abstract.IHasBehaviourController extends Engine.Core.Abstract.IAssignable {}
|
' interface Engine.Core.IHasBehaviourController extends Engine.Core.IAssignable {}
|
||||||
' ' Engine.Core.Abstract.IHasStateEnable --> Engine.Core.Abstract.IStateEnable: has
|
' ' Engine.Core.IHasStateEnable --> Engine.Core.IStateEnable: has
|
||||||
' ' Engine.Core.Abstract.IHasGameManager --> Engine.Core.Abstract.IGameManager: has
|
' ' Engine.Core.IHasUniverse --> Engine.Core.IUniverse: has
|
||||||
' ' Engine.Core.Abstract.IHasHierarchyObject --> Engine.Core.Abstract.IHierarchyObject: has
|
' ' Engine.Core.IHasUniverseObject --> Engine.Core.IUniverseObject: has
|
||||||
' ' Engine.Core.Abstract.IHasBehaviourController --> Engine.Core.Abstract.IBehaviourController: has
|
' ' Engine.Core.IHasBehaviourController --> Engine.Core.IBehaviourController: has
|
||||||
' }
|
' }
|
||||||
|
|
||||||
together {
|
together {
|
||||||
interface Engine.Core.Abstract.ITransform2D {}
|
interface Engine.Core.ITransform2D {}
|
||||||
interface Engine.Core.Abstract.ICamera2D {}
|
interface Engine.Core.ICamera2D {}
|
||||||
interface Engine.Core.Abstract.ICoroutineYield {}
|
interface Engine.Core.ICoroutineYield {}
|
||||||
interface Engine.Core.Abstract.IStateEnable {}
|
interface Engine.Core.IStateEnable {}
|
||||||
interface Engine.Core.Abstract.IInitializable {}
|
interface Engine.Core.IInitializable {}
|
||||||
interface Engine.Core.Abstract.IBehaviourCollector {}
|
interface Engine.Core.IBehaviourCollector {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
|
@@ -1,9 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class BehaviourNotFoundException(string? message) : Exception(message)
|
public class BehaviourNotFoundException(string? message) : NotFoundException(message);
|
||||||
{
|
|
||||||
public static NotAssignedException FromType<TBehaviour>()
|
|
||||||
=> new($"{typeof(TBehaviour).FullName} was not found");
|
|
||||||
}
|
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Exceptions;
|
|
||||||
|
|
||||||
public class HierarchyObjectNotFoundException(string? message) : Exception(message)
|
|
||||||
{
|
|
||||||
public static NotAssignedException FromType<THierarchyObject>()
|
|
||||||
=> new($"{typeof(THierarchyObject).FullName} was not found");
|
|
||||||
}
|
|
9
Engine.Core/Exceptions/NotFoundException.cs
Normal file
9
Engine.Core/Exceptions/NotFoundException.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
public class NotFoundException(string? message) : Exception(message)
|
||||||
|
{
|
||||||
|
public static NotAssignedException FromType<T>()
|
||||||
|
=> new($"{typeof(T).FullName} was not found");
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
public class UniverseObjectNotFoundException(string? message) : NotFoundException(message);
|
@@ -1,6 +1,6 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
@@ -27,7 +27,7 @@ public static class BehaviourControllerExtensions
|
|||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
|
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
|
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
|
||||||
@@ -40,12 +40,25 @@ public static class BehaviourControllerExtensions
|
|||||||
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
|
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively.
|
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns the fallback type if it doesn't exist.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TOriginal">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
|
/// <typeparam name="TFallback">The type of <see cref="IBehaviour"/> to add. It must be assignable from <typeparamref name="TOriginal"/></typeparam>
|
||||||
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to search in.</param>
|
||||||
|
/// <param name="args">Optional arguments to pass to the constructor of the <see cref="IBehaviour"/> if a new one is added.</param>
|
||||||
|
/// <returns>The existing or newly added <see cref="IBehaviour"/> of the specified type.</returns>
|
||||||
|
public static TOriginal GetOrAddBehaviour<TOriginal, TFallback>(this IBehaviourController behaviourController, params object?[]? args)
|
||||||
|
where TOriginal : class
|
||||||
|
where TFallback : class, IBehaviour, TOriginal
|
||||||
|
=> behaviourController.GetBehaviour<TOriginal>() ?? behaviourController.AddBehaviour<TFallback>(args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
||||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the parent hierarchy; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the parent universe; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool TryGetBehaviourInParent<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
public static bool TryGetBehaviourInParent<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
{
|
{
|
||||||
behaviour = GetBehaviourInParent<T>(behaviourController);
|
behaviour = GetBehaviourInParent<T>(behaviourController);
|
||||||
@@ -53,7 +66,7 @@ public static class BehaviourControllerExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively.
|
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
@@ -67,28 +80,49 @@ public static class BehaviourControllerExtensions
|
|||||||
if (controller.GetBehaviour<T>() is T behaviour)
|
if (controller.GetBehaviour<T>() is T behaviour)
|
||||||
return behaviour;
|
return behaviour;
|
||||||
|
|
||||||
controller = controller.HierarchyObject.Parent?.BehaviourController;
|
controller = controller.UniverseObject.Parent?.BehaviourController;
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively. Throws an error if not found.
|
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively. Throws an error if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
|
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s children recursively.
|
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||||
|
/// <param name="behavioursInParent">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||||
|
public static void GetBehavioursInParent<T>(this IBehaviourController behaviourController, IList<T> behavioursInParent) where T : class
|
||||||
|
{
|
||||||
|
IBehaviourController? controller = behaviourController;
|
||||||
|
List<T> cache = [];
|
||||||
|
behavioursInParent.Clear();
|
||||||
|
|
||||||
|
while (controller is not null)
|
||||||
|
{
|
||||||
|
controller.GetBehaviours(cache);
|
||||||
|
foreach (T behaviour in cache)
|
||||||
|
behavioursInParent.Add(behaviour);
|
||||||
|
|
||||||
|
controller = controller.UniverseObject.Parent?.BehaviourController;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
||||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the child hierarchy; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the child universe; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool TryGetBehaviourInChildren<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
public static bool TryGetBehaviourInChildren<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
{
|
{
|
||||||
behaviour = GetBehaviourInChildren<T>(behaviourController);
|
behaviour = GetBehaviourInChildren<T>(behaviourController);
|
||||||
@@ -96,7 +130,7 @@ public static class BehaviourControllerExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s children recursively.
|
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
@@ -106,7 +140,7 @@ public static class BehaviourControllerExtensions
|
|||||||
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
|
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
|
||||||
return localBehaviour;
|
return localBehaviour;
|
||||||
|
|
||||||
foreach (IHierarchyObject child in behaviourController.HierarchyObject)
|
foreach (IUniverseObject child in behaviourController.UniverseObject.Children)
|
||||||
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
||||||
return behaviour;
|
return behaviour;
|
||||||
|
|
||||||
@@ -120,5 +154,29 @@ public static class BehaviourControllerExtensions
|
|||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
|
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||||
|
/// <param name="behavioursInChildren">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||||
|
public static void GetBehavioursInChildren<T>(this IBehaviourController behaviourController, IList<T> behavioursInChildren) where T : class
|
||||||
|
{
|
||||||
|
List<T> cache = [];
|
||||||
|
behavioursInChildren.Clear();
|
||||||
|
|
||||||
|
TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TraverseChildrenForBehaviour<T>(IUniverseObject universeObject, IList<T> behaviours, IList<T> cache) where T : class
|
||||||
|
{
|
||||||
|
universeObject.BehaviourController.GetBehaviours(cache);
|
||||||
|
|
||||||
|
foreach (T behaviour in cache)
|
||||||
|
behaviours.Add(behaviour);
|
||||||
|
|
||||||
|
foreach (IUniverseObject child in universeObject.Children)
|
||||||
|
TraverseChildrenForBehaviour(child, behaviours, cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public static class BehaviourExtensions
|
|
||||||
{
|
|
||||||
public static T? FindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
|
|
||||||
{
|
|
||||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
|
||||||
if (hierarchyObject.BehaviourController.GetBehaviour<T>() is T behaviour)
|
|
||||||
return behaviour;
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryFindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
|
||||||
{
|
|
||||||
behaviour = FindBehaviour<T>(hierarchyObjects);
|
|
||||||
return behaviour is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FindBehaviours<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
|
|
||||||
{
|
|
||||||
behaviours.Clear();
|
|
||||||
List<T> cache = [];
|
|
||||||
|
|
||||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
|
||||||
{
|
|
||||||
hierarchyObject.BehaviourController.GetBehaviours(cache);
|
|
||||||
behaviours.AddRange(cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,16 +0,0 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public static class GameManagerExtensions
|
|
||||||
{
|
|
||||||
public static IHierarchyObject InstantiateHierarchyObject(this IGameManager gameManager, params object?[]? args)
|
|
||||||
=> gameManager.InstantiateHierarchyObject<HierarchyObject>(args);
|
|
||||||
|
|
||||||
public static T GetRequiredHierarchyObject<T>(this IGameManager gameManager) where T : class
|
|
||||||
=> gameManager.GetHierarchyObject<T>() ?? throw new HierarchyObjectNotFoundException($"{gameManager.GetType().FullName}({gameManager.Id}) does not contain any {nameof(IHierarchyObject)} object of type {typeof(T).FullName}");
|
|
||||||
|
|
||||||
public static T FindRequiredBehaviour<T>(this IGameManager gameManager) where T : class
|
|
||||||
=> gameManager.FindBehaviour<T>() ?? throw new BehaviourNotFoundException($"{gameManager.GetType().FullName}({gameManager.Id}) does not contain any {nameof(IHierarchyObject)} with {nameof(IBehaviour)} of type {typeof(T).FullName}");
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public static class HierarchyObjectExtensions
|
|
||||||
{
|
|
||||||
public static T SetHierarchyObject<T>(this T hierarchyObject, string? name = "", IHierarchyObject? parent = null) where T : IHierarchyObject
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(name))
|
|
||||||
hierarchyObject.Name = name;
|
|
||||||
if (parent is not null)
|
|
||||||
hierarchyObject.SetParent(parent);
|
|
||||||
return hierarchyObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T? GetHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
|
|
||||||
{
|
|
||||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
|
||||||
if (hierarchyObject is T @object)
|
|
||||||
return @object;
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
|
||||||
{
|
|
||||||
behaviour = GetHierarchyObject<T>(hierarchyObjects);
|
|
||||||
return behaviour is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void GetHierarchyObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
|
|
||||||
{
|
|
||||||
behaviours.Clear();
|
|
||||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
|
||||||
if (hierarchyObject is T @object)
|
|
||||||
behaviours.Add(@object);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,3 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public static class TransformExtensions
|
public static class TransformExtensions
|
||||||
|
36
Engine.Core/Extensions/UniverseExtensions.cs
Normal file
36
Engine.Core/Extensions/UniverseExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public static class UniverseExtensions
|
||||||
|
{
|
||||||
|
public static IUniverseObject InstantiateUniverseObject(this IUniverse universe, params object?[]? args)
|
||||||
|
=> universe.InstantiateUniverseObject<UniverseObject>(args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through all <see cref="IUniverseObject"/>s to find the specified instance of the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type to be searched through the <see cref="IUniverse"/>.</typeparam>
|
||||||
|
/// <returns>The specified type if found; otherwise, throws <see cref="UniverseObjectNotFoundException"/>.</returns>
|
||||||
|
public static T GetRequiredUniverseObject<T>(this IUniverse universe) where T : class
|
||||||
|
=> universe.GetUniverseObject<T>() ?? throw new UniverseObjectNotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} object of type {typeof(T).FullName}");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through all <see cref="IBehaviours"/>s to find the specified instance of the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type to be searched through the <see cref="IUniverse"/>.</typeparam>
|
||||||
|
/// <returns>The specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
|
public static T FindRequiredBehaviour<T>(this IUniverse universe) where T : class
|
||||||
|
=> universe.FindBehaviour<T>() ?? throw new BehaviourNotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} with {nameof(IBehaviour)} of type {typeof(T).FullName}");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through all <see cref="IUniverseObject"/>s and <see cref="IBehaviours"/>s to find the specified instance of the type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// WARNING: This is more expensive compared to <see cref="GetRequiredUniverseObject{T}(IUniverse)"/> or <see cref="FindRequiredBehaviour{T}(IUniverse)"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="T">Type to be searched through the <see cref="IUniverse"/>.</typeparam>
|
||||||
|
/// <returns>The specified type if found; otherwise, throws <see cref="NotFoundException"/>.</returns>
|
||||||
|
public static T FindRequired<T>(this IUniverse universe) where T : class
|
||||||
|
=> universe.Find<T>() ?? throw new NotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} or {nameof(IBehaviour)} of type {typeof(T).FullName}");
|
||||||
|
}
|
255
Engine.Core/Extensions/UniverseObjectExtensions.cs
Normal file
255
Engine.Core/Extensions/UniverseObjectExtensions.cs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public static class UniverseObjectExtensions
|
||||||
|
{
|
||||||
|
public static T SetUniverseObject<T>(this T universeObject, string? name = "", IUniverseObject? parent = null) where T : IUniverseObject
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
|
universeObject.Name = name;
|
||||||
|
if (parent is not null)
|
||||||
|
universeObject.Parent = parent;
|
||||||
|
return universeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Universe Object Search
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="IUniverseObject"/> of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
|
||||||
|
/// <returns>The first found <see cref="IUniverseObject"/> of the specified type; otherwise, null.</returns>
|
||||||
|
public static T? GetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
|
||||||
|
{
|
||||||
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
|
if (universeObject is T @object)
|
||||||
|
return @object;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a <see cref="IUniverseObject"/> of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
|
||||||
|
/// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the universe objects; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool TryGetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? universeObject) where T : class
|
||||||
|
{
|
||||||
|
universeObject = GetUniverseObject<T>(universeObjects);
|
||||||
|
return universeObject is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through the provided <see cref="IUniverseObject"/>s to collect a list of <see cref="IUniverseObject"/>s of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to search.</param>
|
||||||
|
/// <returns>The found <see cref="IUniverseObject"/>s of the specified types</returns>
|
||||||
|
public static void GetUniverseObjects<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> foundUniverseObjects) where T : class
|
||||||
|
{
|
||||||
|
foundUniverseObjects.Clear();
|
||||||
|
|
||||||
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
|
if (universeObject is T @object)
|
||||||
|
foundUniverseObjects.Add(@object);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Universe Object Search In Parent
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a <see cref="IUniverseObject"/> of the specified type in it's parents recursively.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
|
||||||
|
/// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the parent universe objects; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool TryGetUniverseObjectInParent<T>(this IUniverseObject universeObject, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
|
{
|
||||||
|
behaviour = GetUniverseObjectInParent<T>(universeObject);
|
||||||
|
return behaviour is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="IUniverseObject"/> of the specified type in it's parents recursively.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
|
||||||
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
||||||
|
public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
|
||||||
|
{
|
||||||
|
if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
|
||||||
|
return localUniverseObject;
|
||||||
|
|
||||||
|
IUniverseObject? parent = universeObject;
|
||||||
|
|
||||||
|
while (parent is not null)
|
||||||
|
{
|
||||||
|
if (parent is T behaviour)
|
||||||
|
return behaviour;
|
||||||
|
|
||||||
|
parent = universeObject.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="IUniverseObject"/> of the specified type in the parents recursively. Throws an error if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
|
||||||
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, throws <see cref="UniverseObjectNotFoundException"/>.</returns>
|
||||||
|
public static T GetRequiredUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
|
||||||
|
=> universeObject.GetUniverseObjectInParent<T>() ?? throw new UniverseObjectNotFoundException($"{universeObject.Name}'s {nameof(IUniverseObject)} does not contain any {typeof(T).FullName} on any parent ");
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Universe Object Search In Children
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a <see cref="IUniverseObject"/> of the specified type in it's children recursively.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
|
||||||
|
/// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the child universe objects; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool TryGetUniverseObjectInChildren<T>(this IUniverseObject universeObject, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
|
{
|
||||||
|
behaviour = GetUniverseObjectInChildren<T>(universeObject);
|
||||||
|
return behaviour is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="IUniverseObject"/> of the specified type in it's children recursively.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
|
||||||
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
||||||
|
public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
|
||||||
|
{
|
||||||
|
if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
|
||||||
|
return localUniverseObject;
|
||||||
|
|
||||||
|
foreach (IUniverseObject child in universeObject.Children)
|
||||||
|
if (GetUniverseObjectInChildren<T>(child) is T behaviour)
|
||||||
|
return behaviour;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="IUniverseObject"/> of the specified type in the children recursively. Throws an error if not found.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
|
||||||
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, throws <see cref="UniverseObjectNotFoundException"/>.</returns>
|
||||||
|
public static T GetRequiredUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
|
||||||
|
=> universeObject.GetUniverseObjectInChildren<T>() ?? throw new UniverseObjectNotFoundException($"{universeObject.Name}'s {nameof(IUniverseObject)} does not contain any {typeof(T).FullName} on any children ");
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behaviour Search
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a <see cref="IBehaviour"/> of the specified type in the provided <see cref="IUniverseObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
|
||||||
|
/// <returns>The first found <see cref="IBehaviour"/> of the specified type; otherwise, null.</returns>
|
||||||
|
public static T? FindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
|
||||||
|
{
|
||||||
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
|
if (universeObject.BehaviourController.GetBehaviour<T>() is T behaviour)
|
||||||
|
return behaviour;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find a <see cref="IBehaviour"/> of the specified type in the provided <see cref="IUniverseObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
|
||||||
|
/// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
|
||||||
|
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the provided <see cref="IUniverseObject"/>s; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool TryFindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
|
{
|
||||||
|
behaviour = FindBehaviour<T>(universeObjects);
|
||||||
|
return behaviour is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through the provided <see cref="IUniverseObject"/>s to collect a list of <see cref="IBehaviour"/>s of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
|
/// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
|
||||||
|
public static void FindBehaviours<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> behaviours) where T : class
|
||||||
|
{
|
||||||
|
behaviours.Clear();
|
||||||
|
List<T> cache = [];
|
||||||
|
|
||||||
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
|
{
|
||||||
|
universeObject.BehaviourController.GetBehaviours(cache);
|
||||||
|
foreach (T behaviour in cache)
|
||||||
|
behaviours.Add(behaviour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region General Search
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an object of the specified type in the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// WARNING: This is more expensive compared to <see cref="GetUniverseObject{T}(IEnumerable{IUniverseObject})"/> or <see cref="FindBehaviour{T}(IEnumerable{IUniverseObject})"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
|
||||||
|
/// <returns>The first found instance of the specified type; otherwise, null.</returns>
|
||||||
|
public static T? Find<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
|
||||||
|
{
|
||||||
|
if (universeObjects.GetUniverseObject<T>() is T foundUniverseObject)
|
||||||
|
return foundUniverseObject;
|
||||||
|
|
||||||
|
if (universeObjects.FindBehaviour<T>() is T foundBehaviour)
|
||||||
|
return foundBehaviour;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find an object of the specified type in the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// WARNING: This is more expensive compared to <see cref="TryGetUniverseObject{T}(IEnumerable{IUniverseObject}, out T?)"/> or <see cref="TryFindBehaviour{T}(IEnumerable{IUniverseObject}, out T?)"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
|
||||||
|
/// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
|
||||||
|
/// <returns><see cref="true"/> if an object of the specified type was found in the provided <see cref="IUniverseObject"/>s; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool TryFind<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||||
|
{
|
||||||
|
behaviour = Find<T>(universeObjects);
|
||||||
|
return behaviour is not null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches through the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s to collect a list of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// WARNING: This is more expensive compared to <see cref="GetUniverseObjects{T}(IEnumerable{IUniverseObject}, IList{T})"/> or <see cref="FindBehaviours{T}(IEnumerable{IUniverseObject}, IList{T})"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||||
|
/// <param name="instances">List of objects found wit the specified type.</param>
|
||||||
|
/// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
|
||||||
|
public static void Find<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> instances) where T : class
|
||||||
|
{
|
||||||
|
instances.Clear();
|
||||||
|
List<T> cache = [];
|
||||||
|
|
||||||
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
|
{
|
||||||
|
universeObject.Children.Find(cache);
|
||||||
|
foreach (T behaviour in cache)
|
||||||
|
instances.Add(behaviour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
@@ -1,23 +1,33 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
public class BehaviourControllerFactory
|
public class BehaviourControllerFactory
|
||||||
{
|
{
|
||||||
public static IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
|
public static IBehaviourController Instantiate(IUniverseObject universeObject, IStateEnable? stateEnable = null)
|
||||||
=> Instantiate<BehaviourController>(hierarchyObject);
|
=> Instantiate<BehaviourController>(universeObject, stateEnable);
|
||||||
|
|
||||||
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
|
public static T Instantiate<T>(IUniverseObject universeObject, IStateEnable? stateEnable = null, params object?[]? args)
|
||||||
where T : class, IBehaviourController
|
where T : class, IBehaviourController
|
||||||
{
|
{
|
||||||
T behaviourController = TypeFactory.Get<T>(args);
|
T behaviourController = TypeFactory.Get<T>(args);
|
||||||
|
|
||||||
if (!hierarchyObject.Assign(behaviourController))
|
if (!universeObject.Assign(behaviourController))
|
||||||
throw AssignFailedException.From(hierarchyObject, behaviourController);
|
throw AssignFailedException.From(universeObject, behaviourController);
|
||||||
|
|
||||||
if (!behaviourController.Assign(hierarchyObject))
|
if (!behaviourController.Assign(universeObject))
|
||||||
throw AssignFailedException.From(behaviourController, hierarchyObject);
|
throw AssignFailedException.From(behaviourController, universeObject);
|
||||||
|
|
||||||
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
|
if (!stateEnable.Assign(behaviourController))
|
||||||
|
throw AssignFailedException.From(stateEnable, behaviourController);
|
||||||
|
|
||||||
|
if (!behaviourController.Assign(stateEnable))
|
||||||
|
throw AssignFailedException.From(behaviourController, stateEnable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
StateEnableFactory.Instantiate(behaviourController);
|
||||||
|
|
||||||
return behaviourController;
|
return behaviourController;
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,26 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
public class BehaviourFactory
|
public class BehaviourFactory
|
||||||
{
|
{
|
||||||
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
|
public static T Instantiate<T>(params object?[]? args) where T : class, IBehaviour
|
||||||
=> Instantiate<T>(hierarchyObject, stateEnable: null, args);
|
=> Instantiate<T>(stateEnable: null, args);
|
||||||
|
|
||||||
public static T Instantiate<T>(IHierarchyObject hierarchyObject, IStateEnable? stateEnable, params object?[]? args)
|
public static T Instantiate<T>(IStateEnable? stateEnable, params object?[]? args)
|
||||||
where T : class, IBehaviour
|
where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
T behaviour = TypeFactory.Get<T>(args);
|
T behaviour = TypeFactory.Get<T>(args);
|
||||||
|
|
||||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
if (!stateEnable.Assign(behaviour))
|
if (!stateEnable.Assign(behaviour))
|
||||||
throw AssignFailedException.From(stateEnable, behaviour);
|
throw AssignFailedException.From(stateEnable, behaviour);
|
||||||
|
|
||||||
if (!behaviour.Assign(stateEnable))
|
if (!behaviour.Assign(stateEnable))
|
||||||
throw AssignFailedException.From(behaviour, stateEnable);
|
throw AssignFailedException.From(behaviour, stateEnable);
|
||||||
if (!behaviour.Assign(hierarchyObject.BehaviourController))
|
}
|
||||||
throw AssignFailedException.From(behaviour, hierarchyObject.BehaviourController);
|
else
|
||||||
|
StateEnableFactory.Instantiate(behaviour);
|
||||||
|
|
||||||
return behaviour;
|
return behaviour;
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
|
||||||
|
|
||||||
public class HierarchyObjectFactory
|
|
||||||
{
|
|
||||||
public static IHierarchyObject Instantiate() => Instantiate<HierarchyObject>();
|
|
||||||
public static T Instantiate<T>(params object?[]? args) where T : class, IHierarchyObject
|
|
||||||
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
|
|
||||||
|
|
||||||
public static IHierarchyObject Instantiate(IBehaviourController? behaviourController = null, IStateEnable? stateEnable = null) => Instantiate<HierarchyObject>(behaviourController, stateEnable);
|
|
||||||
public static T Instantiate<T>(
|
|
||||||
IBehaviourController? behaviourController = null,
|
|
||||||
IStateEnable? stateEnable = null,
|
|
||||||
params object?[]? args
|
|
||||||
)
|
|
||||||
where T : class, IHierarchyObject
|
|
||||||
{
|
|
||||||
T hierarchyObject = TypeFactory.Get<T>(args);
|
|
||||||
|
|
||||||
behaviourController ??= TypeFactory.Get<BehaviourController>();
|
|
||||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
|
||||||
|
|
||||||
if (!behaviourController.Assign(hierarchyObject))
|
|
||||||
throw AssignFailedException.From(behaviourController, hierarchyObject);
|
|
||||||
if (!stateEnable.Assign(hierarchyObject))
|
|
||||||
throw AssignFailedException.From(stateEnable, hierarchyObject);
|
|
||||||
|
|
||||||
if (!hierarchyObject.Assign(behaviourController))
|
|
||||||
throw AssignFailedException.From(hierarchyObject, behaviourController);
|
|
||||||
if (!hierarchyObject.Assign(stateEnable))
|
|
||||||
throw AssignFailedException.From(hierarchyObject, stateEnable);
|
|
||||||
|
|
||||||
return hierarchyObject;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,4 +1,3 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
public class TransformFactory
|
public class TransformFactory
|
||||||
|
@@ -1,21 +1,59 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
public static class TypeFactory
|
public static class TypeFactory
|
||||||
{
|
{
|
||||||
public static T Get<T>(params object?[]? args) where T : class
|
private static readonly ConcurrentDictionary<string, Type> registeredTypes = [];
|
||||||
|
|
||||||
|
public static string GetTypeName(Type type) => type.FullName ?? throw new ArgumentException($"{type.Name} must be a resolvable type");
|
||||||
|
|
||||||
|
public static T Get<T>(params object?[]? args) where T : class => (T)Get(typeof(T), args);
|
||||||
|
public static object Get(string fullName, params object?[]? args) => Get(GetType(fullName), args);
|
||||||
|
public static object Get(Type type, params object?[]? args)
|
||||||
{
|
{
|
||||||
T? result;
|
object? result;
|
||||||
|
|
||||||
if (args is not null && args.Length != 0)
|
if (args is not null && args.Length != 0)
|
||||||
result = Activator.CreateInstance(typeof(T), args) as T;
|
result = Activator.CreateInstance(type, args);
|
||||||
else
|
else
|
||||||
result = Activator.CreateInstance(typeof(T)) as T;
|
result = Activator.CreateInstance(type);
|
||||||
|
|
||||||
if (result is null)
|
if (result is null)
|
||||||
throw new Exception($"{typeof(T).Name} of type {typeof(T).Name} could not be created.");
|
throw new Exception($"Type {type.Name} could not be created.");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type GetType(string fullName)
|
||||||
|
{
|
||||||
|
if (registeredTypes.TryGetValue(fullName, out Type? result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
ReloadTypes();
|
||||||
|
|
||||||
|
if (registeredTypes.TryGetValue(fullName, out Type? reloadedType))
|
||||||
|
return reloadedType;
|
||||||
|
|
||||||
|
throw new Exception($"Type {fullName} could not be found in the current domain.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReloadTypes()
|
||||||
|
{
|
||||||
|
registeredTypes.Clear();
|
||||||
|
|
||||||
|
IEnumerable<Type> domainTypes = AppDomain.CurrentDomain
|
||||||
|
.GetAssemblies()
|
||||||
|
.SelectMany(a => a.GetTypes());
|
||||||
|
|
||||||
|
// TODO: Replace this
|
||||||
|
// There are some system & compiler generated types with duplicated names,
|
||||||
|
// it is ugly it will cause headaches in the future because it will not
|
||||||
|
// throw an error if there's a type with an unintended duplicate name
|
||||||
|
foreach (Type type in domainTypes)
|
||||||
|
registeredTypes.TryAdd(GetTypeName(type), type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
Engine.Core/Factory/UniverseObjectFactory.cs
Normal file
43
Engine.Core/Factory/UniverseObjectFactory.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
|
public class UniverseObjectFactory
|
||||||
|
{
|
||||||
|
public static IUniverseObject Instantiate() => Instantiate<UniverseObject>();
|
||||||
|
public static T Instantiate<T>(params object?[]? args) where T : class, IUniverseObject
|
||||||
|
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
|
||||||
|
|
||||||
|
public static IUniverseObject Instantiate(IBehaviourController? behaviourController = null, IStateEnable? stateEnable = null) => Instantiate<UniverseObject>(behaviourController, stateEnable);
|
||||||
|
public static T Instantiate<T>(
|
||||||
|
IBehaviourController? behaviourController = null,
|
||||||
|
IStateEnable? stateEnable = null,
|
||||||
|
params object?[]? args
|
||||||
|
)
|
||||||
|
where T : class, IUniverseObject
|
||||||
|
{
|
||||||
|
T universeObject = TypeFactory.Get<T>(args);
|
||||||
|
|
||||||
|
if (behaviourController is not null)
|
||||||
|
{
|
||||||
|
if (!behaviourController.Assign(universeObject))
|
||||||
|
throw AssignFailedException.From(behaviourController, universeObject);
|
||||||
|
if (!universeObject.Assign(behaviourController))
|
||||||
|
throw AssignFailedException.From(universeObject, behaviourController);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BehaviourControllerFactory.Instantiate(universeObject);
|
||||||
|
|
||||||
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
|
if (!stateEnable.Assign(universeObject))
|
||||||
|
throw AssignFailedException.From(stateEnable, universeObject);
|
||||||
|
if (!universeObject.Assign(stateEnable))
|
||||||
|
throw AssignFailedException.From(universeObject, stateEnable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
StateEnableFactory.Instantiate(universeObject);
|
||||||
|
|
||||||
|
return universeObject;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,142 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
using Syntriax.Engine.Core.Helpers;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("HierarchyObject Count: {_hierarchyObjects.Count}")]
|
|
||||||
public class GameManager : BaseEntity, IGameManager
|
|
||||||
{
|
|
||||||
public event IGameManager.UpdateEventHandler? OnPreUpdate = null;
|
|
||||||
public event IGameManager.UpdateEventHandler? OnUpdate = null;
|
|
||||||
public event IGameManager.PreDrawEventHandler? OnPreDraw = null;
|
|
||||||
|
|
||||||
public event IGameManager.HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
|
|
||||||
public event IGameManager.HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
|
|
||||||
|
|
||||||
private readonly List<IHierarchyObject> _hierarchyObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
|
||||||
private float _timeScale = 1f;
|
|
||||||
|
|
||||||
public IReadOnlyList<IHierarchyObject> HierarchyObjects => _hierarchyObjects;
|
|
||||||
|
|
||||||
public EngineTime Time { get; private set; } = new();
|
|
||||||
public EngineTime UnscaledTime { get; private set; } = new();
|
|
||||||
public float TimeScale
|
|
||||||
{
|
|
||||||
get => _timeScale;
|
|
||||||
set => _timeScale = value.Max(0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Register(IHierarchyObject hierarchyObject)
|
|
||||||
{
|
|
||||||
if (_hierarchyObjects.Contains(hierarchyObject))
|
|
||||||
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is already registered to the {nameof(GameManager)}.");
|
|
||||||
|
|
||||||
hierarchyObject.OnFinalized += OnHierarchyObjectFinalize;
|
|
||||||
hierarchyObject.OnExitedHierarchy += OnHierarchyObjectExitedHierarchy;
|
|
||||||
|
|
||||||
if (!hierarchyObject.Initialize())
|
|
||||||
throw new Exception($"{hierarchyObject.Name} can't be initialized");
|
|
||||||
|
|
||||||
for (int i = 0; i < hierarchyObject.Children.Count; i++)
|
|
||||||
Register(hierarchyObject.Children[i]);
|
|
||||||
|
|
||||||
_hierarchyObjects.Add(hierarchyObject);
|
|
||||||
|
|
||||||
if (!hierarchyObject.EnterHierarchy(this))
|
|
||||||
throw new Exception($"{hierarchyObject.Name} can't enter the hierarchy");
|
|
||||||
|
|
||||||
OnHierarchyObjectRegistered?.Invoke(this, hierarchyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject
|
|
||||||
{
|
|
||||||
T hierarchyObject = Factory.HierarchyObjectFactory.Instantiate<T>(args);
|
|
||||||
Register(hierarchyObject);
|
|
||||||
return hierarchyObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(IHierarchyObject hierarchyObject)
|
|
||||||
{
|
|
||||||
hierarchyObject.SetParent(null);
|
|
||||||
RemoveIncursive(hierarchyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveIncursive(IHierarchyObject hierarchyObject)
|
|
||||||
{
|
|
||||||
if (!_hierarchyObjects.Contains(hierarchyObject))
|
|
||||||
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is not registered to the {nameof(GameManager)}.");
|
|
||||||
|
|
||||||
hierarchyObject.OnFinalized -= OnHierarchyObjectFinalize;
|
|
||||||
hierarchyObject.OnExitedHierarchy -= OnHierarchyObjectExitedHierarchy;
|
|
||||||
|
|
||||||
for (int i = hierarchyObject.Children.Count - 1; i >= 0; i--)
|
|
||||||
Remove(hierarchyObject.Children[i]);
|
|
||||||
|
|
||||||
_hierarchyObjects.Remove(hierarchyObject);
|
|
||||||
|
|
||||||
if (!hierarchyObject.ExitHierarchy())
|
|
||||||
throw new Exception($"{hierarchyObject.Name} can't exit the hierarchy");
|
|
||||||
|
|
||||||
if (!hierarchyObject.Finalize())
|
|
||||||
throw new Exception($"{hierarchyObject.Name} can't be finalized");
|
|
||||||
|
|
||||||
OnHierarchyObjectUnRegistered?.Invoke(this, hierarchyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void InitializeInternal()
|
|
||||||
{
|
|
||||||
foreach (IHierarchyObject hierarchyObject in HierarchyObjects)
|
|
||||||
hierarchyObject.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void FinalizeInternal()
|
|
||||||
{
|
|
||||||
base.FinalizeInternal();
|
|
||||||
for (int i = HierarchyObjects.Count; i >= 0; i--)
|
|
||||||
HierarchyObjects[i].Finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(EngineTime engineTime)
|
|
||||||
{
|
|
||||||
AssertHelpers.AssertInitialized(this);
|
|
||||||
|
|
||||||
UnscaledTime = engineTime;
|
|
||||||
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
|
|
||||||
|
|
||||||
OnPreUpdate?.Invoke(this, Time);
|
|
||||||
|
|
||||||
for (int i = 0; i < HierarchyObjects.Count; i++)
|
|
||||||
HierarchyObjects[i].BehaviourController.Update();
|
|
||||||
|
|
||||||
OnUpdate?.Invoke(this, Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PreDraw()
|
|
||||||
{
|
|
||||||
AssertHelpers.AssertInitialized(this);
|
|
||||||
|
|
||||||
for (int i = 0; i < HierarchyObjects.Count; i++)
|
|
||||||
HierarchyObjects[i].BehaviourController.UpdatePreDraw();
|
|
||||||
|
|
||||||
OnPreDraw?.Invoke(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHierarchyObjectFinalize(IInitializable initializable)
|
|
||||||
{
|
|
||||||
if (initializable is IHierarchyObject hierarchyObject)
|
|
||||||
Remove(hierarchyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHierarchyObjectExitedHierarchy(IHierarchyObject sender, IGameManager gameManager)
|
|
||||||
{
|
|
||||||
if (sender is IHierarchyObject hierarchyObject)
|
|
||||||
Remove(hierarchyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<IHierarchyObject> GetEnumerator() => _hierarchyObjects.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _hierarchyObjects.GetEnumerator();
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Helpers;
|
|
||||||
|
|
||||||
public class AssertHelpers
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertInitialized(IInitializable initializable)
|
|
||||||
=> Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
|
|
||||||
=> Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertEntityAssigned(IHasEntity assignable)
|
|
||||||
=> Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertGameManagerAssigned(IHasGameManager assignable)
|
|
||||||
=> Debug.Assert(assignable.GameManager is not null, $"{assignable.GetType().Name} must be initialized");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertHierarchyObjectAssigned(IHasHierarchyObject assignable)
|
|
||||||
=> Debug.Assert(assignable.HierarchyObject is not null, $"{assignable.GetType().Name} must be initialized");
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
|
|
||||||
=> Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized");
|
|
||||||
}
|
|
500
Engine.Core/Helpers/Event.cs
Normal file
500
Engine.Core/Helpers/Event.cs
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a simple event with no parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event MyEvent = new();
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke();
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered()
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"Event occurred!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime()
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"Event called once!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event called once!
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event occurred!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
public class Event
|
||||||
|
{
|
||||||
|
// We use Ascending order because draw calls are running from last to first
|
||||||
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
|
private ILogger _logger = ILogger.Shared;
|
||||||
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
|
|
||||||
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
|
public void Invoke()
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
try { listeners[i].Callback.Invoke(); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}()";
|
||||||
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
try { onceListeners[i].Callback.Invoke(); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}()";
|
||||||
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||||
|
{
|
||||||
|
listeners = new(initialListenerCount);
|
||||||
|
onceListeners = new(initialOnceListenerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event()
|
||||||
|
{
|
||||||
|
listeners = new(4);
|
||||||
|
onceListeners = new(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void EventHandler();
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an event with only sender parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event<MyBehaviour> MyEvent = new();
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke(this);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered(MyBehaviour sender)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event occurred!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime(MyBehaviour sender)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event called once!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event called once!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSender">Sender type</typeparam>
|
||||||
|
public class Event<TSender> where TSender : class
|
||||||
|
{
|
||||||
|
// We use Ascending order because draw calls are running from last to first
|
||||||
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
|
private ILogger _logger = ILogger.Shared;
|
||||||
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
|
|
||||||
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The caller that's triggering this event.</param>
|
||||||
|
public void Invoke(TSender sender)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
try { listeners[i].Callback.Invoke(sender); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender})";
|
||||||
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
try { onceListeners[i].Callback.Invoke(sender); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender})";
|
||||||
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||||
|
{
|
||||||
|
listeners = new(initialListenerCount);
|
||||||
|
onceListeners = new(initialOnceListenerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event()
|
||||||
|
{
|
||||||
|
listeners = new(4);
|
||||||
|
onceListeners = new(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void EventHandler(TSender sender);
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an event with sender and argument parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event<MyBehaviour, MyArguments> MyEvent = new();
|
||||||
|
///
|
||||||
|
/// private int myInt = 0;
|
||||||
|
/// private bool myBool = false;
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke(this, new MyArguments(myInt, myBool));
|
||||||
|
/// myInt++;
|
||||||
|
/// myBool = !myBool;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered(MyBehaviour sender, MyArguments args)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event occurred with MyInt: {args.MyInt} and MyBool {args.MyBool}!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime(MyBehaviour sender, MyArguments args)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event called once with MyInt: {args.MyInt} and MyBool {args.MyBool}!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public readonly record struct MyArguments(int MyInt, bool MyBool);
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// [Id]'s event occurred with MyInt: 0 and MyBool False!
|
||||||
|
/// [Id]'s event called once with MyInt: 0 and MyBool False!
|
||||||
|
/// [Id]'s event occurred with MyInt: 1 and MyBool True!
|
||||||
|
/// [Id]'s event occurred with MyInt: 2 and MyBool False!
|
||||||
|
/// [Id]'s event occurred with MyInt: 3 and MyBool True!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSender">Sender type</typeparam>
|
||||||
|
public class Event<TSender, TArguments> where TSender : class
|
||||||
|
{
|
||||||
|
// We use Ascending order because draw calls are running from last to first
|
||||||
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
|
private ILogger _logger = ILogger.Shared;
|
||||||
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
|
|
||||||
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The caller that's triggering this event.</param>
|
||||||
|
/// <param name="args">The arguments provided for this event.</param>
|
||||||
|
public void Invoke(TSender sender, TArguments args)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
try { listeners[i].Callback.Invoke(sender, args); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender}, {args})";
|
||||||
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
try { onceListeners[i].Callback.Invoke(sender, args); }
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender}, {args})";
|
||||||
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
|
}
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||||
|
{
|
||||||
|
listeners = new(initialListenerCount);
|
||||||
|
onceListeners = new(initialOnceListenerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event()
|
||||||
|
{
|
||||||
|
listeners = new(4);
|
||||||
|
onceListeners = new(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void EventHandler(TSender sender, TArguments args);
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class EventHelpers
|
||||||
|
{
|
||||||
|
public static void LogInvocationException(object sender, ILogger logger, Exception exception, string methodCallRepresentation)
|
||||||
|
{
|
||||||
|
logger.LogException(sender, exception);
|
||||||
|
logger.LogError(sender, $"Unexpected exception on invocation of method {methodCallRepresentation}");
|
||||||
|
}
|
||||||
|
}
|
10
Engine.Core/Helpers/IPool.cs
Normal file
10
Engine.Core/Helpers/IPool.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface IPool<T>
|
||||||
|
{
|
||||||
|
Event<IPool<T>, T> OnRemoved { get; }
|
||||||
|
Event<IPool<T>, T> OnReturned { get; }
|
||||||
|
|
||||||
|
T Get();
|
||||||
|
void Return(T item);
|
||||||
|
}
|
40
Engine.Core/Helpers/ListPool.cs
Normal file
40
Engine.Core/Helpers/ListPool.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public class ListPool<T> : IPool<List<T>>
|
||||||
|
{
|
||||||
|
public Event<IPool<List<T>>, List<T>> OnReturned { get; } = new();
|
||||||
|
public Event<IPool<List<T>>, List<T>> OnRemoved { get; } = new();
|
||||||
|
|
||||||
|
private readonly Func<List<T>> generator = null!;
|
||||||
|
private readonly Queue<List<T>> queue = new();
|
||||||
|
|
||||||
|
public List<T> Get()
|
||||||
|
{
|
||||||
|
if (!queue.TryDequeue(out List<T>? result))
|
||||||
|
result = generator();
|
||||||
|
|
||||||
|
result.Clear();
|
||||||
|
OnRemoved?.Invoke(this, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(List<T> list)
|
||||||
|
{
|
||||||
|
if (queue.Contains(list))
|
||||||
|
return;
|
||||||
|
|
||||||
|
list.Clear();
|
||||||
|
queue.Enqueue(list);
|
||||||
|
OnReturned?.Invoke(this, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListPool(int initialListCount = 1, int initialListCapacity = 32)
|
||||||
|
{
|
||||||
|
generator = () => new(initialListCapacity);
|
||||||
|
for (int i = 0; i < initialListCount; i++)
|
||||||
|
queue.Enqueue(generator());
|
||||||
|
}
|
||||||
|
}
|
38
Engine.Core/Helpers/Pool.cs
Normal file
38
Engine.Core/Helpers/Pool.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public class Pool<T> : IPool<T>
|
||||||
|
{
|
||||||
|
public Event<IPool<T>, T> OnRemoved { get; } = new();
|
||||||
|
public Event<IPool<T>, T> OnReturned { get; } = new();
|
||||||
|
|
||||||
|
private readonly Func<T> generator = null!;
|
||||||
|
private readonly Queue<T> queue = new();
|
||||||
|
|
||||||
|
public T Get()
|
||||||
|
{
|
||||||
|
if (!queue.TryDequeue(out T? result))
|
||||||
|
result = generator();
|
||||||
|
|
||||||
|
OnRemoved?.Invoke(this, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(T item)
|
||||||
|
{
|
||||||
|
if (queue.Contains(item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
queue.Enqueue(item);
|
||||||
|
OnReturned?.Invoke(this, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pool(Func<T> generator, int initialCapacity = 1)
|
||||||
|
{
|
||||||
|
this.generator = generator;
|
||||||
|
for (int i = 0; i < initialCapacity; i++)
|
||||||
|
queue.Enqueue(generator());
|
||||||
|
}
|
||||||
|
}
|
7
Engine.Core/Helpers/Progression/IProgressionTracker.cs
Normal file
7
Engine.Core/Helpers/Progression/IProgressionTracker.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface IProgressionTracker : IReadOnlyProgressionTracker
|
||||||
|
{
|
||||||
|
void Set(float progression, string status);
|
||||||
|
void Reset();
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface IReadOnlyProgressionTracker
|
||||||
|
{
|
||||||
|
Event<IReadOnlyProgressionTracker, ProgressionUpdatedArguments> OnUpdated { get; }
|
||||||
|
Event<IReadOnlyProgressionTracker> OnEnded { get; }
|
||||||
|
|
||||||
|
float Progression { get; }
|
||||||
|
string Status { get; }
|
||||||
|
|
||||||
|
readonly record struct ProgressionUpdatedArguments(float PreviousProgression, string PreviousStatus);
|
||||||
|
}
|
36
Engine.Core/Helpers/Progression/ProgressionTracker.cs
Normal file
36
Engine.Core/Helpers/Progression/ProgressionTracker.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public class ProgressionTracker : IProgressionTracker
|
||||||
|
{
|
||||||
|
public Event<IReadOnlyProgressionTracker, IReadOnlyProgressionTracker.ProgressionUpdatedArguments> OnUpdated { get; } = new();
|
||||||
|
public Event<IReadOnlyProgressionTracker> OnEnded { get; } = new();
|
||||||
|
|
||||||
|
public float Progression { get; private set; } = 0f;
|
||||||
|
public string Status { get; private set; } = "Default";
|
||||||
|
|
||||||
|
void IProgressionTracker.Set(float progression, string status)
|
||||||
|
{
|
||||||
|
if (Progression >= 1f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float previousProgression = Progression;
|
||||||
|
string previousStatus = Status;
|
||||||
|
|
||||||
|
Progression = progression.Clamp(Progression, 1f);
|
||||||
|
Status = status;
|
||||||
|
|
||||||
|
OnUpdated?.Invoke(this, new(previousProgression, previousStatus));
|
||||||
|
|
||||||
|
if (progression >= 1f)
|
||||||
|
OnEnded?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IProgressionTracker.Reset()
|
||||||
|
{
|
||||||
|
Progression = 0f;
|
||||||
|
Status = "Default";
|
||||||
|
|
||||||
|
OnUpdated.Clear();
|
||||||
|
OnEnded.Clear();
|
||||||
|
}
|
||||||
|
}
|
9
Engine.Core/Helpers/Progression/ProgressiveTask.cs
Normal file
9
Engine.Core/Helpers/Progression/ProgressiveTask.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
|
||||||
|
{
|
||||||
|
public static implicit operator (IReadOnlyProgressionTracker progressionTracker, Task<T> task)(ProgressiveTask<T> value) => (value.ProgressionTracker, value.Task);
|
||||||
|
public static implicit operator ProgressiveTask<T>((IReadOnlyProgressionTracker progressionTracker, Task<T> task) value) => new(value.progressionTracker, value.task);
|
||||||
|
}
|
@@ -1,165 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
|
||||||
public class HierarchyObject : BaseEntity, IHierarchyObject
|
|
||||||
{
|
|
||||||
public event IHierarchyObject.EnteredHierarchyEventHandler? OnEnteredHierarchy = null;
|
|
||||||
public event IHierarchyObject.ExitedHierarchyEventHandler? OnExitedHierarchy = null;
|
|
||||||
public event IHierarchyObject.ParentChangedEventHandler? OnParentChanged = null;
|
|
||||||
public event IHierarchyObject.ChildrenAddedEventHandler? OnChildrenAdded = null;
|
|
||||||
public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
|
|
||||||
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
|
|
||||||
public event INameable.NameChangedEventHandler? OnNameChanged = null;
|
|
||||||
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
|
|
||||||
|
|
||||||
private string _name = nameof(HierarchyObject);
|
|
||||||
private IGameManager _gameManager = null!;
|
|
||||||
private IBehaviourController _behaviourController = null!;
|
|
||||||
private bool _isActive = false;
|
|
||||||
private readonly List<IHierarchyObject> _children = [];
|
|
||||||
|
|
||||||
public IHierarchyObject? Parent { get; private set; } = null;
|
|
||||||
public IReadOnlyList<IHierarchyObject> Children => _children;
|
|
||||||
public IBehaviourController BehaviourController => _behaviourController;
|
|
||||||
public IGameManager GameManager => _gameManager;
|
|
||||||
public bool IsInHierarchy => _gameManager is not null;
|
|
||||||
public bool IsActive => _isActive;
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _name;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == _name) return;
|
|
||||||
|
|
||||||
string previousName = _name;
|
|
||||||
_name = value;
|
|
||||||
OnNameChanged?.Invoke(this, previousName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
|
|
||||||
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
|
|
||||||
{
|
|
||||||
if (IsInHierarchy)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_gameManager = gameManager;
|
|
||||||
UpdateActive();
|
|
||||||
OnEnteringHierarchy(gameManager);
|
|
||||||
OnEnteredHierarchy?.Invoke(this, gameManager);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnExitingHierarchy(IGameManager gameManager) { }
|
|
||||||
bool IHierarchyObject.ExitHierarchy()
|
|
||||||
{
|
|
||||||
if (!IsInHierarchy || _gameManager is not IGameManager gameManager)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
OnExitingHierarchy(gameManager);
|
|
||||||
_gameManager = null!;
|
|
||||||
OnExitedHierarchy?.Invoke(this, gameManager);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetParent(IHierarchyObject? parent)
|
|
||||||
{
|
|
||||||
if (parent == this)
|
|
||||||
throw new Exceptions.AssignFailedException($"{Name} can not parent itself");
|
|
||||||
|
|
||||||
if (Parent == parent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IHierarchyObject? previousParent = Parent;
|
|
||||||
if (previousParent is not null)
|
|
||||||
{
|
|
||||||
previousParent.RemoveChild(this);
|
|
||||||
previousParent.OnActiveChanged -= OnParentActiveChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
Parent = parent;
|
|
||||||
|
|
||||||
if (parent is not null)
|
|
||||||
{
|
|
||||||
if (parent.IsInHierarchy && !IsInHierarchy)
|
|
||||||
parent.GameManager.Register(this);
|
|
||||||
|
|
||||||
parent.AddChild(this);
|
|
||||||
parent.OnActiveChanged += OnParentActiveChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateActive();
|
|
||||||
OnParentChanged?.Invoke(this, previousParent, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddChild(IHierarchyObject parent)
|
|
||||||
{
|
|
||||||
if (_children.Contains(parent))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_children.Add(parent);
|
|
||||||
parent.SetParent(this);
|
|
||||||
OnChildrenAdded?.Invoke(this, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveChild(IHierarchyObject child)
|
|
||||||
{
|
|
||||||
if (!_children.Remove(child))
|
|
||||||
return;
|
|
||||||
|
|
||||||
child.SetParent(null);
|
|
||||||
OnChildrenRemoved?.Invoke(this, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnAssign(IBehaviourController behaviourController) { }
|
|
||||||
public bool Assign(IBehaviourController behaviourController)
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_behaviourController = behaviourController;
|
|
||||||
OnAssign(behaviourController);
|
|
||||||
OnBehaviourControllerAssigned?.Invoke(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAssign(IStateEnable stateEnable)
|
|
||||||
{
|
|
||||||
base.OnAssign(stateEnable);
|
|
||||||
|
|
||||||
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnParentActiveChanged(IActive sender, bool previousState) => UpdateActive();
|
|
||||||
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
|
|
||||||
|
|
||||||
private void UpdateActive()
|
|
||||||
{
|
|
||||||
bool previousActive = IsActive;
|
|
||||||
_isActive = StateEnable.Enabled && (Parent?.IsActive ?? true);
|
|
||||||
|
|
||||||
if (previousActive != IsActive)
|
|
||||||
OnActiveChanged?.Invoke(this, previousActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UnassignInternal()
|
|
||||||
{
|
|
||||||
base.UnassignInternal();
|
|
||||||
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void InitializeInternal()
|
|
||||||
{
|
|
||||||
base.InitializeInternal();
|
|
||||||
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<IHierarchyObject> GetEnumerator() => _children.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
|
|
||||||
}
|
|
@@ -6,29 +6,87 @@ namespace Syntriax.Engine.Core;
|
|||||||
public static class Math
|
public static class Math
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
/// The value of Pi (π).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float PI = 3.1415926535897932f;
|
public const float Pi = 3.1415926535897932f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319.
|
/// The value of Tau (τ), mathematical constant equal to 2π.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float Tau = 2f * PI;
|
public const float Tau = 2f * Pi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base of the natural logarithm, approximately equal to 2.71828.
|
/// The base of the natural logarithm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float E = 2.718281828459045f;
|
public const float E = 2.718281828459045f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The conversion factor from radians to degrees.
|
/// The conversion factor from radians to degrees.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float RadianToDegree = 180f / PI;
|
public const float RadianToDegree = 180f / Pi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The conversion factor from degrees to radians.
|
/// The conversion factor from degrees to radians.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DegreeToRadian = PI / 180f;
|
public const float DegreeToRadian = Pi / 180f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets one minus of given <see cref="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value <see cref="T"/>.</param>
|
||||||
|
/// <returns>One minus of given <see cref="T"/>.</returns>
|
||||||
|
public static T OneMinus<T>(T value) where T : INumber<T> => T.One - value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="T"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="T"/>.</param>
|
||||||
|
/// <param name="value">The second <see cref="T"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="T"/>s.</returns>
|
||||||
|
public static T Add<T>(T left, T value) where T : INumber<T> => left + value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="T"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="T"/> to subtract from.</param>
|
||||||
|
/// <param name="value">The <see cref="T"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="T"/> from the first.</returns>
|
||||||
|
public static T Subtract<T>(T left, T value) where T : INumber<T> => left - value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="T"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="T"/>.</param>
|
||||||
|
/// <param name="multiplier">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="T"/> by the scalar value.</returns>
|
||||||
|
public static T Multiply<T>(T left, T multiplier) where T : INumber<T> => left * multiplier;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="T"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="T"/>.</param>
|
||||||
|
/// <param name="divider">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="T"/> by the scalar value.</returns>
|
||||||
|
public static T Divide<T>(T left, T divider) where T : INumber<T> => left / divider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the true mathematical modulus of a <see cref="T"/> value.
|
||||||
|
/// Unlike the remainder operator (%), this result is always non-negative,
|
||||||
|
/// even when the <paramref name="value"/> operand is negative.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">A numeric type that implements <see cref="INumber{T}"/>.</typeparam>
|
||||||
|
/// <param name="value">The dividend <see cref="T"/> value.</param>
|
||||||
|
/// <param name="modulus">The modulus <see cref="T"/> value (must be non-zero).</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The non-negative remainder of <paramref name="value"/> divided by <paramref name="modulus"/>.
|
||||||
|
/// </returns>
|
||||||
|
public static T Mod<T>(T value, T modulus) where T : INumber<T>
|
||||||
|
{
|
||||||
|
T result = value % modulus;
|
||||||
|
if (result < T.Zero)
|
||||||
|
result += modulus;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the absolute value of a number.
|
/// Returns the absolute value of a number.
|
||||||
@@ -190,6 +248,15 @@ public static class Math
|
|||||||
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
|
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
|
||||||
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rounds a number to an integer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The number to round.</param>
|
||||||
|
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
|
||||||
|
public enum RoundMode { Ceil, Floor };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the square of a number.
|
/// Returns the square of a number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -5,6 +5,24 @@ namespace Syntriax.Engine.Core;
|
|||||||
|
|
||||||
public static class MathExtensions
|
public static class MathExtensions
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="Math.OneMinus{T}(T)" />
|
||||||
|
public static T OneMinus<T>(this T value) where T : INumber<T> => Math.OneMinus(value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Add{T}(T, T)" />
|
||||||
|
public static T Add<T>(this T left, T value) where T : INumber<T> => Math.Add(left, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Subtract{T}(T, T)" />
|
||||||
|
public static T Subtract<T>(this T left, T value) where T : INumber<T> => Math.Subtract(left, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Multiply{T}(T, T)" />
|
||||||
|
public static T Multiply<T>(this T left, T multiplier) where T : INumber<T> => Math.Multiply(left, multiplier);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Divide{T}(T, T)" />
|
||||||
|
public static T Divide<T>(this T left, T divider) where T : INumber<T> => Math.Divide(left, divider);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.Mod{T}(T, T)" />
|
||||||
|
public static T Mod<T>(this T value, T modulus) where T : INumber<T> => Math.Mod(value, modulus);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Abs{T}(T)" />
|
/// <inheritdoc cref="Math.Abs{T}(T)" />
|
||||||
public static T Abs<T>(this T x) where T : INumber<T> => Math.Abs(x);
|
public static T Abs<T>(this T x) where T : INumber<T> => Math.Abs(x);
|
||||||
|
|
||||||
@@ -65,6 +83,9 @@ public static class MathExtensions
|
|||||||
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
|
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
|
||||||
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
|
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
|
||||||
|
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Sqr{T}(T)" />
|
/// <inheritdoc cref="Math.Sqr{T}(T)" />
|
||||||
public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x);
|
public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x);
|
||||||
|
|
||||||
|
10
Engine.Core/Preserver.cs
Normal file
10
Engine.Core/Preserver.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Syntriax.Engine.Core
|
||||||
|
{
|
||||||
|
// This is pretty much so the assembly gets loaded automatically because
|
||||||
|
// the builds include the assembly but sometimes doesn't link load it at startup.
|
||||||
|
// I will hopefully one day fix it and remove this.
|
||||||
|
public static class Preserver
|
||||||
|
{
|
||||||
|
public static void Preserve() { }
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,9 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Vector2D SizeHalf => Size * .5f;
|
public readonly Vector2D SizeHalf => Size * .5f;
|
||||||
|
|
||||||
|
public static bool operator ==(AABB left, AABB right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
|
||||||
|
public static bool operator !=(AABB left, AABB right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -63,12 +66,6 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
return new(lowerBoundary, upperBoundary);
|
return new(lowerBoundary, upperBoundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="AABB"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -78,6 +75,25 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
||||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="AABB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="AABB"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is AABB aabb && this == aabb;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="AABB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="AABB"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="AABB"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -40,6 +38,9 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||||
|
|
||||||
|
public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius;
|
||||||
|
public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the center of the <see cref="Circle"/>.
|
/// Sets the center of the <see cref="Circle"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -79,6 +80,25 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
|
||||||
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Circle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Circle"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Circle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Circle circle && this == circle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Circle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Circle"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Center, Radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Circle"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Circle"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Circle)}({Center}, {Radius})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
151
Engine.Core/Primitives/ColorHSV.cs
Normal file
151
Engine.Core/Primitives/ColorHSV.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an HSV color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hue">Hue of the <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="saturation">Saturation of the <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="value">Value of the <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
|
public readonly struct ColorHSV(float hue, float saturation, float value)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Hue value of the <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Hue = hue.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Saturation value of the <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Saturation = saturation.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Value value of the <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Value = value.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus());
|
||||||
|
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value);
|
||||||
|
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value);
|
||||||
|
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value);
|
||||||
|
public static ColorHSV operator *(ColorHSV color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value);
|
||||||
|
public static ColorHSV operator *(float value, ColorHSV color) => new(color.Hue * value, color.Saturation * value, color.Value * value);
|
||||||
|
public static ColorHSV operator /(ColorHSV color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value);
|
||||||
|
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value;
|
||||||
|
public static bool operator !=(ColorHSV left, ColorHSV right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value;
|
||||||
|
|
||||||
|
public static implicit operator ColorHSV(ColorHSVA hsva) => new(hsva.Hue, hsva.Saturation, hsva.Value);
|
||||||
|
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorHSVA)rgba;
|
||||||
|
public static implicit operator ColorHSV(ColorRGB rgb) => (ColorHSVA)rgb;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the given <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <returns>The inverted <see cref="ColorHSV"/>.</returns>
|
||||||
|
public static ColorHSV Invert(ColorHSV color) => -color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="ColorHSV"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="ColorHSV"/>s.</returns>
|
||||||
|
public static ColorHSV Add(ColorHSV left, ColorHSV right) => left + right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="ColorHSV"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="ColorHSV"/> to subtract from.</param>
|
||||||
|
/// <param name="right">The <see cref="ColorHSV"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="ColorHSV"/> from the first.</returns>
|
||||||
|
public static ColorHSV Subtract(ColorHSV left, ColorHSV right) => left - right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="ColorHSV"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="ColorHSV"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSV Multiply(ColorHSV color, float value) => color * value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="ColorHSV"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting <see cref="ColorHSV"/> (t = 0).</param>
|
||||||
|
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
|
||||||
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
|
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
|
||||||
|
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t)
|
||||||
|
{
|
||||||
|
float hueDiff = to.Hue - from.Hue;
|
||||||
|
float saturationDiff = to.Saturation - from.Saturation;
|
||||||
|
float valueDiff = to.Value - from.Value;
|
||||||
|
|
||||||
|
return from + new ColorHSV(hueDiff * t, saturationDiff * t, valueDiff * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="ColorHSV"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(ColorHSV left, ColorHSV right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorHSV"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ColorHSV"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorHSVExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ColorHSV.Add(ColorHSV, ColorHSV)" />
|
||||||
|
public static ColorHSV Add(this ColorHSV color, ColorHSV value) => ColorHSV.Add(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSV.Subtract(ColorHSV, ColorHSV)" />
|
||||||
|
public static ColorHSV Subtract(this ColorHSV color, ColorHSV value) => ColorHSV.Subtract(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSV.Multiply(ColorHSV, ColorHSV)" />
|
||||||
|
public static ColorHSV Multiply(this ColorHSV color, float value) => ColorHSV.Multiply(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
|
||||||
|
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
|
||||||
|
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSV.ApproximatelyEquals(ColorHSV, ColorHSV, float) " />
|
||||||
|
public static bool ApproximatelyEquals(this ColorHSV left, ColorHSV right, float epsilon = float.Epsilon) => ColorHSV.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
189
Engine.Core/Primitives/ColorHSVA.cs
Normal file
189
Engine.Core/Primitives/ColorHSVA.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an HSV color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hue">Hue of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="saturation">Saturation of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">Value of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="alpha">Alpha of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="ColorHSVA"/> struct with the specified values.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
|
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Hue value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Hue = hue.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Saturation value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Saturation = saturation.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Value value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Value = value.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Alpha value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Alpha = alpha;
|
||||||
|
|
||||||
|
public static ColorHSVA operator -(ColorHSVA color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus(), color.Alpha);
|
||||||
|
public static ColorHSVA operator +(ColorHSVA left, ColorHSVA right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value, left.Alpha + right.Alpha);
|
||||||
|
public static ColorHSVA operator -(ColorHSVA left, ColorHSVA right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value, left.Alpha - right.Alpha);
|
||||||
|
public static ColorHSVA operator *(ColorHSVA left, ColorHSVA right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value, left.Alpha * right.Alpha);
|
||||||
|
public static ColorHSVA operator *(ColorHSVA color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value);
|
||||||
|
public static ColorHSVA operator *(float value, ColorHSVA color) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value);
|
||||||
|
public static ColorHSVA operator /(ColorHSVA color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value, color.Alpha / value);
|
||||||
|
public static bool operator ==(ColorHSVA left, ColorHSVA right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value;
|
||||||
|
public static bool operator !=(ColorHSVA left, ColorHSVA right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value;
|
||||||
|
|
||||||
|
public static implicit operator ColorHSVA(ColorHSV hsv) => new(hsv.Hue, hsv.Saturation, hsv.Value, 1f);
|
||||||
|
public static implicit operator ColorHSVA(ColorRGB rgb) => (ColorRGBA)rgb;
|
||||||
|
public static implicit operator ColorHSVA(ColorRGBA rgba)
|
||||||
|
{
|
||||||
|
float hue;
|
||||||
|
float saturation;
|
||||||
|
float value;
|
||||||
|
|
||||||
|
float rd = rgba.R / 255f;
|
||||||
|
float gd = rgba.G / 255f;
|
||||||
|
float bd = rgba.B / 255f;
|
||||||
|
|
||||||
|
float max = Math.Max(rd, Math.Max(gd, bd));
|
||||||
|
float min = Math.Min(rd, Math.Min(gd, bd));
|
||||||
|
float delta = max - min;
|
||||||
|
|
||||||
|
if (delta.ApproximatelyEquals(0))
|
||||||
|
hue = 0f;
|
||||||
|
else if (max.ApproximatelyEquals(rd))
|
||||||
|
hue = 60f * ((gd - bd) / delta % 6f);
|
||||||
|
else if (max.ApproximatelyEquals(gd))
|
||||||
|
hue = 60f * (((bd - rd) / delta) + 2f);
|
||||||
|
else
|
||||||
|
hue = 60f * (((rd - gd) / delta) + 4f);
|
||||||
|
|
||||||
|
if (hue < 0f)
|
||||||
|
hue += 360f;
|
||||||
|
|
||||||
|
hue /= 360f;
|
||||||
|
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
|
||||||
|
value = max;
|
||||||
|
|
||||||
|
return new(hue, saturation, value, rgba.A / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the given <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns>The inverted <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public static ColorHSVA Invert(ColorHSVA color) => -color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="ColorHSVA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="ColorHSVA"/>s.</returns>
|
||||||
|
public static ColorHSVA Add(ColorHSVA left, ColorHSVA right) => left + right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="ColorHSVA"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="ColorHSVA"/> to subtract from.</param>
|
||||||
|
/// <param name="right">The <see cref="ColorHSVA"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="ColorHSVA"/> from the first.</returns>
|
||||||
|
public static ColorHSVA Subtract(ColorHSVA left, ColorHSVA right) => left - right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="ColorHSVA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="ColorHSVA"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSVA Multiply(ColorHSVA color, float value) => color * value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="ColorHSVA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="ColorHSVA"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSVA Divide(ColorHSVA color, float value) => color / value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs linear interpolation between two <see cref="ColorHSVA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting <see cref="ColorHSVA"/> (t = 0).</param>
|
||||||
|
/// <param name="to">The ending <see cref="ColorHSVA"/> (t = 1).</param>
|
||||||
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
|
/// <returns>The interpolated <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public static ColorHSVA Lerp(ColorHSVA from, ColorHSVA to, float t)
|
||||||
|
{
|
||||||
|
float hueDiff = to.Hue - from.Hue;
|
||||||
|
float saturationDiff = to.Saturation - from.Saturation;
|
||||||
|
float valueDiff = to.Value - from.Value;
|
||||||
|
float alphaDiff = to.Alpha - from.Alpha;
|
||||||
|
|
||||||
|
return from + new ColorHSVA(hueDiff * t, saturationDiff * t, valueDiff * t, alphaDiff * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="ColorHSVA"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="ColorHSVA"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSVA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorHSVA"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorHSVA)}({Hue}, {Saturation}, {Value})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ColorHSVA"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorHSVAExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Add(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Add(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Add(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Subtract(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Subtract(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Subtract(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Multiply(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Multiply(this ColorHSVA color, float value) => ColorHSVA.Multiply(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Divide(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Divide(this ColorHSVA color, float value) => ColorHSVA.Divide(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Lerp(ColorHSVA, ColorHSVA, float)" />
|
||||||
|
public static ColorHSVA Lerp(this ColorHSVA from, ColorHSVA to, float t) => ColorHSVA.Lerp(from, to, t);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.ApproximatelyEquals(ColorHSVA, ColorHSVA, float) " />
|
||||||
|
public static bool ApproximatelyEquals(this ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon) => ColorHSVA.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
138
Engine.Core/Primitives/ColorRGB.cs
Normal file
138
Engine.Core/Primitives/ColorRGB.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an RGB color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="r">Red value of the <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <param name="g">Green value of the <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <param name="b">Blue value of the <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
|
public readonly struct ColorRGB(byte r, byte g, byte b)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Red value of the <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte R = r;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Green value of the <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte G = g;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Blue value of the <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte B = b;
|
||||||
|
|
||||||
|
public static ColorRGB operator -(ColorRGB color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B));
|
||||||
|
public static ColorRGB operator +(ColorRGB left, ColorRGB right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255));
|
||||||
|
public static ColorRGB operator -(ColorRGB left, ColorRGB right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255));
|
||||||
|
public static ColorRGB operator *(ColorRGB left, ColorRGB right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255));
|
||||||
|
public static ColorRGB operator *(ColorRGB color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
|
||||||
|
public static ColorRGB operator *(float value, ColorRGB color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
|
||||||
|
public static ColorRGB operator /(ColorRGB color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255));
|
||||||
|
public static bool operator ==(ColorRGB left, ColorRGB right) => left.R == right.R && left.G == right.G && left.B == right.B;
|
||||||
|
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
|
||||||
|
|
||||||
|
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
|
||||||
|
public static implicit operator ColorRGB(ColorHSVA hsva) => (ColorRGBA)hsva;
|
||||||
|
public static implicit operator ColorRGB(ColorHSV hsv) => (ColorRGBA)hsv;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the given <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <returns>The inverted <see cref="ColorRGB"/>.</returns>
|
||||||
|
public static ColorRGB Invert(ColorRGB color) => -color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="ColorRGB"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="ColorRGB"/>s.</returns>
|
||||||
|
public static ColorRGB Add(ColorRGB left, ColorRGB right) => left + right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="ColorRGB"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="ColorRGB"/> to subtract from.</param>
|
||||||
|
/// <param name="right">The <see cref="ColorRGB"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="ColorRGB"/> from the first.</returns>
|
||||||
|
public static ColorRGB Subtract(ColorRGB left, ColorRGB right) => left - right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="ColorRGB"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="ColorRGB"/> by the scalar value.</returns>
|
||||||
|
public static ColorRGB Multiply(ColorRGB color, float value) => color * value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="ColorRGB"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
|
||||||
|
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting <see cref="ColorRGB"/> (t = 0).</param>
|
||||||
|
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
|
||||||
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
|
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
|
||||||
|
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t)
|
||||||
|
{
|
||||||
|
int redDiff = to.R - from.R;
|
||||||
|
int greenDiff = to.G - from.G;
|
||||||
|
int blueDiff = to.B - from.B;
|
||||||
|
|
||||||
|
return from + new ColorRGB((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorRGB"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ColorRGB"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorRGBExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ColorRGB.Add(ColorRGB, ColorRGB)" />
|
||||||
|
public static ColorRGB Add(this ColorRGB color, ColorRGB value) => ColorRGB.Add(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGB.Subtract(ColorRGB, ColorRGB)" />
|
||||||
|
public static ColorRGB Subtract(this ColorRGB color, ColorRGB value) => ColorRGB.Subtract(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGB.Multiply(ColorRGB, ColorRGB)" />
|
||||||
|
public static ColorRGB Multiply(this ColorRGB color, float value) => ColorRGB.Multiply(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
|
||||||
|
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
|
||||||
|
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
|
||||||
|
}
|
168
Engine.Core/Primitives/ColorRGBA.cs
Normal file
168
Engine.Core/Primitives/ColorRGBA.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an RGBA color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="r">Red value of the <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="g">Green value of the <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="b">Blue value of the <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="a">Alpha value of the <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
|
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Red value of the <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte R = r;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Green value of the <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte G = g;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Blue value of the <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte B = b;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Alpha value of the <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly byte A = a;
|
||||||
|
|
||||||
|
public static ColorRGBA operator -(ColorRGBA color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B), color.A);
|
||||||
|
public static ColorRGBA operator +(ColorRGBA left, ColorRGBA right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255), (byte)(left.A + right.A).Clamp(0, 255));
|
||||||
|
public static ColorRGBA operator -(ColorRGBA left, ColorRGBA right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255), (byte)(left.A - right.A).Clamp(0, 255));
|
||||||
|
public static ColorRGBA operator *(ColorRGBA left, ColorRGBA right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255), (byte)(left.A * right.A).Clamp(0, 255));
|
||||||
|
public static ColorRGBA operator *(ColorRGBA color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
|
||||||
|
public static ColorRGBA operator *(float value, ColorRGBA color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
|
||||||
|
public static ColorRGBA operator /(ColorRGBA color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255), (byte)(color.A / value).Clamp(0, 255));
|
||||||
|
public static bool operator ==(ColorRGBA left, ColorRGBA right) => left.R == right.R && left.G == right.G && left.B == right.B && left.A == right.A;
|
||||||
|
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
|
||||||
|
|
||||||
|
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
|
||||||
|
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorHSVA)hsv;
|
||||||
|
public static implicit operator ColorRGBA(ColorHSVA hsva)
|
||||||
|
{
|
||||||
|
float hue = hsva.Hue * 360f;
|
||||||
|
float chroma = hsva.Value * hsva.Saturation;
|
||||||
|
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
|
||||||
|
float m = hsva.Value - chroma;
|
||||||
|
|
||||||
|
float r1 = 0f;
|
||||||
|
float g1 = 0f;
|
||||||
|
float b1 = 0f;
|
||||||
|
|
||||||
|
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
|
||||||
|
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
|
||||||
|
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
|
||||||
|
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
|
||||||
|
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
|
||||||
|
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
|
||||||
|
|
||||||
|
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
|
||||||
|
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
|
||||||
|
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
|
||||||
|
|
||||||
|
return new(r, g, b, (byte)(hsva.Alpha * 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the given <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <returns>The inverted <see cref="ColorRGBA"/>.</returns>
|
||||||
|
public static ColorRGBA Invert(ColorRGBA color) => -color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="ColorRGBA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="ColorRGBA"/>s.</returns>
|
||||||
|
public static ColorRGBA Add(ColorRGBA left, ColorRGBA right) => left + right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="ColorRGBA"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="ColorRGBA"/> to subtract from.</param>
|
||||||
|
/// <param name="right">The <see cref="ColorRGBA"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="ColorRGBA"/> from the first.</returns>
|
||||||
|
public static ColorRGBA Subtract(ColorRGBA left, ColorRGBA right) => left - right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="ColorRGBA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
||||||
|
public static ColorRGBA Multiply(ColorRGBA color, float value) => color * value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="ColorRGBA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
||||||
|
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting <see cref="ColorRGBA"/> (t = 0).</param>
|
||||||
|
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
|
||||||
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
|
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
|
||||||
|
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t)
|
||||||
|
{
|
||||||
|
int redDiff = to.R - from.R;
|
||||||
|
int greenDiff = to.G - from.G;
|
||||||
|
int blueDiff = to.B - from.B;
|
||||||
|
int alphaDiff = to.A - from.A;
|
||||||
|
|
||||||
|
return from + new ColorRGBA((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t), (byte)(alphaDiff * t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorRGBA"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ColorRGBA"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorRGBAExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ColorRGBA.Add(ColorRGBA, ColorRGBA)" />
|
||||||
|
public static ColorRGBA Add(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Add(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGBA.Subtract(ColorRGBA, ColorRGBA)" />
|
||||||
|
public static ColorRGBA Subtract(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Subtract(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGBA.Multiply(ColorRGBA, ColorRGBA)" />
|
||||||
|
public static ColorRGBA Multiply(this ColorRGBA color, float value) => ColorRGBA.Multiply(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
|
||||||
|
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
|
||||||
|
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
|
||||||
|
}
|
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
@@ -44,6 +43,9 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||||
|
|
||||||
|
public static bool operator ==(Line2D left, Line2D right) => left.From == right.From && left.To == right.To;
|
||||||
|
public static bool operator !=(Line2D left, Line2D right) => left.From != right.From || left.To != right.To;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -68,12 +70,12 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static float GetT(Line2D line, Vector2D point)
|
public static float GetT(Line2D line, Vector2D point)
|
||||||
{
|
{
|
||||||
float fromX = MathF.Abs(line.From.X);
|
float fromX = Math.Abs(line.From.X);
|
||||||
float toX = MathF.Abs(line.To.X);
|
float toX = Math.Abs(line.To.X);
|
||||||
float pointX = MathF.Abs(point.X);
|
float pointX = Math.Abs(point.X);
|
||||||
|
|
||||||
float min = MathF.Min(fromX, toX);
|
float min = Math.Min(fromX, toX);
|
||||||
float max = MathF.Max(fromX, toX) - min;
|
float max = Math.Max(fromX, toX) - min;
|
||||||
|
|
||||||
pointX -= min;
|
pointX -= min;
|
||||||
|
|
||||||
@@ -114,8 +116,8 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool OnSegment(Line2D line, Vector2D point)
|
public static bool OnSegment(Line2D line, Vector2D point)
|
||||||
{
|
{
|
||||||
if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) &&
|
if (point.X <= Math.Max(line.From.X, line.To.X) && point.X >= Math.Min(line.From.X, line.To.X) &&
|
||||||
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
|
point.Y <= Math.Max(line.From.Y, line.To.Y) && point.Y >= Math.Min(line.From.Y, line.To.Y))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -168,17 +170,14 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Vector2D ClosestPointTo(Line2D line, Vector2D point)
|
public static Vector2D ClosestPointTo(Line2D line, Vector2D point)
|
||||||
{
|
{
|
||||||
Vector2D edgeVector = line.From.FromTo(line.To);
|
Vector2D lineRelativeVector = line.From.FromTo(line.To);
|
||||||
Vector2D pointVector = point - line.From;
|
|
||||||
|
|
||||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
Vector2D lineDirection = lineRelativeVector.Normalized;
|
||||||
|
Vector2D pointVector = line.From.FromTo(point);
|
||||||
|
|
||||||
t = MathF.Max(0, MathF.Min(1, t));
|
float dot = lineDirection.Dot(pointVector).Clamp(0f, lineRelativeVector.Magnitude);
|
||||||
|
|
||||||
float closestX = line.From.X + t * edgeVector.X;
|
return lineDirection * dot;
|
||||||
float closestY = line.From.Y + t * edgeVector.Y;
|
|
||||||
|
|
||||||
return new Vector2D((float)closestX, (float)closestY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -190,6 +189,25 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="Line2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="Line2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon)
|
||||||
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
|
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Line2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Line2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Line2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Line2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(From, To);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Line2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Line2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Line2D)}({From}, {To})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -206,6 +224,9 @@ public static class Line2DExtensions
|
|||||||
/// <inheritdoc cref="Line2D.Intersects(Line2D, Vector2D)" />
|
/// <inheritdoc cref="Line2D.Intersects(Line2D, Vector2D)" />
|
||||||
public static bool Intersects(this Line2D line, Vector2D point) => Line2D.Intersects(line, point);
|
public static bool Intersects(this Line2D line, Vector2D point) => Line2D.Intersects(line, point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Line2D.IntersectionPoint(Line2D, Line2D)" />
|
||||||
|
public static Vector2D IntersectionPoint(this Line2D left, Line2D right) => Line2D.IntersectionPoint(left, right);
|
||||||
|
|
||||||
/// <inheritdoc cref="Line2D.GetT(Line2D, Vector2D)" />
|
/// <inheritdoc cref="Line2D.GetT(Line2D, Vector2D)" />
|
||||||
public static float GetT(this Line2D line, Vector2D point) => Line2D.GetT(line, point);
|
public static float GetT(this Line2D line, Vector2D point) => Line2D.GetT(line, point);
|
||||||
|
|
||||||
@@ -215,6 +236,9 @@ public static class Line2DExtensions
|
|||||||
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D, out Vector2D?)" />
|
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D, out Vector2D?)" />
|
||||||
public static bool Intersects(this Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point) => Line2D.Intersects(left, right, out point);
|
public static bool Intersects(this Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point) => Line2D.Intersects(left, right, out point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Line2D.ClosestPointTo(Line2D, Vector2D)" />
|
||||||
|
public static Vector2D ClosestPointTo(this Line2D line, Vector2D point) => Line2D.ClosestPointTo(line, point);
|
||||||
|
|
||||||
/// <inheritdoc cref="Line2D.ApproximatelyEquals(Line2D, Line2D, float)" />
|
/// <inheritdoc cref="Line2D.ApproximatelyEquals(Line2D, Line2D, float)" />
|
||||||
public static bool ApproximatelyEquals(this Line2D left, Line2D right, float epsilon = float.Epsilon) => Line2D.ApproximatelyEquals(left, right, epsilon);
|
public static bool ApproximatelyEquals(this Line2D left, Line2D right, float epsilon = float.Epsilon) => Line2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
}
|
}
|
||||||
|
@@ -1,47 +1,69 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a line equation in the form y = mx + b.
|
/// Represents a <see cref="Line2DEquation"/> in the form y = mx + b.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slope">The slope of the line.</param>
|
/// <param name="slope">The slope of the line.</param>
|
||||||
/// <param name="offsetY">The y-intercept of the line.</param>
|
/// <param name="offsetY">The Y intercept of the line.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and y-intercept.
|
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and Y intercept.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||||
public readonly struct Line2DEquation(float slope, float offsetY)
|
public readonly struct Line2DEquation(float slope, float offsetY)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The slope of the line equation.
|
/// The slope of the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float Slope = slope;
|
public readonly float Slope = slope;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The y-intercept of the line equation.
|
/// The Y intercept of the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float OffsetY = offsetY;
|
public readonly float OffsetY = offsetY;
|
||||||
|
|
||||||
|
public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY;
|
||||||
|
public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
/// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
/// <param name="lineEquation">The <see cref="Line2DEquation"/> to resolve.</param>
|
||||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
/// <param name="x">The X coordinate for which to resolve the Y coordinate.</param>
|
||||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
/// <returns>The Y coordinate resolved using the <see cref="Line2DEquation"/>.</returns>
|
||||||
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two line equations are approximately equal.
|
/// Checks if two <see cref="Line2DEquation"/> are approximately equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="left">The first line equation to compare.</param>
|
/// <param name="left">The first <see cref="Line2DEquation"/> to compare.</param>
|
||||||
/// <param name="right">The second line equation to compare.</param>
|
/// <param name="right">The second <see cref="Line2DEquation"/> to compare.</param>
|
||||||
/// <param name="epsilon">The epsilon range.</param>
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
/// <returns>True if the <see cref="Line2DEquation"/> are approximately equal; otherwise, false.</returns>
|
||||||
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
|
||||||
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
|
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Line2DEquation"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Line2DEquation"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2DEquation"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Line2DEquation"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Line2DEquation"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Slope, OffsetY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Line2DEquation"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Line2DEquation"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Line2DEquation)}({Slope}, {OffsetY})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides extension methods for the LineEquation struct.
|
/// Provides extension methods for the <see cref="Line2DEquation"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Line2DEquationExtensions
|
public static class Line2DEquationExtensions
|
||||||
{
|
{
|
||||||
|
@@ -21,6 +21,9 @@ public readonly struct Projection1D(float min, float max)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float Max = max;
|
public readonly float Max = max;
|
||||||
|
|
||||||
|
public static bool operator ==(Projection1D left, Projection1D right) => left.Min == right.Min && left.Max == right.Max;
|
||||||
|
public static bool operator !=(Projection1D left, Projection1D right) => left.Min != right.Min || left.Max != right.Max;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two projections overlap.
|
/// Checks if two projections overlap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -70,6 +73,35 @@ public readonly struct Projection1D(float min, float max)
|
|||||||
depth = 0f;
|
depth = 0f;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Projection1D"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Projection1D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Projection1D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Projection1D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Projection1D left, Projection1D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Min.ApproximatelyEquals(right.Min, epsilon) && left.Max.ApproximatelyEquals(right.Max, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Projection1D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Projection1D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Projection1D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Projection1D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Projection1D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Min, Max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Projection1D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Projection1D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Projection1D)}({Min}, {Max})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -82,4 +114,7 @@ public static class Projection1DExtensions
|
|||||||
|
|
||||||
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
|
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
|
||||||
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
|
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Projection1D.ApproximatelyEquals(Projection1D, Projection1D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Projection1D left, Projection1D right, float epsilon = float.Epsilon) => Projection1D.ApproximatelyEquals(left, right, epsilon);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -178,11 +176,11 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
if (dot > 0.9995f)
|
if (dot > 0.9995f)
|
||||||
return Lerp(from, to, t);
|
return Lerp(from, to, t);
|
||||||
|
|
||||||
float angle = MathF.Acos(dot);
|
float angle = Math.Acos(dot);
|
||||||
float sinAngle = MathF.Sin(angle);
|
float sinAngle = Math.Sin(angle);
|
||||||
|
|
||||||
float fromWeight = MathF.Sin((1f - t) * angle) / sinAngle;
|
float fromWeight = Math.Sin((1f - t) * angle) / sinAngle;
|
||||||
float toWeight = MathF.Sin(t * angle) / sinAngle;
|
float toWeight = Math.Sin(t * angle) / sinAngle;
|
||||||
|
|
||||||
return from * fromWeight + to * toWeight;
|
return from * fromWeight + to * toWeight;
|
||||||
}
|
}
|
||||||
@@ -213,8 +211,8 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
public static Quaternion FromAxisAngle(Vector3D axis, float angle)
|
public static Quaternion FromAxisAngle(Vector3D axis, float angle)
|
||||||
{
|
{
|
||||||
float halfAngle = angle * .5f;
|
float halfAngle = angle * .5f;
|
||||||
float sinHalf = MathF.Sin(halfAngle);
|
float sinHalf = Math.Sin(halfAngle);
|
||||||
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, MathF.Cos(halfAngle));
|
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -284,24 +282,24 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Quaternion"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Quaternion"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Quaternion objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z) && W.Equals(objVec.W);
|
public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Quaternion"/>.
|
/// Generates a hash code for the <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
||||||
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
|
public override int GetHashCode() => System.HashCode.Combine(W, X, Y, Z);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Quaternion"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
105
Engine.Core/Primitives/Ray2D.cs
Normal file
105
Engine.Core/Primitives/Ray2D.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an infinite ray in 2D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Origin">The <see cref="Vector2D"/> in 2D space where the ray starts from.</param>
|
||||||
|
/// <param name="Direction">Normalized <see cref="Vector2D"/> indicating the ray's is direction.</param>
|
||||||
|
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The starting point of the <see cref="Ray2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D Origin = Origin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The direction in which the <see cref="Ray2D"/> points. Should be a normalized vector.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D Direction = Direction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Ray2D"/> with the same origin but with the direction reversed.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Ray2D Reversed => new(Origin, -Direction);
|
||||||
|
|
||||||
|
public static bool operator ==(Ray2D left, Ray2D right) => left.Origin == right.Origin && left.Direction == right.Direction;
|
||||||
|
public static bool operator !=(Ray2D left, Ray2D right) => left.Origin != right.Origin || left.Direction != right.Direction;
|
||||||
|
public static implicit operator Ray2D(Line2D line) => new(line.From, line.From.FromTo(line.To).Normalized);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="Line2D"/> from a <see cref="Ray2D"/>, extending from its origin in the <see cref="Ray2D"/>'s direction for a given distance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ray">The source <see cref="Ray2D"/>.</param>
|
||||||
|
/// <param name="distance">The length of the line segment to create from the <see cref="Ray2D"/>.</param>
|
||||||
|
/// <returns>A <see cref="Line2D"/> representing the segment of the <see cref="Ray2D"/>.</returns>
|
||||||
|
public static Line2D GetLine(Ray2D ray, float distance)
|
||||||
|
=> new(ray.Origin, ray.Origin + ray.Direction * distance);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the point on the <see cref="Ray2D"/> at a specified distance from its origin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ray">The <see cref="Ray2D"/> to evaluate.</param>
|
||||||
|
/// <param name="distanceFromOrigin">The distance from the origin along the <see cref="Ray2D"/>'s direction.</param>
|
||||||
|
/// <returns>A <see cref="Vector2D"/> representing the point at the given distance on the <see cref="Ray2D"/>.</returns>
|
||||||
|
public static Vector2D Evaluate(Ray2D ray, float distanceFromOrigin)
|
||||||
|
=> ray.Origin + ray.Direction * distanceFromOrigin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the closest point on the <see cref="Ray2D"/> to the specified point.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector2D ClosestPointTo(Ray2D ray, Vector2D point)
|
||||||
|
{
|
||||||
|
Vector2D originToPoint = ray.Origin.FromTo(point);
|
||||||
|
|
||||||
|
float dot = ray.Direction.Dot(originToPoint);
|
||||||
|
|
||||||
|
return ray.Origin + ray.Direction * dot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Ray2D"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Ray2D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Ray2D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Ray2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Ray2D left, Ray2D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Origin.ApproximatelyEquals(right.Origin, epsilon) && left.Direction.ApproximatelyEquals(right.Direction, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Ray2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Ray2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Ray2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Ray2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Origin, Direction);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Ray2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Ray2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Ray2D)}({Origin}, {Direction})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="Ray2D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class Ray2DExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Ray2D.GetLine(Ray2D, float) />
|
||||||
|
public static Line2D ToLine(this Ray2D ray, float distance) => Ray2D.GetLine(ray, distance);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray2D.Evaluate(Ray2D, float) />
|
||||||
|
public static Vector2D Evaluate(this Ray2D ray, float distanceFromOrigin) => Ray2D.Evaluate(ray, distanceFromOrigin);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray2D.ClosestPointTo(Ray2D, Vector2D) />
|
||||||
|
public static Vector2D ClosestPointTo(this Ray2D ray, Vector2D point) => Ray2D.ClosestPointTo(ray, point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray2D.ApproximatelyEquals(Ray2D, Ray2D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Ray2D left, Ray2D right, float epsilon = float.Epsilon) => Ray2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
@@ -1,8 +1,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -13,19 +11,33 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices.
|
/// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||||
public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
||||||
{
|
{
|
||||||
public static readonly Shape2D Triangle = CreateNgon(3, Vector2D.Up);
|
public static Shape2D Triangle => CreateNgon(3, Vector2D.Up);
|
||||||
public static readonly Shape2D Square = CreateNgon(4, Vector2D.One);
|
public static Shape2D Square => CreateNgon(4, Vector2D.One);
|
||||||
public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up);
|
public static Shape2D Pentagon => CreateNgon(5, Vector2D.Up);
|
||||||
public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right);
|
public static Shape2D Hexagon => CreateNgon(6, Vector2D.Right);
|
||||||
|
|
||||||
private readonly List<Vector2D> _verticesList = vertices;
|
public Event<Shape2D> OnShapeUpdated { get; } = new();
|
||||||
|
|
||||||
|
private List<Vector2D> _vertices = vertices;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the vertices of the <see cref="Shape2D"/>.
|
/// Gets the vertices of the <see cref="Shape2D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<Vector2D> Vertices => _verticesList;
|
public IReadOnlyList<Vector2D> Vertices
|
||||||
|
{
|
||||||
|
get => _vertices;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_vertices.Clear();
|
||||||
|
|
||||||
|
foreach (Vector2D vertex in value)
|
||||||
|
_vertices.Add(vertex);
|
||||||
|
|
||||||
|
OnShapeUpdated?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The vertex at the specified index.
|
/// The vertex at the specified index.
|
||||||
@@ -110,7 +122,7 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
|||||||
triangles.Clear();
|
triangles.Clear();
|
||||||
|
|
||||||
for (int i = 2; i < shape.Vertices.Count; i++)
|
for (int i = 2; i < shape.Vertices.Count; i++)
|
||||||
triangles.Add(new Triangle(shape[0], shape[i - 1], shape[i]));
|
triangles.Add(new Triangle(shape[0], shape[i], shape[i - 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -209,13 +221,15 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
|||||||
/// <param name="from">The <see cref="Shape2D"/> to transform.</param>
|
/// <param name="from">The <see cref="Shape2D"/> to transform.</param>
|
||||||
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
|
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
|
||||||
/// <param name="to">The transformed <see cref="Shape2D"/>.</param>
|
/// <param name="to">The transformed <see cref="Shape2D"/>.</param>
|
||||||
public static void Transform(Shape2D from, ITransform2D transform, ref Shape2D to)
|
public static void Transform(Shape2D from, ITransform2D transform, Shape2D to)
|
||||||
{
|
{
|
||||||
to._verticesList.Clear();
|
to._vertices.Clear();
|
||||||
|
|
||||||
int count = from._verticesList.Count;
|
int count = from._vertices.Count;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
to._verticesList.Add(transform.Transform(from[i]));
|
to._vertices.Add(transform.Transform(from[i]));
|
||||||
|
|
||||||
|
to.OnShapeUpdated?.Invoke(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -237,6 +251,34 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Shape2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Shape2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Shape2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Shape2D shape2D && _vertices.Equals(shape2D._vertices);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Shape2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Shape2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Vertices);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Shape2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Shape2D"/>.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder stringBuilder = new(Vertices[0].ToString());
|
||||||
|
for (int i = 1; i < Vertices.Count; i++)
|
||||||
|
{
|
||||||
|
stringBuilder.Append(", ");
|
||||||
|
stringBuilder.Append(Vertices[i].ToString());
|
||||||
|
}
|
||||||
|
return $"{nameof(Shape2D)}({stringBuilder})";
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||||
|
|
||||||
@@ -256,7 +298,7 @@ public static class Shape2DExtensions
|
|||||||
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
|
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D, IList{Triangle})" />
|
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D, IList{Triangle})" />
|
||||||
public static void ToTrianglesConvex(this Shape2D shape, IList<Triangle> lines) => Shape2D.TriangulateConvex(shape, lines);
|
public static void ToTrianglesConvex(this Shape2D shape, IList<Triangle> triangles) => Shape2D.TriangulateConvex(shape, triangles);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D)" />
|
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D)" />
|
||||||
public static List<Triangle> ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape);
|
public static List<Triangle> ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape);
|
||||||
@@ -277,13 +319,13 @@ public static class Shape2DExtensions
|
|||||||
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
|
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" />
|
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" />
|
||||||
public static void Transform(this ITransform2D transform, Shape2D from, ref Shape2D to) => Shape2D.Transform(from, transform, ref to);
|
public static void Transform(this ITransform2D transform, Shape2D from, Shape2D to) => Shape2D.Transform(from, transform, to);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" />
|
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" />
|
||||||
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
|
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, ref Shape2D)" />
|
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D,Shape2D)" />
|
||||||
public static void Transform(this Shape2D from, ITransform2D transform, ref Shape2D to) => Shape2D.Transform(from, transform, ref to);
|
public static void Transform(this Shape2D from, ITransform2D transform, Shape2D to) => Shape2D.Transform(from, transform, to);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" />
|
/// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" />
|
||||||
public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon);
|
public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
|
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
|
||||||
@@ -9,8 +7,11 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
public readonly Vector2D B { get; init; } = B;
|
public readonly Vector2D B { get; init; } = B;
|
||||||
public readonly Vector2D C { get; init; } = C;
|
public readonly Vector2D C { get; init; } = C;
|
||||||
|
|
||||||
|
public static bool operator ==(Triangle left, Triangle right) => left.A == right.A && left.B == right.B && left.C == right.C;
|
||||||
|
public static bool operator !=(Triangle left, Triangle right) => left.A != right.A || left.B != right.B || left.C != right.C;
|
||||||
|
|
||||||
public readonly float Area
|
public readonly float Area
|
||||||
=> .5f * MathF.Abs(
|
=> .5f * Math.Abs(
|
||||||
A.X * (B.Y - C.Y) +
|
A.X * (B.Y - C.Y) +
|
||||||
B.X * (C.Y - A.Y) +
|
B.X * (C.Y - A.Y) +
|
||||||
C.X * (A.Y - B.Y)
|
C.X * (A.Y - B.Y)
|
||||||
@@ -25,7 +26,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
|
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
|
||||||
|
|
||||||
Vector2D center;
|
Vector2D center;
|
||||||
if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon)
|
if (Math.Abs(slopeAB - slopeBC) > float.Epsilon)
|
||||||
{
|
{
|
||||||
float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB));
|
float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB));
|
||||||
float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f;
|
float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f;
|
||||||
@@ -46,6 +47,25 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
|
||||||
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
|
||||||
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
|
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Triangle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Triangle"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Triangle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Triangle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Triangle"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(A, B, C);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Triangle"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Triangle"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Triangle)}({A}, {B}, {C})";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TriangleExtensions
|
public static class TriangleExtensions
|
||||||
|
@@ -1,6 +1,3 @@
|
|||||||
using System;
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,6 +31,11 @@ public readonly struct Vector2D(float x, float y)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float MagnitudeSquared => LengthSquared(this);
|
public float MagnitudeSquared => LengthSquared(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Vector2D"/> with the direction reversed.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D Reversed => -this;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -300,24 +302,24 @@ public readonly struct Vector2D(float x, float y)
|
|||||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Vector2D"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Vector2D"/>.
|
/// Generates a hash code for the <see cref="Vector2D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
||||||
public override int GetHashCode() => HashCode.Combine(X, Y);
|
public override int GetHashCode() => System.HashCode.Combine(X, Y);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Vector2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -273,24 +271,24 @@ public readonly struct Vector3D(float x, float y, float z)
|
|||||||
public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Vector3D"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Vector3D"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Vector3D"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Vector3D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Vector3D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z);
|
public override bool Equals(object? obj) => obj is Vector3D vector3D && this == vector3D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Vector3D"/>.
|
/// Generates a hash code for the <see cref="Vector3D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
|
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
|
||||||
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
|
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Vector3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Vector3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Serialization;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)]
|
||||||
|
public class IgnoreSerializationAttribute : Attribute;
|
@@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Serialization;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||||
|
public class SerializeAllAttribute : Attribute;
|
@@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Serialization;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||||
|
public class SerializeAttribute : Attribute;
|
3
Engine.Core/Serialization/EntityReference.cs
Normal file
3
Engine.Core/Serialization/EntityReference.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Serialization;
|
||||||
|
|
||||||
|
public record class EntityReference(string? Id = null);
|
39
Engine.Core/Serialization/EntityRegistry.cs
Normal file
39
Engine.Core/Serialization/EntityRegistry.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Serialization;
|
||||||
|
|
||||||
|
public class EntityRegistry
|
||||||
|
{
|
||||||
|
public Event<EntityRegistry, EntityRegisteredArguments> OnEntityRegistered = null!;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, Action<IEntity>?> assignCallbacks = [];
|
||||||
|
private readonly Dictionary<string, IEntity> registeredEntities = [];
|
||||||
|
public IReadOnlyDictionary<string, IEntity> RegisteredEntities => registeredEntities;
|
||||||
|
|
||||||
|
public void Add(IEntity entity)
|
||||||
|
{
|
||||||
|
if (registeredEntities.TryAdd(entity.Id, entity))
|
||||||
|
OnEntityRegistered?.Invoke(this, new(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueAssign(string id, Action<IEntity> setMethod)
|
||||||
|
{
|
||||||
|
assignCallbacks.TryAdd(id, null);
|
||||||
|
assignCallbacks[id] = assignCallbacks[id] + setMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AssignAll()
|
||||||
|
{
|
||||||
|
foreach ((string id, Action<IEntity>? action) in assignCallbacks)
|
||||||
|
action?.Invoke(registeredEntities[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
assignCallbacks.Clear();
|
||||||
|
registeredEntities.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct EntityRegisteredArguments(IEntity Entity);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user