80 Commits

Author SHA1 Message Date
0215f8ef1f chore: debumped .NET Core version to 8 2025-05-07 19:30:36 +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
5c1c025fe3 chore: forgotten InvokeSafe usage in TweenExtensions 2025-04-13 22:23:57 +03:00
1d292a104e chore: removed unnecessary null check 2025-04-13 22:23:36 +03:00
70c884acfe refactor!: renamed GameManager to Universe and HierarchyObject to UniverseObject 2025-04-13 21:57:05 +03:00
a9f5974568 fix: InvokeSafe params causing warnings for possible null parameter calls 2025-04-13 21:41:53 +03:00
dae72b11c5 refactor: renamed AssertHelpers namespace to Core.Debug 2025-04-13 19:12:34 +03:00
58eb373c79 feat: safe delegate invocation helper added 2025-04-13 19:08:47 +03:00
00f7b1aaab chore: hierarchy objects now get their type name as their Name in constructor 2025-04-13 13:42:05 +03:00
177 changed files with 3802 additions and 1145 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
*.swp
!Debug

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.

View File

@@ -1,26 +0,0 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform2D"/> field.
/// </summary>
public interface IHasGameManager : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
/// </summary>
event GameManagerAssignedEventHandler? OnGameManagerAssigned;
/// <inheritdoc cref="IGameManager" />
IGameManager GameManager { get; }
/// <summary>
/// Assign a value to the <see cref="IGameManager"/> field of this object.
/// </summary>
/// <param name="gameManager">New <see cref="IGameManager"/> to assign.</param>
/// <returns>
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IGameManager gameManager);
delegate void GameManagerAssignedEventHandler(IHasGameManager sender);
}

View File

@@ -1,26 +0,0 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IHierarchyObject"/> field.
/// </summary>
public interface IHasHierarchyObject : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> value has has been assigned a new value.
/// </summary>
event HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned;
/// <inheritdoc cref="IHierarchyObject" />
IHierarchyObject HierarchyObject { get; }
/// <summary>
/// Assign a value to the <see cref="IHierarchyObject"/> field of this object.
/// </summary>
/// <param name="hierarchyObject">New <see cref="IHierarchyObject"/> to assign.</param>
/// <returns>
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IHierarchyObject hierarchyObject);
delegate void HierarchyObjectAssignedEventHandler(IHasHierarchyObject sender);
}

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.

View File

@@ -0,0 +1,26 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
/// </summary>
public interface IHasUniverse : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IUniverse"/> value has has been assigned a new value.
/// </summary>
event UniverseAssignedEventHandler? OnUniverseAssigned;
/// <inheritdoc cref="IUniverse" />
IUniverse Universe { get; }
/// <summary>
/// Assign a value to the <see cref="IUniverse"/> field of this object.
/// </summary>
/// <param name="universe">New <see cref="IUniverse"/> to assign.</param>
/// <returns>
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IUniverse universe);
delegate void UniverseAssignedEventHandler(IHasUniverse sender);
}

View File

@@ -0,0 +1,26 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
/// </summary>
public interface IHasUniverseObject : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> value has has been assigned a new value.
/// </summary>
event UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned;
/// <inheritdoc cref="IUniverseObject" />
IUniverseObject UniverseObject { get; }
/// <summary>
/// Assign a value to the <see cref="IUniverseObject"/> field of this object.
/// </summary>
/// <param name="universeObject">New <see cref="IUniverseObject"/> to assign.</param>
/// <returns>
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IUniverseObject universeObject);
delegate void UniverseObjectAssignedEventHandler(IHasUniverseObject sender);
}

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an entity which can be active or not.

View File

@@ -1,7 +1,7 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.
/// </summary>
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
{

View File

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

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a collector for the class type of <typeparamref name="T"/>.
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IGameManager"/>.
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IUniverse"/>.
/// </summary>
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
public interface IBehaviourCollector<T> : IHasGameManager, IEnumerable<T> where T : class
public interface IBehaviourCollector<T> : IHasUniverse, IEnumerable<T> where T : class
{
/// <summary>
/// Event triggered when an object of type <typeparamref name="T"/> is added to the collector.

View File

@@ -1,11 +1,11 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IHierarchyObject"/>.
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IUniverseObject"/>.
/// </summary>
public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEnumerable<IBehaviour>
public interface IBehaviourController : IEntity, IHasUniverseObject, IEnumerable<IBehaviour>
{
/// <summary>
/// Event triggered before the update of <see cref="IBehaviour"/>s.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// 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
{

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a basic entity in the engine.

View File

@@ -1,91 +0,0 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a game world responsible for managing <see cref="IHierarchyObject"/>s.
/// </summary>
public interface IGameManager : IEntity, IEnumerable<IHierarchyObject>
{
/// <summary>
/// Event triggered when <see cref="Update(EngineTime)"/> is about to be called called on the <see cref="IGameManager"/>.
/// </summary>
event UpdateEventHandler? OnPreUpdate;
/// <summary>
/// Event triggered when <see cref="Update(EngineTime)"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event PreDrawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is registered to the <see cref="IGameManager"/>.
/// </summary>
event HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is unregistered from the <see cref="IGameManager"/>.
/// </summary>
event HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
/// <summary>
/// Current time scale the <see cref="IGameManager"/> operates on.
/// </summary>
float TimeScale { get; set; }
/// <summary>
/// Contains time data related to this <see cref="IGameManager"/>.
/// </summary>
EngineTime Time { get; }
/// <summary>
/// Contains unscaled time data related to this <see cref="IGameManager"/>.
/// </summary>
EngineTime UnscaledTime { get; }
/// <summary>
/// Gets a read-only list of <see cref="IHierarchyObject"/>s managed by the <see cref="IGameManager"/>.
/// </summary>
IReadOnlyList<IHierarchyObject> HierarchyObjects { get; }
/// <summary>
/// Registers an <see cref="IHierarchyObject"/> to the <see cref="IGameManager"/>.
/// </summary>
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to register.</param>
void Register(IHierarchyObject hierarchyObject);
/// <summary>
/// Instantiates a <see cref="IHierarchyObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
/// </summary>
/// <typeparam name="T">The type of <see cref="IHierarchyObject"/> to instantiate.</typeparam>
/// <param name="args">Constructor parameters for the given type of <see cref="IHierarchyObject"/>.</param>
/// <returns>The instantiated <see cref="IHierarchyObject"/>.</returns>
T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject;
/// <summary>
/// Removes an <see cref="IHierarchyObject"/> from the <see cref="IGameManager"/>.
/// </summary>
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to remove.</param>
void Remove(IHierarchyObject hierarchyObject);
/// <summary>
/// Updates the <see cref="IGameManager"/> with the given delta time.
/// </summary>
/// <param name="engineTime">Delta time.</param>
void Update(EngineTime engineTime);
/// <summary>
/// Performs operations that should be done before the draw calls.
/// </summary>
void PreDraw();
delegate void UpdateEventHandler(IGameManager sender, EngineTime engineTime);
delegate void PreDrawEventHandler(IGameManager sender);
delegate void HierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
delegate void HierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
}

View File

@@ -1,131 +0,0 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an <see cref="IEntity"/> that can enter and exit a hierarchy within the <see cref="IGameManager"/> system.
/// This interface allows for tracking the object's presence in the hierarchy and provides events
/// for notifying when the see enters or exits the hierarchy.
/// </summary>
public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
{
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy.
/// </summary>
event EnteredHierarchyEventHandler? OnEnteredHierarchy;
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy.
/// </summary>
event ExitedHierarchyEventHandler? OnExitedHierarchy;
/// <summary>
/// Event triggered when the <see cref="Parent"/> of the <see cref="IHierarchyObject"/> changes. The second parameter is the old <see cref="IHierarchyObject"/>.
/// </summary>
event ParentChangedEventHandler? OnParentChanged;
/// <summary>
/// Event triggered when a new <see cref="IHierarchyObject"/> is added to the <see cref="Children"/>.
/// </summary>
event ChildrenAddedEventHandler? OnChildrenAdded;
/// <summary>
/// Event triggered when an <see cref="IHierarchyObject"/> is removed from the <see cref="Children"/>.
/// </summary>
event ChildrenRemovedEventHandler? OnChildrenRemoved;
/// <summary>
/// Gets the <see cref="IGameManager"/> this <see cref="IHierarchyObject"/> is connected to, if any.
/// </summary>
IGameManager GameManager { get; }
/// <summary>
/// Indicates whether the <see cref="IHierarchyObject"/> is currently in the hierarchy.
/// </summary>
bool IsInHierarchy { get; }
/// <summary>
/// The parent <see cref="IHierarchyObject"/> of the <see cref="IHierarchyObject"/>.
/// </summary>
IHierarchyObject? Parent { get; }
/// <summary>
/// The <see cref="IHierarchyObject"/>s that have this <see cref="IHierarchyObject"/> as their <see cref="Parent"/>.
/// </summary>
IReadOnlyList<IHierarchyObject> Children { get; }
/// <summary>
/// Internal method to handle entering the hierarchy.
/// This should be called by the system to properly manage hierarchy states.
/// </summary>
/// <param name="gameManager">The <see cref="IGameManager"/> that is managing this hierarchy.</param>
/// <returns>
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully entered the hierarchy;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool EnterHierarchy(IGameManager gameManager);
/// <summary>
/// Internal method to handle exiting the hierarchy.
/// This should be called by the system to properly manage hierarchy states.
/// </summary>
/// <returns>
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully exited the hierarchy;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool ExitHierarchy();
/// <summary>
/// Sets the parent <see cref="IHierarchyObject"/> of this <see cref="IHierarchyObject"/>.
/// </summary>
/// <param name="hierarchyObject">The parent <see cref="IHierarchyObject"/> to set.</param>
void SetParent(IHierarchyObject? hierarchyObject);
/// <summary>
/// Adds a child <see cref="IHierarchyObject"/> to this <see cref="IHierarchyObject"/>.
/// </summary>
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to add.</param>
void AddChild(IHierarchyObject hierarchyObject);
/// <summary>
/// Removes a child <see cref="IHierarchyObject"/> from this <see cref="IHierarchyObject"/>.
/// </summary>
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to remove.</param>
void RemoveChild(IHierarchyObject hierarchyObject);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy of a <see cref="IGameManager">.
/// </summary>
/// <param name="sender">The <see cref="IHierarchyObject"/> that entered the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has entered it's hierarchy.</param>
delegate void EnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy of a <see cref="IGameManager">.
/// </summary>
/// <param name="sender">The <see cref="IHierarchyObject"/> that exited the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has exited it's hierarchy.</param>
delegate void ExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
/// <summary>
/// Delegate for the event triggered when the <see cref="IHierarchyObject"/>'s parent changes.
/// </summary>
/// <param name="sender">The <see cref="IHierarchyObject"/> that the parent has changed.</param>
/// <param name="previousParent">The previous <see cref="IHierarchyObject"/> the sender was a child of.</param>
/// <param name="newParent">The new and current <see cref="IHierarchyObject"/> the sender is a child of.</param>
delegate void ParentChangedEventHandler(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> added as a child.
/// </summary>
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
delegate void ChildrenAddedEventHandler(IHierarchyObject sender, IHierarchyObject childrenAdded);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> removed from being a child.
/// </summary>
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
delegate void ChildrenRemovedEventHandler(IHierarchyObject sender, IHierarchyObject childrenRemoved);
}

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <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.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an entity with a name.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an entity with an enable state that can be toggled.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
/// </summary>
public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
{
/// <summary>
/// Event triggered when <see cref="Update(UniverseTime)"/> is about to be called called on the <see cref="IUniverse"/>.
/// </summary>
event UpdateEventHandler? OnPreUpdate;
/// <summary>
/// Event triggered when <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
/// </summary>
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IUniverse"/>.
/// </summary>
event PreDrawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
/// </summary>
event UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
/// </summary>
event UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered;
/// <summary>
/// Event triggered when <see cref="TimeScale"/> is changed on the <see cref="IUniverse"/>.
/// </summary>
event TimeScaleChangedEventHandler? OnTimeScaleChanged;
/// <summary>
/// Current time scale the <see cref="IUniverse"/> operates on.
/// </summary>
float TimeScale { get; set; }
/// <summary>
/// Contains time data related to this <see cref="IUniverse"/>.
/// </summary>
UniverseTime Time { get; }
/// <summary>
/// Contains unscaled time data related to this <see cref="IUniverse"/>.
/// </summary>
UniverseTime UnscaledTime { get; }
/// <summary>
/// Gets a read-only list of <see cref="IUniverseObject"/>s managed by the <see cref="IUniverse"/>.
/// </summary>
IReadOnlyList<IUniverseObject> UniverseObjects { get; }
/// <summary>
/// Registers an <see cref="IUniverseObject"/> to the <see cref="IUniverse"/>.
/// </summary>
/// <param name="universeObject">The <see cref="IUniverseObject"/> to register.</param>
void Register(IUniverseObject universeObject);
/// <summary>
/// Instantiates a <see cref="IUniverseObject"/> of type T with the given arguments and registers it to the <see cref="IUniverse"/>.
/// </summary>
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to instantiate.</typeparam>
/// <param name="args">Constructor parameters for the given type of <see cref="IUniverseObject"/>.</param>
/// <returns>The instantiated <see cref="IUniverseObject"/>.</returns>
T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject;
/// <summary>
/// Removes an <see cref="IUniverseObject"/> from the <see cref="IUniverse"/>.
/// </summary>
/// <param name="universeObject">The <see cref="IUniverseObject"/> to remove.</param>
void Remove(IUniverseObject universeObject);
/// <summary>
/// Updates the <see cref="IUniverse"/> with the given delta time.
/// </summary>
/// <param name="universeTime">Delta time.</param>
void Update(UniverseTime universeTime);
/// <summary>
/// Performs operations that should be done before the draw calls.
/// </summary>
void PreDraw();
delegate void TimeScaleChangedEventHandler(IUniverse sender, float previousTimeScale);
delegate void UpdateEventHandler(IUniverse sender, UniverseTime engineTime);
delegate void PreDrawEventHandler(IUniverse sender);
delegate void UniverseObjectRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectRegistered);
delegate void UniverseObjectUnRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectUnregistered);
}

View File

@@ -0,0 +1,131 @@
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
/// This interface allows for tracking the object's presence in the universe and provides events
/// for notifying when the see enters or exits the universe.
/// </summary>
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IUniverseObject>
{
/// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
/// </summary>
event EnteredUniverseEventHandler? OnEnteredUniverse;
/// <summary>
/// Event triggered when the <see cref="IUniverseObject"/> exits the universe.
/// </summary>
event ExitedUniverseEventHandler? OnExitedUniverse;
/// <summary>
/// Event triggered when the <see cref="Parent"/> of the <see cref="IUniverseObject"/> changes. The second parameter is the old <see cref="IUniverseObject"/>.
/// </summary>
event ParentChangedEventHandler? OnParentChanged;
/// <summary>
/// Event triggered when a new <see cref="IUniverseObject"/> is added to the <see cref="Children"/>.
/// </summary>
event ChildrenAddedEventHandler? OnChildrenAdded;
/// <summary>
/// Event triggered when an <see cref="IUniverseObject"/> is removed from the <see cref="Children"/>.
/// </summary>
event ChildrenRemovedEventHandler? OnChildrenRemoved;
/// <summary>
/// Gets the <see cref="IUniverse"/> this <see cref="IUniverseObject"/> is connected to, if any.
/// </summary>
IUniverse Universe { get; }
/// <summary>
/// Indicates whether the <see cref="IUniverseObject"/> is currently in the universe.
/// </summary>
bool IsInUniverse { get; }
/// <summary>
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
/// </summary>
IUniverseObject? Parent { get; }
/// <summary>
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
/// </summary>
IReadOnlyList<IUniverseObject> Children { get; }
/// <summary>
/// Internal method to handle entering the universe.
/// This should be called by the system to properly manage universe states.
/// </summary>
/// <param name="universe">The <see cref="IUniverse"/> that is managing this universe.</param>
/// <returns>
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully entered the universe;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool EnterUniverse(IUniverse universe);
/// <summary>
/// Internal method to handle exiting the universe.
/// This should be called by the system to properly manage universe states.
/// </summary>
/// <returns>
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully exited the universe;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool ExitUniverse();
/// <summary>
/// Sets the parent <see cref="IUniverseObject"/> of this <see cref="IUniverseObject"/>.
/// </summary>
/// <param name="universeObject">The parent <see cref="IUniverseObject"/> to set.</param>
void SetParent(IUniverseObject? universeObject);
/// <summary>
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
/// </summary>
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to add.</param>
void AddChild(IUniverseObject universeObject);
/// <summary>
/// Removes a child <see cref="IUniverseObject"/> from this <see cref="IUniverseObject"/>.
/// </summary>
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to remove.</param>
void RemoveChild(IUniverseObject universeObject);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> enters the universe of a <see cref="IUniverse">.
/// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that entered the universe.</param>
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has entered it's universe.</param>
delegate void EnteredUniverseEventHandler(IUniverseObject sender, IUniverse universe);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> exits the universe of a <see cref="IUniverse">.
/// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that exited the universe.</param>
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has exited it's universe.</param>
delegate void ExitedUniverseEventHandler(IUniverseObject sender, IUniverse universe);
/// <summary>
/// Delegate for the event triggered when the <see cref="IUniverseObject"/>'s parent changes.
/// </summary>
/// <param name="sender">The <see cref="IUniverseObject"/> that the parent has changed.</param>
/// <param name="previousParent">The previous <see cref="IUniverseObject"/> the sender was a child of.</param>
/// <param name="newParent">The new and current <see cref="IUniverseObject"/> the sender is a child of.</param>
delegate void ParentChangedEventHandler(IUniverseObject sender, IUniverseObject? previousParent, IUniverseObject? newParent);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> added as a child.
/// </summary>
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
delegate void ChildrenAddedEventHandler(IUniverseObject sender, IUniverseObject childrenAdded);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> removed from being a child.
/// </summary>
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
delegate void ChildrenRemovedEventHandler(IUniverseObject sender, IUniverseObject childrenRemoved);
}

View File

@@ -2,14 +2,12 @@ using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
{
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
@@ -19,29 +17,29 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public IReadOnlyList<T> Behaviours => activeBehaviours;
public IGameManager GameManager { get; private set; } = null!;
public IUniverse Universe { get; private set; } = null!;
public T this[Index index] => activeBehaviours[index];
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IGameManager gameManager) => Assign(gameManager);
public ActiveBehaviourCollector(IUniverse universe) => Assign(universe);
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
foreach (IBehaviour item in universeObject.BehaviourController)
OnBehaviourAdded(universeObject.BehaviourController, item);
}
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
foreach (IBehaviour item in universeObject.BehaviourController)
OnBehaviourRemoved(universeObject.BehaviourController, item);
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
@@ -63,12 +61,12 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
{
activeBehaviours.Add(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, behaviour);
OnCollected?.InvokeSafe(this, behaviour);
}
else if (activeBehaviours.Remove(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, behaviour);
OnRemoved?.InvokeSafe(this, behaviour);
}
}
@@ -85,40 +83,40 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
OnRemoved?.InvokeSafe(this, tBehaviour);
}
}
public bool Assign(IGameManager gameManager)
public bool Assign(IUniverse universe)
{
if (GameManager is not null)
if (Universe is not null)
return false;
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
foreach (IUniverseObject universeObject in universe.UniverseObjects)
OnUniverseObjectRegistered(universe, universeObject);
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered;
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered;
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
Universe = universe;
OnUniverseAssigned?.InvokeSafe(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
if (Universe is null)
return false;
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
OnUniverseObjectUnregistered(Universe, universeObject);
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered;
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
Universe = null!;
OnUnassigned?.InvokeSafe(this);
return true;
}

View File

@@ -1,7 +1,5 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
@@ -26,5 +24,5 @@ public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> whe
}
public ActiveBehaviourCollectorSorted() { }
public ActiveBehaviourCollectorSorted(IGameManager gameManager, Comparison<T> sortBy) : base(gameManager) => SortBy = sortBy;
public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe) => SortBy = sortBy;
}

View File

@@ -1,6 +1,6 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public abstract class BaseEntity : IEntity
{
@@ -33,7 +33,7 @@ public abstract class BaseEntity : IEntity
string previousId = _id;
_id = value;
OnIdChanged?.Invoke(this, previousId);
OnIdChanged?.InvokeSafe(this, previousId);
}
}
@@ -47,9 +47,9 @@ public abstract class BaseEntity : IEntity
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
OnInitialized?.InvokeSafe(this);
else
OnFinalized?.Invoke(this);
OnFinalized?.InvokeSafe(this);
}
}
@@ -62,7 +62,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnAssign(stateEnable);
OnStateEnableAssigned?.Invoke(this);
OnStateEnableAssigned?.InvokeSafe(this);
return true;
}
@@ -76,7 +76,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = null!;
_stateEnable.Unassign();
OnUnassigned?.Invoke(this);
OnUnassigned?.InvokeSafe(this);
return true;
}

View File

@@ -1,14 +1,11 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Helpers;
namespace Syntriax.Engine.Core;
public abstract class Behaviour : BehaviourBase
{
private bool isInitializedThisFrame = false;
protected IGameManager GameManager => BehaviourController.HierarchyObject.GameManager;
protected IHierarchyObject HierarchyObject => BehaviourController.HierarchyObject;
protected IUniverse Universe => BehaviourController.UniverseObject.Universe;
protected IUniverseObject UniverseObject => BehaviourController.UniverseObject;
public Behaviour()
{
@@ -28,13 +25,13 @@ public abstract class Behaviour : BehaviourBase
BehaviourController.OnPreUpdate += PreUpdate;
BehaviourController.OnPreDraw += PreDraw;
BehaviourController.OnUpdate += Update;
BehaviourController.HierarchyObject.OnEnteredHierarchy += EnteredHierarchy;
BehaviourController.HierarchyObject.OnExitedHierarchy += ExitedHierarchy;
BehaviourController.UniverseObject.OnEnteredUniverse += EnteredUniverse;
BehaviourController.UniverseObject.OnExitedUniverse += ExitedUniverse;
OnInitialize();
if (HierarchyObject.IsInHierarchy)
EnteredHierarchy(HierarchyObject, GameManager);
if (UniverseObject.IsInUniverse)
EnteredUniverse(UniverseObject, Universe);
}
protected virtual void OnFinalize() { }
@@ -43,20 +40,20 @@ public abstract class Behaviour : BehaviourBase
BehaviourController.OnPreUpdate -= PreUpdate;
BehaviourController.OnPreDraw -= PreDraw;
BehaviourController.OnUpdate -= Update;
BehaviourController.HierarchyObject.OnEnteredHierarchy -= EnteredHierarchy;
BehaviourController.HierarchyObject.OnExitedHierarchy -= ExitedHierarchy;
BehaviourController.UniverseObject.OnEnteredUniverse -= EnteredUniverse;
BehaviourController.UniverseObject.OnExitedUniverse -= ExitedUniverse;
OnFinalize();
if (HierarchyObject.IsInHierarchy)
ExitedHierarchy(HierarchyObject, GameManager);
if (UniverseObject.IsInUniverse)
ExitedUniverse(UniverseObject, Universe);
}
protected virtual void OnPreUpdatePreActiveCheck() { }
protected virtual void OnPreUpdate() { }
protected virtual void PreUpdate(IBehaviourController _)
{
AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnPreUpdatePreActiveCheck();
@@ -80,7 +77,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnUpdate() { }
protected virtual void Update(IBehaviourController _)
{
AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnUpdatePreActiveCheck();
@@ -94,7 +91,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreDraw() { }
protected virtual void PreDraw(IBehaviourController _)
{
AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnPreDrawPreActiveCheck();
@@ -104,9 +101,9 @@ public abstract class Behaviour : BehaviourBase
OnPreDraw();
}
protected virtual void OnEnteredHierarchy(IGameManager gameManager) { }
protected virtual void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnEnteredHierarchy(gameManager);
protected virtual void OnEnteredUniverse(IUniverse universe) { }
protected virtual void EnteredUniverse(IUniverseObject sender, IUniverse universe) => OnEnteredUniverse(universe);
protected virtual void OnExitedHierarchy(IGameManager gameManager) { }
protected virtual void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnExitedHierarchy(gameManager);
protected virtual void OnExitedUniverse(IUniverse universe) { }
protected virtual void ExitedUniverse(IUniverseObject sender, IUniverse universe) => OnExitedUniverse(universe);
}

View File

@@ -1,5 +1,3 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public abstract class Behaviour2D : Behaviour, IBehaviour2D
@@ -23,6 +21,6 @@ public abstract class Behaviour2D : Behaviour, IBehaviour2D
protected sealed override void FirstActiveFrame() => base.FirstActiveFrame();
protected sealed override void Update(IBehaviourController behaviourController) => base.Update(behaviourController);
protected sealed override void PreDraw(IBehaviourController behaviourController) => base.PreDraw(behaviourController);
protected sealed override void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.EnteredHierarchy(sender, gameManager);
protected sealed override void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.ExitedHierarchy(sender, gameManager);
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,7 +1,3 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
using Syntriax.Engine.Core.Helpers;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
@@ -25,7 +21,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
int previousPriority = _priority;
_priority = value;
OnPriorityChanged?.Invoke(this, previousPriority);
OnPriorityChanged?.InvokeSafe(this, previousPriority);
}
}
@@ -40,16 +36,16 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
_behaviourController = behaviourController;
OnAssign(behaviourController);
behaviourController.OnHierarchyObjectAssigned += OnHierarchyObjectAssigned;
if (behaviourController.HierarchyObject is not null)
OnHierarchyObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
behaviourController.OnUniverseObjectAssigned += OnUniverseObjectAssigned;
if (behaviourController.UniverseObject is not null)
OnUniverseObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.InvokeSafe(this);
return true;
}
private void OnHierarchyObjectAssigned(IHasHierarchyObject sender)
private void OnUniverseObjectAssigned(IHasUniverseObject sender)
{
sender.HierarchyObject.OnActiveChanged += OnHierarchyObjectActiveChanged;
sender.UniverseObject.OnActiveChanged += OnUniverseObjectActiveChanged;
UpdateActive();
}
@@ -63,26 +59,26 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
protected override void UnassignInternal()
{
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
BehaviourController.OnHierarchyObjectAssigned -= OnHierarchyObjectAssigned;
BehaviourController.OnUniverseObjectAssigned -= OnUniverseObjectAssigned;
base.UnassignInternal();
_behaviourController = null!;
}
protected override void InitializeInternal()
{
AssertHelpers.AssertBehaviourControllerAssigned(this);
AssertHelpers.AssertStateEnableAssigned(this);
Debug.Assert.AssertBehaviourControllerAssigned(this);
Debug.Assert.AssertStateEnableAssigned(this);
}
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
private void OnHierarchyObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void OnUniverseObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void UpdateActive()
{
bool previousActive = IsActive;
_isActive = StateEnable.Enabled && _behaviourController.HierarchyObject.IsActive;
_isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
OnActiveChanged?.InvokeSafe(this, previousActive);
}
}

View File

@@ -2,14 +2,12 @@ using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
{
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
@@ -17,29 +15,29 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
protected readonly List<T> behaviours = new(32);
public IReadOnlyList<T> Behaviours => behaviours;
public IGameManager GameManager { get; private set; } = null!;
public IUniverse Universe { get; private set; } = null!;
public T this[Index index] => behaviours[index];
public BehaviourCollector() { }
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
public BehaviourCollector(IUniverse universe) => Assign(universe);
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
foreach (IBehaviour item in universeObject.BehaviourController)
OnBehaviourAdded(universeObject.BehaviourController, item);
}
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
foreach (IBehaviour item in universeObject.BehaviourController)
OnBehaviourRemoved(universeObject.BehaviourController, item);
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
@@ -50,7 +48,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
behaviours.Add(tBehaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, tBehaviour);
OnCollected?.InvokeSafe(this, tBehaviour);
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
@@ -63,41 +61,41 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return;
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, tBehaviour);
OnRemoved?.InvokeSafe(this, tBehaviour);
}
protected virtual void OnAssign(IGameManager gameManager) { }
public bool Assign(IGameManager gameManager)
protected virtual void OnAssign(IUniverse universe) { }
public bool Assign(IUniverse universe)
{
if (GameManager is not null)
if (Universe is not null)
return false;
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
foreach (IUniverseObject universeObject in universe.UniverseObjects)
OnUniverseObjectRegistered(universe, universeObject);
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered;
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered;
GameManager = gameManager;
OnAssign(gameManager);
OnGameManagerAssigned?.Invoke(this);
Universe = universe;
OnAssign(universe);
OnUniverseAssigned?.InvokeSafe(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
if (Universe is null)
return false;
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
OnUniverseObjectUnregistered(Universe, universeObject);
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered;
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
Universe = null!;
OnUnassigned?.InvokeSafe(this);
return true;
}

View File

@@ -1,7 +1,5 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
@@ -26,5 +24,5 @@ public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
}
public BehaviourCollectorSorted() { }
public BehaviourCollectorSorted(IGameManager gameManager, Comparison<T> sortBy) : base(gameManager) => SortBy = sortBy;
public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe) => SortBy = sortBy;
}

View File

@@ -3,13 +3,10 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Helpers;
namespace Syntriax.Engine.Core;
[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.UpdateEventHandler? OnUpdate = null;
@@ -17,51 +14,29 @@ public class BehaviourController : IBehaviourController
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IHasHierarchyObject.HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasUniverseObject.UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
private IHierarchyObject _hierarchyObject = null!;
private bool _initialized = false;
private IUniverseObject _universeObject = null!;
public IHierarchyObject HierarchyObject => _hierarchyObject;
public bool IsInitialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
else
OnFinalized?.Invoke(this);
}
}
public IUniverseObject UniverseObject => _universeObject;
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
{
InsertBehaviourByPriority(behaviour);
behaviour.Assign(this);
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
behaviour.Initialize();
if (IsInitialized)
behaviour.Initialize();
behaviour.OnPriorityChanged += OnPriorityChange;
OnBehaviourAdded?.Invoke(this, behaviour);
OnBehaviourAdded?.InvokeSafe(this, behaviour);
return behaviour;
}
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_hierarchyObject, args));
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_universeObject, args));
public T? GetBehaviour<T>()
{
@@ -116,81 +91,63 @@ public class BehaviourController : IBehaviourController
public void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour
{
if (!behaviours.Contains(behaviour))
throw new Exception($"{behaviour.GetType().Name} does not exist in {HierarchyObject.Name}'s {nameof(IBehaviourController)}.");
throw new Exception($"{behaviour.GetType().Name} does not exist in {UniverseObject.Name}'s {nameof(IBehaviourController)}.");
behaviour.OnPriorityChanged -= OnPriorityChange;
behaviour.Finalize();
behaviours.Remove(behaviour);
OnBehaviourRemoved?.Invoke(this, behaviour);
OnBehaviourRemoved?.InvokeSafe(this, behaviour);
}
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
public bool Assign(IHierarchyObject hierarchyObject)
protected virtual void OnAssign(IUniverseObject universeObject) { }
public bool Assign(IUniverseObject universeObject)
{
if (HierarchyObject is not null && HierarchyObject.IsInitialized)
if (UniverseObject is not null && UniverseObject.IsInitialized)
return false;
_hierarchyObject = hierarchyObject;
OnAssign(hierarchyObject);
OnHierarchyObjectAssigned?.Invoke(this);
_universeObject = universeObject;
OnAssign(universeObject);
OnUniverseObjectAssigned?.InvokeSafe(this);
return true;
}
public bool Initialize()
protected override void InitializeInternal()
{
if (IsInitialized)
return false;
AssertHelpers.AssertHierarchyObjectAssigned(this);
Debug.Assert.AssertUniverseObjectAssigned(this);
foreach (IBehaviour behaviour in behaviours)
behaviour.Initialize();
IsInitialized = true;
return true;
}
public bool Finalize()
protected override void FinalizeInternal()
{
if (!IsInitialized)
return false;
foreach (IBehaviour behaviour in behaviours)
behaviour.Finalize();
IsInitialized = false;
return true;
}
public bool Unassign()
{
if (IsInitialized)
return false;
_hierarchyObject = null!;
OnUnassigned?.Invoke(this);
return true;
}
public void Update()
{
if (!HierarchyObject.StateEnable.Enabled)
Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return;
OnPreUpdate?.Invoke(this);
OnUpdate?.Invoke(this);
OnPreUpdate?.InvokeSafe(this);
OnUpdate?.InvokeSafe(this);
}
public void UpdatePreDraw()
{
if (!HierarchyObject.StateEnable.Enabled)
Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return;
OnPreDraw?.Invoke(this);
OnPreDraw?.InvokeSafe(this);
}
public BehaviourController() { }
public BehaviourController(IHierarchyObject hierarchyObject) => Assign(hierarchyObject);
public BehaviourController(IUniverseObject universeObject) => Assign(universeObject);
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
{

View File

@@ -1,11 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class CoroutineManager : HierarchyObject
public class CoroutineManager : UniverseObject
{
private readonly List<IEnumerator> enumerators = [];
@@ -20,17 +18,17 @@ public class CoroutineManager : HierarchyObject
enumerators.Remove(enumerator);
}
protected override void OnEnteringHierarchy(IGameManager gameManager)
protected override void OnEnteringUniverse(IUniverse universe)
{
gameManager.OnUpdate += OnUpdate;
universe.OnUpdate += OnUpdate;
}
protected override void OnExitingHierarchy(IGameManager gameManager)
protected override void OnExitingUniverse(IUniverse universe)
{
gameManager.OnUpdate -= OnUpdate;
universe.OnUpdate -= OnUpdate;
}
private void OnUpdate(IGameManager sender, EngineTime time)
private void OnUpdate(IUniverse sender, UniverseTime time)
{
for (int i = enumerators.Count - 1; i >= 0; i--)
{

View File

@@ -1,7 +1,5 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class CoroutineYield(Func<bool> condition) : ICoroutineYield

View File

@@ -0,0 +1,30 @@
using System.Runtime.CompilerServices;
namespace Syntriax.Engine.Core.Debug;
public static class Assert
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertInitialized(IInitializable initializable)
=> System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertEntityAssigned(IHasEntity assignable)
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertUniverseAssigned(IHasUniverse assignable)
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized");
}

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

@@ -6,41 +6,41 @@ skinparam nodesep 100
title Core Engine Relations
interface Engine.Core.Abstract.IEntity extends Engine.Core.Abstract.IInitializable {}
interface Engine.Core.Abstract.IHierarchyObject extends Engine.Core.Abstract.IEntity, Engine.Core.Abstract.INameable {}
interface Engine.Core.IEntity extends Engine.Core.IInitializable {}
interface Engine.Core.IUniverseObject extends Engine.Core.IEntity, Engine.Core.INameable {}
interface Engine.Core.Abstract.INameable {}
interface Engine.Core.INameable {}
Engine.Core.Abstract.IHierarchyObject --> Engine.Core.Abstract.IBehaviourController: has
Engine.Core.Abstract.IBehaviourController "1" --> "0..*" Engine.Core.Abstract.IBehaviour: has
Engine.Core.IUniverseObject --> Engine.Core.IBehaviourController: has
Engine.Core.IBehaviourController "1" --> "0..*" Engine.Core.IBehaviour: has
interface Engine.Core.Abstract.IBehaviourController {}
interface Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.Abstract.IBehaviour2D extends Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.Abstract.IBehaviour3D extends Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.IBehaviourController {}
interface Engine.Core.IBehaviour {}
interface Engine.Core.IBehaviour2D extends Engine.Core.IBehaviour {}
interface Engine.Core.IBehaviour3D extends Engine.Core.IBehaviour {}
interface Engine.Core.Abstract.IGameManager {}
Engine.Core.Abstract.IGameManager "1" -r-> "0..*" Engine.Core.Abstract.IHierarchyObject: has
interface Engine.Core.IUniverse {}
Engine.Core.IUniverse "1" -r-> "0..*" Engine.Core.IUniverseObject: has
' together {
' interface Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasStateEnable extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasGameManager extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasHierarchyObject extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasBehaviourController extends Engine.Core.Abstract.IAssignable {}
' ' Engine.Core.Abstract.IHasStateEnable --> Engine.Core.Abstract.IStateEnable: has
' ' Engine.Core.Abstract.IHasGameManager --> Engine.Core.Abstract.IGameManager: has
' ' Engine.Core.Abstract.IHasHierarchyObject --> Engine.Core.Abstract.IHierarchyObject: has
' ' Engine.Core.Abstract.IHasBehaviourController --> Engine.Core.Abstract.IBehaviourController: has
' interface Engine.Core.IAssignable {}
' interface Engine.Core.IHasStateEnable extends Engine.Core.IAssignable {}
' interface Engine.Core.IHasUniverse extends Engine.Core.IAssignable {}
' interface Engine.Core.IHasUniverseObject extends Engine.Core.IAssignable {}
' interface Engine.Core.IHasBehaviourController extends Engine.Core.IAssignable {}
' ' Engine.Core.IHasStateEnable --> Engine.Core.IStateEnable: has
' ' Engine.Core.IHasUniverse --> Engine.Core.IUniverse: has
' ' Engine.Core.IHasUniverseObject --> Engine.Core.IUniverseObject: has
' ' Engine.Core.IHasBehaviourController --> Engine.Core.IBehaviourController: has
' }
together {
interface Engine.Core.Abstract.ITransform2D {}
interface Engine.Core.Abstract.ICamera2D {}
interface Engine.Core.Abstract.ICoroutineYield {}
interface Engine.Core.Abstract.IStateEnable {}
interface Engine.Core.Abstract.IInitializable {}
interface Engine.Core.Abstract.IBehaviourCollector {}
interface Engine.Core.ITransform2D {}
interface Engine.Core.ICamera2D {}
interface Engine.Core.ICoroutineYield {}
interface Engine.Core.IStateEnable {}
interface Engine.Core.IInitializable {}
interface Engine.Core.IBehaviourCollector {}
}
@enduml

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core;
@@ -27,7 +26,7 @@ public static class BehaviourControllerExtensions
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
/// <summary>
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
@@ -40,12 +39,12 @@ public static class BehaviourControllerExtensions
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
/// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively.
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the parent hierarchy; otherwise, <see cref="false"/>.</returns>
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the parent universe; otherwise, <see cref="false"/>.</returns>
public static bool TryGetBehaviourInParent<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetBehaviourInParent<T>(behaviourController);
@@ -53,7 +52,7 @@ public static class BehaviourControllerExtensions
}
/// <summary>
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively.
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
@@ -67,28 +66,28 @@ public static class BehaviourControllerExtensions
if (controller.GetBehaviour<T>() is T behaviour)
return behaviour;
controller = controller.HierarchyObject.Parent?.BehaviourController;
controller = controller.UniverseObject.Parent?.BehaviourController;
}
return default;
}
/// <summary>
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s parents recursively. Throws an error if not found.
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively. Throws an error if not found.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
/// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s children recursively.
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the child hierarchy; otherwise, <see cref="false"/>.</returns>
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the child universe; otherwise, <see cref="false"/>.</returns>
public static bool TryGetBehaviourInChildren<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetBehaviourInChildren<T>(behaviourController);
@@ -96,7 +95,7 @@ public static class BehaviourControllerExtensions
}
/// <summary>
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IHierarchyObject"/>'s children recursively.
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
@@ -106,7 +105,7 @@ public static class BehaviourControllerExtensions
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
return localBehaviour;
foreach (IHierarchyObject child in behaviourController.HierarchyObject)
foreach (IUniverseObject child in behaviourController.UniverseObject)
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
return behaviour;
@@ -120,5 +119,5 @@ public static class BehaviourControllerExtensions
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.HierarchyObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
}

View File

@@ -1,35 +1,33 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class BehaviourExtensions
{
public static T? FindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
public static T? FindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
{
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject.BehaviourController.GetBehaviour<T>() is T behaviour)
foreach (IUniverseObject universeObject in universeObjects)
if (universeObject.BehaviourController.GetBehaviour<T>() is T behaviour)
return behaviour;
return default;
}
public static bool TryFindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
public static bool TryFindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = FindBehaviour<T>(hierarchyObjects);
behaviour = FindBehaviour<T>(universeObjects);
return behaviour is not null;
}
public static void FindBehaviours<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
public static void FindBehaviours<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
List<T> cache = [];
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
foreach (IUniverseObject universeObject in universeObjects)
{
hierarchyObject.BehaviourController.GetBehaviours(cache);
universeObject.BehaviourController.GetBehaviours(cache);
behaviours.AddRange(cache);
}
}

View File

@@ -1,16 +0,0 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core;
public static class GameManagerExtensions
{
public static IHierarchyObject InstantiateHierarchyObject(this IGameManager gameManager, params object?[]? args)
=> gameManager.InstantiateHierarchyObject<HierarchyObject>(args);
public static T GetRequiredHierarchyObject<T>(this IGameManager gameManager) where T : class
=> gameManager.GetHierarchyObject<T>() ?? throw new HierarchyObjectNotFoundException($"{gameManager.GetType().FullName}({gameManager.Id}) does not contain any {nameof(IHierarchyObject)} object of type {typeof(T).FullName}");
public static T FindRequiredBehaviour<T>(this IGameManager gameManager) where T : class
=> gameManager.FindBehaviour<T>() ?? throw new BehaviourNotFoundException($"{gameManager.GetType().FullName}({gameManager.Id}) does not contain any {nameof(IHierarchyObject)} with {nameof(IBehaviour)} of type {typeof(T).FullName}");
}

View File

@@ -1,41 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class HierarchyObjectExtensions
{
public static T SetHierarchyObject<T>(this T hierarchyObject, string? name = "", IHierarchyObject? parent = null) where T : IHierarchyObject
{
if (!string.IsNullOrWhiteSpace(name))
hierarchyObject.Name = name;
if (parent is not null)
hierarchyObject.SetParent(parent);
return hierarchyObject;
}
public static T? GetHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
{
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject is T @object)
return @object;
return default;
}
public static bool TryGetHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetHierarchyObject<T>(hierarchyObjects);
return behaviour is not null;
}
public static void GetHierarchyObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject is T @object)
behaviours.Add(@object);
}
}

View File

@@ -1,5 +1,3 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class TransformExtensions

View File

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

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core;
public static class UniverseObjectExtensions
{
public static T SetUniverseObject<T>(this T universeObject, string? name = "", IUniverseObject? parent = null) where T : IUniverseObject
{
if (!string.IsNullOrWhiteSpace(name))
universeObject.Name = name;
if (parent is not null)
universeObject.SetParent(parent);
return universeObject;
}
public static T? GetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
{
foreach (IUniverseObject universeObject in universeObjects)
if (universeObject is T @object)
return @object;
return default;
}
public static bool TryGetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetUniverseObject<T>(universeObjects);
return behaviour is not null;
}
public static void GetUniverseObjects<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
foreach (IUniverseObject universeObject in universeObjects)
if (universeObject is T @object)
behaviours.Add(@object);
}
}

View File

@@ -1,23 +1,22 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;
public class BehaviourControllerFactory
{
public static IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
=> Instantiate<BehaviourController>(hierarchyObject);
public static IBehaviourController Instantiate(IUniverseObject universeObject)
=> Instantiate<BehaviourController>(universeObject);
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args)
where T : class, IBehaviourController
{
T behaviourController = TypeFactory.Get<T>(args);
if (!hierarchyObject.Assign(behaviourController))
throw AssignFailedException.From(hierarchyObject, behaviourController);
if (!universeObject.Assign(behaviourController))
throw AssignFailedException.From(universeObject, behaviourController);
if (!behaviourController.Assign(hierarchyObject))
throw AssignFailedException.From(behaviourController, hierarchyObject);
if (!behaviourController.Assign(universeObject))
throw AssignFailedException.From(behaviourController, universeObject);
return behaviourController;
}

View File

@@ -1,14 +1,13 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;
public class BehaviourFactory
{
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(hierarchyObject, stateEnable: null, args);
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(universeObject, stateEnable: null, args);
public static T Instantiate<T>(IHierarchyObject hierarchyObject, IStateEnable? stateEnable, params object?[]? args)
public static T Instantiate<T>(IUniverseObject universeObject, IStateEnable? stateEnable, params object?[]? args)
where T : class, IBehaviour
{
T behaviour = TypeFactory.Get<T>(args);
@@ -19,8 +18,8 @@ public class BehaviourFactory
if (!behaviour.Assign(stateEnable))
throw AssignFailedException.From(behaviour, stateEnable);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignFailedException.From(behaviour, hierarchyObject.BehaviourController);
if (!behaviour.Assign(universeObject.BehaviourController))
throw AssignFailedException.From(behaviour, universeObject.BehaviourController);
return behaviour;
}

View File

@@ -1,37 +0,0 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;
public class HierarchyObjectFactory
{
public static IHierarchyObject Instantiate() => Instantiate<HierarchyObject>();
public static T Instantiate<T>(params object?[]? args) where T : class, IHierarchyObject
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
public static IHierarchyObject Instantiate(IBehaviourController? behaviourController = null, IStateEnable? stateEnable = null) => Instantiate<HierarchyObject>(behaviourController, stateEnable);
public static T Instantiate<T>(
IBehaviourController? behaviourController = null,
IStateEnable? stateEnable = null,
params object?[]? args
)
where T : class, IHierarchyObject
{
T hierarchyObject = TypeFactory.Get<T>(args);
behaviourController ??= TypeFactory.Get<BehaviourController>();
stateEnable ??= TypeFactory.Get<StateEnable>();
if (!behaviourController.Assign(hierarchyObject))
throw AssignFailedException.From(behaviourController, hierarchyObject);
if (!stateEnable.Assign(hierarchyObject))
throw AssignFailedException.From(stateEnable, hierarchyObject);
if (!hierarchyObject.Assign(behaviourController))
throw AssignFailedException.From(hierarchyObject, behaviourController);
if (!hierarchyObject.Assign(stateEnable))
throw AssignFailedException.From(hierarchyObject, stateEnable);
return hierarchyObject;
}
}

View File

@@ -1,4 +1,3 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;

View File

@@ -1,5 +1,3 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core.Factory;
public class TransformFactory

View File

@@ -1,21 +1,59 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Syntriax.Engine.Core.Factory;
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)
result = Activator.CreateInstance(typeof(T), args) as T;
result = Activator.CreateInstance(type, args);
else
result = Activator.CreateInstance(typeof(T)) as T;
result = Activator.CreateInstance(type);
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;
}
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

@@ -0,0 +1,36 @@
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;
public class UniverseObjectFactory
{
public static IUniverseObject Instantiate() => Instantiate<UniverseObject>();
public static T Instantiate<T>(params object?[]? args) where T : class, IUniverseObject
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
public static IUniverseObject Instantiate(IBehaviourController? behaviourController = null, IStateEnable? stateEnable = null) => Instantiate<UniverseObject>(behaviourController, stateEnable);
public static T Instantiate<T>(
IBehaviourController? behaviourController = null,
IStateEnable? stateEnable = null,
params object?[]? args
)
where T : class, IUniverseObject
{
T universeObject = TypeFactory.Get<T>(args);
behaviourController ??= TypeFactory.Get<BehaviourController>();
stateEnable ??= TypeFactory.Get<StateEnable>();
if (!behaviourController.Assign(universeObject))
throw AssignFailedException.From(behaviourController, universeObject);
if (!stateEnable.Assign(universeObject))
throw AssignFailedException.From(stateEnable, universeObject);
if (!universeObject.Assign(behaviourController))
throw AssignFailedException.From(universeObject, behaviourController);
if (!universeObject.Assign(stateEnable))
throw AssignFailedException.From(universeObject, stateEnable);
return universeObject;
}
}

View File

@@ -1,142 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Helpers;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("HierarchyObject Count: {_hierarchyObjects.Count}")]
public class GameManager : BaseEntity, IGameManager
{
public event IGameManager.UpdateEventHandler? OnPreUpdate = null;
public event IGameManager.UpdateEventHandler? OnUpdate = null;
public event IGameManager.PreDrawEventHandler? OnPreDraw = null;
public event IGameManager.HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
public event IGameManager.HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
private readonly List<IHierarchyObject> _hierarchyObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
private float _timeScale = 1f;
public IReadOnlyList<IHierarchyObject> HierarchyObjects => _hierarchyObjects;
public EngineTime Time { get; private set; } = new();
public EngineTime UnscaledTime { get; private set; } = new();
public float TimeScale
{
get => _timeScale;
set => _timeScale = value.Max(0f);
}
public void Register(IHierarchyObject hierarchyObject)
{
if (_hierarchyObjects.Contains(hierarchyObject))
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is already registered to the {nameof(GameManager)}.");
hierarchyObject.OnFinalized += OnHierarchyObjectFinalize;
hierarchyObject.OnExitedHierarchy += OnHierarchyObjectExitedHierarchy;
if (!hierarchyObject.Initialize())
throw new Exception($"{hierarchyObject.Name} can't be initialized");
for (int i = 0; i < hierarchyObject.Children.Count; i++)
Register(hierarchyObject.Children[i]);
_hierarchyObjects.Add(hierarchyObject);
if (!hierarchyObject.EnterHierarchy(this))
throw new Exception($"{hierarchyObject.Name} can't enter the hierarchy");
OnHierarchyObjectRegistered?.Invoke(this, hierarchyObject);
}
public T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject
{
T hierarchyObject = Factory.HierarchyObjectFactory.Instantiate<T>(args);
Register(hierarchyObject);
return hierarchyObject;
}
public void Remove(IHierarchyObject hierarchyObject)
{
hierarchyObject.SetParent(null);
RemoveIncursive(hierarchyObject);
}
private void RemoveIncursive(IHierarchyObject hierarchyObject)
{
if (!_hierarchyObjects.Contains(hierarchyObject))
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is not registered to the {nameof(GameManager)}.");
hierarchyObject.OnFinalized -= OnHierarchyObjectFinalize;
hierarchyObject.OnExitedHierarchy -= OnHierarchyObjectExitedHierarchy;
for (int i = hierarchyObject.Children.Count - 1; i >= 0; i--)
Remove(hierarchyObject.Children[i]);
_hierarchyObjects.Remove(hierarchyObject);
if (!hierarchyObject.ExitHierarchy())
throw new Exception($"{hierarchyObject.Name} can't exit the hierarchy");
if (!hierarchyObject.Finalize())
throw new Exception($"{hierarchyObject.Name} can't be finalized");
OnHierarchyObjectUnRegistered?.Invoke(this, hierarchyObject);
}
protected override void InitializeInternal()
{
foreach (IHierarchyObject hierarchyObject in HierarchyObjects)
hierarchyObject.Initialize();
}
protected override void FinalizeInternal()
{
base.FinalizeInternal();
for (int i = HierarchyObjects.Count; i >= 0; i--)
HierarchyObjects[i].Finalize();
}
public void Update(EngineTime engineTime)
{
AssertHelpers.AssertInitialized(this);
UnscaledTime = engineTime;
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
OnPreUpdate?.Invoke(this, Time);
for (int i = 0; i < HierarchyObjects.Count; i++)
HierarchyObjects[i].BehaviourController.Update();
OnUpdate?.Invoke(this, Time);
}
public void PreDraw()
{
AssertHelpers.AssertInitialized(this);
for (int i = 0; i < HierarchyObjects.Count; i++)
HierarchyObjects[i].BehaviourController.UpdatePreDraw();
OnPreDraw?.Invoke(this);
}
private void OnHierarchyObjectFinalize(IInitializable initializable)
{
if (initializable is IHierarchyObject hierarchyObject)
Remove(hierarchyObject);
}
private void OnHierarchyObjectExitedHierarchy(IHierarchyObject sender, IGameManager gameManager)
{
if (sender is IHierarchyObject hierarchyObject)
Remove(hierarchyObject);
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _hierarchyObjects.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _hierarchyObjects.GetEnumerator();
}

View File

@@ -1,33 +0,0 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core.Helpers;
public class AssertHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertInitialized(IInitializable initializable)
=> Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
=> Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertEntityAssigned(IHasEntity assignable)
=> Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertGameManagerAssigned(IHasGameManager assignable)
=> Debug.Assert(assignable.GameManager is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertHierarchyObjectAssigned(IHasHierarchyObject assignable)
=> Debug.Assert(assignable.HierarchyObject is not null, $"{assignable.GetType().Name} must be initialized");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
=> Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized");
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Syntriax.Engine.Core;
public static class DelegateExtensions
{
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,7 @@
namespace Syntriax.Engine.Core;
public interface IProgressionTracker : IReadOnlyProgressionTracker
{
void Set(float progression, string status);
void Reset();
}

View File

@@ -0,0 +1,13 @@
namespace Syntriax.Engine.Core;
public interface IReadOnlyProgressionTracker
{
event ProgressionUpdatedEventHandler? OnUpdated;
event ProgressionEndedEventHandler? OnEnded;
float Progression { get; }
string Status { get; }
delegate void ProgressionUpdatedEventHandler(IReadOnlyProgressionTracker sender, float previousProgression, string previousStatus);
delegate void ProgressionEndedEventHandler(IReadOnlyProgressionTracker sender);
}

View File

@@ -0,0 +1,36 @@
namespace Syntriax.Engine.Core;
public class ProgressionTracker : IProgressionTracker
{
public event IReadOnlyProgressionTracker.ProgressionUpdatedEventHandler? OnUpdated = null;
public event IReadOnlyProgressionTracker.ProgressionEndedEventHandler? OnEnded = null;
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?.InvokeSafe(this, previousProgression, previousStatus);
if (progression >= 1f)
OnEnded?.InvokeSafe(this);
}
void IProgressionTracker.Reset()
{
Progression = 0f;
Status = "Default";
OnUpdated = null;
OnEnded = null;
}
}

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>
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>
/// Returns the absolute value of a number.
/// </summary>
@@ -190,6 +248,15 @@ public static class Math
/// <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);
/// <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>
/// Returns the square of a number.
/// </summary>

View File

@@ -5,6 +5,24 @@ namespace Syntriax.Engine.Core;
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)" />
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)" />
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)" />
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 Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <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;
namespace Syntriax.Engine.Core;
@@ -68,12 +67,12 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary>
public static float GetT(Line2D line, Vector2D point)
{
float fromX = MathF.Abs(line.From.X);
float toX = MathF.Abs(line.To.X);
float pointX = MathF.Abs(point.X);
float fromX = Math.Abs(line.From.X);
float toX = Math.Abs(line.To.X);
float pointX = Math.Abs(point.X);
float min = MathF.Min(fromX, toX);
float max = MathF.Max(fromX, toX) - min;
float min = Math.Min(fromX, toX);
float max = Math.Max(fromX, toX) - min;
pointX -= min;
@@ -114,8 +113,8 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary>
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) &&
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
if (point.X <= Math.Max(line.From.X, line.To.X) && point.X >= Math.Min(line.From.X, line.To.X) &&
point.Y <= Math.Max(line.From.Y, line.To.Y) && point.Y >= Math.Min(line.From.Y, line.To.Y))
return true;
return false;
@@ -173,7 +172,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
t = MathF.Max(0, MathF.Min(1, t));
t = Math.Max(0, Math.Min(1, t));
float closestX = line.From.X + t * edgeVector.X;
float closestY = line.From.Y + t * edgeVector.Y;

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
@@ -178,11 +176,11 @@ public readonly struct Quaternion(float x, float y, float z, float w)
if (dot > 0.9995f)
return Lerp(from, to, t);
float angle = MathF.Acos(dot);
float sinAngle = MathF.Sin(angle);
float angle = Math.Acos(dot);
float sinAngle = Math.Sin(angle);
float fromWeight = MathF.Sin((1f - t) * angle) / sinAngle;
float toWeight = MathF.Sin(t * angle) / sinAngle;
float fromWeight = Math.Sin((1f - t) * angle) / sinAngle;
float toWeight = Math.Sin(t * angle) / sinAngle;
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)
{
float halfAngle = angle * .5f;
float sinHalf = MathF.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, MathF.Cos(halfAngle));
float sinHalf = Math.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
}
/// <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"/>.
/// </summary>
/// <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>

View File

@@ -1,8 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
/// <summary>
@@ -13,19 +11,33 @@ namespace Syntriax.Engine.Core;
/// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices.
/// </remarks>
[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 readonly Shape2D Square = CreateNgon(4, Vector2D.One);
public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up);
public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right);
public static Shape2D Triangle => CreateNgon(3, Vector2D.Up);
public static Shape2D Square => CreateNgon(4, Vector2D.One);
public static Shape2D Pentagon => CreateNgon(5, Vector2D.Up);
public static Shape2D Hexagon => CreateNgon(6, Vector2D.Right);
private readonly List<Vector2D> _verticesList = vertices;
public event ShapeUpdatedEventHandler? OnShapeUpdated = null;
private List<Vector2D> _vertices = vertices;
/// <summary>
/// Gets the vertices of the <see cref="Shape2D"/>.
/// </summary>
public IReadOnlyList<Vector2D> Vertices => _verticesList;
public IReadOnlyList<Vector2D> Vertices
{
get => _vertices;
set
{
_vertices.Clear();
foreach (Vector2D vertex in value)
_vertices.Add(vertex);
OnShapeUpdated?.InvokeSafe(this);
}
}
/// <summary>
/// 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="transform">The <see cref="ITransform2D"/> to apply.</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++)
to._verticesList.Add(transform.Transform(from[i]));
to._vertices.Add(transform.Transform(from[i]));
to.OnShapeUpdated?.InvokeSafe(to);
}
/// <summary>
@@ -242,6 +256,8 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
public delegate void ShapeUpdatedEventHandler(Shape2D shape2D);
}
/// <summary>
@@ -277,13 +293,13 @@ public static class Shape2DExtensions
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
/// <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)" />
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, ref Shape2D)" />
public static void Transform(this Shape2D from, ITransform2D transform, ref Shape2D to) => Shape2D.Transform(from, transform, ref to);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D,Shape2D)" />
public static void Transform(this Shape2D from, ITransform2D transform, Shape2D to) => Shape2D.Transform(from, transform, to);
/// <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);

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
[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 float Area
=> .5f * MathF.Abs(
=> .5f * Math.Abs(
A.X * (B.Y - C.Y) +
B.X * (C.Y - A.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);
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 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;
/// <summary>
@@ -317,7 +314,7 @@ public readonly struct Vector2D(float x, float y)
/// Generates a hash code for the <see cref="Vector2D"/>.
/// </summary>
/// <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>

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
@@ -290,7 +288,7 @@ public readonly struct Vector3D(float x, float y, float z)
/// Generates a hash code for the <see cref="Vector3D"/>.
/// </summary>
/// <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>

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 EntityRegisteredEventHandler? 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?.InvokeSafe(this, 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?.InvokeSafe(registeredEntities[id]);
}
public void Reset()
{
assignCallbacks.Clear();
registeredEntities.Clear();
}
public delegate void EntityRegisteredEventHandler(EntityRegistry sender, 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,5 +1,3 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class StateEnable : IStateEnable
@@ -23,7 +21,7 @@ public class StateEnable : IStateEnable
bool previousState = _enabled;
_enabled = value;
OnEnabledChanged?.Invoke(this, previousState);
OnEnabledChanged?.InvokeSafe(this, previousState);
}
}
@@ -35,7 +33,7 @@ public class StateEnable : IStateEnable
_entity = entity;
OnAssign(entity);
OnEntityAssigned?.Invoke(this);
OnEntityAssigned?.InvokeSafe(this);
return true;
}
@@ -45,7 +43,7 @@ public class StateEnable : IStateEnable
return false;
_entity = null!;
OnUnassigned?.Invoke(this);
OnUnassigned?.InvokeSafe(this);
return true;
}
}

View File

@@ -3,6 +3,5 @@ namespace Syntriax.Engine.Core;
internal static class Constants
{
internal static int BEHAVIOURS_SIZE_INITIAL = 16;
internal static int GAME_OBJECTS_SIZE_INITIAL = 256;
internal static int DRAWABLE_OBJECTS_SIZE_INITIAL = 256;
internal static int UNIVERSE_OBJECTS_SIZE_INITIAL = 256;
}

View File

@@ -1,8 +1,8 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Serialization;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {HierarchyObject.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 event ITransform2D.PositionChangedEventHandler? OnPositionChanged = null;
@@ -13,9 +13,9 @@ public class Transform2D : Behaviour, ITransform2D
private Vector2D _scale = Vector2D.One;
private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f;
[Serialize] private Vector2D _localPosition = Vector2D.Zero;
[Serialize] private Vector2D _localScale = Vector2D.One;
[Serialize] private float _localRotation = 0f;
private ITransform2D? parentTransform = null;
@@ -31,7 +31,7 @@ public class Transform2D : Behaviour, ITransform2D
_position = value;
UpdateLocalPosition();
OnPositionChanged?.Invoke(this, _position);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
}
@@ -47,7 +47,7 @@ public class Transform2D : Behaviour, ITransform2D
_scale = value;
UpdateLocalScale();
OnScaleChanged?.Invoke(this, previousScale);
OnScaleChanged?.InvokeSafe(this, previousScale);
}
}
@@ -63,7 +63,7 @@ public class Transform2D : Behaviour, ITransform2D
_rotation = value;
UpdateLocalRotation();
OnRotationChanged?.Invoke(this, previousRotation);
OnRotationChanged?.InvokeSafe(this, previousRotation);
}
}
@@ -79,7 +79,7 @@ public class Transform2D : Behaviour, ITransform2D
_localPosition = value;
UpdatePosition();
OnPositionChanged?.Invoke(this, previousPosition);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
}
@@ -97,8 +97,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale();
UpdatePosition();
OnScaleChanged?.Invoke(this, previousScale);
OnPositionChanged?.Invoke(this, previousPosition);
OnScaleChanged?.InvokeSafe(this, previousScale);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
}
@@ -114,7 +114,7 @@ public class Transform2D : Behaviour, ITransform2D
_localRotation = value;
UpdateRotation();
OnRotationChanged?.Invoke(this, previousRotation);
OnRotationChanged?.InvokeSafe(this, previousRotation);
}
}
@@ -125,7 +125,7 @@ public class Transform2D : Behaviour, ITransform2D
UpdatePosition();
OnPositionChanged?.Invoke(this, previousPosition);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
private void RecalculateScale(ITransform2D _, Vector2D previousScale)
@@ -138,8 +138,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale();
UpdatePosition();
OnScaleChanged?.Invoke(this, previousScale);
OnPositionChanged?.Invoke(this, previousPosition);
OnScaleChanged?.InvokeSafe(this, previousScale);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
private void RecalculateRotation(ITransform2D _, float previousRotation)
@@ -152,8 +152,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdateRotation();
UpdatePosition();
OnRotationChanged?.Invoke(this, previousRotation);
OnPositionChanged?.Invoke(this, previousPosition);
OnRotationChanged?.InvokeSafe(this, previousRotation);
OnPositionChanged?.InvokeSafe(this, previousPosition);
}
private void UpdateLocalPosition()
@@ -206,16 +206,16 @@ public class Transform2D : Behaviour, ITransform2D
protected override void InitializeInternal()
{
UpdateReferences(HierarchyObject.Parent);
HierarchyObject.OnParentChanged += OnParentChanged;
UpdateReferences(UniverseObject.Parent);
UniverseObject.OnParentChanged += OnParentChanged;
}
protected override void FinalizeInternal()
{
HierarchyObject.OnParentChanged -= OnParentChanged;
UniverseObject.OnParentChanged -= OnParentChanged;
}
private void UpdateReferences(IHierarchyObject? parent)
private void UpdateReferences(IUniverseObject? parent)
{
ITransform2D? previousParent = parentTransform;
if (previousParent is not null)
@@ -223,7 +223,7 @@ public class Transform2D : Behaviour, ITransform2D
previousParent.OnPositionChanged -= RecalculatePosition;
previousParent.OnScaleChanged -= RecalculateScale;
previousParent.OnRotationChanged -= RecalculateRotation;
previousParent.BehaviourController.HierarchyObject.OnParentChanged -= OnParentChanged;
previousParent.BehaviourController.UniverseObject.OnParentChanged -= OnParentChanged;
previousParent.BehaviourController.OnBehaviourAdded -= LookForTransform2D;
}
@@ -234,22 +234,22 @@ public class Transform2D : Behaviour, ITransform2D
parentTransform.OnPositionChanged += RecalculatePosition;
parentTransform.OnScaleChanged += RecalculateScale;
parentTransform.OnRotationChanged += RecalculateRotation;
parentTransform.BehaviourController.HierarchyObject.OnParentChanged += OnParentChanged;
parentTransform.BehaviourController.UniverseObject.OnParentChanged += OnParentChanged;
UpdatePosition();
UpdateScale();
UpdateRotation();
}
else if (HierarchyObject.Parent is not null)
HierarchyObject.Parent.BehaviourController.OnBehaviourAdded += LookForTransform2D;
else if (UniverseObject.Parent is not null)
UniverseObject.Parent.BehaviourController.OnBehaviourAdded += LookForTransform2D;
UpdateLocalPosition();
UpdateLocalScale();
UpdateLocalRotation();
OnPositionChanged?.Invoke(this, Position);
OnScaleChanged?.Invoke(this, Scale);
OnRotationChanged?.Invoke(this, Rotation);
OnPositionChanged?.InvokeSafe(this, Position);
OnScaleChanged?.InvokeSafe(this, Scale);
OnRotationChanged?.InvokeSafe(this, Rotation);
}
private void LookForTransform2D(IBehaviourController sender, IBehaviour behaviourAdded)
@@ -257,10 +257,10 @@ public class Transform2D : Behaviour, ITransform2D
if (behaviourAdded is not ITransform2D transform2D)
return;
UpdateReferences(HierarchyObject.Parent);
UpdateReferences(UniverseObject.Parent);
}
private void OnParentChanged(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent)
private void OnParentChanged(IUniverseObject sender, IUniverseObject? previousParent, IUniverseObject? newParent)
{
UpdateReferences(newParent);
}

150
Engine.Core/Universe.cs Normal file
View File

@@ -0,0 +1,150 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")]
public class Universe : BaseEntity, IUniverse
{
public event IUniverse.UpdateEventHandler? OnPreUpdate = null;
public event IUniverse.UpdateEventHandler? OnUpdate = null;
public event IUniverse.PreDrawEventHandler? OnPreDraw = null;
public event IUniverse.UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered = null;
public event IUniverse.UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered = null;
public event IUniverse.TimeScaleChangedEventHandler? OnTimeScaleChanged = null;
private readonly List<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
private float _timeScale = 1f;
public IReadOnlyList<IUniverseObject> UniverseObjects => _universeObjects;
public UniverseTime Time { get; private set; } = new();
public UniverseTime UnscaledTime { get; private set; } = new();
public float TimeScale
{
get => _timeScale;
set
{
value = value.Max(0f);
if (value == _timeScale)
return;
float previousTimeScale = _timeScale;
_timeScale = value;
OnTimeScaleChanged?.InvokeSafe(this, previousTimeScale);
}
}
public void Register(IUniverseObject universeObject)
{
if (_universeObjects.Contains(universeObject))
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}.");
universeObject.OnFinalized += OnUniverseObjectFinalize;
universeObject.OnExitedUniverse += OnUniverseObjectExitedUniverse;
if (!universeObject.Initialize())
throw new Exception($"{universeObject.Name} can't be initialized");
for (int i = 0; i < universeObject.Children.Count; i++)
Register(universeObject.Children[i]);
_universeObjects.Add(universeObject);
if (!universeObject.EnterUniverse(this))
throw new Exception($"{universeObject.Name} can't enter the universe");
OnUniverseObjectRegistered?.InvokeSafe(this, universeObject);
}
public T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject
{
T universeObject = Factory.UniverseObjectFactory.Instantiate<T>(args);
Register(universeObject);
return universeObject;
}
public void Remove(IUniverseObject universeObject)
{
universeObject.SetParent(null);
RemoveIncursive(universeObject);
}
private void RemoveIncursive(IUniverseObject universeObject)
{
if (!_universeObjects.Contains(universeObject))
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}.");
universeObject.OnFinalized -= OnUniverseObjectFinalize;
universeObject.OnExitedUniverse -= OnUniverseObjectExitedUniverse;
for (int i = universeObject.Children.Count - 1; i >= 0; i--)
Remove(universeObject.Children[i]);
_universeObjects.Remove(universeObject);
if (!universeObject.ExitUniverse())
throw new Exception($"{universeObject.Name} can't exit the universe");
if (!universeObject.Finalize())
throw new Exception($"{universeObject.Name} can't be finalized");
OnUniverseObjectUnRegistered?.InvokeSafe(this, universeObject);
}
protected override void InitializeInternal()
{
foreach (IUniverseObject universeObject in UniverseObjects)
universeObject.Initialize();
}
protected override void FinalizeInternal()
{
base.FinalizeInternal();
for (int i = UniverseObjects.Count; i >= 0; i--)
UniverseObjects[i].Finalize();
}
public void Update(UniverseTime engineTime)
{
Debug.Assert.AssertInitialized(this);
UnscaledTime = engineTime;
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
OnPreUpdate?.InvokeSafe(this, Time);
for (int i = 0; i < UniverseObjects.Count; i++)
UniverseObjects[i].BehaviourController.Update();
OnUpdate?.InvokeSafe(this, Time);
}
public void PreDraw()
{
Debug.Assert.AssertInitialized(this);
for (int i = 0; i < UniverseObjects.Count; i++)
UniverseObjects[i].BehaviourController.UpdatePreDraw();
OnPreDraw?.InvokeSafe(this);
}
private void OnUniverseObjectFinalize(IInitializable initializable)
{
if (initializable is IUniverseObject universeObject)
Remove(universeObject);
}
private void OnUniverseObjectExitedUniverse(IUniverseObject sender, IUniverse universe)
{
if (sender is IUniverseObject universeObject)
Remove(universeObject);
}
public IEnumerator<IUniverseObject> GetEnumerator() => _universeObjects.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _universeObjects.GetEnumerator();
}

View File

@@ -1,33 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class HierarchyObject : BaseEntity, IHierarchyObject
public class UniverseObject : BaseEntity, IUniverseObject
{
public event IHierarchyObject.EnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.ExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IHierarchyObject.ParentChangedEventHandler? OnParentChanged = null;
public event IHierarchyObject.ChildrenAddedEventHandler? OnChildrenAdded = null;
public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IUniverseObject.EnteredUniverseEventHandler? OnEnteredUniverse = null;
public event IUniverseObject.ExitedUniverseEventHandler? OnExitedUniverse = null;
public event IUniverseObject.ParentChangedEventHandler? OnParentChanged = null;
public event IUniverseObject.ChildrenAddedEventHandler? OnChildrenAdded = null;
public event IUniverseObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private string _name = nameof(HierarchyObject);
private IGameManager _gameManager = null!;
private string _name = nameof(UniverseObject);
private IUniverse _universe = null!;
private IBehaviourController _behaviourController = null!;
private bool _isActive = false;
private readonly List<IHierarchyObject> _children = [];
private readonly List<IUniverseObject> _children = [];
public IHierarchyObject? Parent { get; private set; } = null;
public IReadOnlyList<IHierarchyObject> Children => _children;
public IUniverseObject? Parent { get; private set; } = null;
public IReadOnlyList<IUniverseObject> Children => _children;
public IBehaviourController BehaviourController => _behaviourController;
public IGameManager GameManager => _gameManager;
public bool IsInHierarchy => _gameManager is not null;
public IUniverse Universe => _universe;
public bool IsInUniverse => _universe is not null;
public bool IsActive => _isActive;
public string Name
@@ -39,36 +37,36 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
OnNameChanged?.InvokeSafe(this, previousName);
}
}
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
protected virtual void OnEnteringUniverse(IUniverse universe) { }
bool IUniverseObject.EnterUniverse(IUniverse universe)
{
if (IsInHierarchy)
if (IsInUniverse)
return false;
_gameManager = gameManager;
_universe = universe;
UpdateActive();
OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager);
OnEnteringUniverse(universe);
OnEnteredUniverse?.InvokeSafe(this, universe);
return true;
}
protected virtual void OnExitingHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.ExitHierarchy()
protected virtual void OnExitingUniverse(IUniverse universe) { }
bool IUniverseObject.ExitUniverse()
{
if (!IsInHierarchy || _gameManager is not IGameManager gameManager)
if (!IsInUniverse || _universe is not IUniverse universe)
return false;
OnExitingHierarchy(gameManager);
_gameManager = null!;
OnExitedHierarchy?.Invoke(this, gameManager);
OnExitingUniverse(universe);
_universe = null!;
OnExitedUniverse?.InvokeSafe(this, universe);
return true;
}
public void SetParent(IHierarchyObject? parent)
public void SetParent(IUniverseObject? parent)
{
if (parent == this)
throw new Exceptions.AssignFailedException($"{Name} can not parent itself");
@@ -76,7 +74,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
if (Parent == parent)
return;
IHierarchyObject? previousParent = Parent;
IUniverseObject? previousParent = Parent;
if (previousParent is not null)
{
previousParent.RemoveChild(this);
@@ -87,34 +85,34 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
if (parent is not null)
{
if (parent.IsInHierarchy && !IsInHierarchy)
parent.GameManager.Register(this);
if (parent.IsInUniverse && !IsInUniverse)
parent.Universe.Register(this);
parent.AddChild(this);
parent.OnActiveChanged += OnParentActiveChanged;
}
UpdateActive();
OnParentChanged?.Invoke(this, previousParent, parent);
OnParentChanged?.InvokeSafe(this, previousParent, parent);
}
public void AddChild(IHierarchyObject parent)
public void AddChild(IUniverseObject parent)
{
if (_children.Contains(parent))
return;
_children.Add(parent);
parent.SetParent(this);
OnChildrenAdded?.Invoke(this, parent);
OnChildrenAdded?.InvokeSafe(this, parent);
}
public void RemoveChild(IHierarchyObject child)
public void RemoveChild(IUniverseObject child)
{
if (!_children.Remove(child))
return;
child.SetParent(null);
OnChildrenRemoved?.Invoke(this, child);
OnChildrenRemoved?.InvokeSafe(this, child);
}
protected virtual void OnAssign(IBehaviourController behaviourController) { }
@@ -125,7 +123,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
_behaviourController = behaviourController;
OnAssign(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
OnBehaviourControllerAssigned?.InvokeSafe(this);
return true;
}
@@ -145,7 +143,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
_isActive = StateEnable.Enabled && (Parent?.IsActive ?? true);
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
OnActiveChanged?.InvokeSafe(this, previousActive);
}
protected override void UnassignInternal()
@@ -158,8 +156,14 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
{
base.InitializeInternal();
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
_behaviourController.Initialize();
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _children.GetEnumerator();
public UniverseObject()
{
_name = GetType().Name;
}
public IEnumerator<IUniverseObject> GetEnumerator() => _children.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
}

View File

@@ -2,7 +2,7 @@ using System;
namespace Syntriax.Engine.Core;
public readonly struct EngineTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
public readonly struct UniverseTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
{
public readonly TimeSpan TimeSinceStart = TimeSinceStart;
public readonly TimeSpan DeltaSpan = TimeDelta;

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
/// <summary>
/// Represents a <see cref="ICollider2D"/> with the shape of a <see cref="Circle"/>.

View File

@@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
/// <summary>
/// Represents a 2D collider.

View File

@@ -1,5 +1,3 @@
using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
/// <summary>

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
/// <summary>
/// Represents a 2D collision resolver.

View File

@@ -1,4 +1,4 @@
namespace Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
/// <summary>
/// Represents a 2D physics engine.

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