Compare commits
32 Commits
feat/physi
...
main
Author | SHA1 | Date |
---|---|---|
Syntriax | 72492a9f5a | |
Syntriax | 2f043c19a6 | |
Syntriax | dbb263ebed | |
Syntriax | 5d897f2f56 | |
Syntriax | 1dc8f3d272 | |
Syntriax | 0725468f2c | |
Syntriax | 5826230e7a | |
Syntriax | 636331e18f | |
Syntriax | 09a8e71fe3 | |
Syntriax | 81a0cf645a | |
Syntriax | ab0e868d52 | |
Syntriax | 0257911018 | |
Syntriax | 2b19b24a26 | |
Syntriax | 2f4137dae2 | |
Syntriax | 4ce9c8e0d9 | |
Syntriax | 3e9c393817 | |
Syntriax | a1f63d2728 | |
Syntriax | 6c36d4d21d | |
Syntriax | de336d0ee5 | |
Syntriax | 8619778d52 | |
Syntriax | 4facfdb6cf | |
Syntriax | f61f71dfe0 | |
Syntriax | 005c78a26e | |
Syntriax | 01a99daf8a | |
Syntriax | 8269c789a6 | |
Syntriax | 3817ebebfe | |
Syntriax | fa7eeed267 | |
Syntriax | 07666359f2 | |
Syntriax | 514e5b5762 | |
Syntriax | 1438b19e35 | |
Syntriax | 4000e761a7 | |
Syntriax | e7587a0827 |
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// </summary>
|
||||
Action<IAssignable>? OnUnassigned { get; set; }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableBehaviourController : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableBehaviourController : IAssignable
|
|||
IBehaviourController BehaviourController { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="behaviourController">New <see cref="IBehaviourController"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableEntity : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableEntity>? OnEntityAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableEntity : IAssignable
|
|||
IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="entity">New <see cref="IEntity"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableGameManager : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; }
|
||||
|
||||
/// <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);
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableGameObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// Event triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableGameObject : IAssignable
|
|||
IGameObject GameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IGameObject"/> field of this object
|
||||
/// Assign a value to the <see cref="IGameObject"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">New <see cref="IGameObject"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableStateEnable : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableStateEnable : IAssignable
|
|||
IStateEnable StateEnable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="stateEnable">New <see cref="IStateEnable"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableTransform : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// Event triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableTransform>? OnTransformAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableTransform : IAssignable
|
|||
ITransform Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="ITransform"/> field of this object
|
||||
/// Assign a value to the <see cref="ITransform"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="transform">New <see cref="ITransform"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public abstract class BaseEntity : IEntity
|
||||
{
|
||||
public Action<IEntity, string>? OnIdChanged { get; set; } = null;
|
||||
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
|
||||
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _id = string.Empty;
|
||||
|
||||
public virtual IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public virtual bool IsActive => StateEnable.Enabled;
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
if (value == _id)
|
||||
return;
|
||||
|
||||
string previousId = _id;
|
||||
|
||||
_id = value;
|
||||
OnIdChanged?.Invoke(this, previousId);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void UnassignInternal() { }
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
UnassignInternal();
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal() { }
|
||||
public bool Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void FinalizeInternal() { }
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
FinalizeInternal();
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected BaseEntity() => _id = Guid.NewGuid().ToString("D");
|
||||
protected BaseEntity(string id) => _id = id;
|
||||
}
|
|
@ -3,22 +3,22 @@ using System;
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for every behaviour an object in the game might have, controlled by <see cref="IBehaviourController"/>.
|
||||
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
|
||||
/// </summary>
|
||||
public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignableStateEnable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="Priority"/> has changed.
|
||||
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
||||
/// </summary>
|
||||
Action<IBehaviour>? OnPriorityChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Call priority of the <see cref="IBehaviour"/>.
|
||||
/// The priority of the <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="IBehaviour"/> is active.
|
||||
/// The value indicating whether the <see cref="IBehaviour"/> is active.
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
}
|
||||
|
|
|
@ -5,89 +5,101 @@ using System.Diagnostics.CodeAnalysis;
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for controlling <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviourController : IAssignableGameObject
|
||||
public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBehaviour>
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="Update()"/> is called but right before the <see cref="OnUpdate"/> action is triggered.
|
||||
/// Event triggered before the update of <see cref="IBehaviour"/>s.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="Update()"/> is called.
|
||||
/// Event triggered during the update of <see cref="IBehaviour"/>s.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="OnPreDraw()"/> is called.
|
||||
/// Event triggered before the drawing phase.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnPreDraw { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been registered a new <see cref="IBehaviour"/>.
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been removed an existing <see cref="IBehaviour"/>.
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers the provided <see cref="IBehaviour"/> to be controlled by the <see cref="IBehaviourController"/>.
|
||||
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <param name="behaviour">Uninitialized <see cref="IBehaviour"/> to be registered.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The provided <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to add.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
T AddBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the provided <see cref="IBehaviour"/> type and registers it to the <see cref="IBehaviourController"/>.
|
||||
/// Adds a <see cref="IBehaviour"/> of the specified type to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <param name="args">Constructor parameters for the given <see cref="IBehaviour"/> class.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The instantiated <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="args">Construction parameters for the <see cref="IBehaviour"/>.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Looks up and tries to get the <see cref="IBehaviour"/> that is controlled by the <see cref="IBehaviourController"/>.
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="behaviour">If return value is <see cref="true"/> outputs the class found in the <see cref="IBehaviourController"/>. If the return value is falls, this parameter is <see cref="null"/></param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/></typeparam>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the type of <see cref="IBehaviour"/> is present in the <see cref="IBehaviourController"/>, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, <see cref="null"/>.</returns>
|
||||
T? GetBehaviour<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, see.</param>
|
||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found; otherwise, <see cref="false"/>.</returns>
|
||||
bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour);
|
||||
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/>.</typeparam>
|
||||
/// <returns>Returns a list of all the matching <see cref="IBehaviour"/>s found in the <see cref="IBehaviourController"/>.</returns>
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <returns>A list of <see cref="IBehaviour"/>s of the specified type.</returns>
|
||||
IList<T> GetBehaviours<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type and stores them in the provided list.
|
||||
/// </summary>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <param name="behaviours">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||
void GetBehaviours<T>(List<T> behaviours);
|
||||
|
||||
/// <summary>
|
||||
/// Removes <see cref="IBehaviour"/>s of the specified type from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to remove.</typeparam>
|
||||
/// <param name="removeAll">A flag indicating whether to remove all <see cref="IBehaviour"/>s of the specified type.</param>
|
||||
void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// Removes the specified <see cref="IBehaviour"/> from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to remove.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
|
||||
void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// To be called in every frame of the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that a new frame is happening.
|
||||
/// Updates all <see cref="IBehaviour"/>s in the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void Update();
|
||||
|
||||
/// <summary>
|
||||
/// To be called before every draw call from the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that the engine is about to start drawing into the screen.
|
||||
/// Performs pre-draw operations.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void UpdatePreDraw();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D camera in the engine.
|
||||
/// </summary>
|
||||
public interface ICamera2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// The zoom level of the camera.
|
||||
/// </summary>
|
||||
float Zoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from screen coordinates to world coordinates.
|
||||
/// </summary>
|
||||
/// <param name="screenPosition">The position in screen coordinates.</param>
|
||||
/// <returns>The position in world coordinates.</returns>
|
||||
Vector2D ScreenToWorldPosition(Vector2D screenPosition);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from world coordinates to screen coordinates.
|
||||
/// </summary>
|
||||
/// <param name="worldPosition">The position in world coordinates.</param>
|
||||
/// <returns>The position in screen coordinates.</returns>
|
||||
Vector2D WorldToScreenPosition(Vector2D worldPosition);
|
||||
}
|
|
@ -1,5 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic entity in the engine.
|
||||
/// </summary>
|
||||
public interface IEntity : IInitialize, IAssignableStateEnable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
||||
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
Action<IEntity, string>? OnIdChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
}
|
||||
|
|
|
@ -3,19 +3,55 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a game world responsible for managing <see cref="IGameObject"/>s.
|
||||
/// </summary>
|
||||
public interface IGameManager : IEntity, IEnumerable<IGameObject>
|
||||
{
|
||||
Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; }
|
||||
Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IGameObject"/> is registered to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IGameObject"/> is unregistered from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of <see cref="IGameObject"/>s managed by the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IGameObject> GameObjects { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IGameObject"/> to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The <see cref="IGameObject"/> to register.</param>
|
||||
void RegisterGameObject(IGameObject gameObject);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="IGameObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IGameObject"/> to instantiate.</typeparam>
|
||||
/// <param name="args">Constructor parameters for the given type of <see cref="IGameObject"/>.</param>
|
||||
/// <returns>The instantiated <see cref="IGameObject"/>.</returns>
|
||||
T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="IGameObject"/> from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The <see cref="IGameObject"/> to remove.</param>
|
||||
/// <returns>The removed <see cref="IGameObject"/>.</returns>
|
||||
IGameObject RemoveGameObject(IGameObject gameObject);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="IGameManager"/> with the given engine time data.
|
||||
/// </summary>
|
||||
/// <param name="time">The engine time.</param>
|
||||
void Update(EngineTime time);
|
||||
|
||||
/// <summary>
|
||||
/// Performs operations that should be done before the draw calls.
|
||||
/// </summary>
|
||||
void PreDraw();
|
||||
}
|
||||
|
|
|
@ -2,9 +2,18 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IGameObject : IEntity, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
|
||||
/// <summary>
|
||||
/// Represents a game object with various properties and functionalities.
|
||||
/// </summary>
|
||||
public interface IGameObject : IEntity, IAssignableGameManager, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Update"/> method is called.
|
||||
/// </summary>
|
||||
Action<IGameObject>? OnUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the game object.
|
||||
/// </summary>
|
||||
void Update();
|
||||
}
|
||||
|
|
|
@ -2,12 +2,35 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public interface IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
||||
/// </summary>
|
||||
Action<IInitialize>? OnInitialized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Finalize"/> method is called successfully.
|
||||
/// </summary>
|
||||
Action<IInitialize>? OnFinalized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the entity has been initialized.
|
||||
/// </summary>
|
||||
bool Initialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the entity.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if initialization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the entity so it can either be recycled or garbage collected.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Finalize();
|
||||
}
|
||||
|
|
|
@ -2,8 +2,18 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with a name.
|
||||
/// </summary>
|
||||
public interface INameable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the name of the entity changes.
|
||||
/// </summary>
|
||||
Action<IEntity>? OnNameChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the entity.
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,8 +2,18 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with an enable state that can be toggled.
|
||||
/// </summary>
|
||||
public interface IStateEnable : IAssignableEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
||||
/// </summary>
|
||||
Action<IStateEnable>? OnEnabledChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
||||
/// </summary>
|
||||
bool Enabled { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,14 +2,38 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the transformation properties of an object such as position, scale, and rotation.
|
||||
/// </summary>
|
||||
public interface ITransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnPositionChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnScaleChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnRotationChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position of the <see cref="ITransform"/> in 2D space.
|
||||
/// </summary>
|
||||
Vector2D Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
Vector2D Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the <see cref="ITransform"/> in degrees.
|
||||
/// </summary>
|
||||
float Rotation { get; set; }
|
||||
}
|
||||
|
|
|
@ -6,43 +6,20 @@ using Syntriax.Engine.Core.Exceptions;
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||
public abstract class Behaviour : IBehaviour
|
||||
public abstract class Behaviour : BaseEntity, IBehaviour
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IBehaviour>? OnPriorityChanged { get; set; } = null;
|
||||
|
||||
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private int _priority = 0;
|
||||
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
public override bool IsActive => base.IsActive && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
|
||||
public int Priority
|
||||
{
|
||||
|
@ -57,17 +34,6 @@ public abstract class Behaviour : IBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IBehaviourController behaviourController)
|
||||
{
|
||||
if (Initialized)
|
||||
|
@ -78,36 +44,16 @@ public abstract class Behaviour : IBehaviour
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
protected override void UnassignInternal()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
base.UnassignInternal();
|
||||
_behaviourController = null!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
base.InitializeInternal();
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
public Action<BehaviourCacher<T>, T>? OnCached { get; set; } = null;
|
||||
public Action<BehaviourCacher<T>, T>? OnUncached { get; set; } = null;
|
||||
|
||||
private readonly List<T> _behaviours = new(32);
|
||||
|
||||
public IReadOnlyList<T> Behaviours => _behaviours;
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
|
||||
public T this[Index index] => _behaviours[index];
|
||||
|
||||
public BehaviourCacher() { }
|
||||
public BehaviourCacher(IGameManager gameManager) => Assign(gameManager);
|
||||
|
||||
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
private void OnGameObjectUnregistered(IGameManager manager, IGameObject gameObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not T tBehaviour)
|
||||
return;
|
||||
|
||||
_behaviours.Add(tBehaviour);
|
||||
OnCached?.Invoke(this, tBehaviour);
|
||||
}
|
||||
|
||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not T tBehaviour)
|
||||
return;
|
||||
|
||||
if (!_behaviours.Remove(tBehaviour))
|
||||
return;
|
||||
|
||||
OnUncached?.Invoke(this, tBehaviour);
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
{
|
||||
if (GameManager is not null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in gameManager)
|
||||
{
|
||||
OnGameObjectRegistered(gameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourAdded(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
|
||||
gameManager.OnGameObjectRegistered += OnGameObjectRegistered;
|
||||
gameManager.OnGameObjectUnRegistered += OnGameObjectUnregistered;
|
||||
|
||||
GameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (GameManager is null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in GameManager)
|
||||
{
|
||||
OnGameObjectUnregistered(GameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourRemoved(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
|
||||
GameManager.OnGameObjectRegistered -= OnGameObjectRegistered;
|
||||
GameManager.OnGameObjectUnRegistered -= OnGameObjectUnregistered;
|
||||
|
||||
GameManager = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
@ -40,19 +41,19 @@ public class BehaviourController : IBehaviourController
|
|||
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
||||
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_gameObject, args));
|
||||
|
||||
public bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
public T? GetBehaviour<T>()
|
||||
{
|
||||
foreach (var behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T result)
|
||||
continue;
|
||||
if (behaviourItem is T result)
|
||||
return result;
|
||||
|
||||
behaviour = result;
|
||||
return true;
|
||||
return default;
|
||||
}
|
||||
|
||||
behaviour = default;
|
||||
return false;
|
||||
public bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
behaviour = GetBehaviour<T>();
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public IList<T> GetBehaviours<T>()
|
||||
|
@ -70,6 +71,18 @@ public class BehaviourController : IBehaviourController
|
|||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||
}
|
||||
|
||||
public void GetBehaviours<T>(List<T> behaviors)
|
||||
{
|
||||
behaviors.Clear();
|
||||
foreach (var behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T _)
|
||||
continue;
|
||||
|
||||
behaviours.Add(behaviourItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||
{
|
||||
for (int i = behaviours.Count; i >= 0; i--)
|
||||
|
@ -157,4 +170,7 @@ public class BehaviourController : IBehaviourController
|
|||
behaviours.Remove(behaviour);
|
||||
InsertBehaviourByPriority(behaviour);
|
||||
}
|
||||
|
||||
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace Syntriax.Engine.Core;
|
|||
|
||||
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
|
||||
{
|
||||
public readonly TimeSpan Total { get; init; } = Total;
|
||||
public readonly TimeSpan Elapsed { get; init; } = Elapsed;
|
||||
public readonly TimeSpan Total = Total;
|
||||
public readonly TimeSpan Elapsed = Elapsed;
|
||||
|
||||
public readonly float DeltaTimeFrame = (float)Elapsed.TotalMilliseconds;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class BehaviourExtensions
|
||||
{
|
||||
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
behaviour = default;
|
||||
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
if (gameObject.BehaviourController.TryGetBehaviour(out behaviour))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours)
|
||||
{
|
||||
behaviours.Clear();
|
||||
List<T> cache = [];
|
||||
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
{
|
||||
gameObject.BehaviourController.GetBehaviours(cache);
|
||||
behaviours.AddRange(cache);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class GameManagerExtensions
|
||||
{
|
||||
public static IGameObject InstantiateGameObject(this IGameManager gameManager, params object?[]? args)
|
||||
=> gameManager.InstantiateGameObject<GameObject>(args);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
public static IGameObject SetGameObject(this IGameObject gameObject, string name)
|
||||
{
|
||||
gameObject.Name = name;
|
||||
return gameObject;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static ITransform SetTransform(this ITransform transform, Vector2D? position = null, float? rotation = null, Vector2D? scale = null)
|
||||
{
|
||||
if (position.HasValue) transform.Position = position.Value;
|
||||
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||
if (scale.HasValue) transform.Scale = scale.Value;
|
||||
return transform;
|
||||
}
|
||||
}
|
|
@ -1,35 +1,188 @@
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Vector2D"/> type.
|
||||
/// </summary>
|
||||
public static class Vector2DExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The ending <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Vector2D"/> with its components inverted.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
|
||||
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||
public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value);
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="Vector2D"/>s component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="vectorToAdd">The vector <see cref="Vector2D"/> to be added.</param>
|
||||
/// <returns>The result of the addition.</returns>
|
||||
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector2D"/> from another component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="vectorToSubtract">The <see cref="Vector2D"/> to be subtracted.</param>
|
||||
/// <returns>The result of the subtraction.</returns>
|
||||
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to multiply.</param>
|
||||
/// <param name="value">The scalar value to multiply with.</param>
|
||||
/// <returns>The result of the multiplication.</returns>
|
||||
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to divide.</param>
|
||||
/// <param name="value">The scalar value to divide with.</param>
|
||||
/// <returns>The result of the division.</returns>
|
||||
public static Vector2D Divide(this Vector2D vector, float value) => Vector2D.Divide(vector, value);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector2D"/> with the absolute values of each component.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> with absolute values.</returns>
|
||||
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to reflect.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector2D"/> of the reflecting surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector2D"/> (creates a <see cref="Vector2D"/> with the same direction but with a length of 1).
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Vector2D"/> pointing from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> pointing from <paramref name="from"/> to <paramref name="to"/>.</returns>
|
||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
|
||||
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
|
||||
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
|
||||
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
|
||||
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
|
||||
/// <returns>The clamped <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The start <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The end <see cref="Vector2D"/>.</param>
|
||||
/// <param name="t">The interpolation parameter (between 0 and 1).</param>
|
||||
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle in radians between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two <see cref="Vector2D"/>s are approximately equal within a certain epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="epsilon">The maximum difference allowed between components.</param>
|
||||
/// <returns>True if the <see cref="Vector2D"/>s are approximately equal, false otherwise.</returns>
|
||||
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
||||
|
|
|
@ -9,22 +9,15 @@ using Syntriax.Engine.Core.Factory;
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
|
||||
public class GameManager : IGameManager
|
||||
public class GameManager : BaseEntity, IGameManager
|
||||
{
|
||||
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||
public Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
|
||||
|
||||
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private GameObjectFactory _gameObjectFactory = null!;
|
||||
private bool _initialized = false;
|
||||
|
||||
private GameObjectFactory GameObjectFactory
|
||||
{
|
||||
|
@ -36,21 +29,20 @@ public class GameManager : IGameManager
|
|||
}
|
||||
}
|
||||
|
||||
public bool Initialized => _initialized;
|
||||
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||
|
||||
public IStateEnable StateEnable
|
||||
public override IStateEnable StateEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stateEnable is null)
|
||||
if (base.StateEnable is null)
|
||||
{
|
||||
Assign(new StateEnableFactory().Instantiate(this));
|
||||
if (_stateEnable is null)
|
||||
throw NotAssignedException.From(this, _stateEnable);
|
||||
if (base.StateEnable is null)
|
||||
throw NotAssignedException.From(this, base.StateEnable);
|
||||
}
|
||||
|
||||
return _stateEnable;
|
||||
return base.StateEnable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,52 +70,20 @@ public class GameManager : IGameManager
|
|||
return gameObject;
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
base.InitializeInternal();
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.Initialize();
|
||||
|
||||
_initialized = true;
|
||||
OnInitialized?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
protected override void FinalizeInternal()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
base.FinalizeInternal();
|
||||
for (int i = GameObjects.Count; i >= 0; i--)
|
||||
GameObjects[i].Finalize();
|
||||
|
||||
OnFinalized?.Invoke(this);
|
||||
_initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(EngineTime time)
|
||||
|
@ -143,6 +103,8 @@ public class GameManager : IGameManager
|
|||
|
||||
private void Register(IGameObject gameObject)
|
||||
{
|
||||
gameObject.Assign(this);
|
||||
|
||||
gameObject.OnFinalized += OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Add(gameObject);
|
||||
|
|
|
@ -6,47 +6,27 @@ using Syntriax.Engine.Core.Exceptions;
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||
public class GameObject : IGameObject
|
||||
public class GameObject : BaseEntity, IGameObject
|
||||
{
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
public Action<IEntity>? OnNameChanged { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
|
||||
public Action<IGameObject>? OnUpdated { get; set; } = null;
|
||||
|
||||
|
||||
private ITransform _transform = null!;
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private IGameManager _gameManager = null!;
|
||||
|
||||
private string _name = nameof(GameObject);
|
||||
private bool _initialized = false;
|
||||
|
||||
public ITransform Transform => _transform;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
public IGameManager GameManager => _gameManager;
|
||||
|
||||
public string Name
|
||||
{
|
||||
|
@ -60,17 +40,14 @@ public class GameObject : IGameObject
|
|||
}
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
base.InitializeInternal();
|
||||
|
||||
NotAssignedException.Check(this, _transform);
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
NotAssignedException.Check(this, _gameManager);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
|
@ -81,29 +58,12 @@ public class GameObject : IGameObject
|
|||
OnUpdated?.Invoke(this);
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
protected override void FinalizeInternal()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
base.FinalizeInternal();
|
||||
|
||||
|
||||
System.Threading.Tasks.Parallel.ForEach(
|
||||
_behaviourController.GetBehaviours<IBehaviour>(),
|
||||
behaviour => behaviour.Finalize()
|
||||
);
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
foreach (IBehaviour behaviour in _behaviourController.GetBehaviours<IBehaviour>())
|
||||
behaviour.Finalize();
|
||||
}
|
||||
|
||||
public bool Assign(ITransform transform)
|
||||
|
@ -126,17 +86,24 @@ public class GameObject : IGameObject
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
public bool Assign(IGameManager gameManager)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_gameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void UnassignInternal()
|
||||
{
|
||||
base.UnassignInternal();
|
||||
|
||||
_stateEnable = null!;
|
||||
_transform = null!;
|
||||
_behaviourController = null!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
_gameManager = null!;
|
||||
}
|
||||
|
||||
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
|
||||
|
|
|
@ -4,32 +4,187 @@ using System.Numerics;
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class Math
|
||||
{
|
||||
public const float RadianToDegree = 180f / PI;
|
||||
public const float DegreeToRadian = PI / 180f;
|
||||
|
||||
public const float E = 2.718281828459045f;
|
||||
{/// <summary>
|
||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
||||
/// </summary>
|
||||
public const float PI = 3.1415926535897932f;
|
||||
|
||||
/// <summary>
|
||||
/// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319.
|
||||
/// </summary>
|
||||
public const float Tau = 2f * PI;
|
||||
|
||||
/// <summary>
|
||||
/// The base of the natural logarithm, approximately equal to 2.71828.
|
||||
/// </summary>
|
||||
public const float E = 2.718281828459045f;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from radians to degrees.
|
||||
/// </summary>
|
||||
public const float RadianToDegree = 180f / PI;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from degrees to radians.
|
||||
/// </summary>
|
||||
public const float DegreeToRadian = PI / 180f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute value of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The absolute value of <paramref name="x"/>.</returns>
|
||||
public static T Abs<T>(T x) where T : INumber<T> => x > T.Zero ? x : -x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arccosine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arccosine of <paramref name="x"/>.</returns>
|
||||
public static float Acos(float x) => MathF.Acos(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arcsine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arcsine of <paramref name="x"/>.</returns>
|
||||
public static float Asin(float x) => MathF.Asin(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle whose tangent is the quotient of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="y">The y-coordinate of a point.</param>
|
||||
/// <param name="x">The x-coordinate of a point.</param>
|
||||
/// <returns>The angle, measured in radians.</returns>
|
||||
public static float Atan2(float y, float x) => MathF.Atan2(y, x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hyperbolic arctangent of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The hyperbolic arctangent of <paramref name="x"/>.</returns>
|
||||
public static float Atanh(float x) => MathF.Atanh(x);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a number between a minimum and maximum value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smallest integral value that is greater than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The smallest integral value that is greater than or equal to <paramref name="x"/>.</returns>
|
||||
public static float Ceiling(float x) => MathF.Ceiling(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The magnitude value.</param>
|
||||
/// <param name="y">The sign value.</param>
|
||||
/// <returns>A value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.</returns>
|
||||
public static float CopySign(float x, float y) => MathF.CopySign(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the largest integral value that is less than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The largest integral value that is less than or equal to <paramref name="x"/>.</returns>
|
||||
public static float Floor(float x) => MathF.Floor(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remainder of the division of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="x">The dividend.</param>
|
||||
/// <param name="y">The divisor.</param>
|
||||
/// <returns>The remainder of the division of <paramref name="x"/> by <paramref name="y"/>.</returns>
|
||||
public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the natural (base e) logarithm of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <param name="y">The base.</param>
|
||||
/// <returns>The natural logarithm of <paramref name="x"/> with base <paramref name="y"/>.</returns>
|
||||
public static float Log(float x, float y) => MathF.Log(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the larger of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The larger of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Max<T>(T x, T y) where T : INumber<T> => (x > y) ? x : y;
|
||||
public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is larger.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is larger.</returns>
|
||||
public static T AbsMax<T>(T x, T y) where T : INumber<T> => (Abs(x) > Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smaller of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The smaller of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Min<T>(T x, T y) where T : INumber<T> => (x < y) ? x : y;
|
||||
public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is smaller.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is smaller.</returns>
|
||||
public static T AbsMin<T>(T x, T y) where T : INumber<T> => (Abs(x) < Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specified number raised to the specified power.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to raise to a power.</param>
|
||||
/// <param name="y">The power to raise <paramref name="x"/> to.</param>
|
||||
/// <returns>The number <paramref name="x"/> raised to the power <paramref name="y"/>.</returns>
|
||||
public static float Pow(float x, float y) => MathF.Pow(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Rounds a number to a specified number of fractional digits.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to round.</param>
|
||||
/// <param name="digits">The number of fractional digits in the return value.</param>
|
||||
/// <param name="mode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
|
||||
/// <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>
|
||||
/// Returns the square of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to square.</param>
|
||||
/// <returns>The square of <paramref name="x"/>.</returns>
|
||||
public static T Sqr<T>(T x) where T : INumber<T> => x * x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square root of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The square root of <paramref name="x"/>.</returns>
|
||||
public static float Sqrt(float x) => MathF.Sqrt(x);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the integral part of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The integral part of <paramref name="x"/>.</returns>
|
||||
public static float Truncate(float x) => MathF.Truncate(x);
|
||||
|
||||
}
|
||||
|
|
|
@ -9,5 +9,7 @@ public static class Time
|
|||
public static TimeSpan Total => _engineTime.Total;
|
||||
public static TimeSpan Elapsed => _engineTime.Elapsed;
|
||||
|
||||
public static float DeltaTimeFrame => _engineTime.DeltaTimeFrame;
|
||||
|
||||
public static void SetTime(EngineTime engineTime) => _engineTime = engineTime;
|
||||
}
|
||||
|
|
|
@ -2,21 +2,65 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a two-dimensional vector.
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||
public readonly struct Vector2D(float X, float Y)
|
||||
public readonly struct Vector2D(float x, float y)
|
||||
{
|
||||
public readonly float X { get; init; } = X;
|
||||
public readonly float Y { get; init; } = Y;
|
||||
/// <summary>
|
||||
/// The X coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float X = x;
|
||||
|
||||
public readonly float Magnitude => Length(this);
|
||||
public readonly float MagnitudeSquared => LengthSquared(this);
|
||||
public readonly Vector2D Normalized => Normalize(this);
|
||||
/// <summary>
|
||||
/// The Y coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float Y = y;
|
||||
|
||||
/// <summary>
|
||||
/// The magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float Magnitude => Length(this);
|
||||
|
||||
/// <summary>
|
||||
/// The squared magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
||||
/// </summary>
|
||||
public Vector2D Normalized => Normalize(this);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing upwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Up = new(0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing downwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Down = new(0f, -1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing leftwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Left = new(-1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing rightwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Right = new(1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the zero <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Zero = new(0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the <see cref="Vector2D"/> with both components equal to 1.
|
||||
/// </summary>
|
||||
public readonly static Vector2D One = new(1f, 1f);
|
||||
|
||||
public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
|
||||
|
@ -28,38 +72,189 @@ public readonly struct Vector2D(float X, float Y)
|
|||
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
|
||||
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The start <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The end <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the direction of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
public static Vector2D Subdivide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector2D"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="Vector2D"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="Vector2D"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="Vector2D"/> from the first.</returns>
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Divide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the absolute value of each component of the vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> with each component's absolute value.</returns>
|
||||
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector2D"/> (creates a unit <see cref="Vector2D"/> with the same direction).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to normalize.</param>
|
||||
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The incident <see cref="Vector2D"/>.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector2D"/> of the surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Vector2D"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> from the starting point to the ending point.</returns>
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
|
||||
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
|
||||
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
|
||||
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
|
||||
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector2D"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="Vector2D"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the Orientation of 3 <see cref="Vector2D"/>s
|
||||
/// Determines the orientation of three points represented by <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <returns>0 -> Collinear, 1 -> Clockwise, 2 -> Counterclockwise</returns>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="middle">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The third <see cref="Vector2D"/>.</param>
|
||||
/// <returns>
|
||||
/// <para>0 - Collinear.</para>
|
||||
/// <para>1 - Clockwise.</para>
|
||||
/// <para>2 - Counterclockwise.</para>
|
||||
/// </returns>
|
||||
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
|
||||
{
|
||||
Vector2D leftToMiddle = left.FromTo(middle);
|
||||
|
@ -73,11 +268,32 @@ public readonly struct Vector2D(float X, float Y)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Vector2D"/>s are approximately equal within a specified epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="Vector2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Vector2D"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,18 @@ using Syntriax.Engine.Physics2D.Primitives;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with the shape of a <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public interface ICircleCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The local <see cref="Circle"/> shape of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Circle CircleLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The world space representation of the <see cref="Circle"/> shape.
|
||||
/// </summary>
|
||||
Circle CircleWorld { get; }
|
||||
}
|
||||
|
|
|
@ -4,15 +4,38 @@ using Syntriax.Engine.Core.Abstract;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collider.
|
||||
/// </summary>
|
||||
public interface ICollider2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is detected.
|
||||
/// </summary>
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is resolved.
|
||||
/// </summary>
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRigidBody2D"/> associated with the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
IRigidBody2D? RigidBody2D { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="ICollider2D"/> is a trigger.
|
||||
/// </summary>
|
||||
bool IsTrigger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates <see cref="ICollider2D"/> properties.
|
||||
/// </summary>
|
||||
void Recalculate();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,19 @@ using Syntriax.Engine.Physics2D.Abstract;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision detector.
|
||||
/// </summary>
|
||||
public interface ICollisionDetector2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to detect a collision between two <see cref="ICollider2D"/>s.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of the first <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <param name="left">The first <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
/// <returns><see cref="true"/> if a collision is detected, otherwise <see cref="false"/>.</returns>
|
||||
bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision resolver.
|
||||
/// </summary>
|
||||
public interface ICollisionResolver2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves collisions based on collision detection information provided.
|
||||
/// </summary>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
void Resolve(CollisionDetectionInformation collisionInformation);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics engine.
|
||||
/// </summary>
|
||||
public interface IPhysicsEngine2D
|
||||
{
|
||||
int IterationCount { get; set; }
|
||||
|
||||
void AddRigidBody(IRigidBody2D rigidBody);
|
||||
void RemoveRigidBody(IRigidBody2D rigidBody);
|
||||
/// <summary>
|
||||
/// The number of iterations the <see cref="IPhysicsEngine2D"/> performs per step.
|
||||
/// </summary>
|
||||
int IterationPerStep { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Advances the physics simulation by the specified time.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The time step.</param>
|
||||
void Step(float deltaTime);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics object's responsive attributes.
|
||||
/// </summary>
|
||||
public interface IPhysicsMaterial2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The friction coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Friction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The restitution (bounciness) coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Restitution { get; }
|
||||
}
|
||||
|
|
|
@ -3,13 +3,33 @@ using Syntriax.Engine.Core.Abstract;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D rigid body in the engine.
|
||||
/// </summary>
|
||||
public interface IRigidBody2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// The physics material of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
IPhysicsMaterial2D Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The velocity of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
Vector2D Velocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The angular velocity (rotation rate) of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float AngularVelocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float Mass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IRigidBody2D"/> is static/immovable.
|
||||
/// </summary>
|
||||
bool IsStatic { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,8 +2,19 @@ using Syntriax.Engine.Physics2D.Primitives;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with a custom <see cref="Shape"/>.
|
||||
/// </summary>
|
||||
public interface IShapeCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the local <see cref="Shape"/> of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Shape ShapeLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world space representation of the <see cref="Shape"/>.
|
||||
/// </summary>
|
||||
Shape ShapeWorld { get; }
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
|||
private readonly List<ICollider2D> colliders = new(64);
|
||||
|
||||
private int _iterationCount = 1;
|
||||
private ICollisionDetector2D collisionDetector = new CollisionDetector2D();
|
||||
private ICollisionResolver2D collisionResolver = new CollisionResolver2D();
|
||||
|
||||
public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
private readonly ICollisionDetector2D collisionDetector = null!;
|
||||
private readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
|
||||
public void AddRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
|
@ -38,9 +39,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
|||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationCount;
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++)
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
for (int i = 0; i < rigidBodies.Count; i++)
|
||||
|
@ -100,7 +101,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
|||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic)
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
|
@ -122,4 +123,16 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
|||
|
||||
colliders.Remove(collider2D);
|
||||
}
|
||||
|
||||
public PhysicsEngine2D()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
|
||||
private int _iterationPerStep = 1;
|
||||
|
||||
protected readonly ICollisionDetector2D collisionDetector = null!;
|
||||
protected readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
protected BehaviourCacher<IRigidBody2D> rigidBodyCacher = new();
|
||||
protected BehaviourCacher<ICollider2D> colliderCacher = new();
|
||||
|
||||
|
||||
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
foreach (var rigidBody in rigidBodyCacher)
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
foreach (var collider in colliderCacher)
|
||||
collider.Recalculate();
|
||||
|
||||
// Can Parallel
|
||||
for (int x = 0; x < colliderCacher.Behaviours.Count; x++)
|
||||
{
|
||||
ICollider2D? colliderX = colliderCacher.Behaviours[x];
|
||||
if (!colliderX.IsActive)
|
||||
return;
|
||||
|
||||
for (int y = x + 1; y < colliderCacher.Behaviours.Count; y++)
|
||||
{
|
||||
ICollider2D? colliderY = colliderCacher.Behaviours[y];
|
||||
|
||||
if (!colliderY.IsActive)
|
||||
return;
|
||||
|
||||
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
|
||||
if (bothCollidersAreTriggers)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreStatic = colliderX.RigidBody2D?.IsStatic ?? true && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic;
|
||||
if (bothCollidersAreStatic)
|
||||
continue;
|
||||
|
||||
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
|
||||
{
|
||||
if (colliderX.IsTrigger)
|
||||
{
|
||||
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
|
||||
continue;
|
||||
}
|
||||
else if (colliderY.IsTrigger)
|
||||
{
|
||||
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
|
||||
continue;
|
||||
}
|
||||
|
||||
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
|
||||
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
|
||||
|
||||
collisionResolver?.Resolve(information);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
{
|
||||
if (GameManager is not null)
|
||||
return false;
|
||||
|
||||
colliderCacher.Assign(gameManager);
|
||||
rigidBodyCacher.Assign(gameManager);
|
||||
|
||||
GameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (GameManager is null)
|
||||
return false;
|
||||
|
||||
colliderCacher.Unassign();
|
||||
rigidBodyCacher.Unassign();
|
||||
|
||||
GameManager = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(IGameManager gameManager)
|
||||
{
|
||||
Assign(gameManager);
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(IGameManager gameManager, ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
Assign(gameManager);
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
|
@ -4,16 +4,47 @@ using Syntriax.Engine.Core;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
||||
/// </summary>
|
||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB"/>.</param>
|
||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="AABB"/> struct with the specified lower and upper boundaries.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||
public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
|
||||
public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
{
|
||||
public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary;
|
||||
public readonly Vector2D UpperBoundary { get; init; } = UpperBoundary;
|
||||
/// <summary>
|
||||
/// The lower boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D LowerBoundary = lowerBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// The upper boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D UpperBoundary = upperBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the center point of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets half the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D SizeHalf => Size * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
||||
{
|
||||
int counter = 0;
|
||||
|
@ -34,13 +65,33 @@ public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
|
|||
return new(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="AABB"/>.</param>
|
||||
/// <param name="right">The second <see cref="AABB"/>.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(AABB left, AABB right)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="AABB"/> struct.
|
||||
/// </summary>
|
||||
public static class AABBExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a collection of <see cref="Vector2D"/>s to an <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="AABB"/>.</param>
|
||||
/// <param name="right">The second <see cref="AABB"/>.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right);
|
||||
}
|
||||
|
|
|
@ -1,47 +1,115 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Center: {Center.ToString(), nq}, Radius: {Radius}")]
|
||||
public readonly struct Circle(Vector2D Center, float Radius)
|
||||
/// <summary>
|
||||
/// Represents a 2D circle.
|
||||
/// </summary>
|
||||
/// <param name="center">The center of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the Circle struct with the specified center and radius.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
||||
public readonly struct Circle(Vector2D center, float radius)
|
||||
{
|
||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||
/// <summary>
|
||||
/// The center of the circle.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center = center;
|
||||
|
||||
public readonly Vector2D Center { get; init; } = Center;
|
||||
public readonly float Radius { get; init; } = Radius;
|
||||
/// <summary>
|
||||
/// The radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Radius = radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float RadiusSquared => Radius * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diameter of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Diameter => 2f * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// A predefined unit <see cref="Circle"/> with a center at the origin and a radius of 1.
|
||||
/// </summary>
|
||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius);
|
||||
|
||||
/// <summary>
|
||||
/// Displaces the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Projection Project(Circle circle, Vector2D projectionVector)
|
||||
{
|
||||
float projectedCenter = circle.Center.Dot(projectionVector);
|
||||
return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
public static Circle TransformCircle(ITransform transform, Circle circle)
|
||||
=> new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Circle"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(Circle left, Circle right)
|
||||
=> left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Circle"/> struct.
|
||||
/// </summary>
|
||||
public static class CircleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Circle"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(), nq}, To: {To.ToString(), nq}, Direction: {Direction.ToString(), nq}, Length: {Length}")]
|
||||
public readonly struct Line(Vector2D From, Vector2D To)
|
||||
/// <summary>
|
||||
/// Represents a 2D line segment defined by two endpoints.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the Line struct with the specified endpoints.
|
||||
/// </remarks>
|
||||
/// <param name="from">The starting point of the <see cref="Line"/> segment.</param>
|
||||
/// <param name="to">The ending point of the <see cref="Line"/> segment.</param>
|
||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
||||
public readonly struct Line(Vector2D from, Vector2D to)
|
||||
{
|
||||
public readonly Vector2D From { get; init; } = From;
|
||||
public readonly Vector2D To { get; init; } = To;
|
||||
/// <summary>
|
||||
/// The starting point of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D From = from;
|
||||
|
||||
/// <summary>
|
||||
/// The ending point of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D To = to;
|
||||
|
||||
/// <summary>
|
||||
/// The reversed <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Line Reversed => new(To, From);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized direction <see cref="Vector2D"/> of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D Direction => From.FromTo(To).Normalize();
|
||||
|
||||
/// <summary>
|
||||
/// The length of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly float Length => From.FromTo(To).Length();
|
||||
|
||||
/// <summary>
|
||||
/// The squared length of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||
|
||||
/// <summary>
|
||||
/// The equation of the <see cref="Line"/> defined by this <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static LineEquation GetLineEquation(Line line)
|
||||
{
|
||||
Vector2D slopeVector = line.From.FromTo(line.To);
|
||||
|
@ -27,9 +59,15 @@ public readonly struct Line(Vector2D From, Vector2D To)
|
|||
return new LineEquation(slope, yOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line"/>.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line line, Vector2D point)
|
||||
=> LineEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the parameter 't' representing the point's position on the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static float GetT(Line line, Vector2D point)
|
||||
{
|
||||
float fromX = MathF.Abs(line.From.X);
|
||||
|
@ -52,23 +90,63 @@ public readonly struct Line(Vector2D From, Vector2D To)
|
|||
return t;
|
||||
}
|
||||
|
||||
public static bool Exist(Line line, List<Vector2D> vertices)
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Line"/> segment intersects with another <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line left, Line right)
|
||||
{
|
||||
for (int i = 0; i < vertices.Count - 1; i++)
|
||||
{
|
||||
Vector2D vertexCurrent = vertices[i];
|
||||
Vector2D vertexNext = vertices[i];
|
||||
if (line.From == vertexCurrent && line.To == vertexNext) return true;
|
||||
if (line.From == vertexNext && line.To == vertexCurrent) return true;
|
||||
}
|
||||
int o1 = Vector2D.Orientation(left.From, left.To, right.From);
|
||||
int o2 = Vector2D.Orientation(left.From, left.To, right.To);
|
||||
int o3 = Vector2D.Orientation(right.From, right.To, left.From);
|
||||
int o4 = Vector2D.Orientation(right.From, right.To, left.To);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
return true;
|
||||
|
||||
if (o1 == 0 && OnSegment(left, right.From)) return true;
|
||||
if (o2 == 0 && OnSegment(left, right.To)) return true;
|
||||
if (o3 == 0 && OnSegment(right, left.From)) return true;
|
||||
if (o4 == 0 && OnSegment(right, left.To)) return true;
|
||||
|
||||
Vector2D vertexFirst = vertices[0];
|
||||
Vector2D vertexLast = vertices[^1];
|
||||
if (line.From == vertexFirst && line.To == vertexLast) return true;
|
||||
if (line.From == vertexLast && line.To == vertexFirst) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the point lies within the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static bool OnSegment(Line 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))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="Line"/> segments intersect.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line left, Line right, [NotNullWhen(returnValue: true)] out Vector2D? point)
|
||||
{
|
||||
point = null;
|
||||
|
||||
bool result = Intersects(left, right);
|
||||
|
||||
if (result)
|
||||
point = IntersectionPoint(left, right);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the point of intersection between two <see cref="Line"/> segments.
|
||||
/// </summary>
|
||||
public static Vector2D IntersectionPoint(Line left, Line right)
|
||||
=> Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the parameter 't' representing the intersection point's position on the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static float IntersectionParameterT(Line left, Line right)
|
||||
{
|
||||
float numerator = (left.From.X - right.From.X) * (right.From.Y - right.To.Y) - (left.From.Y - right.From.Y) * (right.From.X - right.To.X);
|
||||
|
@ -81,12 +159,18 @@ public readonly struct Line(Vector2D From, Vector2D To)
|
|||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between the two endpoints of the <see cref="Line"/> segment using parameter 't'.
|
||||
/// </summary>
|
||||
public static Vector2D Lerp(Line line, float t)
|
||||
=> new Vector2D(
|
||||
=> new(
|
||||
line.From.X + (line.To.X - line.From.X) * t,
|
||||
line.From.Y + (line.To.Y - line.From.Y) * t
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point on the <see cref="Line"/> segment to the specified point.
|
||||
/// </summary>
|
||||
public static Vector2D ClosestPointTo(Line line, Vector2D point)
|
||||
{
|
||||
// Convert edge points to vectors
|
||||
|
@ -106,53 +190,20 @@ public readonly struct Line(Vector2D From, Vector2D To)
|
|||
return new Vector2D((float)closestX, (float)closestY);
|
||||
}
|
||||
|
||||
public static Vector2D IntersectionPoint(Line left, Line right)
|
||||
=> Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right));
|
||||
|
||||
public static bool Intersects(Line left, Line right)
|
||||
{
|
||||
int o1 = Vector2D.Orientation(left.From, left.To, right.From);
|
||||
int o2 = Vector2D.Orientation(left.From, left.To, right.To);
|
||||
int o3 = Vector2D.Orientation(right.From, right.To, left.From);
|
||||
int o4 = Vector2D.Orientation(right.From, right.To, left.To);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
return true;
|
||||
|
||||
if (o1 == 0 && OnSegment(left, right.From)) return true;
|
||||
if (o2 == 0 && OnSegment(left, right.To)) return true;
|
||||
if (o3 == 0 && OnSegment(right, left.From)) return true;
|
||||
if (o4 == 0 && OnSegment(right, left.To)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool OnSegment(Line 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))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Intersects(Line left, Line right, [NotNullWhen(returnValue: true)] out Vector2D? point)
|
||||
{
|
||||
point = null;
|
||||
|
||||
bool result = Intersects(left, right);
|
||||
|
||||
if (result)
|
||||
point = IntersectionPoint(left, right);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Line"/> segments are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(Line left, Line right)
|
||||
=> left.From.ApproximatelyEquals(right.From) && left.To.ApproximatelyEquals(right.To);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the Line struct.
|
||||
/// </summary>
|
||||
public static class LineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Line"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(this Line left, Line right) => Line.ApproximatelyEquals(left, right);
|
||||
}
|
||||
|
|
|
@ -2,20 +2,63 @@ using Syntriax.Engine.Core;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line equation in the form y = mx + b.
|
||||
/// </summary>
|
||||
/// <param name="slope">The slope of the line.</param>
|
||||
/// <param name="offsetY">The y-intercept of the line.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="LineEquation"/> struct with the specified slope and y-intercept.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||
public readonly struct LineEquation(float Slope, float OffsetY)
|
||||
public readonly struct LineEquation(float slope, float offsetY)
|
||||
{
|
||||
public readonly float Slope { get; init; } = Slope;
|
||||
public readonly float OffsetY { get; init; } = OffsetY;
|
||||
/// <summary>
|
||||
/// The slope of the line equation.
|
||||
/// </summary>
|
||||
public readonly float Slope = slope;
|
||||
|
||||
/// <summary>
|
||||
/// The y-intercept of the line equation.
|
||||
/// </summary>
|
||||
public readonly float OffsetY = offsetY;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
||||
/// </summary>
|
||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
||||
public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two line equations are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first line equation to compare.</param>
|
||||
/// <param name="right">The second line equation to compare.</param>
|
||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
||||
public static bool ApproximatelyEquals(LineEquation left, LineEquation right)
|
||||
=> left.Slope.ApproximatelyEquals(right.Slope) && left.OffsetY.ApproximatelyEquals(right.OffsetY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the LineEquation struct.
|
||||
/// </summary>
|
||||
public static class LineEquationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
||||
/// </summary>
|
||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
||||
public static float Resolve(this LineEquation lineEquation, float x) => LineEquation.Resolve(lineEquation, x);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two line equations are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first line equation to compare.</param>
|
||||
/// <param name="right">The second line equation to compare.</param>
|
||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
||||
public static bool ApproximatelyEquals(this LineEquation left, LineEquation right) => LineEquation.ApproximatelyEquals(left, right);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,41 @@
|
|||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range of values along a single axis.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum value of the projection.</param>
|
||||
/// <param name="max">The maximum value of the projection.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Projection"/> struct with the specified minimum and maximum values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||
public readonly struct Projection(float Min, float Max)
|
||||
public readonly struct Projection(float min, float max)
|
||||
{
|
||||
public readonly float Min { get; init; } = Min;
|
||||
public readonly float Max { get; init; } = Max;
|
||||
/// <summary>
|
||||
/// Gets the minimum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Min = min;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Max = max;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap and calculates the depth of the overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <param name="depth">The depth of the overlap, if any.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection left, Projection right, out float depth)
|
||||
{
|
||||
// TODO Try to improve this
|
||||
|
@ -42,8 +71,26 @@ public readonly struct Projection(float Min, float Max)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Projection"/> struct.
|
||||
/// </summary>
|
||||
public static class ProjectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap and calculates the depth of the overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <param name="depth">The depth of the overlap, if any.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
|
||||
}
|
||||
|
|
|
@ -6,22 +6,55 @@ using Syntriax.Engine.Core.Abstract;
|
|||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a shape defined by a collection of vertices.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices of the shape.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Shape"/> struct with the specified vertices.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||
public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||
public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
||||
{
|
||||
public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
|
||||
public static readonly Shape Box = CreateNgon(4, Vector2D.One);
|
||||
public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
|
||||
public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
|
||||
|
||||
public readonly IList<Vector2D> Vertices { get; init; } = Vertices;
|
||||
private readonly List<Vector2D> _verticesList = vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertices of the shape.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Vector2D> Vertices => _verticesList;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the vertex to get or set.</param>
|
||||
/// <returns>The vertex at the specified index.</returns>
|
||||
public Vector2D this[System.Index index] => Vertices[index];
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the current shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to copy.</param>
|
||||
/// <returns>A copy of the input shape.</returns>
|
||||
public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regular polygon (ngon) with the specified number of vertices.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">The number of vertices in the polygon.</param>
|
||||
/// <returns>A regular polygon with the specified number of vertices.</returns>
|
||||
public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regular polygon (ngon) with the specified number of vertices and a rotation position.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">The number of vertices in the polygon.</param>
|
||||
/// <param name="positionToRotate">The position to use for rotation.</param>
|
||||
/// <returns>A regular polygon with the specified number of vertices and rotation position.</returns>
|
||||
public static Shape CreateNgon(int vertexCount, Vector2D positionToRotate)
|
||||
{
|
||||
if (vertexCount < 3)
|
||||
|
@ -37,6 +70,11 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return new(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the super triangle that encloses the given shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the given shape.</returns>
|
||||
public static Triangle GetSuperTriangle(Shape shape)
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
|
@ -63,6 +101,11 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <param name="lines">The list to populate with lines.</param>
|
||||
public static void GetLines(Shape shape, IList<Line> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
|
@ -71,6 +114,11 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
lines.Add(new(shape.Vertices[^1], shape.Vertices[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <returns>A list of lines that form the edges of the shape.</returns>
|
||||
public static List<Line> GetLines(Shape shape)
|
||||
{
|
||||
List<Line> lines = new(shape.Vertices.Count - 1);
|
||||
|
@ -78,6 +126,12 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <param name="list">The list to populate with projected values.</param>
|
||||
public static void Project(Shape shape, Vector2D projectionVector, IList<float> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
@ -87,6 +141,12 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
list.Add(projectionVector.Dot(shape[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the shape onto the vector.</returns>
|
||||
public static Projection Project(Shape shape, Vector2D projectionVector)
|
||||
{
|
||||
float min = float.MaxValue;
|
||||
|
@ -102,6 +162,12 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return new(min, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <returns>The transformed shape.</returns>
|
||||
public static Shape TransformShape(Shape shape, ITransform transform)
|
||||
{
|
||||
List<Vector2D> vertices = new(shape.Vertices.Count);
|
||||
|
@ -113,15 +179,27 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return new Shape(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="from">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="to">The transformed shape.</param>
|
||||
public static void TransformShape(Shape from, ITransform transform, ref Shape to)
|
||||
{
|
||||
to.Vertices.Clear();
|
||||
to._verticesList.Clear();
|
||||
|
||||
int count = from.Vertices.Count;
|
||||
int count = from._verticesList.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
to.Vertices.Add(transform.TransformVector2D(from[i]));
|
||||
to._verticesList.Add(transform.TransformVector2D(from[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two shapes are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first shape to compare.</param>
|
||||
/// <param name="right">The second shape to compare.</param>
|
||||
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool ApproximatelyEquals(Shape left, Shape right)
|
||||
{
|
||||
if (left.Vertices.Count != right.Vertices.Count)
|
||||
|
@ -134,22 +212,83 @@ public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Shape"/> struct.
|
||||
/// </summary>
|
||||
public static class ShapeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a copy of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to copy.</param>
|
||||
/// <returns>A copy of the input shape.</returns>
|
||||
public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the super triangle that encloses the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the shape.</returns>
|
||||
public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <param name="lines">The list to populate with lines.</param>
|
||||
public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <returns>A list of lines that form the edges of the shape.</returns>
|
||||
public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <param name="list">The list to populate with projected values.</param>
|
||||
public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the shape onto the vector.</returns>
|
||||
public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="shape">The shape to transform.</param>
|
||||
/// <returns>The transformed shape.</returns>
|
||||
public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="from">The shape to transform.</param>
|
||||
/// <param name="to">The transformed shape.</param>
|
||||
public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two shapes are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first shape to compare.</param>
|
||||
/// <param name="right">The second shape to compare.</param>
|
||||
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Work In Progress
|
||||
|
||||
This engine is still in development but the implemented features include:
|
||||
- Modular Systems
|
||||
- Behaviour System
|
||||
- 2D Physics Engine(**Not Fully Completed, but usable**)
|
||||
- Rigid Body Simulations
|
||||
- Collision Detection (Convex Shape & Circle)
|
||||
- Collision Resolution (**Not Fully Completed**)
|
||||
- Vector2D, AABB, Circle, Line, LineEquation, Projection & Shape Data Types
|
||||
- General Math
|
||||
|
||||
---
|
||||
|
||||
**A detailed README file will be written in the future. If you want to check out how to use this, please checkout this example Pong game made using this engine on top of [MonoGame](https://monogame.net/) from the link bellow.**
|
||||
|
||||
[Pong Source Code](https://git.syntriax.com/Syntriax/Engine-Pong)
|
Loading…
Reference in New Issue