131 Commits

Author SHA1 Message Date
29a7f5880f feat: transform up, down, left & right properties added 2025-06-09 18:59:15 +03:00
eee3056614 fix: events not having default parameterless constructor 2025-06-09 18:34:20 +03:00
152b0e93db feat: added list pools 2025-06-09 18:33:47 +03:00
3f914fe46f refactor: extracted interface from pool and added events 2025-06-09 18:19:32 +03:00
62b54ee836 feat: event listener counts as constructor parameters 2025-06-09 18:19:08 +03:00
6a41407005 feat: added raycasting support for physics engine 2D 2025-06-09 18:11:20 +03:00
adfa6c6ba0 feat: Vector2D.Reversed property added 2025-06-09 18:04:41 +03:00
a53766f472 fix: forgotten extension method for Line2D.IntersectionPoint 2025-06-09 17:51:34 +03:00
40735c713a feat: added basic pool helper 2025-06-09 17:51:06 +03:00
2054ae3a35 feat: added Ray2D primitive 2025-06-09 16:55:42 +03:00
9066e11c12 perf: simplified Line2D.ClosestPointTo method 2025-06-08 23:40:00 +03:00
f16a7e55c9 chore: fixed record struct arguments' naming 2025-06-08 21:12:16 +03:00
e3b32b3c4a chore: removed unused variables 2025-06-08 21:11:47 +03:00
a02584f3b6 chore: removed DelegateExtensions.InvokeSafe 2025-06-07 18:19:56 +03:00
45524e474e refactor: updated systems to use the update interfaces 2025-06-06 20:26:19 +03:00
fbdea47dc7 docs: updated physics interface delta parameter comment 2025-06-05 23:28:08 +03:00
f5fbd4e5ef feat: IPhysicsIteration interface added 2025-06-05 23:23:34 +03:00
c7f63dc638 refactor: rewritten MonoGameWindow to take in a universe as a constructor parameter 2025-06-04 20:13:01 +03:00
beecefec1c refactor: switched from universe objects to behaviours on all managers like update, draw & physics etc. 2025-06-03 23:59:40 +03:00
24d1a1d764 feat: ISpriteBatch added for MonoGame integration 2025-06-03 23:38:25 +03:00
9edf3b0aa6 feat: one time listeners for events added 2025-06-03 11:43:46 +03:00
8d49fb467c fix: sprite batcher not collecting drawables 2025-06-01 18:36:20 +03:00
2caa042317 feat: basic MonoGame integration implementations added 2025-06-01 15:02:25 +03:00
fe8bde855d fix: draw and update call orders being reverted 2025-06-01 14:45:28 +03:00
ac620264b1 refactor: removed unnecessary overrides from Behaviour class 2025-06-01 14:31:05 +03:00
f31b84f519 refactor: renamed sort comparer names to be more readable 2025-06-01 14:18:50 +03:00
efb7cc7452 refactor: moved behaviour shortcut properties to base 2025-06-01 14:18:25 +03:00
7a3202a053 chore: simplified type names on physics engine 2D 2025-06-01 10:26:38 +03:00
86c9ed2ba9 feat: parameterless Event type 2025-05-31 20:24:45 +03:00
56321864fb fix: tween manager not returning the cancelled tweens back into the pool 2025-05-31 12:10:57 +03:00
6adc002f1a chore: renamed tween manager queue to pool for better readability 2025-05-31 12:08:44 +03:00
1acc8bdb8f perf!: improved sorted behaviour collector by using binary insertion to reduce performance impact 2025-05-31 12:00:32 +03:00
61e2761580 perf!: events refactored throughout all the project to use Event<> class
All delegate events are refactored to use the Event<TSender> and Event<TSender, TArgument> for performance issues regarding delegate events creating garbage, also this gives us better control on event invocation since C# Delegates did also create unnecessary garbage during Delegate.DynamicInvoke
2025-05-31 00:32:58 +03:00
996e61d0ad perf: tween manager pooling 2025-05-30 23:53:18 +03:00
b1b5af94d3 perf!: behaviour controller memory allocation issues fixed by removing the enumerable interface 2025-05-30 13:04:09 +03:00
b0f8b0dad6 refactor: behaviour collector Count and indexer accessors added 2025-05-29 23:17:11 +03:00
67d7f401b8 refactor: memory leaks caused by behaviour collectors fixed 2025-05-29 22:34:01 +03:00
9bf17cc191 perf: physics engine memory leaks fixed 2025-05-29 22:33:47 +03:00
bf8fbebae3 perf: DelegateExtensions.InvokeSafe marked obsolete for memory allocation reasons, soon to be removed 2025-05-29 21:48:08 +03:00
1b0f25e854 perf: update manager list precache 2025-05-29 10:30:30 +03:00
61a7f685c1 perf: delegate InvokeSafe method allocations are lowered 2025-05-29 00:16:00 +03:00
feb2a05aa3 feat: additive transform tweens added 2025-05-28 16:55:48 +03:00
cd30047e4a feat: GetOrAddBehaviour with fallback type added 2025-05-28 16:55:38 +03:00
a3b03efd47 feat: IPhysicsEngine2D.StepIndividual method for individual object simulation 2025-05-27 15:54:07 +03:00
4213b3f8b5 fix: fixed an issue where when there is an inactive collider in the universe messing up the physics engine 2025-05-27 13:52:53 +03:00
d3fb612904 feat: extension methods for parent & children behaviour list search 2025-05-27 13:36:42 +03:00
8f8558a262 docs: added performance warnings to find methods 2025-05-25 13:56:59 +03:00
2df41e1881 docs: added universe and universe object extension documentation comments 2025-05-25 13:28:36 +03:00
114fa82b9d feat: Find & FindRequired for general type search 2025-05-25 12:59:37 +03:00
bcce427376 feat: added GetUniverseObject/InChildren/InParent to UniverseObjectExtensions 2025-05-25 12:20:37 +03:00
6a750f8ce0 refactor: organized extension methods 2025-05-25 12:05:02 +03:00
3e02ee7b6f refactor: changed concrete list arguments to interface list arguments 2025-05-25 11:43:05 +03:00
6b9020bd24 fix: update manager not calling first frame methods once 2025-05-24 19:56:22 +03:00
832514ba7d docs: added documentation to draw & update interfaces 2025-05-24 13:59:36 +03:00
877a004a13 refactor: added pre, regular & post physics update interfaces 2025-05-24 13:59:07 +03:00
b1970d93f9 refactor: draw & update managers to use active & sorted by priority collector 2025-05-23 22:39:32 +03:00
e7bd924494 refactor: update & draw calls have been refactored into systems 2025-05-22 23:51:08 +03:00
37b87f0f85 feat: added post, regular & post events for Update and Draw 2025-05-22 23:10:47 +03:00
3b6a93d37a refactor: behaviour factory universe object parameter removed 2025-05-18 00:38:49 +03:00
0bf38234c6 feat: async serializer methods 2025-05-04 19:00:54 +03:00
ed6969c16a feat: progression trackers added 2025-05-04 18:57:26 +03:00
b0b421151f refactor: TypeFactory ReloadTypes made multithread friendly 2025-05-04 18:57:01 +03:00
41c5def097 refactor: renamed DelegateHelpers to DelegateExtensions 2025-05-04 18:52:47 +03:00
fbbdfb07fa chore: bumped .netcore version to 9 2025-05-04 18:46:21 +03:00
bf283d804c chore: updated Shape2D tween to look more aesthetic by choosing more linearly distributed vertices instead of the last vertex 2025-05-03 23:31:06 +03:00
063ea08707 feat: added RoundToInt RoundMode for midway values 2025-05-03 23:30:02 +03:00
fd11a94ddf refactor: easings have a singleton base so we don't create an unnecessary instance or cache everytime 2025-05-03 22:38:40 +03:00
be2295b92d feat: added engine member tween extensions 2025-05-03 22:23:52 +03:00
a93e55619c refactor: extracted interface from TweenManager 2025-05-03 22:23:28 +03:00
48ae24af47 chore: added safeguard value clamps for color operations 2025-05-03 22:21:58 +03:00
1366a417f1 feat: added Math.OneMinus method 2025-05-03 22:16:14 +03:00
4bfe98852c refactor: tween extensions method spacings fixed 2025-05-03 20:46:20 +03:00
98edbe1af5 chore: disabled all ImplicitUsings 2025-05-03 20:41:26 +03:00
3725a3b0fd feat: added preserver class & method to preserve assembly loading 2025-05-03 20:22:35 +03:00
f43ab36742 feat: added loggers 2025-05-03 17:01:58 +03:00
c7aafd85bc refactor: renamed assert helper and moved to Debug subfolder 2025-05-03 15:37:52 +03:00
5de08b8fe4 refactor: primitives now use Core.Math for math 2025-05-02 18:57:42 +03:00
16e4077d40 chore: HSV hue is normalized between 0 and 1 2025-05-02 18:54:08 +03:00
fc3c1ed1f9 refactor: Shape2D converted into a class as it has a reference type 2025-05-02 12:46:23 +03:00
b100b5c2fe feat: added color primitives 2025-05-02 00:51:58 +03:00
5e28ba8814 chore: updated README.md 2025-05-02 00:14:58 +03:00
4c235e3230 feat: added basic math operations as Math methods 2025-05-02 00:14:41 +03:00
131203d578 refactor: Yaml serialization moved from Core to own project 2025-05-02 00:00:03 +03:00
bd5eb432b7 feat: serialized state machine & states 2025-05-02 00:00:03 +03:00
d2ca85568f feat: entity register for serialized entity references 2025-05-02 00:00:03 +03:00
4c41870732 perf: made SerializedClass private and public fields optional 2025-05-02 00:00:03 +03:00
f77afa3632 chore: removed forgotten removed project reference 2025-05-02 00:00:03 +03:00
eb61598489 chore: reordered UniverserObjectSerializer fields for better readable yaml output 2025-05-02 00:00:03 +03:00
efe51b491d chore: universe serializer filters in only the root universe objects 2025-05-02 00:00:03 +03:00
fa3a4d1e0d feat: added universe serializer 2025-05-02 00:00:03 +03:00
6e7a0993f5 refactor: renamed converters to serializers 2025-05-02 00:00:03 +03:00
d70bee2c6b feat: serializable Transform2D 2025-05-02 00:00:03 +03:00
5812f43117 refactor: moved type container one directory up 2025-05-02 00:00:03 +03:00
d102c5471d feat: type container added back for field/property serialization 2025-05-02 00:00:03 +03:00
fb363970fc refactor: moved serialization into core project 2025-05-02 00:00:03 +03:00
791349686b chore: removed unused classes 2025-05-02 00:00:03 +03:00
3a0942ff46 fix: ignore serialization objects being included in serialization fixed 2025-05-02 00:00:03 +03:00
b002dd469a feat: behaviour & behaviour controller converters added 2025-05-02 00:00:03 +03:00
f92f36442c feat: state enable converted added 2025-05-02 00:00:03 +03:00
bb934b59f3 feat: wip universe object converter added 2025-05-02 00:00:03 +03:00
c704173183 feat: serialize all attribute 2025-05-02 00:00:03 +03:00
c3876add1e chore: added serialized entity class 2025-05-02 00:00:03 +03:00
35a75d993b chore: experimentations 2025-05-02 00:00:03 +03:00
2637f99456 fix: fixed fields/properties like behaviour controllers not being explored by entity finder 2025-05-02 00:00:03 +03:00
9581f5aa54 refactor: removed unnecessary logs 2025-05-02 00:00:02 +03:00
82cc25a9ef feat: entity finder added 2025-05-02 00:00:02 +03:00
336e7e16e7 chore: memberInfo.HasAttribute method added 2025-05-02 00:00:02 +03:00
a3a8fb4e84 chore: depth limit for debugging 2025-05-01 23:59:43 +03:00
35f6c3850e fix: GetTypeData not including base class proprety & fields 2025-04-28 22:29:08 +03:00
f51d5f342e chore: added a generic converter 2025-04-28 22:29:08 +03:00
9c129cefe2 feat: added state enable serialization 2025-04-28 22:29:08 +03:00
a254bb721b chore: changed entity reference order 2025-04-28 22:29:08 +03:00
5fa7420c04 feat: added entity converter 2025-04-28 22:29:08 +03:00
5bcc256777 feat: added type container serialization 2025-04-28 22:29:08 +03:00
680d718957 chore: moved primitive converters under subfolder 2025-04-28 22:29:08 +03:00
20bc6a1adb chore: updated to forked version of YamlDotNet that fixes sequence indentations 2025-04-28 22:29:08 +03:00
eb454a471c feat: added primitive serialization 2025-04-28 22:29:08 +03:00
c205e710bc chore: some experimentations with DotNetYaml 2025-04-28 22:29:08 +03:00
cddb30c631 refactor: optimized & added reload method for type factory 2025-04-28 22:26:33 +03:00
29f6c83bf0 chore: removed unnecessary partial keyword 2025-04-27 22:28:35 +03:00
c20f210b29 refactor: rewritten GetType in a more readable way 2025-04-27 22:28:21 +03:00
1ea1844677 fix: Transform2D not raising OnPositionChanged event with correct parameters 2025-04-26 14:26:17 +03:00
5b2c13f8bf fix: BehaviourController assigning a new state enable to all newly added behaviours fixed 2025-04-26 14:10:40 +03:00
c39ee44442 fix: behaviour controller initializing added behaviours when it itself is not initialized 2025-04-25 21:54:05 +03:00
4623b4861a fix: behaviour controllers of universe objects not being initialized 2025-04-25 21:26:01 +03:00
0a868b82e5 fix: behaviour controller not respecting it's own state enable 2025-04-25 21:05:20 +03:00
d92d16cfad refactor: IBehaviourController is now an IEntity as well 2025-04-22 15:50:26 +03:00
0184d1758c feat: added more methods for TypeFactory 2025-04-20 00:06:48 +03:00
6e5b805803 chore: updated core diagram 2025-04-15 23:41:07 +03:00
8293c58f9f refactor: removed X.Abstract namespaces and moved StateMachine to under Systems namespace 2025-04-15 23:33:58 +03:00
94d01521d4 feat: IUniverse.OnTimeChanged event added 2025-04-14 12:19:23 +03:00
209 changed files with 5845 additions and 1181 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "Engine.Serializers/YamlDotNet"]
path = Engine.Serializers/YamlDotNet
url = git@github.com:Syntriax/YamlDotNet.git

View File

@@ -482,3 +482,5 @@ $RECYCLE.BIN/
# Vim temporary swap files # Vim temporary swap files
*.swp *.swp
!Debug

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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="IUniverse"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
@@ -8,7 +8,7 @@ public interface IHasUniverse : IAssignable
/// <summary> /// <summary>
/// Event triggered when the <see cref="IUniverse"/> value has has been assigned a new value. /// Event triggered when the <see cref="IUniverse"/> value has has been assigned a new value.
/// </summary> /// </summary>
event UniverseAssignedEventHandler? OnUniverseAssigned; Event<IHasUniverse> OnUniverseAssigned { get; }
/// <inheritdoc cref="IUniverse" /> /// <inheritdoc cref="IUniverse" />
IUniverse Universe { get; } IUniverse Universe { get; }
@@ -21,6 +21,4 @@ public interface IHasUniverse : 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(IUniverse universe); bool Assign(IUniverse universe);
delegate void UniverseAssignedEventHandler(IHasUniverse sender);
} }

View File

@@ -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="IUniverseObject"/> field. /// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
@@ -8,7 +8,7 @@ public interface IHasUniverseObject : IAssignable
/// <summary> /// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> value has has been assigned a new value. /// Event triggered when the <see cref="IUniverseObject"/> value has has been assigned a new value.
/// </summary> /// </summary>
event UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned; Event<IHasUniverseObject> OnUniverseObjectAssigned { get; }
/// <inheritdoc cref="IUniverseObject" /> /// <inheritdoc cref="IUniverseObject" />
IUniverseObject UniverseObject { get; } IUniverseObject UniverseObject { get; }
@@ -21,6 +21,4 @@ public interface IHasUniverseObject : 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(IUniverseObject universeObject); bool Assign(IUniverseObject universeObject);
delegate void UniverseObjectAssignedEventHandler(IHasUniverseObject sender);
} }

View File

@@ -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);
} }

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
/// Represents a behaviour that any object in the engine that 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.
@@ -8,12 +8,12 @@ public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasSta
/// <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);
} }

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core;
public interface IBehaviour2D : IBehaviour public interface IBehaviour2D : IBehaviour
{ {

View File

@@ -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="IUniverse"/>. /// 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> : IHasUniverse, 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);
} }

View File

@@ -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="IUniverseObject"/>. /// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>.
/// </summary> /// </summary>
public interface IBehaviourController : IInitializable, IHasUniverseObject, 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, IHasUniverseObject, IEnu
/// <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);
} }

View File

@@ -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.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core;
public interface ICoroutineYield public interface ICoroutineYield
{ {

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s. /// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
@@ -10,27 +10,47 @@ public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
/// <summary> /// <summary>
/// Event triggered when <see cref="Update(UniverseTime)"/> is about to be called called on the <see cref="IUniverse"/>. /// Event triggered when <see cref="Update(UniverseTime)"/> is about to be called called on the <see cref="IUniverse"/>.
/// </summary> /// </summary>
event UpdateEventHandler? OnPreUpdate; Event<IUniverse, UpdateArguments> OnPreUpdate { get; }
/// <summary> /// <summary>
/// Event triggered when <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>. /// Event triggered when <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
/// </summary> /// </summary>
event UpdateEventHandler? OnUpdate; Event<IUniverse, UpdateArguments> OnUpdate { get; }
/// <summary> /// <summary>
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IUniverse"/>. /// Event triggered after <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
/// </summary> /// </summary>
event PreDrawEventHandler? OnPreDraw; 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> /// <summary>
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>. /// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
/// </summary> /// </summary>
event UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered; Event<IUniverse, UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; }
/// <summary> /// <summary>
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>. /// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
/// </summary> /// </summary>
event UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered; 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> /// <summary>
/// Current time scale the <see cref="IUniverse"/> operates on. /// Current time scale the <see cref="IUniverse"/> operates on.
@@ -79,13 +99,12 @@ public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
void Update(UniverseTime universeTime); void Update(UniverseTime universeTime);
/// <summary> /// <summary>
/// Performs operations that should be done before the draw calls. /// Performs operations that should be done to the draw.
/// </summary> /// </summary>
void PreDraw(); void Draw();
delegate void UpdateEventHandler(IUniverse sender, UniverseTime engineTime); readonly record struct TimeScaleChangedArguments(float PreviousTimeScale);
delegate void PreDrawEventHandler(IUniverse sender); readonly record struct UpdateArguments(UniverseTime EngineTime);
readonly record struct UniverseObjectRegisteredArguments(IUniverseObject UniverseObjectRegistered);
delegate void UniverseObjectRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectRegistered); readonly record struct UniverseObjectUnRegisteredArguments(IUniverseObject UniverseObjectUnregistered);
delegate void UniverseObjectUnRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectUnregistered);
} }

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system. /// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
@@ -12,27 +12,27 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
/// <summary> /// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe. /// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
/// </summary> /// </summary>
event EnteredUniverseEventHandler? OnEnteredUniverse; Event<IUniverseObject, EnteredUniverseArguments> OnEnteredUniverse { get; }
/// <summary> /// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> exits the universe. /// Event triggered when the <see cref="IUniverseObject"/> exits the universe.
/// </summary> /// </summary>
event ExitedUniverseEventHandler? OnExitedUniverse; Event<IUniverseObject, ExitedUniverseArguments> OnExitedUniverse { get; }
/// <summary> /// <summary>
/// Event triggered when the <see cref="Parent"/> of the <see cref="IUniverseObject"/> changes. The second parameter is the old <see cref="IUniverseObject"/>. /// Event triggered when the <see cref="Parent"/> of the <see cref="IUniverseObject"/> changes. The second parameter is the old <see cref="IUniverseObject"/>.
/// </summary> /// </summary>
event ParentChangedEventHandler? OnParentChanged; Event<IUniverseObject, ParentChangedArguments> OnParentChanged { get; }
/// <summary> /// <summary>
/// Event triggered when a new <see cref="IUniverseObject"/> is added to the <see cref="Children"/>. /// Event triggered when a new <see cref="IUniverseObject"/> is added to the <see cref="Children"/>.
/// </summary> /// </summary>
event ChildrenAddedEventHandler? OnChildrenAdded; Event<IUniverseObject, ChildrenAddedArguments> OnChildrenAdded { get; }
/// <summary> /// <summary>
/// Event triggered when an <see cref="IUniverseObject"/> is removed from the <see cref="Children"/>. /// Event triggered when an <see cref="IUniverseObject"/> is removed from the <see cref="Children"/>.
/// </summary> /// </summary>
event ChildrenRemovedEventHandler? OnChildrenRemoved; Event<IUniverseObject, ChildrenRemovedArguments> OnChildrenRemoved { get; }
/// <summary> /// <summary>
/// Gets the <see cref="IUniverse"/> this <see cref="IUniverseObject"/> is connected to, if any. /// Gets the <see cref="IUniverse"/> this <see cref="IUniverseObject"/> is connected to, if any.
@@ -94,38 +94,38 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
void RemoveChild(IUniverseObject universeObject); void RemoveChild(IUniverseObject universeObject);
/// <summary> /// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> enters the universe of a <see cref="IUniverse">. /// Arguments for the event triggered when the <see cref="IUniverseObject"/> enters the universe of a <see cref="IUniverse">.
/// </summary> /// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that entered the universe.</param> /// <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> /// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has entered it's universe.</param>
delegate void EnteredUniverseEventHandler(IUniverseObject sender, IUniverse universe); readonly record struct EnteredUniverseArguments(IUniverse Universe);
/// <summary> /// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> exits the universe of a <see cref="IUniverse">. /// Arguments for the event triggered when the <see cref="IUniverseObject"/> exits the universe of a <see cref="IUniverse">.
/// </summary> /// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that exited the universe.</param> /// <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> /// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has exited it's universe.</param>
delegate void ExitedUniverseEventHandler(IUniverseObject sender, IUniverse universe); readonly record struct ExitedUniverseArguments(IUniverse Universe);
/// <summary> /// <summary>
/// Delegate for the event triggered when the <see cref="IUniverseObject"/>'s parent changes. /// Arguments for the event triggered when the <see cref="IUniverseObject"/>'s parent changes.
/// </summary> /// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that the parent has changed.</param> /// <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="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> /// <param name="newParent">The new and current <see cref="IUniverseObject"/> the sender is a child of.</param>
delegate void ParentChangedEventHandler(IUniverseObject sender, IUniverseObject? previousParent, IUniverseObject? newParent); readonly record struct ParentChangedArguments(IUniverseObject? PreviousParent, IUniverseObject? CurrentParent);
/// <summary> /// <summary>
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> added as a child. /// Arguments for the event triggered when a new <see cref="IUniverseObject"/> added as a child.
/// </summary> /// </summary>
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param> /// <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> /// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
delegate void ChildrenAddedEventHandler(IUniverseObject sender, IUniverseObject childrenAdded); readonly record struct ChildrenAddedArguments(IUniverseObject ChildrenAdded);
/// <summary> /// <summary>
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> removed from being a child. /// Delegate for the event triggered when a new <see cref="IUniverseObject"/> removed from being a child.
/// </summary> /// </summary>
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param> /// <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> /// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
delegate void ChildrenRemovedEventHandler(IUniverseObject sender, IUniverseObject childrenRemoved); readonly record struct ChildrenRemovedArguments(IUniverseObject ChildrenRemoved);
} }

View File

@@ -1,91 +1,92 @@
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 IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = 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 IUniverse Universe { get; private set; } = null!;
public T this[Index index] => activeBehaviours[index]; private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IUniverse universe) => Assign(universe);
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
{ {
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded; IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in universeObject.BehaviourController) universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
OnBehaviourAdded(universeObject.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 OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject) private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{ {
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded; IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in universeObject.BehaviourController) universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
OnBehaviourRemoved(universeObject.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?.InvokeSafe(this, behaviour); OnCollected?.Invoke(this, new(behaviour));
} }
else if (activeBehaviours.Remove(behaviour)) else if (activeBehaviours.Remove(behaviour))
{ {
OnBehaviourRemove(behaviour); OnBehaviourRemove(behaviour);
OnRemoved?.InvokeSafe(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?.InvokeSafe(this, tBehaviour); OnRemoved?.Invoke(this, new(tBehaviour));
} }
} }
@@ -95,13 +96,13 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return false; return false;
foreach (IUniverseObject universeObject in universe.UniverseObjects) foreach (IUniverseObject universeObject in universe.UniverseObjects)
OnUniverseObjectRegistered(universe, universeObject); OnUniverseObjectRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered; universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered; universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
Universe = universe; Universe = universe;
OnUniverseAssigned?.InvokeSafe(this); OnUniverseAssigned?.Invoke(this);
return true; return true;
} }
@@ -112,16 +113,36 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return false; return false;
foreach (IUniverseObject universeObject in Universe.UniverseObjects) foreach (IUniverseObject universeObject in Universe.UniverseObjects)
OnUniverseObjectUnregistered(Universe, universeObject); OnUniverseObjectUnregistered(Universe, new(universeObject));
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered; Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered; Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
Universe = null!; Universe = null!;
OnUnassigned?.InvokeSafe(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);
}
} }

View File

@@ -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(IUniverse universe, Comparison<T> sortBy) : base(universe) => 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);
}
} }

View File

@@ -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?.InvokeSafe(this, previousId); OnIdChanged?.Invoke(this, new(previousId));
} }
} }
@@ -47,9 +45,9 @@ public abstract class BaseEntity : IEntity
_initialized = value; _initialized = value;
if (value) if (value)
OnInitialized?.InvokeSafe(this); OnInitialized?.Invoke(this);
else else
OnFinalized?.InvokeSafe(this); OnFinalized?.Invoke(this);
} }
} }
@@ -62,7 +60,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = stateEnable; _stateEnable = stateEnable;
_stateEnable.Assign(this); _stateEnable.Assign(this);
OnAssign(stateEnable); OnAssign(stateEnable);
OnStateEnableAssigned?.InvokeSafe(this); OnStateEnableAssigned?.Invoke(this);
return true; return true;
} }
@@ -76,7 +74,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = null!; _stateEnable = null!;
_stateEnable.Unassign(); _stateEnable.Unassign();
OnUnassigned?.InvokeSafe(this); OnUnassigned?.Invoke(this);
return true; return true;
} }

View File

@@ -1,111 +1,50 @@
using Syntriax.Engine.Core.Abstract;
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 IUniverse Universe => BehaviourController.UniverseObject.Universe;
protected IUniverseObject UniverseObject => BehaviourController.UniverseObject;
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.UniverseObject.OnEnteredUniverse += EnteredUniverse;
BehaviourController.UniverseObject.OnExitedUniverse += ExitedUniverse;
OnInitialize(); OnInitialize();
if (UniverseObject.IsInUniverse) if (UniverseObject.IsInUniverse)
EnteredUniverse(UniverseObject, Universe); 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.UniverseObject.OnEnteredUniverse -= EnteredUniverse;
BehaviourController.UniverseObject.OnExitedUniverse -= ExitedUniverse;
OnFinalize(); OnFinalize();
if (UniverseObject.IsInUniverse) if (UniverseObject.IsInUniverse)
ExitedUniverse(UniverseObject, Universe); ExitedUniverse(UniverseObject, new(Universe));
}
protected virtual void OnPreUpdatePreActiveCheck() { }
protected virtual void OnPreUpdate() { }
protected virtual void PreUpdate(IBehaviourController _)
{
Debug.AssertHelpers.AssertInitialized(this);
OnPreUpdatePreActiveCheck();
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 _)
{
Debug.AssertHelpers.AssertInitialized(this);
OnUpdatePreActiveCheck();
if (!IsActive)
return;
OnUpdate();
}
protected virtual void OnPreDrawPreActiveCheck() { }
protected virtual void OnPreDraw() { }
protected virtual void PreDraw(IBehaviourController _)
{
Debug.AssertHelpers.AssertInitialized(this);
OnPreDrawPreActiveCheck();
if (!StateEnable.Enabled)
return;
OnPreDraw();
} }
protected virtual void OnEnteredUniverse(IUniverse universe) { } protected virtual void OnEnteredUniverse(IUniverse universe) { }
protected virtual void EnteredUniverse(IUniverseObject sender, IUniverse universe) => OnEnteredUniverse(universe); protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
protected virtual void OnExitedUniverse(IUniverse universe) { } protected virtual void OnExitedUniverse(IUniverse universe) { }
protected virtual void ExitedUniverse(IUniverseObject sender, IUniverse universe) => OnExitedUniverse(universe); protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
} }

View File

@@ -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 EnteredUniverse(IUniverseObject sender, IUniverse universe) => base.EnteredUniverse(sender, universe);
protected sealed override void ExitedUniverse(IUniverseObject sender, IUniverse universe) => base.ExitedUniverse(sender, universe);
} }

View File

@@ -1,13 +1,18 @@
using Syntriax.Engine.Core.Abstract;
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;
@@ -23,7 +28,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
int previousPriority = _priority; int previousPriority = _priority;
_priority = value; _priority = value;
OnPriorityChanged?.InvokeSafe(this, previousPriority); OnPriorityChanged?.Invoke(this, new(previousPriority));
} }
} }
@@ -38,16 +43,16 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
_behaviourController = behaviourController; _behaviourController = behaviourController;
OnAssign(behaviourController); OnAssign(behaviourController);
behaviourController.OnUniverseObjectAssigned += OnUniverseObjectAssigned; behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
if (behaviourController.UniverseObject is not null) if (behaviourController.UniverseObject is not null)
OnUniverseObjectAssigned(behaviourController); OnUniverseObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.InvokeSafe(this); OnBehaviourControllerAssigned?.Invoke(this);
return true; return true;
} }
private void OnUniverseObjectAssigned(IHasUniverseObject sender) private void OnUniverseObjectAssigned(IHasUniverseObject sender)
{ {
sender.UniverseObject.OnActiveChanged += OnUniverseObjectActiveChanged; sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
UpdateActive(); UpdateActive();
} }
@@ -55,25 +60,26 @@ 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.OnUniverseObjectAssigned -= OnUniverseObjectAssigned; StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
base.UnassignInternal(); base.UnassignInternal();
_behaviourController = null!; _behaviourController = null!;
} }
protected override void InitializeInternal() protected override void InitializeInternal()
{ {
Debug.AssertHelpers.AssertBehaviourControllerAssigned(this); Debug.Assert.AssertBehaviourControllerAssigned(this);
Debug.AssertHelpers.AssertStateEnableAssigned(this); Debug.Assert.AssertStateEnableAssigned(this);
} }
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive(); private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
private void OnUniverseObjectActiveChanged(IActive sender, bool previousState) => UpdateActive(); private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
private void UpdateActive() private void UpdateActive()
{ {
@@ -81,6 +87,13 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
_isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive; _isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
if (previousActive != IsActive) if (previousActive != IsActive)
OnActiveChanged?.InvokeSafe(this, previousActive); OnActiveChanged?.Invoke(this, new(previousActive));
}
protected BehaviourBase()
{
delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
delegateOnStateEnabledChanged = OnStateEnabledChanged;
} }
} }

View File

@@ -1,69 +1,69 @@
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 IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = 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 IUniverse Universe { get; private set; } = null!;
public T this[Index index] => behaviours[index]; private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
public BehaviourCollector() { }
public BehaviourCollector(IUniverse universe) => Assign(universe);
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
{ {
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded; IUniverseObject universeObject = args.UniverseObjectRegistered;
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in universeObject.BehaviourController) universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
OnBehaviourAdded(universeObject.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 OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject) private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
{ {
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded; IUniverseObject universeObject = args.UniverseObjectUnregistered;
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in universeObject.BehaviourController) universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
OnBehaviourRemoved(universeObject.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?.InvokeSafe(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?.InvokeSafe(this, tBehaviour); OnRemoved?.Invoke(this, new(tBehaviour));
} }
protected virtual void OnAssign(IUniverse universe) { } protected virtual void OnAssign(IUniverse universe) { }
@@ -73,14 +73,14 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return false; return false;
foreach (IUniverseObject universeObject in universe.UniverseObjects) foreach (IUniverseObject universeObject in universe.UniverseObjects)
OnUniverseObjectRegistered(universe, universeObject); OnUniverseObjectRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered; universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered; universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
Universe = universe; Universe = universe;
OnAssign(universe); OnAssign(universe);
OnUniverseAssigned?.InvokeSafe(this); OnUniverseAssigned?.Invoke(this);
return true; return true;
} }
@@ -91,16 +91,34 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return false; return false;
foreach (IUniverseObject universeObject in Universe.UniverseObjects) foreach (IUniverseObject universeObject in Universe.UniverseObjects)
OnUniverseObjectUnregistered(Universe, universeObject); OnUniverseObjectUnregistered(Universe, new(universeObject));
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered; Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered; Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
Universe = null!; Universe = null!;
OnUnassigned?.InvokeSafe(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);
}
} }

View File

@@ -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(IUniverse universe, Comparison<T> sortBy) : base(universe) => 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);
}
} }

View File

@@ -1,66 +1,43 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Syntriax.Engine.Core.Abstract;
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 IHasUniverseObject.UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
private IUniverseObject _universeObject = null!; private IUniverseObject _universeObject = null!;
private bool _initialized = false;
public IUniverseObject UniverseObject => _universeObject; public IUniverseObject UniverseObject => _universeObject;
public int Count => behaviours.Count;
public bool IsInitialized public IBehaviour this[Index index] => behaviours[index];
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.InvokeSafe(this);
else
OnFinalized?.InvokeSafe(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?.InvokeSafe(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>(_universeObject, args)); {
T behaviour = Factory.BehaviourFactory.Instantiate<T>(args);
return AddBehaviour(behaviour);
}
public T? GetBehaviour<T>() public T? GetBehaviour<T>()
{ {
@@ -117,10 +94,10 @@ public class BehaviourController : IBehaviourController
if (!behaviours.Contains(behaviour)) if (!behaviours.Contains(behaviour))
throw new Exception($"{behaviour.GetType().Name} does not exist in {UniverseObject.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?.InvokeSafe(this, behaviour); OnBehaviourRemoved?.Invoke(this, new(behaviour));
} }
protected virtual void OnAssign(IUniverseObject universeObject) { } protected virtual void OnAssign(IUniverseObject universeObject) { }
@@ -131,61 +108,22 @@ public class BehaviourController : IBehaviourController
_universeObject = universeObject; _universeObject = universeObject;
OnAssign(universeObject); OnAssign(universeObject);
OnUniverseObjectAssigned?.InvokeSafe(this); OnUniverseObjectAssigned?.Invoke(this);
return true; return true;
} }
public bool Initialize() protected override void InitializeInternal()
{ {
if (IsInitialized) Debug.Assert.AssertUniverseObjectAssigned(this);
return false;
Debug.AssertHelpers.AssertUniverseObjectAssigned(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;
_universeObject = null!;
OnUnassigned?.InvokeSafe(this);
return true;
}
public void Update()
{
if (!UniverseObject.StateEnable.Enabled)
return;
OnPreUpdate?.InvokeSafe(this);
OnUpdate?.InvokeSafe(this);
}
public void UpdatePreDraw()
{
if (!UniverseObject.StateEnable.Enabled)
return;
OnPreDraw?.InvokeSafe(this);
} }
public BehaviourController() { } public BehaviourController() { }
@@ -208,12 +146,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();
} }

View File

@@ -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 : UniverseObject public class CoroutineManager : Behaviour, IUpdate
{ {
private readonly List<IEnumerator> enumerators = []; private readonly List<IEnumerator> enumerators = [];
@@ -20,17 +18,7 @@ public class CoroutineManager : UniverseObject
enumerators.Remove(enumerator); enumerators.Remove(enumerator);
} }
protected override void OnEnteringUniverse(IUniverse universe) void IUpdate.Update()
{
universe.OnUpdate += OnUpdate;
}
protected override void OnExitingUniverse(IUniverse universe)
{
universe.OnUpdate -= OnUpdate;
}
private void OnUpdate(IUniverse sender, UniverseTime 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 : UniverseObject
enumerators.RemoveAt(i); enumerators.RemoveAt(i);
} }
} }
public CoroutineManager() => Priority = int.MinValue;
} }

View File

@@ -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

View File

@@ -1,10 +1,8 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core.Debug; namespace Syntriax.Engine.Core.Debug;
public class AssertHelpers public static class Assert
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertInitialized(IInitializable initializable) public static void AssertInitialized(IInitializable initializable)

View 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);
}

View File

@@ -0,0 +1,20 @@
using System;
using System.IO;
namespace Syntriax.Engine.Core.Debug;
public class FileLogger : LoggerBase
{
public readonly string FilePath;
public FileLogger(string filePath)
{
FilePath = filePath;
File.Open(filePath, FileMode.Create).Close();
}
protected override void Write(string message)
{
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
}
}

View File

@@ -0,0 +1,15 @@
namespace Syntriax.Engine.Core.Debug;
public interface ILogger
{
Level FilterLevel { get; set; }
void Log(string message, Level level = Level.Info, bool force = false);
enum Level
{
Info,
Warning,
Error,
};
}

View 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.Info;
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);
}

View File

@@ -0,0 +1,28 @@
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);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}");
}
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
{
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}");
}
}

View File

@@ -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>

View File

@@ -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.IUniverseObject 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.IUniverseObject --> 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.IUniverse {} interface Engine.Core.IUniverse {}
Engine.Core.Abstract.IUniverse "1" -r-> "0..*" Engine.Core.Abstract.IUniverseObject: 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.IHasUniverse extends Engine.Core.Abstract.IAssignable {} ' interface Engine.Core.IHasUniverse extends Engine.Core.IAssignable {}
' interface Engine.Core.Abstract.IHasUniverseObject 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.IHasUniverse --> Engine.Core.Abstract.IUniverse: has ' ' Engine.Core.IHasUniverse --> Engine.Core.IUniverse: has
' ' Engine.Core.Abstract.IHasUniverseObject --> Engine.Core.Abstract.IUniverseObject: 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

View File

@@ -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");
}

View 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");
}

View File

@@ -1,9 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Exceptions; namespace Syntriax.Engine.Core.Exceptions;
public class UniverseObjectNotFoundException(string? message) : Exception(message) public class UniverseObjectNotFoundException(string? message) : NotFoundException(message);
{
public static NotAssignedException FromType<TUniverseObject>()
=> new($"{typeof(TUniverseObject).FullName} was not found");
}

View File

@@ -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;
@@ -39,6 +39,19 @@ public static class BehaviourControllerExtensions
public static T GetOrAddBehaviour<T>(this IBehaviourController behaviourController, params object?[]? args) where T : class, IBehaviour public static T GetOrAddBehaviour<T>(this IBehaviourController behaviourController, params object?[]? args) where T : class, IBehaviour
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args); => behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
/// <summary>
/// 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> /// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively. /// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
/// </summary> /// </summary>
@@ -82,6 +95,27 @@ public static class BehaviourControllerExtensions
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.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent"); => behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
/// <summary>
/// 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> /// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively. /// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
/// </summary> /// </summary>
@@ -121,4 +155,28 @@ public static class BehaviourControllerExtensions
/// <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.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children "); => behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'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)
TraverseChildrenForBehaviour(child, behaviours, cache);
}
} }

View File

@@ -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<IUniverseObject> universeObjects) where T : class
{
foreach (IUniverseObject universeObject in universeObjects)
if (universeObject.BehaviourController.GetBehaviour<T>() is T behaviour)
return behaviour;
return default;
}
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;
}
public static void FindBehaviours<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
List<T> cache = [];
foreach (IUniverseObject universeObject in universeObjects)
{
universeObject.BehaviourController.GetBehaviours(cache);
behaviours.AddRange(cache);
}
}
}

View File

@@ -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

View File

@@ -1,4 +1,3 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions; using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
@@ -8,9 +7,30 @@ public static class UniverseExtensions
public static IUniverseObject InstantiateUniverseObject(this IUniverse universe, params object?[]? args) public static IUniverseObject InstantiateUniverseObject(this IUniverse universe, params object?[]? args)
=> universe.InstantiateUniverseObject<UniverseObject>(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 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}"); => 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 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}"); => 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}");
} }

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
@@ -16,6 +16,13 @@ public static class UniverseObjectExtensions
return universeObject; 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 public static T? GetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
{ {
foreach (IUniverseObject universeObject in universeObjects) foreach (IUniverseObject universeObject in universeObjects)
@@ -25,17 +32,224 @@ public static class UniverseObjectExtensions
return default; return default;
} }
public static bool TryGetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class /// <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
{ {
behaviour = GetUniverseObject<T>(universeObjects); 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; return behaviour is not null;
} }
public static void GetUniverseObjects<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class /// <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.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.GetUniverseObject<T>() is T localUniverseObject)
return localUniverseObject;
foreach (IUniverseObject child in universeObject)
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(); behaviours.Clear();
List<T> cache = [];
foreach (IUniverseObject universeObject in universeObjects) foreach (IUniverseObject universeObject in universeObjects)
if (universeObject is T @object) {
behaviours.Add(@object); 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.Find(cache);
foreach (T behaviour in cache)
instances.Add(behaviour);
}
}
#endregion
}

View File

@@ -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;

View File

@@ -1,14 +1,13 @@
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>(IUniverseObject universeObject, params object?[]? args) where T : class, IBehaviour public static T Instantiate<T>(params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(universeObject, stateEnable: null, args); => Instantiate<T>(stateEnable: null, args);
public static T Instantiate<T>(IUniverseObject universeObject, 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);
@@ -19,8 +18,6 @@ public class BehaviourFactory
if (!behaviour.Assign(stateEnable)) if (!behaviour.Assign(stateEnable))
throw AssignFailedException.From(behaviour, stateEnable); throw AssignFailedException.From(behaviour, stateEnable);
if (!behaviour.Assign(universeObject.BehaviourController))
throw AssignFailedException.From(behaviour, universeObject.BehaviourController);
return behaviour; return behaviour;
} }

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
} }

View File

@@ -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;

View File

@@ -1,17 +0,0 @@
using System;
namespace Syntriax.Engine.Core;
public static class DelegateHelpers
{
public static void InvokeSafe(this Delegate @delegate, params object?[] args)
{
foreach (Delegate invocation in @delegate.GetInvocationList())
try { invocation.DynamicInvoke(args); }
catch (Exception exception)
{
string methodCallRepresentation = $"{invocation.Method.DeclaringType?.FullName}.{invocation.Method.Name}({string.Join(", ", args)})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
public class Event
{
private readonly List<EventHandler> listeners = null!;
private readonly List<EventHandler> onceListeners = null!;
public void AddListener(EventHandler listener) => listeners.Add(listener);
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
public void Invoke()
{
for (int i = 0; i < listeners.Count; i++)
try { listeners[i].Invoke(); }
catch (Exception exception)
{
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}()";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
for (int i = onceListeners.Count - 1; i >= 0; i--)
{
try { onceListeners[i].Invoke(); }
catch (Exception exception)
{
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}()";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
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();
}
public class Event<TSender>
{
private readonly List<EventHandler> listeners = null!;
private readonly List<EventHandler> onceListeners = null!;
public void AddListener(EventHandler listener) => listeners.Add(listener);
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
public void Invoke(TSender sender)
{
for (int i = 0; i < listeners.Count; i++)
try { listeners[i].Invoke(sender); }
catch (Exception exception)
{
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({sender})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
for (int i = onceListeners.Count - 1; i >= 0; i--)
{
try { onceListeners[i].Invoke(sender); }
catch (Exception exception)
{
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({sender})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
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);
}
public class Event<TSender, TArguments>
{
private readonly List<EventHandler> listeners = null!;
private readonly List<EventHandler> onceListeners = null!;
public void AddListener(EventHandler listener) => listeners.Add(listener);
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
public void Invoke(TSender sender, TArguments args)
{
for (int i = 0; i < listeners.Count; i++)
try { listeners[i].Invoke(sender, args); }
catch (Exception exception)
{
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({string.Join(", ", sender, args)})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
for (int i = onceListeners.Count - 1; i >= 0; i--)
{
try { onceListeners[i].Invoke(sender, args); }
catch (Exception exception)
{
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({string.Join(", ", sender, args)})";
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
}
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);
}

View 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);
}

View 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(Func<List<T>> generator, int initialCapacity = 1)
{
this.generator = generator;
for (int i = 0; i < initialCapacity; i++)
queue.Enqueue(generator());
}
}

View 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());
}
}

View File

@@ -0,0 +1,7 @@
namespace Syntriax.Engine.Core;
public interface IProgressionTracker : IReadOnlyProgressionTracker
{
void Set(float progression, string status);
void Reset();
}

View File

@@ -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);
}

View 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();
}
}

View 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);
}

View File

@@ -30,6 +30,64 @@ public static class Math
/// </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.
/// </summary> /// </summary>
@@ -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>

View File

@@ -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
View 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() { }
}
}

View File

@@ -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>

View File

@@ -0,0 +1,185 @@
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().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f));
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f));
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f));
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value);
public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value);
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba;
public static implicit operator ColorHSV(ColorRGB rgb)
{
float hue;
float saturation;
float value;
float rd = rgb.R / 255f;
float gd = rgb.G / 255f;
float bd = rgb.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);
}
/// <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>
/// Calculates the <see cref="ColorHSV"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorHSV"/> from the starting point to the ending point.</returns>
public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from;
/// <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) => from + FromTo(from, to) * t;
/// <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>
/// 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 objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value);
/// <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>
/// 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.FromTo(ColorHSV, ColorHSV)" />
public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to);
/// <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);
}

View File

@@ -0,0 +1,164 @@
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(ColorHSV hsv)
{
float hue = hsv.Hue * 360f;
float chroma = hsv.Value * hsv.Saturation;
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
float m = hsv.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);
}
/// <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>
/// Calculates the <see cref="ColorRGB"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGB"/> from the starting point to the ending point.</returns>
public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from;
/// <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) => from + FromTo(from, to) * t;
/// <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>
/// 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 objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B);
/// <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>
/// 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.FromTo(ColorRGB, ColorRGB)" />
public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to);
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
}

View File

@@ -0,0 +1,147 @@
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) => (ColorRGB)hsv;
/// <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>
/// Calculates the <see cref="ColorRGBA"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGBA"/> from the starting point to the ending point.</returns>
public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from;
/// <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) => from + FromTo(from, to) * t;
/// <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>
/// 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 objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A);
/// <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>
/// 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.FromTo(ColorRGBA, ColorRGBA)" />
public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to);
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
@@ -68,12 +67,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 +113,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 +167,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>
@@ -206,6 +202,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 +214,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);
} }

View File

@@ -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>
@@ -301,7 +299,7 @@ public readonly struct Quaternion(float x, float y, float z, float w)
/// 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(X, Y, Z);
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,66 @@
namespace Syntriax.Engine.Core;
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 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>
/// 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);
}

View File

@@ -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.
@@ -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>
@@ -277,13 +291,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);

View File

@@ -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}")]
@@ -10,7 +8,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
public readonly Vector2D C { get; init; } = C; public readonly Vector2D C { get; init; } = 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 +23,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;

View File

@@ -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>
@@ -317,7 +319,7 @@ public readonly struct Vector2D(float x, float y)
/// 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> /// <summary>

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
@@ -290,7 +288,7 @@ public readonly struct Vector3D(float x, float y, float z)
/// 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> /// <summary>

View File

@@ -0,0 +1,6 @@
using System;
namespace Syntriax.Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)]
public class IgnoreSerializationAttribute : Attribute;

View File

@@ -0,0 +1,6 @@
using System;
namespace Syntriax.Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class SerializeAllAttribute : Attribute;

View File

@@ -0,0 +1,6 @@
using System;
namespace Syntriax.Engine.Core.Serialization;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class SerializeAttribute : Attribute;

View File

@@ -0,0 +1,3 @@
namespace Syntriax.Engine.Core.Serialization;
public record class EntityReference(string? Id = null);

View 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);
}

View File

@@ -0,0 +1,18 @@
using System;
namespace Syntriax.Engine.Core.Serialization;
public interface ISerializer
{
object Deserialize(string configuration);
object Deserialize(string configuration, Type type);
T Deserialize<T>(string configuration);
string Serialize(object instance);
ProgressiveTask<object> DeserializeAsync(string configuration);
ProgressiveTask<object> DeserializeAsync(string configuration, Type type);
ProgressiveTask<T> DeserializeAsync<T>(string configuration);
ProgressiveTask<string> SerializeAsync(object instance);
}

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Syntriax.Engine.Core.Factory;
namespace Syntriax.Engine.Core.Serialization;
public class SerializedClass
{
private const BindingFlags PRIVATE_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.NonPublic;
private const BindingFlags PUBLIC_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public;
public string Type { get; set; } = string.Empty;
public Dictionary<string, object?> Public { get; set; } = [];
public Dictionary<string, object?> Private { get; set; } = [];
public SerializedClass() { }
public SerializedClass(object @class)
{
UpdateClass(@class);
}
private void UpdateClass(object @class)
{
Type type = @class.GetType();
Type = type.FullName ?? type.Name;
bool shouldSerializeAll = type.HasAttribute<SerializeAllAttribute>();
Public.Clear();
Private.Clear();
foreach (PropertyInfo privatePropertyInfo in Utils.GetPropertyInfosIncludingBaseClasses(type, PRIVATE_BINDING_FLAGS))
{
if (privatePropertyInfo.HasAttribute<IgnoreSerializationAttribute>())
continue;
if (privatePropertyInfo.SetMethod is null)
continue;
if (!shouldSerializeAll && !privatePropertyInfo.HasAttribute<SerializeAttribute>())
continue;
object? value = privatePropertyInfo.GetValue(@class);
if (value is IEntity entity)
Private.Add(privatePropertyInfo.Name, entity.Id);
else
Private.Add(privatePropertyInfo.Name, value);
}
foreach (PropertyInfo publicPropertyInfo in Utils.GetPropertyInfosIncludingBaseClasses(type, PUBLIC_BINDING_FLAGS))
{
if (publicPropertyInfo.HasAttribute<IgnoreSerializationAttribute>())
continue;
if (publicPropertyInfo.SetMethod is null)
continue;
if (!shouldSerializeAll && !publicPropertyInfo.HasAttribute<SerializeAttribute>())
continue;
object? value = publicPropertyInfo.GetValue(@class);
if (value is IEntity entity)
Public.Add(publicPropertyInfo.Name, entity.Id);
else
Public.Add(publicPropertyInfo.Name, value);
}
foreach (FieldInfo privateFieldInfo in Utils.GetFieldInfosIncludingBaseClasses(type, PRIVATE_BINDING_FLAGS))
{
if (privateFieldInfo.HasAttribute<System.Runtime.CompilerServices.CompilerGeneratedAttribute>())
continue;
if (!shouldSerializeAll && !privateFieldInfo.HasAttribute<SerializeAttribute>())
continue;
object? value = privateFieldInfo.GetValue(@class);
if (value is IEntity entity)
Private.Add(privateFieldInfo.Name, entity.Id);
else
Private.Add(privateFieldInfo.Name, value);
}
foreach (FieldInfo publicFieldInfo in Utils.GetFieldInfosIncludingBaseClasses(type, PUBLIC_BINDING_FLAGS))
{
if (publicFieldInfo.HasAttribute<System.Runtime.CompilerServices.CompilerGeneratedAttribute>())
continue;
if (!shouldSerializeAll && !publicFieldInfo.HasAttribute<SerializeAttribute>())
continue;
object? value = publicFieldInfo.GetValue(@class);
if (value is IEntity entity)
Public.Add(publicFieldInfo.Name, entity.Id);
else
Public.Add(publicFieldInfo.Name, value);
}
}
public object CreateInstance()
{
Type type = TypeFactory.GetType(Type);
object instance = TypeFactory.Get(type);
foreach ((string key, object? value) in Private)
AssignVariable(key, type, instance, value, PRIVATE_BINDING_FLAGS);
foreach ((string key, object? value) in Public)
AssignVariable(key, type, instance, value, PUBLIC_BINDING_FLAGS);
return instance;
}
public object CreateInstance(EntityRegistry? entityRegistry)
{
if (entityRegistry is null)
return CreateInstance();
Type type = TypeFactory.GetType(Type);
object instance = TypeFactory.Get(type);
foreach ((string key, object? value) in Private)
AssignVariable(key, type, instance, value, PRIVATE_BINDING_FLAGS, entityRegistry);
foreach ((string key, object? value) in Public)
AssignVariable(key, type, instance, value, PUBLIC_BINDING_FLAGS, entityRegistry);
return instance;
}
private static void AssignVariable(string key, Type type, object instance, object? value, BindingFlags bindingFlags, EntityRegistry entityRegistry)
{
if (type.GetField(key, bindingFlags) is FieldInfo fieldInfo)
{
if (typeof(IEntity).IsAssignableFrom(fieldInfo.FieldType))
entityRegistry.QueueAssign(value?.ToString() ?? "", (entity) => fieldInfo.SetValue(instance, entity));
else
fieldInfo.SetValue(instance, value);
}
else if (type.GetProperty(key, bindingFlags) is PropertyInfo propertyInfo)
{
if (typeof(IEntity).IsAssignableFrom(propertyInfo.PropertyType))
entityRegistry.QueueAssign(value?.ToString() ?? "", (entity) => propertyInfo.SetValue(instance, entity));
else
propertyInfo.SetValue(instance, value);
}
}
private static void AssignVariable(string key, Type type, object instance, object? value, BindingFlags bindingFlags)
{
if (type.GetField(key, bindingFlags) is FieldInfo fieldInfo)
fieldInfo.SetValue(instance, value);
else if (type.GetProperty(key, bindingFlags) is PropertyInfo propertyInfo)
propertyInfo.SetValue(instance, value);
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace Syntriax.Engine.Core.Serialization;
public class TypeContainer
{
public object? Value { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public TypeContainer() { }
public TypeContainer(Type type) { Type = type.FullName ?? string.Empty; }
public TypeContainer(object? value) { Value = value; Type = value?.GetType().FullName ?? string.Empty; }
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Syntriax.Engine.Core.Serialization;
public static class Utils
{
public static bool HasAttribute<T>(this MemberInfo memberInfo) where T : Attribute => memberInfo.GetCustomAttribute<T>() is not null;
public static bool IsEnumerable(this Type type) => typeof(System.Collections.IEnumerable).IsAssignableFrom(type) && type != typeof(string);
public static TypeData GetTypeData(this Type objectType)
{
List<EventInfo> eventInfos = objectType.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.OrderBy(ei => ei.Name)
.ToList();
List<FieldInfo> fieldInfos = GetFieldInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)
.Where(pi => !eventInfos.Any(ei => pi.Name.CompareTo(ei.Name) == 0)).ToList();
List<PropertyInfo> propertyInfos = GetPropertyInfosIncludingBaseClasses(objectType, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public)
.Where(pi => pi.SetMethod is not null && !eventInfos.Any(ei => pi.Name.CompareTo(ei.Name) == 0))
.ToList();
return new TypeData(fieldInfos, propertyInfos);
}
public static List<FieldInfo> GetFieldInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags)
{
if (type.BaseType is null)
return [.. type.GetFields(bindingFlags)];
Type currentType = type;
FieldInfoComparer fieldComparer = new();
HashSet<FieldInfo> fieldInfoList = new(type.GetFields(bindingFlags), fieldComparer);
while (currentType.BaseType is Type baseType)
{
currentType = baseType;
fieldInfoList.UnionWith(currentType!.GetFields(bindingFlags));
}
return [.. fieldInfoList.OrderBy(fi => fi.Name)];
}
public static List<PropertyInfo> GetPropertyInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags)
{
if (type.BaseType is null)
return [.. type.GetProperties(bindingFlags)];
Type currentType = type;
PropertyInfoComparer propertyComparer = new();
HashSet<PropertyInfo> propertyInfoList = new(type.GetProperties(bindingFlags), propertyComparer);
while (currentType.BaseType is Type baseType)
{
currentType = baseType;
propertyInfoList.UnionWith(currentType.GetProperties(bindingFlags));
}
return [.. propertyInfoList.OrderBy(pi => pi.Name)];
}
private class FieldInfoComparer : IEqualityComparer<FieldInfo>
{
public bool Equals(FieldInfo? x, FieldInfo? y) => x?.DeclaringType == y?.DeclaringType && x?.Name == y?.Name;
public int GetHashCode(FieldInfo obj) => obj.Name.GetHashCode() ^ obj.DeclaringType!.GetHashCode();
}
private class PropertyInfoComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo? x, PropertyInfo? y) => x?.DeclaringType == y?.DeclaringType && x?.Name == y?.Name;
public int GetHashCode(PropertyInfo obj) => obj.Name.GetHashCode() ^ obj.DeclaringType!.GetHashCode();
}
}
public record struct TypeData(IEnumerable<FieldInfo> Fields, IEnumerable<PropertyInfo> Properties)
{
public static implicit operator (IEnumerable<FieldInfo> fields, IEnumerable<PropertyInfo> properties)(TypeData value) => (value.Fields, value.Properties);
public static implicit operator TypeData((IEnumerable<FieldInfo> fields, IEnumerable<PropertyInfo> properties) value) => new(value.fields, value.properties);
}

View File

@@ -1,12 +1,10 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
public class StateEnable : IStateEnable public class StateEnable : IStateEnable
{ {
public event IAssignable.UnassignEventHandler? OnUnassigned = null; public Event<IStateEnable, IStateEnable.EnabledChangedArguments> OnEnabledChanged { get; } = new();
public event IHasEntity.EntityAssignedEventHandler? OnEntityAssigned = null; public Event<IHasEntity> OnEntityAssigned { get; } = new();
public event IStateEnable.EnabledChangedEventHandler? OnEnabledChanged = null; public Event<IAssignable>? OnUnassigned { get; } = new();
private bool _enabled = true; private bool _enabled = true;
private IEntity _entity = null!; private IEntity _entity = null!;
@@ -23,7 +21,7 @@ public class StateEnable : IStateEnable
bool previousState = _enabled; bool previousState = _enabled;
_enabled = value; _enabled = value;
OnEnabledChanged?.InvokeSafe(this, previousState); OnEnabledChanged?.Invoke(this, new(previousState));
} }
} }
@@ -35,7 +33,7 @@ public class StateEnable : IStateEnable
_entity = entity; _entity = entity;
OnAssign(entity); OnAssign(entity);
OnEntityAssigned?.InvokeSafe(this); OnEntityAssigned?.Invoke(this);
return true; return true;
} }
@@ -45,7 +43,7 @@ public class StateEnable : IStateEnable
return false; return false;
_entity = null!; _entity = null!;
OnUnassigned?.InvokeSafe(this); OnUnassigned?.Invoke(this);
return true; return true;
} }
} }

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified when the draw phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IDraw : IBehaviour
{
/// <summary>
/// Calls draw logic for the <see cref="IBehaviour"/> to be displayed visually.
/// </summary>
void Draw();
}

View File

@@ -0,0 +1,6 @@
namespace Syntriax.Engine.Core;
public interface IFirstFrameUpdate : IBehaviour
{
void FirstActiveFrame();
}

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified after the draw phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IPostDraw : IBehaviour
{
/// <summary>
/// Updates the state of the <see cref="IBehaviour"/> after the main draw phase happens.
/// </summary>
void PostDraw();
}

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified after the update phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IPostUpdate : IBehaviour
{
/// <summary>
/// Updates the state of the <see cref="IBehaviour"/> after the main update phase happens.
/// </summary>
void PostUpdate();
}

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified before the draw phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IPreDraw : IBehaviour
{
/// <summary>
/// Updates the state of the <see cref="IBehaviour"/> before the main draw phase happens.
/// </summary>
void PreDraw();
}

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified before the update phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IPreUpdate : IBehaviour
{
/// <summary>
/// Updates the state of the <see cref="IBehaviour"/> before the main update phase happens.
/// </summary>
void PreUpdate();
}

View File

@@ -0,0 +1,12 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a <see cref="IBehaviour"/> to be notified when the update phase of the <see cref="IUniverse"/> occurs.
/// </summary>
public interface IUpdate : IBehaviour
{
/// <summary>
/// Updates the state of the <see cref="IBehaviour"/>.
/// </summary>
void Update();
}

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
public class DrawManager : Behaviour
{
// We use Descending order because draw calls are running from last to first
private static Comparer<IBehaviour> SortByDescendingPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
private readonly ActiveBehaviourCollectorSorted<IPreDraw> preDrawEntities = new() { SortBy = SortByDescendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IDraw> drawEntities = new() { SortBy = SortByDescendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IPostDraw> postDrawEntities = new() { SortBy = SortByDescendingPriority() };
private void OnPreDraw(IUniverse sender)
{
for (int i = preDrawEntities.Count - 1; i >= 0; i--)
preDrawEntities[i].PreDraw();
}
private void OnDraw(IUniverse sender)
{
for (int i = drawEntities.Count - 1; i >= 0; i--)
drawEntities[i].Draw();
}
private void OnPostDraw(IUniverse sender)
{
for (int i = postDrawEntities.Count - 1; i >= 0; i--)
postDrawEntities[i].PostDraw();
}
protected override void OnEnteredUniverse(IUniverse universe)
{
preDrawEntities.Assign(universe);
drawEntities.Assign(universe);
postDrawEntities.Assign(universe);
universe.OnPreDraw.AddListener(OnPreDraw);
universe.OnDraw.AddListener(OnDraw);
universe.OnPostDraw.AddListener(OnPostDraw);
}
protected override void OnExitedUniverse(IUniverse universe)
{
preDrawEntities.Unassign();
drawEntities.Unassign();
postDrawEntities.Unassign();
universe.OnPreDraw.RemoveListener(OnPreDraw);
universe.OnDraw.RemoveListener(OnDraw);
universe.OnPostDraw.RemoveListener(OnPostDraw);
}
}

View File

@@ -0,0 +1,74 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
public class UpdateManager : Behaviour
{
// We use Ascending order because draw calls are running from last to first
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private readonly ActiveBehaviourCollectorSorted<IFirstFrameUpdate> firstFrameUpdates = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IPreUpdate> preUpdateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IUpdate> updateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly ActiveBehaviourCollectorSorted<IPostUpdate> postUpdateEntities = new() { SortBy = SortByAscendingPriority() };
private readonly List<IFirstFrameUpdate> toCallFirstFrameUpdates = new(32);
protected override void OnEnteredUniverse(IUniverse universe)
{
firstFrameUpdates.Assign(universe);
preUpdateEntities.Assign(universe);
updateEntities.Assign(universe);
postUpdateEntities.Assign(universe);
universe.OnPreUpdate.AddListener(OnPreUpdate);
universe.OnUpdate.AddListener(OnUpdate);
universe.OnPostUpdate.AddListener(OnPostUpdate);
}
protected override void OnExitedUniverse(IUniverse universe)
{
firstFrameUpdates.Unassign();
preUpdateEntities.Unassign();
updateEntities.Unassign();
postUpdateEntities.Unassign();
universe.OnPreUpdate.RemoveListener(OnPreUpdate);
universe.OnUpdate.RemoveListener(OnUpdate);
universe.OnPostUpdate.RemoveListener(OnPostUpdate);
}
private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args)
{
for (int i = toCallFirstFrameUpdates.Count - 1; i >= 0; i--)
{
toCallFirstFrameUpdates[i].FirstActiveFrame();
toCallFirstFrameUpdates.RemoveAt(i);
}
for (int i = preUpdateEntities.Count - 1; i >= 0; i--)
preUpdateEntities[i].PreUpdate();
}
private void OnUpdate(IUniverse sender, IUniverse.UpdateArguments args)
{
for (int i = updateEntities.Count - 1; i >= 0; i--)
updateEntities[i].Update();
}
private void OnPostUpdate(IUniverse sender, IUniverse.UpdateArguments args)
{
for (int i = postUpdateEntities.Count - 1; i >= 0; i--)
postUpdateEntities[i].PostUpdate();
}
private void OnFirstFrameCollected(IBehaviourCollector<IFirstFrameUpdate> sender, IBehaviourCollector<IFirstFrameUpdate>.BehaviourCollectedArguments args)
{
toCallFirstFrameUpdates.Add(args.BehaviourCollected);
}
public UpdateManager()
{
firstFrameUpdates.OnCollected.AddListener(OnFirstFrameCollected);
}
}

View File

@@ -1,24 +1,29 @@
using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Core.Serialization;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")] [System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
public class Transform2D : Behaviour, ITransform2D public class Transform2D : Behaviour, ITransform2D
{ {
public event ITransform2D.PositionChangedEventHandler? OnPositionChanged = null; public Event<ITransform2D, ITransform2D.PositionChangedArguments> OnPositionChanged { get; } = new();
public event ITransform2D.ScaleChangedEventHandler? OnScaleChanged = null; public Event<ITransform2D, ITransform2D.ScaleChangedArguments> OnScaleChanged { get; } = new();
public event ITransform2D.RotationChangedEventHandler? OnRotationChanged = null; public Event<ITransform2D, ITransform2D.RotationChangedArguments> OnRotationChanged { get; } = new();
private Vector2D _position = Vector2D.Zero; private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One; private Vector2D _scale = Vector2D.One;
private float _rotation = 0f; private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero; [Serialize] private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One; [Serialize] private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f; [Serialize] private float _localRotation = 0f;
private ITransform2D? parentTransform = null; private ITransform2D? parentTransform = null;
public Vector2D Up => Vector2D.Up.Rotate(Rotation * Math.DegreeToRadian);
public Vector2D Down => Vector2D.Down.Rotate(Rotation * Math.DegreeToRadian);
public Vector2D Left => Vector2D.Left.Rotate(Rotation * Math.DegreeToRadian);
public Vector2D Right => Vector2D.Right.Rotate(Rotation * Math.DegreeToRadian);
public Vector2D Position public Vector2D Position
{ {
get => _position; get => _position;
@@ -31,7 +36,7 @@ public class Transform2D : Behaviour, ITransform2D
_position = value; _position = value;
UpdateLocalPosition(); UpdateLocalPosition();
OnPositionChanged?.InvokeSafe(this, _position); OnPositionChanged?.Invoke(this, new(previousPosition));
} }
} }
@@ -47,7 +52,7 @@ public class Transform2D : Behaviour, ITransform2D
_scale = value; _scale = value;
UpdateLocalScale(); UpdateLocalScale();
OnScaleChanged?.InvokeSafe(this, previousScale); OnScaleChanged?.Invoke(this, new(previousScale));
} }
} }
@@ -63,7 +68,7 @@ public class Transform2D : Behaviour, ITransform2D
_rotation = value; _rotation = value;
UpdateLocalRotation(); UpdateLocalRotation();
OnRotationChanged?.InvokeSafe(this, previousRotation); OnRotationChanged?.Invoke(this, new(previousRotation));
} }
} }
@@ -79,7 +84,7 @@ public class Transform2D : Behaviour, ITransform2D
_localPosition = value; _localPosition = value;
UpdatePosition(); UpdatePosition();
OnPositionChanged?.InvokeSafe(this, previousPosition); OnPositionChanged?.Invoke(this, new(previousPosition));
} }
} }
@@ -97,8 +102,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale(); UpdateScale();
UpdatePosition(); UpdatePosition();
OnScaleChanged?.InvokeSafe(this, previousScale); OnScaleChanged?.Invoke(this, new(previousScale));
OnPositionChanged?.InvokeSafe(this, previousPosition); OnPositionChanged?.Invoke(this, new(previousPosition));
} }
} }
@@ -114,21 +119,21 @@ public class Transform2D : Behaviour, ITransform2D
_localRotation = value; _localRotation = value;
UpdateRotation(); UpdateRotation();
OnRotationChanged?.InvokeSafe(this, previousRotation); OnRotationChanged?.Invoke(this, new(previousRotation));
} }
} }
private void RecalculatePosition(ITransform2D _, Vector2D previousPosition) private void RecalculatePosition(ITransform2D _, ITransform2D.PositionChangedArguments args)
{ {
if (parentTransform is null) if (parentTransform is null)
return; return;
UpdatePosition(); UpdatePosition();
OnPositionChanged?.InvokeSafe(this, previousPosition); OnPositionChanged?.Invoke(this, args);
} }
private void RecalculateScale(ITransform2D _, Vector2D previousScale) private void RecalculateScale(ITransform2D _, ITransform2D.ScaleChangedArguments args)
{ {
if (parentTransform is null) if (parentTransform is null)
return; return;
@@ -138,11 +143,11 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale(); UpdateScale();
UpdatePosition(); UpdatePosition();
OnScaleChanged?.InvokeSafe(this, previousScale); OnScaleChanged?.Invoke(this, args);
OnPositionChanged?.InvokeSafe(this, previousPosition); OnPositionChanged?.Invoke(this, new(previousPosition));
} }
private void RecalculateRotation(ITransform2D _, float previousRotation) private void RecalculateRotation(ITransform2D _, ITransform2D.RotationChangedArguments args)
{ {
if (parentTransform is null) if (parentTransform is null)
return; return;
@@ -152,8 +157,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdateRotation(); UpdateRotation();
UpdatePosition(); UpdatePosition();
OnRotationChanged?.InvokeSafe(this, previousRotation); OnRotationChanged?.Invoke(this, args);
OnPositionChanged?.InvokeSafe(this, previousPosition); OnPositionChanged?.Invoke(this, new(previousPosition));
} }
private void UpdateLocalPosition() private void UpdateLocalPosition()
@@ -207,12 +212,12 @@ public class Transform2D : Behaviour, ITransform2D
protected override void InitializeInternal() protected override void InitializeInternal()
{ {
UpdateReferences(UniverseObject.Parent); UpdateReferences(UniverseObject.Parent);
UniverseObject.OnParentChanged += OnParentChanged; UniverseObject.OnParentChanged.AddListener(OnParentChanged);
} }
protected override void FinalizeInternal() protected override void FinalizeInternal()
{ {
UniverseObject.OnParentChanged -= OnParentChanged; UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
} }
private void UpdateReferences(IUniverseObject? parent) private void UpdateReferences(IUniverseObject? parent)
@@ -220,48 +225,48 @@ public class Transform2D : Behaviour, ITransform2D
ITransform2D? previousParent = parentTransform; ITransform2D? previousParent = parentTransform;
if (previousParent is not null) if (previousParent is not null)
{ {
previousParent.OnPositionChanged -= RecalculatePosition; previousParent.OnPositionChanged.RemoveListener(RecalculatePosition);
previousParent.OnScaleChanged -= RecalculateScale; previousParent.OnScaleChanged.RemoveListener(RecalculateScale);
previousParent.OnRotationChanged -= RecalculateRotation; previousParent.OnRotationChanged.RemoveListener(RecalculateRotation);
previousParent.BehaviourController.UniverseObject.OnParentChanged -= OnParentChanged; previousParent.BehaviourController.UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
previousParent.BehaviourController.OnBehaviourAdded -= LookForTransform2D; previousParent.BehaviourController.OnBehaviourAdded.RemoveListener(LookForTransform2D);
} }
parentTransform = parent?.BehaviourController.GetBehaviour<ITransform2D>(); parentTransform = parent?.BehaviourController.GetBehaviour<ITransform2D>();
if (parentTransform is not null) if (parentTransform is not null)
{ {
parentTransform.OnPositionChanged += RecalculatePosition; parentTransform.OnPositionChanged.AddListener(RecalculatePosition);
parentTransform.OnScaleChanged += RecalculateScale; parentTransform.OnScaleChanged.AddListener(RecalculateScale);
parentTransform.OnRotationChanged += RecalculateRotation; parentTransform.OnRotationChanged.AddListener(RecalculateRotation);
parentTransform.BehaviourController.UniverseObject.OnParentChanged += OnParentChanged; parentTransform.BehaviourController.UniverseObject.OnParentChanged.AddListener(OnParentChanged);
UpdatePosition(); UpdatePosition();
UpdateScale(); UpdateScale();
UpdateRotation(); UpdateRotation();
} }
else if (UniverseObject.Parent is not null) else if (UniverseObject.Parent is not null)
UniverseObject.Parent.BehaviourController.OnBehaviourAdded += LookForTransform2D; UniverseObject.Parent.BehaviourController.OnBehaviourAdded.AddListener(LookForTransform2D);
UpdateLocalPosition(); UpdateLocalPosition();
UpdateLocalScale(); UpdateLocalScale();
UpdateLocalRotation(); UpdateLocalRotation();
OnPositionChanged?.InvokeSafe(this, Position); OnPositionChanged?.Invoke(this, new(Position));
OnScaleChanged?.InvokeSafe(this, Scale); OnScaleChanged?.Invoke(this, new(Scale));
OnRotationChanged?.InvokeSafe(this, Rotation); OnRotationChanged?.Invoke(this, new(Rotation));
} }
private void LookForTransform2D(IBehaviourController sender, IBehaviour behaviourAdded) private void LookForTransform2D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)
{ {
if (behaviourAdded is not ITransform2D transform2D) if (args.BehaviourAdded is not ITransform2D)
return; return;
UpdateReferences(UniverseObject.Parent); UpdateReferences(UniverseObject.Parent);
} }
private void OnParentChanged(IUniverseObject sender, IUniverseObject? previousParent, IUniverseObject? newParent) private void OnParentChanged(IUniverseObject sender, IUniverseObject.ParentChangedArguments args)
{ {
UpdateReferences(newParent); UpdateReferences(args.CurrentParent);
} }
} }

View File

@@ -2,23 +2,33 @@ using System;
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;
[System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")] [System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")]
public class Universe : BaseEntity, IUniverse public class Universe : BaseEntity, IUniverse
{ {
public event IUniverse.UpdateEventHandler? OnPreUpdate = null; public Event<IUniverse, IUniverse.UpdateArguments> OnPreUpdate { get; } = new();
public event IUniverse.UpdateEventHandler? OnUpdate = null; public Event<IUniverse, IUniverse.UpdateArguments> OnUpdate { get; } = new();
public event IUniverse.PreDrawEventHandler? OnPreDraw = null; public Event<IUniverse, IUniverse.UpdateArguments> OnPostUpdate { get; } = new();
public Event<IUniverse> OnPreDraw { get; } = new();
public Event<IUniverse> OnDraw { get; } = new();
public Event<IUniverse> OnPostDraw { get; } = new();
public Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; } = new();
public Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments> OnUniverseObjectUnRegistered { get; } = new();
public Event<IUniverse, IUniverse.TimeScaleChangedArguments> OnTimeScaleChanged { get; } = new();
public event IUniverse.UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered = null; private readonly Event<IInitializable>.EventHandler delegateOnUniverseObjectFinalize = null!;
public event IUniverse.UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered = null; private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateOnUniverseObjectExitedUniverse = null!;
private readonly List<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL); private readonly List<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
private float _timeScale = 1f; private float _timeScale = 1f;
public Universe()
{
delegateOnUniverseObjectFinalize = OnUniverseObjectFinalize;
delegateOnUniverseObjectExitedUniverse = OnUniverseObjectExitedUniverse;
}
public IReadOnlyList<IUniverseObject> UniverseObjects => _universeObjects; public IReadOnlyList<IUniverseObject> UniverseObjects => _universeObjects;
public UniverseTime Time { get; private set; } = new(); public UniverseTime Time { get; private set; } = new();
@@ -26,7 +36,16 @@ public class Universe : BaseEntity, IUniverse
public float TimeScale public float TimeScale
{ {
get => _timeScale; get => _timeScale;
set => _timeScale = value.Max(0f); set
{
value = value.Max(0f);
if (value == _timeScale)
return;
float previousTimeScale = _timeScale;
_timeScale = value;
OnTimeScaleChanged?.Invoke(this, new(previousTimeScale));
}
} }
public void Register(IUniverseObject universeObject) public void Register(IUniverseObject universeObject)
@@ -34,8 +53,8 @@ public class Universe : BaseEntity, IUniverse
if (_universeObjects.Contains(universeObject)) if (_universeObjects.Contains(universeObject))
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}."); throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}.");
universeObject.OnFinalized += OnUniverseObjectFinalize; universeObject.OnFinalized.AddListener(delegateOnUniverseObjectFinalize);
universeObject.OnExitedUniverse += OnUniverseObjectExitedUniverse; universeObject.OnExitedUniverse.AddListener(delegateOnUniverseObjectExitedUniverse);
if (!universeObject.Initialize()) if (!universeObject.Initialize())
throw new Exception($"{universeObject.Name} can't be initialized"); throw new Exception($"{universeObject.Name} can't be initialized");
@@ -48,7 +67,7 @@ public class Universe : BaseEntity, IUniverse
if (!universeObject.EnterUniverse(this)) if (!universeObject.EnterUniverse(this))
throw new Exception($"{universeObject.Name} can't enter the universe"); throw new Exception($"{universeObject.Name} can't enter the universe");
OnUniverseObjectRegistered?.InvokeSafe(this, universeObject); OnUniverseObjectRegistered?.Invoke(this, new(universeObject));
} }
public T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject public T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject
@@ -69,8 +88,8 @@ public class Universe : BaseEntity, IUniverse
if (!_universeObjects.Contains(universeObject)) if (!_universeObjects.Contains(universeObject))
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}."); throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}.");
universeObject.OnFinalized -= OnUniverseObjectFinalize; universeObject.OnFinalized.RemoveListener(delegateOnUniverseObjectFinalize);
universeObject.OnExitedUniverse -= OnUniverseObjectExitedUniverse; universeObject.OnExitedUniverse.RemoveListener(delegateOnUniverseObjectExitedUniverse);
for (int i = universeObject.Children.Count - 1; i >= 0; i--) for (int i = universeObject.Children.Count - 1; i >= 0; i--)
Remove(universeObject.Children[i]); Remove(universeObject.Children[i]);
@@ -83,7 +102,7 @@ public class Universe : BaseEntity, IUniverse
if (!universeObject.Finalize()) if (!universeObject.Finalize())
throw new Exception($"{universeObject.Name} can't be finalized"); throw new Exception($"{universeObject.Name} can't be finalized");
OnUniverseObjectUnRegistered?.InvokeSafe(this, universeObject); OnUniverseObjectUnRegistered?.Invoke(this, new(universeObject));
} }
protected override void InitializeInternal() protected override void InitializeInternal()
@@ -101,27 +120,23 @@ public class Universe : BaseEntity, IUniverse
public void Update(UniverseTime engineTime) public void Update(UniverseTime engineTime)
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
UnscaledTime = engineTime; UnscaledTime = engineTime;
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale))); Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
OnPreUpdate?.InvokeSafe(this, Time); OnPreUpdate?.Invoke(this, new(Time));
OnUpdate?.Invoke(this, new(Time));
for (int i = 0; i < UniverseObjects.Count; i++) OnPostUpdate?.Invoke(this, new(Time));
UniverseObjects[i].BehaviourController.Update();
OnUpdate?.InvokeSafe(this, Time);
} }
public void PreDraw() public void Draw()
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
for (int i = 0; i < UniverseObjects.Count; i++) OnPreDraw?.Invoke(this);
UniverseObjects[i].BehaviourController.UpdatePreDraw(); OnDraw?.Invoke(this);
OnPostDraw?.Invoke(this);
OnPreDraw?.InvokeSafe(this);
} }
private void OnUniverseObjectFinalize(IInitializable initializable) private void OnUniverseObjectFinalize(IInitializable initializable)
@@ -130,7 +145,7 @@ public class Universe : BaseEntity, IUniverse
Remove(universeObject); Remove(universeObject);
} }
private void OnUniverseObjectExitedUniverse(IUniverseObject sender, IUniverse universe) private void OnUniverseObjectExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args)
{ {
if (sender is IUniverseObject universeObject) if (sender is IUniverseObject universeObject)
Remove(universeObject); Remove(universeObject);

View File

@@ -1,21 +1,20 @@
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;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")] [System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class UniverseObject : BaseEntity, IUniverseObject public class UniverseObject : BaseEntity, IUniverseObject
{ {
public event IUniverseObject.EnteredUniverseEventHandler? OnEnteredUniverse = null; public Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments> OnEnteredUniverse { get; } = new();
public event IUniverseObject.ExitedUniverseEventHandler? OnExitedUniverse = null; public Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments> OnExitedUniverse { get; } = new();
public event IUniverseObject.ParentChangedEventHandler? OnParentChanged = null; public Event<IUniverseObject, IUniverseObject.ParentChangedArguments> OnParentChanged { get; } = new();
public event IUniverseObject.ChildrenAddedEventHandler? OnChildrenAdded = null; public Event<IUniverseObject, IUniverseObject.ChildrenAddedArguments> OnChildrenAdded { get; } = new();
public event IUniverseObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null; public Event<IUniverseObject, IUniverseObject.ChildrenRemovedArguments> OnChildrenRemoved { get; } = new();
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null; public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null; public Event<INameable, INameable.NameChangedArguments> OnNameChanged { get; } = new();
public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
private string _name = nameof(UniverseObject); private string _name = nameof(UniverseObject);
private IUniverse _universe = null!; private IUniverse _universe = null!;
@@ -39,7 +38,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
string previousName = _name; string previousName = _name;
_name = value; _name = value;
OnNameChanged?.InvokeSafe(this, previousName); OnNameChanged?.Invoke(this, new(previousName));
} }
} }
@@ -52,7 +51,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
_universe = universe; _universe = universe;
UpdateActive(); UpdateActive();
OnEnteringUniverse(universe); OnEnteringUniverse(universe);
OnEnteredUniverse?.InvokeSafe(this, universe); OnEnteredUniverse?.Invoke(this, new(universe));
return true; return true;
} }
@@ -64,7 +63,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
OnExitingUniverse(universe); OnExitingUniverse(universe);
_universe = null!; _universe = null!;
OnExitedUniverse?.InvokeSafe(this, universe); OnExitedUniverse?.Invoke(this, new(universe));
return true; return true;
} }
@@ -80,7 +79,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
if (previousParent is not null) if (previousParent is not null)
{ {
previousParent.RemoveChild(this); previousParent.RemoveChild(this);
previousParent.OnActiveChanged -= OnParentActiveChanged; previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged);
} }
Parent = parent; Parent = parent;
@@ -91,11 +90,11 @@ public class UniverseObject : BaseEntity, IUniverseObject
parent.Universe.Register(this); parent.Universe.Register(this);
parent.AddChild(this); parent.AddChild(this);
parent.OnActiveChanged += OnParentActiveChanged; parent.OnActiveChanged.AddListener(OnParentActiveChanged);
} }
UpdateActive(); UpdateActive();
OnParentChanged?.InvokeSafe(this, previousParent, parent); OnParentChanged?.Invoke(this, new(previousParent, parent));
} }
public void AddChild(IUniverseObject parent) public void AddChild(IUniverseObject parent)
@@ -105,7 +104,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
_children.Add(parent); _children.Add(parent);
parent.SetParent(this); parent.SetParent(this);
OnChildrenAdded?.InvokeSafe(this, parent); OnChildrenAdded?.Invoke(this, new(parent));
} }
public void RemoveChild(IUniverseObject child) public void RemoveChild(IUniverseObject child)
@@ -114,7 +113,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
return; return;
child.SetParent(null); child.SetParent(null);
OnChildrenRemoved?.InvokeSafe(this, child); OnChildrenRemoved?.Invoke(this, new(child));
} }
protected virtual void OnAssign(IBehaviourController behaviourController) { } protected virtual void OnAssign(IBehaviourController behaviourController) { }
@@ -125,7 +124,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
_behaviourController = behaviourController; _behaviourController = behaviourController;
OnAssign(behaviourController); OnAssign(behaviourController);
OnBehaviourControllerAssigned?.InvokeSafe(this); OnBehaviourControllerAssigned?.Invoke(this);
return true; return true;
} }
@@ -133,11 +132,11 @@ public class UniverseObject : BaseEntity, IUniverseObject
{ {
base.OnAssign(stateEnable); base.OnAssign(stateEnable);
stateEnable.OnEnabledChanged += OnStateEnabledChanged; stateEnable.OnEnabledChanged.AddListener(OnStateEnabledChanged);
} }
private void OnParentActiveChanged(IActive sender, bool previousState) => UpdateActive(); private void OnParentActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive(); private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
private void UpdateActive() private void UpdateActive()
{ {
@@ -145,19 +144,20 @@ public class UniverseObject : BaseEntity, IUniverseObject
_isActive = StateEnable.Enabled && (Parent?.IsActive ?? true); _isActive = StateEnable.Enabled && (Parent?.IsActive ?? true);
if (previousActive != IsActive) if (previousActive != IsActive)
OnActiveChanged?.InvokeSafe(this, previousActive); OnActiveChanged?.Invoke(this, new(previousActive));
} }
protected override void UnassignInternal() protected override void UnassignInternal()
{ {
base.UnassignInternal(); base.UnassignInternal();
StateEnable.OnEnabledChanged -= OnStateEnabledChanged; StateEnable.OnEnabledChanged.RemoveListener(OnStateEnabledChanged);
} }
protected override void InitializeInternal() protected override void InitializeInternal()
{ {
base.InitializeInternal(); base.InitializeInternal();
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this); _behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
_behaviourController.Initialize();
} }
public UniverseObject() public UniverseObject()

Some files were not shown because too many files have changed in this diff Show More