Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
22b3a342a8 | |||
d0aee7fe16 | |||
be15cba9e1 | |||
ac0eac3fbb | |||
3c23ac7f1e | |||
57807c2a62 | |||
3452194941 | |||
11612ff0db | |||
63bc94c7a6 | |||
e00319d7ff | |||
11719440dc | |||
f246d68aa7 | |||
6e87c67096 | |||
b8217f2106 | |||
9824980cbf | |||
93a79cd075 | |||
f6e52abcc1 | |||
03232f72e8 | |||
37aca44e45 | |||
9f4d95a57b | |||
65eac57fce | |||
08311acc9a | |||
f8fbae6130 | |||
df06e8d134 | |||
ad365dc722 | |||
200e8ae7da | |||
65cfaf1b4a | |||
83b155fc5e | |||
7db56e7f3e | |||
42064875a0 | |||
41245c0c1c | |||
0e5cc8f898 | |||
c8bb991865 | |||
bc1c76d746 | |||
8f03628bd6 | |||
a1feb0bad3 | |||
978cba96c8 | |||
7212094a3d | |||
14843ddeba | |||
5315db0077 | |||
026f343d43 | |||
da5f31f9d7 | |||
fa1614f238 | |||
0c096d39db | |||
dae6549bad | |||
767fc28488 | |||
c3be8f60b7 | |||
33cb44bf36 | |||
4c1018ddec | |||
cf7061fd58 | |||
e6b7b9953f | |||
4a3775a0de | |||
4d353662a1 | |||
ca0b2de917 | |||
2335c3ec62 | |||
30ccab1b93 | |||
f56d6a7fc8 |
@ -37,11 +37,21 @@ public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Event<IUniverse> OnPostDraw { get; }
|
Event<IUniverse> OnPostDraw { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is about to be registered to the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectRegisteredArguments> OnPreUniverseObjectRegistered { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
|
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Event<IUniverse, UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; }
|
Event<IUniverse, UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a <see cref="IUniverseObject"/> is about to be unregistered from the <see cref="IUniverse"/>.
|
||||||
|
/// </summary>
|
||||||
|
Event<IUniverse, UniverseObjectUnRegisteredArguments> OnPreUniverseObjectUnRegistered { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
|
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,7 +7,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// This interface allows for tracking the object's presence in the universe and provides events
|
/// This interface allows for tracking the object's presence in the universe and provides events
|
||||||
/// for notifying when the see enters or exits the universe.
|
/// for notifying when the see enters or exits the universe.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IUniverseObject>
|
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
|
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
|
||||||
@ -47,7 +47,7 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
|
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IUniverseObject? Parent { get; }
|
IUniverseObject? Parent { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
|
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
|
||||||
@ -75,12 +75,6 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
internal bool ExitUniverse();
|
internal bool ExitUniverse();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the parent <see cref="IUniverseObject"/> of this <see cref="IUniverseObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="universeObject">The parent <see cref="IUniverseObject"/> to set.</param>
|
|
||||||
void SetParent(IUniverseObject? universeObject);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
|
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -44,6 +44,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
_behaviourController = behaviourController;
|
_behaviourController = behaviourController;
|
||||||
OnAssign(behaviourController);
|
OnAssign(behaviourController);
|
||||||
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
|
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
|
||||||
|
behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
||||||
if (behaviourController.UniverseObject is not null)
|
if (behaviourController.UniverseObject is not null)
|
||||||
OnUniverseObjectAssigned(behaviourController);
|
OnUniverseObjectAssigned(behaviourController);
|
||||||
OnBehaviourControllerAssigned?.Invoke(this);
|
OnBehaviourControllerAssigned?.Invoke(this);
|
||||||
@ -68,6 +69,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
|
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
|
||||||
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
|
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
|
||||||
|
BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
base.UnassignInternal();
|
base.UnassignInternal();
|
||||||
_behaviourController = null!;
|
_behaviourController = null!;
|
||||||
}
|
}
|
||||||
@ -76,6 +78,8 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
{
|
{
|
||||||
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
||||||
Debug.Assert.AssertStateEnableAssigned(this);
|
Debug.Assert.AssertStateEnableAssigned(this);
|
||||||
|
|
||||||
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
|
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
|
||||||
@ -84,7 +88,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|||||||
private void UpdateActive()
|
private void UpdateActive()
|
||||||
{
|
{
|
||||||
bool previousActive = IsActive;
|
bool previousActive = IsActive;
|
||||||
_isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
_isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
||||||
|
|
||||||
if (previousActive != IsActive)
|
if (previousActive != IsActive)
|
||||||
OnActiveChanged?.Invoke(this, new(previousActive));
|
OnActiveChanged?.Invoke(this, new(previousActive));
|
||||||
|
@ -76,7 +76,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
|||||||
OnUniverseObjectRegistered(universe, new(universeObject));
|
OnUniverseObjectRegistered(universe, new(universeObject));
|
||||||
|
|
||||||
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
|
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
|
||||||
universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
|
universe.OnPreUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
Universe = universe;
|
Universe = universe;
|
||||||
OnAssign(universe);
|
OnAssign(universe);
|
||||||
@ -94,7 +94,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
|||||||
OnUniverseObjectUnregistered(Universe, new(universeObject));
|
OnUniverseObjectUnregistered(Universe, new(universeObject));
|
||||||
|
|
||||||
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
|
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
|
||||||
Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
|
Universe.OnPreUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
|
||||||
|
|
||||||
Universe = null!;
|
Universe = null!;
|
||||||
OnUnassigned?.Invoke(this);
|
OnUnassigned?.Invoke(this);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
@ -50,17 +49,13 @@ public class BehaviourController : BaseEntity, IBehaviourController
|
|||||||
|
|
||||||
public IReadOnlyList<T> GetBehaviours<T>()
|
public IReadOnlyList<T> GetBehaviours<T>()
|
||||||
{
|
{
|
||||||
List<T>? behaviours = null;
|
List<T> behaviours = [];
|
||||||
|
|
||||||
foreach (IBehaviour behaviourItem in this.behaviours)
|
foreach (IBehaviour behaviourItem in this.behaviours)
|
||||||
{
|
if (behaviourItem is T behaviour)
|
||||||
if (behaviourItem is not T behaviour)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
behaviours ??= [];
|
|
||||||
behaviours.Add(behaviour);
|
behaviours.Add(behaviour);
|
||||||
}
|
|
||||||
|
|
||||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
return behaviours;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetBehaviours<T>(IList<T> results)
|
public void GetBehaviours<T>(IList<T> results)
|
||||||
@ -77,7 +72,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
|
|||||||
|
|
||||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
for (int i = behaviours.Count; i >= 0; i--)
|
for (int i = behaviours.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (behaviours[i] is not T behaviour)
|
if (behaviours[i] is not T behaviour)
|
||||||
continue;
|
continue;
|
||||||
|
@ -10,21 +10,21 @@ public static class Assert
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
|
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
|
||||||
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IBehaviourController)}");
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertEntityAssigned(IHasEntity assignable)
|
public static void AssertEntityAssigned(IHasEntity assignable)
|
||||||
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IEntity)}");
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertUniverseAssigned(IHasUniverse assignable)
|
public static void AssertUniverseAssigned(IHasUniverse assignable)
|
||||||
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverse)}");
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
|
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
|
||||||
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverseObject)}");
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
|
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
|
||||||
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IStateEnable)}");
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,23 @@ public class FileLogger : LoggerBase
|
|||||||
{
|
{
|
||||||
public readonly string FilePath;
|
public readonly string FilePath;
|
||||||
|
|
||||||
public FileLogger(string filePath)
|
|
||||||
{
|
|
||||||
FilePath = filePath;
|
|
||||||
File.Open(filePath, FileMode.Create).Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Write(string message)
|
protected override void Write(string message)
|
||||||
{
|
{
|
||||||
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
|
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FileLogger(string filePath)
|
||||||
|
{
|
||||||
|
FilePath = filePath;
|
||||||
|
|
||||||
|
bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0;
|
||||||
|
|
||||||
|
if (isRelativePath)
|
||||||
|
FilePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath));
|
||||||
|
|
||||||
|
if (Path.GetDirectoryName(FilePath) is string directoryPath)
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
|
||||||
|
File.Open(FilePath, FileMode.Create).Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,15 @@ namespace Syntriax.Engine.Core.Debug;
|
|||||||
|
|
||||||
public interface ILogger
|
public interface ILogger
|
||||||
{
|
{
|
||||||
|
static ILogger Shared { get; set; } = new ConsoleLogger();
|
||||||
|
|
||||||
Level FilterLevel { get; set; }
|
Level FilterLevel { get; set; }
|
||||||
|
|
||||||
void Log(string message, Level level = Level.Info, bool force = false);
|
void Log(string message, Level level = Level.Info, bool force = false);
|
||||||
|
|
||||||
enum Level
|
enum Level
|
||||||
{
|
{
|
||||||
|
Trace,
|
||||||
Info,
|
Info,
|
||||||
Warning,
|
Warning,
|
||||||
Error,
|
Error,
|
||||||
|
@ -4,7 +4,7 @@ namespace Syntriax.Engine.Core.Debug;
|
|||||||
|
|
||||||
public abstract class LoggerBase : ILogger
|
public abstract class LoggerBase : ILogger
|
||||||
{
|
{
|
||||||
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Info;
|
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Trace;
|
||||||
|
|
||||||
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||||
{
|
{
|
||||||
|
9
Engine.Core/Debug/LoggerContainer.cs
Normal file
9
Engine.Core/Debug/LoggerContainer.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class LoggerContainer : Behaviour, ILogger
|
||||||
|
{
|
||||||
|
public ILogger Logger { get; set; } = ILogger.Shared;
|
||||||
|
|
||||||
|
public ILogger.Level FilterLevel { get => Logger.FilterLevel; set => Logger.FilterLevel = value; }
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => Logger.Log(message, level, force);
|
||||||
|
}
|
@ -16,13 +16,21 @@ public static class LoggerExtensions
|
|||||||
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
|
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
|
||||||
{
|
{
|
||||||
Log(logger, caller, message, ILogger.Level.Error, force);
|
Log(logger, caller, message, ILogger.Level.Error, force);
|
||||||
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}");
|
LogTrace(logger, caller, new StackTrace(), force);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
|
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
|
||||||
{
|
{
|
||||||
|
Log(logger, caller, $"Exception of type {exception.GetType().Name} occured", ILogger.Level.Error, force);
|
||||||
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
|
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
|
||||||
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
|
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
|
||||||
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}");
|
|
||||||
|
// Not using LogTrace because exception.StackTrace is a type of string
|
||||||
|
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}", ILogger.Level.Trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogTrace<T>(this ILogger logger, T caller, StackTrace? stackTrace = null, bool force = false)
|
||||||
|
{
|
||||||
|
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{stackTrace ?? new()}", ILogger.Level.Trace, force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
Engine.Core/Debug/LoggerWrapper.cs
Normal file
23
Engine.Core/Debug/LoggerWrapper.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger
|
||||||
|
{
|
||||||
|
private readonly ILogger firstLogger = firstLogger;
|
||||||
|
private readonly ILogger secondLogger = secondLogger;
|
||||||
|
|
||||||
|
public ILogger.Level FilterLevel
|
||||||
|
{
|
||||||
|
get => firstLogger.FilterLevel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
firstLogger.FilterLevel = value;
|
||||||
|
secondLogger.FilterLevel = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||||
|
{
|
||||||
|
firstLogger.Log(message, level, force);
|
||||||
|
secondLogger.Log(message, level, force);
|
||||||
|
}
|
||||||
|
}
|
6
Engine.Core/Debug/LoggerWrapperExtensions.cs
Normal file
6
Engine.Core/Debug/LoggerWrapperExtensions.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
|
public static class LoggerWrapperExtensions
|
||||||
|
{
|
||||||
|
public static ILogger WrapWith(this ILogger thisLogger, ILogger logger) => new LoggerWrapper(thisLogger, logger);
|
||||||
|
}
|
@ -27,7 +27,7 @@ public static class BehaviourControllerExtensions
|
|||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
|
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
|
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
|
||||||
@ -93,7 +93,7 @@ public static class BehaviourControllerExtensions
|
|||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
|
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list.
|
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list.
|
||||||
@ -140,7 +140,7 @@ public static class BehaviourControllerExtensions
|
|||||||
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
|
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
|
||||||
return localBehaviour;
|
return localBehaviour;
|
||||||
|
|
||||||
foreach (IUniverseObject child in behaviourController.UniverseObject)
|
foreach (IUniverseObject child in behaviourController.UniverseObject.Children)
|
||||||
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
||||||
return behaviour;
|
return behaviour;
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ public static class BehaviourControllerExtensions
|
|||||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||||
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
||||||
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
|
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list.
|
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list.
|
||||||
@ -176,7 +176,7 @@ public static class BehaviourControllerExtensions
|
|||||||
foreach (T behaviour in cache)
|
foreach (T behaviour in cache)
|
||||||
behaviours.Add(behaviour);
|
behaviours.Add(behaviour);
|
||||||
|
|
||||||
foreach (IUniverseObject child in universeObject)
|
foreach (IUniverseObject child in universeObject.Children)
|
||||||
TraverseChildrenForBehaviour(child, behaviours, cache);
|
TraverseChildrenForBehaviour(child, behaviours, cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ public static class UniverseObjectExtensions
|
|||||||
if (!string.IsNullOrWhiteSpace(name))
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
universeObject.Name = name;
|
universeObject.Name = name;
|
||||||
if (parent is not null)
|
if (parent is not null)
|
||||||
universeObject.SetParent(parent);
|
universeObject.Parent = parent;
|
||||||
return universeObject;
|
return universeObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ public static class UniverseObjectExtensions
|
|||||||
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
||||||
public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
|
public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
|
||||||
{
|
{
|
||||||
if (universeObject.GetUniverseObject<T>() is T localUniverseObject)
|
if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
|
||||||
return localUniverseObject;
|
return localUniverseObject;
|
||||||
|
|
||||||
IUniverseObject? parent = universeObject;
|
IUniverseObject? parent = universeObject;
|
||||||
@ -129,10 +129,10 @@ public static class UniverseObjectExtensions
|
|||||||
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
/// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
|
||||||
public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
|
public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
|
||||||
{
|
{
|
||||||
if (universeObject.GetUniverseObject<T>() is T localUniverseObject)
|
if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
|
||||||
return localUniverseObject;
|
return localUniverseObject;
|
||||||
|
|
||||||
foreach (IUniverseObject child in universeObject)
|
foreach (IUniverseObject child in universeObject.Children)
|
||||||
if (GetUniverseObjectInChildren<T>(child) is T behaviour)
|
if (GetUniverseObjectInChildren<T>(child) is T behaviour)
|
||||||
return behaviour;
|
return behaviour;
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ public static class UniverseObjectExtensions
|
|||||||
|
|
||||||
foreach (IUniverseObject universeObject in universeObjects)
|
foreach (IUniverseObject universeObject in universeObjects)
|
||||||
{
|
{
|
||||||
universeObject.Find(cache);
|
universeObject.Children.Find(cache);
|
||||||
foreach (T behaviour in cache)
|
foreach (T behaviour in cache)
|
||||||
instances.Add(behaviour);
|
instances.Add(behaviour);
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ namespace Syntriax.Engine.Core.Factory;
|
|||||||
|
|
||||||
public class BehaviourControllerFactory
|
public class BehaviourControllerFactory
|
||||||
{
|
{
|
||||||
public static IBehaviourController Instantiate(IUniverseObject universeObject)
|
public static IBehaviourController Instantiate(IUniverseObject universeObject, IStateEnable? stateEnable = null)
|
||||||
=> Instantiate<BehaviourController>(universeObject);
|
=> Instantiate<BehaviourController>(universeObject, stateEnable);
|
||||||
|
|
||||||
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args)
|
public static T Instantiate<T>(IUniverseObject universeObject, IStateEnable? stateEnable = null, params object?[]? args)
|
||||||
where T : class, IBehaviourController
|
where T : class, IBehaviourController
|
||||||
{
|
{
|
||||||
T behaviourController = TypeFactory.Get<T>(args);
|
T behaviourController = TypeFactory.Get<T>(args);
|
||||||
@ -18,6 +18,17 @@ public class BehaviourControllerFactory
|
|||||||
if (!behaviourController.Assign(universeObject))
|
if (!behaviourController.Assign(universeObject))
|
||||||
throw AssignFailedException.From(behaviourController, universeObject);
|
throw AssignFailedException.From(behaviourController, universeObject);
|
||||||
|
|
||||||
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
|
if (!stateEnable.Assign(behaviourController))
|
||||||
|
throw AssignFailedException.From(stateEnable, behaviourController);
|
||||||
|
|
||||||
|
if (!behaviourController.Assign(stateEnable))
|
||||||
|
throw AssignFailedException.From(behaviourController, stateEnable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
StateEnableFactory.Instantiate(behaviourController);
|
||||||
|
|
||||||
return behaviourController;
|
return behaviourController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,15 @@ public class BehaviourFactory
|
|||||||
{
|
{
|
||||||
T behaviour = TypeFactory.Get<T>(args);
|
T behaviour = TypeFactory.Get<T>(args);
|
||||||
|
|
||||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
if (!stateEnable.Assign(behaviour))
|
if (!stateEnable.Assign(behaviour))
|
||||||
throw AssignFailedException.From(stateEnable, behaviour);
|
throw AssignFailedException.From(stateEnable, behaviour);
|
||||||
|
|
||||||
if (!behaviour.Assign(stateEnable))
|
if (!behaviour.Assign(stateEnable))
|
||||||
throw AssignFailedException.From(behaviour, stateEnable);
|
throw AssignFailedException.From(behaviour, stateEnable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
StateEnableFactory.Instantiate(behaviour);
|
||||||
|
|
||||||
return behaviour;
|
return behaviour;
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,25 @@ public class UniverseObjectFactory
|
|||||||
{
|
{
|
||||||
T universeObject = TypeFactory.Get<T>(args);
|
T universeObject = TypeFactory.Get<T>(args);
|
||||||
|
|
||||||
behaviourController ??= TypeFactory.Get<BehaviourController>();
|
if (behaviourController is not null)
|
||||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
{
|
||||||
|
|
||||||
if (!behaviourController.Assign(universeObject))
|
if (!behaviourController.Assign(universeObject))
|
||||||
throw AssignFailedException.From(behaviourController, universeObject);
|
throw AssignFailedException.From(behaviourController, universeObject);
|
||||||
if (!stateEnable.Assign(universeObject))
|
|
||||||
throw AssignFailedException.From(stateEnable, universeObject);
|
|
||||||
|
|
||||||
if (!universeObject.Assign(behaviourController))
|
if (!universeObject.Assign(behaviourController))
|
||||||
throw AssignFailedException.From(universeObject, behaviourController);
|
throw AssignFailedException.From(universeObject, behaviourController);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BehaviourControllerFactory.Instantiate(universeObject);
|
||||||
|
|
||||||
|
if (stateEnable is not null)
|
||||||
|
{
|
||||||
|
if (!stateEnable.Assign(universeObject))
|
||||||
|
throw AssignFailedException.From(stateEnable, universeObject);
|
||||||
if (!universeObject.Assign(stateEnable))
|
if (!universeObject.Assign(stateEnable))
|
||||||
throw AssignFailedException.From(universeObject, stateEnable);
|
throw AssignFailedException.From(universeObject, stateEnable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
StateEnableFactory.Instantiate(universeObject);
|
||||||
|
|
||||||
return universeObject;
|
return universeObject;
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,146 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core.Debug;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a simple event with no parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event MyEvent = new();
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke();
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered()
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"Event occurred!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime()
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"Event called once!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event called once!
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event occurred!
|
||||||
|
/// Event occurred!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
public class Event
|
public class Event
|
||||||
{
|
{
|
||||||
private readonly List<EventHandler> listeners = null!;
|
// We use Ascending order because draw calls are running from last to first
|
||||||
private readonly List<EventHandler> onceListeners = null!;
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
private ILogger _logger = ILogger.Shared;
|
||||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
|
|
||||||
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
public void Invoke()
|
public void Invoke()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < listeners.Count; i++)
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
try { listeners[i].Invoke(); }
|
try { listeners[i].Callback.Invoke(); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}()";
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}()";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
try { onceListeners[i].Invoke(); }
|
try { onceListeners[i].Callback.Invoke(); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}()";
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}()";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
onceListeners.RemoveAt(i);
|
onceListeners.RemoveAt(i);
|
||||||
}
|
}
|
||||||
@ -48,35 +159,148 @@ public class Event
|
|||||||
}
|
}
|
||||||
|
|
||||||
public delegate void EventHandler();
|
public delegate void EventHandler();
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Event<TSender>
|
/// <summary>
|
||||||
|
/// Represents an event with only sender parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event<MyBehaviour> MyEvent = new();
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke(this);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered(MyBehaviour sender)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event occurred!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime(MyBehaviour sender)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event called once!");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event called once!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// [Id]'s event occurred!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSender">Sender type</typeparam>
|
||||||
|
public class Event<TSender> where TSender : class
|
||||||
{
|
{
|
||||||
private readonly List<EventHandler> listeners = null!;
|
// We use Ascending order because draw calls are running from last to first
|
||||||
private readonly List<EventHandler> onceListeners = null!;
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
private ILogger _logger = ILogger.Shared;
|
||||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
|
|
||||||
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The caller that's triggering this event.</param>
|
||||||
public void Invoke(TSender sender)
|
public void Invoke(TSender sender)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < listeners.Count; i++)
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
try { listeners[i].Invoke(sender); }
|
try { listeners[i].Callback.Invoke(sender); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({sender})";
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender})";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
try { onceListeners[i].Invoke(sender); }
|
try { onceListeners[i].Callback.Invoke(sender); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({sender})";
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender})";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
onceListeners.RemoveAt(i);
|
onceListeners.RemoveAt(i);
|
||||||
}
|
}
|
||||||
@ -95,35 +319,156 @@ public class Event<TSender>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public delegate void EventHandler(TSender sender);
|
public delegate void EventHandler(TSender sender);
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Event<TSender, TArguments>
|
/// <summary>
|
||||||
|
/// Represents an event with sender and argument parameters.
|
||||||
|
/// <para>Example usage:</para>
|
||||||
|
/// <code>
|
||||||
|
/// public class MyBehaviour : Behaviour, IUpdate
|
||||||
|
/// {
|
||||||
|
/// public readonly Event<MyBehaviour, MyArguments> MyEvent = new();
|
||||||
|
///
|
||||||
|
/// private int myInt = 0;
|
||||||
|
/// private bool myBool = false;
|
||||||
|
///
|
||||||
|
/// public MyBehaviour()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime);
|
||||||
|
/// MyEvent.AddListener(OnEventTriggered);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public void Update()
|
||||||
|
/// {
|
||||||
|
/// MyEvent.Invoke(this, new MyArguments(myInt, myBool));
|
||||||
|
/// myInt++;
|
||||||
|
/// myBool = !myBool;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private void OnEventTriggered(MyBehaviour sender, MyArguments args)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event occurred with MyInt: {args.MyInt} and MyBool {args.MyBool}!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// private static void OnEventTriggeredOneTime(MyBehaviour sender, MyArguments args)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine($"{sender.Id}'s event called once with MyInt: {args.MyInt} and MyBool {args.MyBool}!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public readonly record struct MyArguments(int MyInt, bool MyBool);
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// The output of the example code above would be:
|
||||||
|
/// <code>
|
||||||
|
/// [Id]'s event occurred with MyInt: 0 and MyBool False!
|
||||||
|
/// [Id]'s event called once with MyInt: 0 and MyBool False!
|
||||||
|
/// [Id]'s event occurred with MyInt: 1 and MyBool True!
|
||||||
|
/// [Id]'s event occurred with MyInt: 2 and MyBool False!
|
||||||
|
/// [Id]'s event occurred with MyInt: 3 and MyBool True!
|
||||||
|
/// ...
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSender">Sender type</typeparam>
|
||||||
|
public class Event<TSender, TArguments> where TSender : class
|
||||||
{
|
{
|
||||||
private readonly List<EventHandler> listeners = null!;
|
// We use Ascending order because draw calls are running from last to first
|
||||||
private readonly List<EventHandler> onceListeners = null!;
|
private static readonly Comparer<ListenerData> SortByAscendingPriority = Comparer<ListenerData>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
private ILogger _logger = ILogger.Shared;
|
||||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; }
|
||||||
public void RemoveListener(EventHandler listener) => listeners.Remove(listener);
|
|
||||||
public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener);
|
private readonly List<ListenerData> listeners = null!;
|
||||||
|
private readonly List<ListenerData> onceListeners = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked whenever the event is triggered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called when the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
listeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback to be called the next time the event is triggered.</param>
|
||||||
|
/// <param name="priority">Priority of the callback.</param>
|
||||||
|
public void AddOneTimeListener(EventHandler listener, int priority = 0)
|
||||||
|
{
|
||||||
|
ListenerData listenerData = new(listener, priority);
|
||||||
|
|
||||||
|
int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority);
|
||||||
|
if (insertIndex < 0)
|
||||||
|
insertIndex = ~insertIndex;
|
||||||
|
|
||||||
|
onceListeners.Insert(insertIndex, listenerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddListener(EventHandler)"/></param>
|
||||||
|
public void RemoveListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
if (listeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes the callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The callback that was previously registered by <see cref="AddOneTimeListener(EventHandler)"/></param>
|
||||||
|
public void RemoveOneTimeListener(EventHandler listener)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < onceListeners.Count; i++)
|
||||||
|
if (onceListeners[i].Callback == listener)
|
||||||
|
{
|
||||||
|
onceListeners.RemoveAt(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribes all listeners that was previously registered by either <see cref="AddListener(EventHandler)"/> or <see cref="AddOneTimeListener(EventHandler)"/>.
|
||||||
|
/// </summary>
|
||||||
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
public void Clear() { listeners.Clear(); onceListeners.Clear(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The caller that's triggering this event.</param>
|
||||||
|
/// <param name="args">The arguments provided for this event.</param>
|
||||||
public void Invoke(TSender sender, TArguments args)
|
public void Invoke(TSender sender, TArguments args)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < listeners.Count; i++)
|
for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
try { listeners[i].Invoke(sender, args); }
|
try { listeners[i].Callback.Invoke(sender, args); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({string.Join(", ", sender, args)})";
|
string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender}, {args})";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
for (int i = onceListeners.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
try { onceListeners[i].Invoke(sender, args); }
|
try { onceListeners[i].Callback.Invoke(sender, args); }
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({string.Join(", ", sender, args)})";
|
string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender}, {args})";
|
||||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
|
||||||
}
|
}
|
||||||
onceListeners.RemoveAt(i);
|
onceListeners.RemoveAt(i);
|
||||||
}
|
}
|
||||||
@ -142,4 +487,14 @@ public class Event<TSender, TArguments>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public delegate void EventHandler(TSender sender, TArguments args);
|
public delegate void EventHandler(TSender sender, TArguments args);
|
||||||
|
private record struct ListenerData(EventHandler Callback, int Priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class EventHelpers
|
||||||
|
{
|
||||||
|
public static void LogInvocationException(object sender, ILogger logger, Exception exception, string methodCallRepresentation)
|
||||||
|
{
|
||||||
|
logger.LogException(sender, exception);
|
||||||
|
logger.LogError(sender, $"Unexpected exception on invocation of method {methodCallRepresentation}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,10 @@ public class ListPool<T> : IPool<List<T>>
|
|||||||
OnReturned?.Invoke(this, list);
|
OnReturned?.Invoke(this, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListPool(Func<List<T>> generator, int initialCapacity = 1)
|
public ListPool(int initialListCount = 1, int initialListCapacity = 32)
|
||||||
{
|
{
|
||||||
this.generator = generator;
|
generator = () => new(initialListCapacity);
|
||||||
for (int i = 0; i < initialCapacity; i++)
|
for (int i = 0; i < initialListCount; i++)
|
||||||
queue.Enqueue(generator());
|
queue.Enqueue(generator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,29 +6,29 @@ namespace Syntriax.Engine.Core;
|
|||||||
public static class Math
|
public static class Math
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
/// The value of Pi (π).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float PI = 3.1415926535897932f;
|
public const float Pi = 3.1415926535897932f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319.
|
/// The value of Tau (τ), mathematical constant equal to 2π.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float Tau = 2f * PI;
|
public const float Tau = 2f * Pi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base of the natural logarithm, approximately equal to 2.71828.
|
/// The base of the natural logarithm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float E = 2.718281828459045f;
|
public const float E = 2.718281828459045f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The conversion factor from radians to degrees.
|
/// The conversion factor from radians to degrees.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float RadianToDegree = 180f / PI;
|
public const float RadianToDegree = 180f / Pi;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The conversion factor from degrees to radians.
|
/// The conversion factor from degrees to radians.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DegreeToRadian = PI / 180f;
|
public const float DegreeToRadian = Pi / 180f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets one minus of given <see cref="T"/>.
|
/// Gets one minus of given <see cref="T"/>.
|
||||||
|
@ -38,6 +38,9 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Vector2D SizeHalf => Size * .5f;
|
public readonly Vector2D SizeHalf => Size * .5f;
|
||||||
|
|
||||||
|
public static bool operator ==(AABB left, AABB right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
|
||||||
|
public static bool operator !=(AABB left, AABB right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -63,12 +66,6 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
return new(lowerBoundary, upperBoundary);
|
return new(lowerBoundary, upperBoundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="AABB"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -78,6 +75,25 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
||||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="AABB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="AABB"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is AABB aabb && this == aabb;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="AABB"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="AABB"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="AABB"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -38,6 +38,9 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||||
|
|
||||||
|
public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius;
|
||||||
|
public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the center of the <see cref="Circle"/>.
|
/// Sets the center of the <see cref="Circle"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,6 +80,25 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
|
||||||
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Circle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Circle"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Circle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Circle circle && this == circle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Circle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Circle"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Center, Radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Circle"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Circle"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Circle)}({Center}, {Radius})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -27,49 +27,19 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float Value = value.Clamp(0f, 1f);
|
public readonly float Value = value.Clamp(0f, 1f);
|
||||||
|
|
||||||
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f));
|
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus());
|
||||||
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f));
|
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value);
|
||||||
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f));
|
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value);
|
||||||
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f));
|
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value);
|
||||||
public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
|
public static ColorHSV operator *(ColorHSV color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value);
|
||||||
public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
|
public static ColorHSV operator *(float value, ColorHSV color) => new(color.Hue * value, color.Saturation * value, color.Value * value);
|
||||||
public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f));
|
public static ColorHSV operator /(ColorHSV color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value);
|
||||||
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value);
|
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value;
|
||||||
public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value);
|
public static bool operator !=(ColorHSV left, ColorHSV right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value;
|
||||||
|
|
||||||
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba;
|
public static implicit operator ColorHSV(ColorHSVA hsva) => new(hsva.Hue, hsva.Saturation, hsva.Value);
|
||||||
public static implicit operator ColorHSV(ColorRGB rgb)
|
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorHSVA)rgba;
|
||||||
{
|
public static implicit operator ColorHSV(ColorRGB rgb) => (ColorHSVA)rgb;
|
||||||
float hue;
|
|
||||||
float saturation;
|
|
||||||
float value;
|
|
||||||
|
|
||||||
float rd = rgb.R / 255f;
|
|
||||||
float gd = rgb.G / 255f;
|
|
||||||
float bd = rgb.B / 255f;
|
|
||||||
|
|
||||||
float max = Math.Max(rd, Math.Max(gd, bd));
|
|
||||||
float min = Math.Min(rd, Math.Min(gd, bd));
|
|
||||||
float delta = max - min;
|
|
||||||
|
|
||||||
if (delta.ApproximatelyEquals(0))
|
|
||||||
hue = 0f;
|
|
||||||
else if (max.ApproximatelyEquals(rd))
|
|
||||||
hue = 60f * ((gd - bd) / delta % 6f);
|
|
||||||
else if (max.ApproximatelyEquals(gd))
|
|
||||||
hue = 60f * (((bd - rd) / delta) + 2f);
|
|
||||||
else
|
|
||||||
hue = 60f * (((rd - gd) / delta) + 4f);
|
|
||||||
|
|
||||||
if (hue < 0f)
|
|
||||||
hue += 360f;
|
|
||||||
|
|
||||||
hue /= 360f;
|
|
||||||
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
|
|
||||||
value = max;
|
|
||||||
|
|
||||||
return new(hue, saturation, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inverts the given <see cref="ColorHSV"/>.
|
/// Inverts the given <see cref="ColorHSV"/>.
|
||||||
@ -110,14 +80,6 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
|
|||||||
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
|
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
|
||||||
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
|
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the <see cref="ColorHSV"/> from one point to another.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="from">The starting point.</param>
|
|
||||||
/// <param name="to">The ending point.</param>
|
|
||||||
/// <returns>The <see cref="ColorHSV"/> from the starting point to the ending point.</returns>
|
|
||||||
public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
|
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -125,13 +87,14 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
|
|||||||
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
|
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
|
||||||
/// <param name="t">The interpolation parameter.</param>
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
|
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
|
||||||
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) => from + FromTo(from, to) * t;
|
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t)
|
||||||
|
{
|
||||||
|
float hueDiff = to.Hue - from.Hue;
|
||||||
|
float saturationDiff = to.Saturation - from.Saturation;
|
||||||
|
float valueDiff = to.Value - from.Value;
|
||||||
|
|
||||||
/// <summary>
|
return from + new ColorHSV(hueDiff * t, saturationDiff * t, valueDiff * t);
|
||||||
/// Converts the <see cref="ColorHSV"/> to its string representation.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
|
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
|
||||||
@ -148,13 +111,19 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorHSV objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value);
|
public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
|
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorHSV"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -174,9 +143,6 @@ public static class ColorHSVExtensions
|
|||||||
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
|
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
|
||||||
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
|
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorHSV.FromTo(ColorHSV, ColorHSV)" />
|
|
||||||
public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to);
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
|
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
|
||||||
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
|
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
|
||||||
|
|
||||||
|
189
Engine.Core/Primitives/ColorHSVA.cs
Normal file
189
Engine.Core/Primitives/ColorHSVA.cs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an HSV color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hue">Hue of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="saturation">Saturation of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">Value of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="alpha">Alpha of the <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="ColorHSVA"/> struct with the specified values.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
|
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Hue value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Hue = hue.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Saturation value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Saturation = saturation.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Value value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Value = value.Clamp(0f, 1f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Alpha value of the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Alpha = alpha;
|
||||||
|
|
||||||
|
public static ColorHSVA operator -(ColorHSVA color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus(), color.Alpha);
|
||||||
|
public static ColorHSVA operator +(ColorHSVA left, ColorHSVA right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value, left.Alpha + right.Alpha);
|
||||||
|
public static ColorHSVA operator -(ColorHSVA left, ColorHSVA right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value, left.Alpha - right.Alpha);
|
||||||
|
public static ColorHSVA operator *(ColorHSVA left, ColorHSVA right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value, left.Alpha * right.Alpha);
|
||||||
|
public static ColorHSVA operator *(ColorHSVA color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value);
|
||||||
|
public static ColorHSVA operator *(float value, ColorHSVA color) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value);
|
||||||
|
public static ColorHSVA operator /(ColorHSVA color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value, color.Alpha / value);
|
||||||
|
public static bool operator ==(ColorHSVA left, ColorHSVA right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value;
|
||||||
|
public static bool operator !=(ColorHSVA left, ColorHSVA right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value;
|
||||||
|
|
||||||
|
public static implicit operator ColorHSVA(ColorHSV hsv) => new(hsv.Hue, hsv.Saturation, hsv.Value, 1f);
|
||||||
|
public static implicit operator ColorHSVA(ColorRGB rgb) => (ColorRGBA)rgb;
|
||||||
|
public static implicit operator ColorHSVA(ColorRGBA rgba)
|
||||||
|
{
|
||||||
|
float hue;
|
||||||
|
float saturation;
|
||||||
|
float value;
|
||||||
|
|
||||||
|
float rd = rgba.R / 255f;
|
||||||
|
float gd = rgba.G / 255f;
|
||||||
|
float bd = rgba.B / 255f;
|
||||||
|
|
||||||
|
float max = Math.Max(rd, Math.Max(gd, bd));
|
||||||
|
float min = Math.Min(rd, Math.Min(gd, bd));
|
||||||
|
float delta = max - min;
|
||||||
|
|
||||||
|
if (delta.ApproximatelyEquals(0))
|
||||||
|
hue = 0f;
|
||||||
|
else if (max.ApproximatelyEquals(rd))
|
||||||
|
hue = 60f * ((gd - bd) / delta % 6f);
|
||||||
|
else if (max.ApproximatelyEquals(gd))
|
||||||
|
hue = 60f * (((bd - rd) / delta) + 2f);
|
||||||
|
else
|
||||||
|
hue = 60f * (((rd - gd) / delta) + 4f);
|
||||||
|
|
||||||
|
if (hue < 0f)
|
||||||
|
hue += 360f;
|
||||||
|
|
||||||
|
hue /= 360f;
|
||||||
|
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
|
||||||
|
value = max;
|
||||||
|
|
||||||
|
return new(hue, saturation, value, rgba.A / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the given <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns>The inverted <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public static ColorHSVA Invert(ColorHSVA color) => -color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds two <see cref="ColorHSVA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns>The sum of the two <see cref="ColorHSVA"/>s.</returns>
|
||||||
|
public static ColorHSVA Add(ColorHSVA left, ColorHSVA right) => left + right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subtracts one <see cref="ColorHSVA"/> from another.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The <see cref="ColorHSVA"/> to subtract from.</param>
|
||||||
|
/// <param name="right">The <see cref="ColorHSVA"/> to subtract.</param>
|
||||||
|
/// <returns>The result of subtracting the second <see cref="ColorHSVA"/> from the first.</returns>
|
||||||
|
public static ColorHSVA Subtract(ColorHSVA left, ColorHSVA right) => left - right;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="ColorHSVA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of multiplying the <see cref="ColorHSVA"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSVA Multiply(ColorHSVA color, float value) => color * value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Divides a <see cref="ColorHSVA"/> by a scalar value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color">The <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="value">The scalar value.</param>
|
||||||
|
/// <returns>The result of dividing the <see cref="ColorHSVA"/> by the scalar value.</returns>
|
||||||
|
public static ColorHSVA Divide(ColorHSVA color, float value) => color / value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs linear interpolation between two <see cref="ColorHSVA"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting <see cref="ColorHSVA"/> (t = 0).</param>
|
||||||
|
/// <param name="to">The ending <see cref="ColorHSVA"/> (t = 1).</param>
|
||||||
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
|
/// <returns>The interpolated <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public static ColorHSVA Lerp(ColorHSVA from, ColorHSVA to, float t)
|
||||||
|
{
|
||||||
|
float hueDiff = to.Hue - from.Hue;
|
||||||
|
float saturationDiff = to.Saturation - from.Saturation;
|
||||||
|
float valueDiff = to.Value - from.Value;
|
||||||
|
float alphaDiff = to.Alpha - from.Alpha;
|
||||||
|
|
||||||
|
return from + new ColorHSVA(hueDiff * t, saturationDiff * t, valueDiff * t, alphaDiff * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="ColorHSVA"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="ColorHSVA"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSVA"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSVA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="ColorHSVA"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorHSVA"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorHSVA"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorHSVA)}({Hue}, {Saturation}, {Value})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ColorHSVA"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorHSVAExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Add(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Add(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Add(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Subtract(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Subtract(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Subtract(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Multiply(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Multiply(this ColorHSVA color, float value) => ColorHSVA.Multiply(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Divide(ColorHSVA, ColorHSVA)" />
|
||||||
|
public static ColorHSVA Divide(this ColorHSVA color, float value) => ColorHSVA.Divide(color, value);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.Lerp(ColorHSVA, ColorHSVA, float)" />
|
||||||
|
public static ColorHSVA Lerp(this ColorHSVA from, ColorHSVA to, float t) => ColorHSVA.Lerp(from, to, t);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ColorHSVA.ApproximatelyEquals(ColorHSVA, ColorHSVA, float) " />
|
||||||
|
public static bool ApproximatelyEquals(this ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon) => ColorHSVA.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
@ -38,30 +38,8 @@ public readonly struct ColorRGB(byte r, byte g, byte b)
|
|||||||
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
|
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
|
||||||
|
|
||||||
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
|
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
|
||||||
public static implicit operator ColorRGB(ColorHSV hsv)
|
public static implicit operator ColorRGB(ColorHSVA hsva) => (ColorRGBA)hsva;
|
||||||
{
|
public static implicit operator ColorRGB(ColorHSV hsv) => (ColorRGBA)hsv;
|
||||||
float hue = hsv.Hue * 360f;
|
|
||||||
float chroma = hsv.Value * hsv.Saturation;
|
|
||||||
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
|
|
||||||
float m = hsv.Value - chroma;
|
|
||||||
|
|
||||||
float r1 = 0f;
|
|
||||||
float g1 = 0f;
|
|
||||||
float b1 = 0f;
|
|
||||||
|
|
||||||
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
|
|
||||||
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
|
|
||||||
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
|
|
||||||
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
|
|
||||||
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
|
|
||||||
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
|
|
||||||
|
|
||||||
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
|
|
||||||
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
|
|
||||||
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
|
|
||||||
|
|
||||||
return new(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inverts the given <see cref="ColorRGB"/>.
|
/// Inverts the given <see cref="ColorRGB"/>.
|
||||||
@ -102,14 +80,6 @@ public readonly struct ColorRGB(byte r, byte g, byte b)
|
|||||||
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
|
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
|
||||||
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
|
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the <see cref="ColorRGB"/> from one point to another.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="from">The starting point.</param>
|
|
||||||
/// <param name="to">The ending point.</param>
|
|
||||||
/// <returns>The <see cref="ColorRGB"/> from the starting point to the ending point.</returns>
|
|
||||||
public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
|
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -117,26 +87,33 @@ public readonly struct ColorRGB(byte r, byte g, byte b)
|
|||||||
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
|
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
|
||||||
/// <param name="t">The interpolation parameter.</param>
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
|
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
|
||||||
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) => from + FromTo(from, to) * t;
|
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t)
|
||||||
|
{
|
||||||
|
int redDiff = to.R - from.R;
|
||||||
|
int greenDiff = to.G - from.G;
|
||||||
|
int blueDiff = to.B - from.B;
|
||||||
|
|
||||||
/// <summary>
|
return from + new ColorRGB((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t));
|
||||||
/// Converts the <see cref="ColorRGB"/> to its string representation.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
|
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorRGB objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B);
|
public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
|
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
|
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorRGB"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -156,9 +133,6 @@ public static class ColorRGBExtensions
|
|||||||
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
|
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
|
||||||
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
|
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorRGB.FromTo(ColorRGB, ColorRGB)" />
|
|
||||||
public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to);
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
|
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
|
||||||
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
|
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,31 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
|||||||
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
|
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
|
||||||
|
|
||||||
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
|
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
|
||||||
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorRGB)hsv;
|
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorHSVA)hsv;
|
||||||
|
public static implicit operator ColorRGBA(ColorHSVA hsva)
|
||||||
|
{
|
||||||
|
float hue = hsva.Hue * 360f;
|
||||||
|
float chroma = hsva.Value * hsva.Saturation;
|
||||||
|
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
|
||||||
|
float m = hsva.Value - chroma;
|
||||||
|
|
||||||
|
float r1 = 0f;
|
||||||
|
float g1 = 0f;
|
||||||
|
float b1 = 0f;
|
||||||
|
|
||||||
|
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
|
||||||
|
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
|
||||||
|
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
|
||||||
|
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
|
||||||
|
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
|
||||||
|
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
|
||||||
|
|
||||||
|
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
|
||||||
|
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
|
||||||
|
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
|
||||||
|
|
||||||
|
return new(r, g, b, (byte)(hsva.Alpha * 255));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inverts the given <see cref="ColorRGBA"/>.
|
/// Inverts the given <see cref="ColorRGBA"/>.
|
||||||
@ -85,14 +109,6 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
|||||||
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
||||||
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
|
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the <see cref="ColorRGBA"/> from one point to another.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="from">The starting point.</param>
|
|
||||||
/// <param name="to">The ending point.</param>
|
|
||||||
/// <returns>The <see cref="ColorRGBA"/> from the starting point to the ending point.</returns>
|
|
||||||
public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
|
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -100,26 +116,34 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
|||||||
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
|
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
|
||||||
/// <param name="t">The interpolation parameter.</param>
|
/// <param name="t">The interpolation parameter.</param>
|
||||||
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
|
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
|
||||||
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) => from + FromTo(from, to) * t;
|
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t)
|
||||||
|
{
|
||||||
|
int redDiff = to.R - from.R;
|
||||||
|
int greenDiff = to.G - from.G;
|
||||||
|
int blueDiff = to.B - from.B;
|
||||||
|
int alphaDiff = to.A - from.A;
|
||||||
|
|
||||||
/// <summary>
|
return from + new ColorRGBA((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t), (byte)(alphaDiff * t));
|
||||||
/// Converts the <see cref="ColorRGBA"/> to its string representation.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
|
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorRGBA objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A);
|
public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
|
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
|
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="ColorRGBA"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -139,9 +163,6 @@ public static class ColorRGBAExtensions
|
|||||||
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
|
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
|
||||||
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
|
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorRGBA.FromTo(ColorRGBA, ColorRGBA)" />
|
|
||||||
public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to);
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
|
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
|
||||||
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
|
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,9 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||||
|
|
||||||
|
public static bool operator ==(Line2D left, Line2D right) => left.From == right.From && left.To == right.To;
|
||||||
|
public static bool operator !=(Line2D left, Line2D right) => left.From != right.From || left.To != right.To;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -186,6 +189,25 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// <returns><see cref="true"/> if the <see cref="Line2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the <see cref="Line2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon)
|
||||||
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
|
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Line2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Line2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Line2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Line2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(From, To);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Line2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Line2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Line2D)}({From}, {To})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,47 +1,69 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a line equation in the form y = mx + b.
|
/// Represents a <see cref="Line2DEquation"/> in the form y = mx + b.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slope">The slope of the line.</param>
|
/// <param name="slope">The slope of the line.</param>
|
||||||
/// <param name="offsetY">The y-intercept of the line.</param>
|
/// <param name="offsetY">The Y intercept of the line.</param>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and y-intercept.
|
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and Y intercept.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||||
public readonly struct Line2DEquation(float slope, float offsetY)
|
public readonly struct Line2DEquation(float slope, float offsetY)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The slope of the line equation.
|
/// The slope of the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float Slope = slope;
|
public readonly float Slope = slope;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The y-intercept of the line equation.
|
/// The Y intercept of the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float OffsetY = offsetY;
|
public readonly float OffsetY = offsetY;
|
||||||
|
|
||||||
|
public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY;
|
||||||
|
public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
/// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
/// <param name="lineEquation">The <see cref="Line2DEquation"/> to resolve.</param>
|
||||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
/// <param name="x">The X coordinate for which to resolve the Y coordinate.</param>
|
||||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
/// <returns>The Y coordinate resolved using the <see cref="Line2DEquation"/>.</returns>
|
||||||
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two line equations are approximately equal.
|
/// Checks if two <see cref="Line2DEquation"/> are approximately equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="left">The first line equation to compare.</param>
|
/// <param name="left">The first <see cref="Line2DEquation"/> to compare.</param>
|
||||||
/// <param name="right">The second line equation to compare.</param>
|
/// <param name="right">The second <see cref="Line2DEquation"/> to compare.</param>
|
||||||
/// <param name="epsilon">The epsilon range.</param>
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
/// <returns>True if the <see cref="Line2DEquation"/> are approximately equal; otherwise, false.</returns>
|
||||||
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
|
||||||
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
|
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Line2DEquation"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Line2DEquation"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2DEquation"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Line2DEquation"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Line2DEquation"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Slope, OffsetY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Line2DEquation"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Line2DEquation"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Line2DEquation)}({Slope}, {OffsetY})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides extension methods for the LineEquation struct.
|
/// Provides extension methods for the <see cref="Line2DEquation"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Line2DEquationExtensions
|
public static class Line2DEquationExtensions
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,9 @@ public readonly struct Projection1D(float min, float max)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly float Max = max;
|
public readonly float Max = max;
|
||||||
|
|
||||||
|
public static bool operator ==(Projection1D left, Projection1D right) => left.Min == right.Min && left.Max == right.Max;
|
||||||
|
public static bool operator !=(Projection1D left, Projection1D right) => left.Min != right.Min || left.Max != right.Max;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if two projections overlap.
|
/// Checks if two projections overlap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -70,6 +73,35 @@ public readonly struct Projection1D(float min, float max)
|
|||||||
depth = 0f;
|
depth = 0f;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Projection1D"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Projection1D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Projection1D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Projection1D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Projection1D left, Projection1D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Min.ApproximatelyEquals(right.Min, epsilon) && left.Max.ApproximatelyEquals(right.Max, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Projection1D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Projection1D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Projection1D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Projection1D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Projection1D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Min, Max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Projection1D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Projection1D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Projection1D)}({Min}, {Max})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -82,4 +114,7 @@ public static class Projection1DExtensions
|
|||||||
|
|
||||||
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
|
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
|
||||||
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
|
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Projection1D.ApproximatelyEquals(Projection1D, Projection1D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Projection1D left, Projection1D right, float epsilon = float.Epsilon) => Projection1D.ApproximatelyEquals(left, right, epsilon);
|
||||||
}
|
}
|
||||||
|
@ -282,24 +282,24 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Quaternion"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Quaternion"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Quaternion objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z) && W.Equals(objVec.W);
|
public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Quaternion"/>.
|
/// Generates a hash code for the <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
|
public override int GetHashCode() => System.HashCode.Combine(W, X, Y, Z);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Quaternion"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an infinite ray in 2D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Origin">The <see cref="Vector2D"/> in 2D space where the ray starts from.</param>
|
||||||
|
/// <param name="Direction">Normalized <see cref="Vector2D"/> indicating the ray's is direction.</param>
|
||||||
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -17,6 +22,8 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Ray2D Reversed => new(Origin, -Direction);
|
public readonly Ray2D Reversed => new(Origin, -Direction);
|
||||||
|
|
||||||
|
public static bool operator ==(Ray2D left, Ray2D right) => left.Origin == right.Origin && left.Direction == right.Direction;
|
||||||
|
public static bool operator !=(Ray2D left, Ray2D right) => left.Origin != right.Origin || left.Direction != right.Direction;
|
||||||
public static implicit operator Ray2D(Line2D line) => new(line.From, line.From.FromTo(line.To).Normalized);
|
public static implicit operator Ray2D(Line2D line) => new(line.From, line.From.FromTo(line.To).Normalized);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -48,6 +55,35 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
|||||||
|
|
||||||
return ray.Origin + ray.Direction * dot;
|
return ray.Origin + ray.Direction * dot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Ray2D"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Ray2D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Ray2D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Ray2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Ray2D left, Ray2D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Origin.ApproximatelyEquals(right.Origin, epsilon) && left.Direction.ApproximatelyEquals(right.Direction, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Ray2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Ray2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Ray2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Ray2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Origin, Direction);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Ray2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Ray2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Ray2D)}({Origin}, {Direction})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -63,4 +99,7 @@ public static class Ray2DExtensions
|
|||||||
|
|
||||||
/// <inheritdoc cref="Ray2D.ClosestPointTo(Ray2D, Vector2D) />
|
/// <inheritdoc cref="Ray2D.ClosestPointTo(Ray2D, Vector2D) />
|
||||||
public static Vector2D ClosestPointTo(this Ray2D ray, Vector2D point) => Ray2D.ClosestPointTo(ray, point);
|
public static Vector2D ClosestPointTo(this Ray2D ray, Vector2D point) => Ray2D.ClosestPointTo(ray, point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray2D.ApproximatelyEquals(Ray2D, Ray2D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Ray2D left, Ray2D right, float epsilon = float.Epsilon) => Ray2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
|||||||
triangles.Clear();
|
triangles.Clear();
|
||||||
|
|
||||||
for (int i = 2; i < shape.Vertices.Count; i++)
|
for (int i = 2; i < shape.Vertices.Count; i++)
|
||||||
triangles.Add(new Triangle(shape[0], shape[i - 1], shape[i]));
|
triangles.Add(new Triangle(shape[0], shape[i], shape[i - 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -251,6 +251,34 @@ public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Shape2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Shape2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Shape2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Shape2D shape2D && _vertices.Equals(shape2D._vertices);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Shape2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Shape2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Vertices);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Shape2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Shape2D"/>.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
System.Text.StringBuilder stringBuilder = new(Vertices[0].ToString());
|
||||||
|
for (int i = 1; i < Vertices.Count; i++)
|
||||||
|
{
|
||||||
|
stringBuilder.Append(", ");
|
||||||
|
stringBuilder.Append(Vertices[i].ToString());
|
||||||
|
}
|
||||||
|
return $"{nameof(Shape2D)}({stringBuilder})";
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||||
|
|
||||||
@ -270,7 +298,7 @@ public static class Shape2DExtensions
|
|||||||
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
|
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D, IList{Triangle})" />
|
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D, IList{Triangle})" />
|
||||||
public static void ToTrianglesConvex(this Shape2D shape, IList<Triangle> lines) => Shape2D.TriangulateConvex(shape, lines);
|
public static void ToTrianglesConvex(this Shape2D shape, IList<Triangle> triangles) => Shape2D.TriangulateConvex(shape, triangles);
|
||||||
|
|
||||||
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D)" />
|
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D)" />
|
||||||
public static List<Triangle> ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape);
|
public static List<Triangle> ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape);
|
||||||
|
@ -7,6 +7,9 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
public readonly Vector2D B { get; init; } = B;
|
public readonly Vector2D B { get; init; } = B;
|
||||||
public readonly Vector2D C { get; init; } = C;
|
public readonly Vector2D C { get; init; } = C;
|
||||||
|
|
||||||
|
public static bool operator ==(Triangle left, Triangle right) => left.A == right.A && left.B == right.B && left.C == right.C;
|
||||||
|
public static bool operator !=(Triangle left, Triangle right) => left.A != right.A || left.B != right.B || left.C != right.C;
|
||||||
|
|
||||||
public readonly float Area
|
public readonly float Area
|
||||||
=> .5f * Math.Abs(
|
=> .5f * Math.Abs(
|
||||||
A.X * (B.Y - C.Y) +
|
A.X * (B.Y - C.Y) +
|
||||||
@ -44,6 +47,25 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
|
||||||
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
|
||||||
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
|
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Triangle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Triangle"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Triangle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Triangle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Triangle"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(A, B, C);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Triangle"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Triangle"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Triangle)}({A}, {B}, {C})";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TriangleExtensions
|
public static class TriangleExtensions
|
||||||
|
@ -302,24 +302,24 @@ public readonly struct Vector2D(float x, float y)
|
|||||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Vector2D"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Vector2D"/>.
|
/// Generates a hash code for the <see cref="Vector2D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(X, Y);
|
public override int GetHashCode() => System.HashCode.Combine(X, Y);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Vector2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -271,24 +271,24 @@ public readonly struct Vector3D(float x, float y, float z)
|
|||||||
public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon)
|
public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon)
|
||||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon);
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="Vector3D"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="Vector3D"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified object is equal to the current <see cref="Vector3D"/>.
|
/// Determines whether the specified object is equal to the current <see cref="Vector3D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Vector3D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z);
|
public override bool Equals(object? obj) => obj is Vector3D vector3D && this == vector3D;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Vector3D"/>.
|
/// Generates a hash code for the <see cref="Vector3D"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
|
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
|
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Vector3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Vector3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
6
Engine.Core/Systems/Abstract/IEnterUniverse.cs
Normal file
6
Engine.Core/Systems/Abstract/IEnterUniverse.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface IEnterUniverse : IBehaviour
|
||||||
|
{
|
||||||
|
void EnterUniverse(IUniverse universe);
|
||||||
|
}
|
6
Engine.Core/Systems/Abstract/IExitUniverse.cs
Normal file
6
Engine.Core/Systems/Abstract/IExitUniverse.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface IExitUniverse : IBehaviour
|
||||||
|
{
|
||||||
|
void ExitUniverse(IUniverse universe);
|
||||||
|
}
|
6
Engine.Core/Systems/Abstract/ILastFrameUpdate.cs
Normal file
6
Engine.Core/Systems/Abstract/ILastFrameUpdate.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public interface ILastFrameUpdate : IBehaviour
|
||||||
|
{
|
||||||
|
void LastActiveFrame();
|
||||||
|
}
|
71
Engine.Core/Systems/UniverseEntranceManager.cs
Normal file
71
Engine.Core/Systems/UniverseEntranceManager.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public class UniverseEntranceManager : Behaviour
|
||||||
|
{
|
||||||
|
// We use Ascending order because we are using reverse for loop to call them
|
||||||
|
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
|
private readonly ActiveBehaviourCollectorSorted<IEnterUniverse> enterUniverses = new() { SortBy = SortByAscendingPriority() };
|
||||||
|
private readonly ActiveBehaviourCollectorSorted<IExitUniverse> exitUniverses = new() { SortBy = SortByAscendingPriority() };
|
||||||
|
|
||||||
|
private readonly List<IEnterUniverse> toCallEnterUniverses = new(32);
|
||||||
|
private readonly List<IExitUniverse> toCallExitUniverses = new(32);
|
||||||
|
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
enterUniverses.Assign(universe);
|
||||||
|
|
||||||
|
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||||
|
OnUniverseObjectRegistered(Universe, new(universeObject));
|
||||||
|
|
||||||
|
universe.OnUniverseObjectRegistered.AddListener(OnUniverseObjectRegistered);
|
||||||
|
universe.OnUniverseObjectUnRegistered.AddListener(OnUniverseObjectUnRegistered);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
enterUniverses.Unassign();
|
||||||
|
|
||||||
|
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||||
|
OnUniverseObjectUnRegistered(Universe, new(universeObject));
|
||||||
|
|
||||||
|
universe.OnUniverseObjectRegistered.RemoveListener(OnUniverseObjectRegistered);
|
||||||
|
universe.OnUniverseObjectUnRegistered.RemoveListener(OnUniverseObjectUnRegistered);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectUnRegistered(IUniverse sender, IUniverse.UniverseObjectUnRegisteredArguments args)
|
||||||
|
{
|
||||||
|
for (int i = toCallExitUniverses.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
toCallExitUniverses[i].ExitUniverse(Universe);
|
||||||
|
toCallExitUniverses.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectRegistered(IUniverse sender, IUniverse.UniverseObjectRegisteredArguments args)
|
||||||
|
{
|
||||||
|
for (int i = toCallEnterUniverses.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
toCallEnterUniverses[i].EnterUniverse(Universe);
|
||||||
|
toCallEnterUniverses.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnterUniverseCollected(IBehaviourCollector<IEnterUniverse> sender, IBehaviourCollector<IEnterUniverse>.BehaviourCollectedArguments args)
|
||||||
|
{
|
||||||
|
toCallEnterUniverses.Add(args.BehaviourCollected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExitUniverseCollected(IBehaviourCollector<IExitUniverse> sender, IBehaviourCollector<IExitUniverse>.BehaviourCollectedArguments args)
|
||||||
|
{
|
||||||
|
toCallExitUniverses.Add(args.BehaviourCollected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniverseEntranceManager()
|
||||||
|
{
|
||||||
|
enterUniverses.OnCollected.AddListener(OnEnterUniverseCollected);
|
||||||
|
exitUniverses.OnCollected.AddListener(OnExitUniverseCollected);
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,11 @@ namespace Syntriax.Engine.Core;
|
|||||||
|
|
||||||
public class UpdateManager : Behaviour
|
public class UpdateManager : Behaviour
|
||||||
{
|
{
|
||||||
// We use Ascending order because draw calls are running from last to first
|
// We use Ascending order because we are using reverse for loop to call them
|
||||||
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
private readonly ActiveBehaviourCollectorSorted<IFirstFrameUpdate> firstFrameUpdates = new() { SortBy = SortByAscendingPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IFirstFrameUpdate> firstFrameUpdates = new() { SortBy = SortByAscendingPriority() };
|
||||||
|
private readonly ActiveBehaviourCollector<ILastFrameUpdate> lastFrameUpdates = new();
|
||||||
private readonly ActiveBehaviourCollectorSorted<IPreUpdate> preUpdateEntities = new() { SortBy = SortByAscendingPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IPreUpdate> preUpdateEntities = new() { SortBy = SortByAscendingPriority() };
|
||||||
private readonly ActiveBehaviourCollectorSorted<IUpdate> updateEntities = new() { SortBy = SortByAscendingPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IUpdate> updateEntities = new() { SortBy = SortByAscendingPriority() };
|
||||||
private readonly ActiveBehaviourCollectorSorted<IPostUpdate> postUpdateEntities = new() { SortBy = SortByAscendingPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IPostUpdate> postUpdateEntities = new() { SortBy = SortByAscendingPriority() };
|
||||||
@ -17,6 +18,7 @@ public class UpdateManager : Behaviour
|
|||||||
protected override void OnEnteredUniverse(IUniverse universe)
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
{
|
{
|
||||||
firstFrameUpdates.Assign(universe);
|
firstFrameUpdates.Assign(universe);
|
||||||
|
lastFrameUpdates.Assign(universe);
|
||||||
preUpdateEntities.Assign(universe);
|
preUpdateEntities.Assign(universe);
|
||||||
updateEntities.Assign(universe);
|
updateEntities.Assign(universe);
|
||||||
postUpdateEntities.Assign(universe);
|
postUpdateEntities.Assign(universe);
|
||||||
@ -29,6 +31,7 @@ public class UpdateManager : Behaviour
|
|||||||
protected override void OnExitedUniverse(IUniverse universe)
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
{
|
{
|
||||||
firstFrameUpdates.Unassign();
|
firstFrameUpdates.Unassign();
|
||||||
|
lastFrameUpdates.Unassign();
|
||||||
preUpdateEntities.Unassign();
|
preUpdateEntities.Unassign();
|
||||||
updateEntities.Unassign();
|
updateEntities.Unassign();
|
||||||
postUpdateEntities.Unassign();
|
postUpdateEntities.Unassign();
|
||||||
@ -67,8 +70,14 @@ public class UpdateManager : Behaviour
|
|||||||
toCallFirstFrameUpdates.Add(args.BehaviourCollected);
|
toCallFirstFrameUpdates.Add(args.BehaviourCollected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnLastFrameRemoved(IBehaviourCollector<ILastFrameUpdate> sender, IBehaviourCollector<ILastFrameUpdate>.BehaviourRemovedArguments args)
|
||||||
|
{
|
||||||
|
args.BehaviourRemoved.LastActiveFrame();
|
||||||
|
}
|
||||||
|
|
||||||
public UpdateManager()
|
public UpdateManager()
|
||||||
{
|
{
|
||||||
firstFrameUpdates.OnCollected.AddListener(OnFirstFrameCollected);
|
firstFrameUpdates.OnCollected.AddListener(OnFirstFrameCollected);
|
||||||
|
lastFrameUpdates.OnRemoved.AddListener(OnLastFrameRemoved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ public class Universe : BaseEntity, IUniverse
|
|||||||
public Event<IUniverse> OnPreDraw { get; } = new();
|
public Event<IUniverse> OnPreDraw { get; } = new();
|
||||||
public Event<IUniverse> OnDraw { get; } = new();
|
public Event<IUniverse> OnDraw { get; } = new();
|
||||||
public Event<IUniverse> OnPostDraw { get; } = new();
|
public Event<IUniverse> OnPostDraw { get; } = new();
|
||||||
|
public Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments> OnPreUniverseObjectRegistered { get; } = new();
|
||||||
public Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; } = new();
|
public Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments> OnUniverseObjectRegistered { get; } = new();
|
||||||
|
public Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments> OnPreUniverseObjectUnRegistered { get; } = new();
|
||||||
public Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments> OnUniverseObjectUnRegistered { get; } = new();
|
public Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments> OnUniverseObjectUnRegistered { get; } = new();
|
||||||
public Event<IUniverse, IUniverse.TimeScaleChangedArguments> OnTimeScaleChanged { get; } = new();
|
public Event<IUniverse, IUniverse.TimeScaleChangedArguments> OnTimeScaleChanged { get; } = new();
|
||||||
|
|
||||||
@ -53,6 +55,8 @@ public class Universe : BaseEntity, IUniverse
|
|||||||
if (_universeObjects.Contains(universeObject))
|
if (_universeObjects.Contains(universeObject))
|
||||||
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}.");
|
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}.");
|
||||||
|
|
||||||
|
OnPreUniverseObjectRegistered?.Invoke(this, new(universeObject));
|
||||||
|
|
||||||
universeObject.OnFinalized.AddListener(delegateOnUniverseObjectFinalize);
|
universeObject.OnFinalized.AddListener(delegateOnUniverseObjectFinalize);
|
||||||
universeObject.OnExitedUniverse.AddListener(delegateOnUniverseObjectExitedUniverse);
|
universeObject.OnExitedUniverse.AddListener(delegateOnUniverseObjectExitedUniverse);
|
||||||
|
|
||||||
@ -79,7 +83,7 @@ public class Universe : BaseEntity, IUniverse
|
|||||||
|
|
||||||
public void Remove(IUniverseObject universeObject)
|
public void Remove(IUniverseObject universeObject)
|
||||||
{
|
{
|
||||||
universeObject.SetParent(null);
|
universeObject.Parent = null;
|
||||||
RemoveIncursive(universeObject);
|
RemoveIncursive(universeObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +92,8 @@ public class Universe : BaseEntity, IUniverse
|
|||||||
if (!_universeObjects.Contains(universeObject))
|
if (!_universeObjects.Contains(universeObject))
|
||||||
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}.");
|
throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}.");
|
||||||
|
|
||||||
|
OnPreUniverseObjectUnRegistered?.Invoke(this, new(universeObject));
|
||||||
|
|
||||||
universeObject.OnFinalized.RemoveListener(delegateOnUniverseObjectFinalize);
|
universeObject.OnFinalized.RemoveListener(delegateOnUniverseObjectFinalize);
|
||||||
universeObject.OnExitedUniverse.RemoveListener(delegateOnUniverseObjectExitedUniverse);
|
universeObject.OnExitedUniverse.RemoveListener(delegateOnUniverseObjectExitedUniverse);
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
@ -21,8 +20,8 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
private IBehaviourController _behaviourController = null!;
|
private IBehaviourController _behaviourController = null!;
|
||||||
private bool _isActive = false;
|
private bool _isActive = false;
|
||||||
private readonly List<IUniverseObject> _children = [];
|
private readonly List<IUniverseObject> _children = [];
|
||||||
|
private IUniverseObject? _parent = null;
|
||||||
|
|
||||||
public IUniverseObject? Parent { get; private set; } = null;
|
|
||||||
public IReadOnlyList<IUniverseObject> Children => _children;
|
public IReadOnlyList<IUniverseObject> Children => _children;
|
||||||
public IBehaviourController BehaviourController => _behaviourController;
|
public IBehaviourController BehaviourController => _behaviourController;
|
||||||
public IUniverse Universe => _universe;
|
public IUniverse Universe => _universe;
|
||||||
@ -42,6 +41,40 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IUniverseObject? Parent
|
||||||
|
{
|
||||||
|
get => _parent;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == this)
|
||||||
|
throw new Exceptions.AssignFailedException($"{Name} can not parent itself");
|
||||||
|
|
||||||
|
if (_parent == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IUniverseObject? previousParent = Parent;
|
||||||
|
if (previousParent is not null)
|
||||||
|
{
|
||||||
|
previousParent.RemoveChild(this);
|
||||||
|
previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
_parent = value;
|
||||||
|
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
if (value.IsInUniverse && !IsInUniverse)
|
||||||
|
value.Universe.Register(this);
|
||||||
|
|
||||||
|
value.AddChild(this);
|
||||||
|
value.OnActiveChanged.AddListener(OnParentActiveChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateActive();
|
||||||
|
OnParentChanged?.Invoke(this, new(previousParent, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void OnEnteringUniverse(IUniverse universe) { }
|
protected virtual void OnEnteringUniverse(IUniverse universe) { }
|
||||||
bool IUniverseObject.EnterUniverse(IUniverse universe)
|
bool IUniverseObject.EnterUniverse(IUniverse universe)
|
||||||
{
|
{
|
||||||
@ -67,43 +100,13 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetParent(IUniverseObject? parent)
|
|
||||||
{
|
|
||||||
if (parent == this)
|
|
||||||
throw new Exceptions.AssignFailedException($"{Name} can not parent itself");
|
|
||||||
|
|
||||||
if (Parent == parent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IUniverseObject? previousParent = Parent;
|
|
||||||
if (previousParent is not null)
|
|
||||||
{
|
|
||||||
previousParent.RemoveChild(this);
|
|
||||||
previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
Parent = parent;
|
|
||||||
|
|
||||||
if (parent is not null)
|
|
||||||
{
|
|
||||||
if (parent.IsInUniverse && !IsInUniverse)
|
|
||||||
parent.Universe.Register(this);
|
|
||||||
|
|
||||||
parent.AddChild(this);
|
|
||||||
parent.OnActiveChanged.AddListener(OnParentActiveChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateActive();
|
|
||||||
OnParentChanged?.Invoke(this, new(previousParent, parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddChild(IUniverseObject parent)
|
public void AddChild(IUniverseObject parent)
|
||||||
{
|
{
|
||||||
if (_children.Contains(parent))
|
if (_children.Contains(parent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_children.Add(parent);
|
_children.Add(parent);
|
||||||
parent.SetParent(this);
|
parent.Parent = this;
|
||||||
OnChildrenAdded?.Invoke(this, new(parent));
|
OnChildrenAdded?.Invoke(this, new(parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +115,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
if (!_children.Remove(child))
|
if (!_children.Remove(child))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
child.SetParent(null);
|
child.Parent = null;
|
||||||
OnChildrenRemoved?.Invoke(this, new(child));
|
OnChildrenRemoved?.Invoke(this, new(child));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +136,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
base.OnAssign(stateEnable);
|
base.OnAssign(stateEnable);
|
||||||
|
|
||||||
stateEnable.OnEnabledChanged.AddListener(OnStateEnabledChanged);
|
stateEnable.OnEnabledChanged.AddListener(OnStateEnabledChanged);
|
||||||
|
UpdateActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnParentActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
|
private void OnParentActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
|
||||||
@ -164,7 +168,4 @@ public class UniverseObject : BaseEntity, IUniverseObject
|
|||||||
{
|
{
|
||||||
_name = GetType().Name;
|
_name = GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<IUniverseObject> GetEnumerator() => _children.GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.Xna.Framework.Content;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
public interface ILoadContent : IBehaviour
|
||||||
|
{
|
||||||
|
void LoadContent(ContentManager content);
|
||||||
|
}
|
@ -4,7 +4,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Integration.MonoGame;
|
namespace Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw
|
public class DrawableShape : Behaviour2D, IDrawableTriangle, IPreDraw
|
||||||
{
|
{
|
||||||
private readonly Shape2D shape = new([]);
|
private readonly Shape2D shape = new([]);
|
||||||
private readonly List<Triangle> worldTriangles = [];
|
private readonly List<Triangle> worldTriangles = [];
|
||||||
@ -23,7 +23,7 @@ public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw
|
|||||||
|
|
||||||
protected void UpdateWorldShape() => shape.Transform(Transform, worldShape);
|
protected void UpdateWorldShape() => shape.Transform(Transform, worldShape);
|
||||||
|
|
||||||
public DrawableShapeBehaviour() => shape = Shape2D.Triangle;
|
public DrawableShape() => shape = Shape2D.Triangle;
|
||||||
public DrawableShapeBehaviour(Shape2D shape) => this.shape = shape;
|
public DrawableShape(Shape2D shape) => this.shape = shape;
|
||||||
public DrawableShapeBehaviour(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; }
|
public DrawableShape(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; }
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ using Syntriax.Engine.Systems.Input;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Integration.MonoGame;
|
namespace Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
public class KeyboardInputsBehaviour : Behaviour, IButtonInputs<Keys>, IUpdate
|
public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
|
||||||
{
|
{
|
||||||
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
|
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
|
||||||
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
|
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
|
@ -0,0 +1,53 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
|
public class LoadContentManager : Behaviour, IFirstFrameUpdate
|
||||||
|
{
|
||||||
|
// We use Ascending order because we are using reverse for loop to call them
|
||||||
|
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
|
private readonly ActiveBehaviourCollectorSorted<ILoadContent> loadContents = new() { SortBy = SortByAscendingPriority() };
|
||||||
|
private readonly List<ILoadContent> toCallLoadContents = new(32);
|
||||||
|
|
||||||
|
private MonoGameWindowContainer monoGameWindowContainer = null!;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
monoGameWindowContainer = Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnEnteredUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
loadContents.Assign(universe);
|
||||||
|
|
||||||
|
universe.OnPreUpdate.AddListener(OnPreUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExitedUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
loadContents.Unassign();
|
||||||
|
|
||||||
|
universe.OnPreUpdate.RemoveListener(OnPreUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args)
|
||||||
|
{
|
||||||
|
for (int i = toCallLoadContents.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
toCallLoadContents[i].LoadContent(monoGameWindowContainer.Window.Content);
|
||||||
|
toCallLoadContents.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFirstFrameCollected(IBehaviourCollector<ILoadContent> sender, IBehaviourCollector<ILoadContent>.BehaviourCollectedArguments args)
|
||||||
|
{
|
||||||
|
toCallLoadContents.Add(args.BehaviourCollected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadContentManager()
|
||||||
|
{
|
||||||
|
loadContents.OnCollected.AddListener(OnFirstFrameCollected);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Integration.MonoGame;
|
namespace Syntriax.Engine.Integration.MonoGame;
|
||||||
|
|
||||||
public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
|
public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
|
||||||
{
|
{
|
||||||
public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null;
|
public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null;
|
||||||
public event ViewportChangedArguments? OnViewportChanged = null;
|
public event ViewportChangedArguments? OnViewportChanged = null;
|
||||||
@ -103,7 +103,7 @@ public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUp
|
|||||||
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
||||||
protected sealed override void FinalizeInternal() => Transform = null!;
|
protected sealed override void FinalizeInternal() => Transform = null!;
|
||||||
|
|
||||||
public delegate void MatrixTransformChangedArguments(MonoGameCamera2DBehaviour sender);
|
public delegate void MatrixTransformChangedArguments(MonoGameCamera2D sender);
|
||||||
public delegate void ViewportChangedArguments(MonoGameCamera2DBehaviour sender);
|
public delegate void ViewportChangedArguments(MonoGameCamera2D sender);
|
||||||
public delegate void ZoomChangedArguments(MonoGameCamera2DBehaviour sender);
|
public delegate void ZoomChangedArguments(MonoGameCamera2D sender);
|
||||||
}
|
}
|
@ -9,14 +9,14 @@ public class SpriteBatcher : BehaviourBase, IFirstFrameUpdate, IDraw
|
|||||||
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
|
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
|
||||||
|
|
||||||
private ISpriteBatch spriteBatch = null!;
|
private ISpriteBatch spriteBatch = null!;
|
||||||
private MonoGameCamera2DBehaviour camera2D = null!;
|
private MonoGameCamera2D camera2D = null!;
|
||||||
|
|
||||||
private readonly ActiveBehaviourCollectorSorted<IDrawableSprite> drawableSprites = new() { SortBy = SortByPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IDrawableSprite> drawableSprites = new() { SortBy = SortByPriority() };
|
||||||
|
|
||||||
public void FirstActiveFrame()
|
public void FirstActiveFrame()
|
||||||
{
|
{
|
||||||
MonoGameWindowContainer windowContainer = Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
MonoGameWindowContainer windowContainer = Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
||||||
camera2D = Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
|
camera2D = Universe.FindRequiredBehaviour<MonoGameCamera2D>();
|
||||||
|
|
||||||
spriteBatch = new SpriteBatchWrapper(windowContainer.Window.GraphicsDevice);
|
spriteBatch = new SpriteBatchWrapper(windowContainer.Window.GraphicsDevice);
|
||||||
drawableSprites.Unassign();
|
drawableSprites.Unassign();
|
||||||
|
@ -11,13 +11,13 @@ public class TriangleBatcher : BehaviourBase, ITriangleBatch, IFirstFrameUpdate,
|
|||||||
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
|
||||||
|
|
||||||
private TriangleBatch triangleBatch = null!;
|
private TriangleBatch triangleBatch = null!;
|
||||||
private MonoGameCamera2DBehaviour camera2D = null!;
|
private MonoGameCamera2D camera2D = null!;
|
||||||
private readonly ActiveBehaviourCollectorSorted<IDrawableTriangle> drawableShapes = new() { SortBy = SortByAscendingPriority() };
|
private readonly ActiveBehaviourCollectorSorted<IDrawableTriangle> drawableShapes = new() { SortBy = SortByAscendingPriority() };
|
||||||
|
|
||||||
public void FirstActiveFrame()
|
public void FirstActiveFrame()
|
||||||
{
|
{
|
||||||
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
||||||
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
|
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2D>();
|
||||||
|
|
||||||
triangleBatch = new(windowContainer.Window.GraphicsDevice);
|
triangleBatch = new(windowContainer.Window.GraphicsDevice);
|
||||||
drawableShapes.Unassign();
|
drawableShapes.Unassign();
|
||||||
|
@ -18,7 +18,16 @@ public static class EngineConverterExtensions
|
|||||||
public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y);
|
public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Color ToColor(this ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B, rgba.A);
|
public static Color ToColor(this ColorRGBA color) => new(color.R, color.G, color.B, color.A);
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Color ToPreMultipliedColor(this ColorRGBA color)
|
||||||
|
{
|
||||||
|
float alphaMultiplier = color.A / 255f;
|
||||||
|
return new((byte)(color.R * alphaMultiplier), (byte)(color.G * alphaMultiplier), (byte)(color.B * alphaMultiplier), color.A);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ColorRGBA ToColorRGBA(this Color color) => new(color.R, color.G, color.B, color.A);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y);
|
public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y);
|
||||||
|
@ -21,6 +21,9 @@ public class MonoGameWindow : Game
|
|||||||
|
|
||||||
Universe.InstantiateUniverseObject().SetUniverseObject("Window Container")
|
Universe.InstantiateUniverseObject().SetUniverseObject("Window Container")
|
||||||
.BehaviourController.AddBehaviour<MonoGameWindowContainer>(this);
|
.BehaviourController.AddBehaviour<MonoGameWindowContainer>(this);
|
||||||
|
|
||||||
|
Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader")
|
||||||
|
.BehaviourController.AddBehaviour<LoadContentManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Physics2D;
|
namespace Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
public abstract class Collider2DBase : Behaviour2D, ICollider2D
|
||||||
{
|
{
|
||||||
public Event<ICollider2D, CollisionDetectionInformation> OnCollisionDetected { get; } = new();
|
public Event<ICollider2D, CollisionDetectionInformation> OnCollisionDetected { get; } = new();
|
||||||
public Event<ICollider2D, CollisionDetectionInformation> OnCollisionResolved { get; } = new();
|
public Event<ICollider2D, CollisionDetectionInformation> OnCollisionResolved { get; } = new();
|
||||||
@ -18,7 +18,7 @@ public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
|||||||
protected bool NeedsRecalculation { get; set; } = true;
|
protected bool NeedsRecalculation { get; set; } = true;
|
||||||
protected IRigidBody2D? _rigidBody2D = null;
|
protected IRigidBody2D? _rigidBody2D = null;
|
||||||
|
|
||||||
protected Collider2DBehaviourBase()
|
protected Collider2DBase()
|
||||||
{
|
{
|
||||||
delegateOnBehaviourAddedToController = OnBehaviourAddedToController;
|
delegateOnBehaviourAddedToController = OnBehaviourAddedToController;
|
||||||
delegateOnBehaviourRemovedFromController = OnBehaviourRemovedFromController;
|
delegateOnBehaviourRemovedFromController = OnBehaviourRemovedFromController;
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Physics2D;
|
namespace Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D
|
public class Collider2DCircle : Collider2DBase, ICircleCollider2D
|
||||||
{
|
{
|
||||||
private Circle _circleLocal = Circle.UnitCircle;
|
private Circle _circleLocal = Circle.UnitCircle;
|
||||||
|
|
||||||
@ -19,6 +19,6 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide
|
|||||||
|
|
||||||
public override void CalculateCollider() => CircleWorld = Transform.Transform(_circleLocal);
|
public override void CalculateCollider() => CircleWorld = Transform.Transform(_circleLocal);
|
||||||
|
|
||||||
public Collider2DCircleBehaviour() { }
|
public Collider2DCircle() { }
|
||||||
public Collider2DCircleBehaviour(Circle circle) => CircleLocal = circle;
|
public Collider2DCircle(Circle circle) => CircleLocal = circle;
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Physics2D;
|
namespace Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D
|
public class Collider2DShape : Collider2DBase, IShapeCollider2D
|
||||||
{
|
{
|
||||||
public Shape2D ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
|
public Shape2D ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
|
||||||
public Shape2D ShapeLocal
|
public Shape2D ShapeLocal
|
||||||
@ -15,13 +15,13 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Shape2D _shapeWorld = Shape2D.Square.CreateCopy();
|
private Shape2D _shapeWorld = Shape2D.Square;
|
||||||
private Shape2D _shapeLocal = Shape2D.Square;
|
private Shape2D _shapeLocal = Shape2D.Square;
|
||||||
|
|
||||||
public override void CalculateCollider() => ShapeLocal.Transform(Transform, _shapeWorld);
|
public override void CalculateCollider() => ShapeLocal.Transform(Transform, _shapeWorld);
|
||||||
|
|
||||||
public Collider2DShapeBehaviour() { }
|
public Collider2DShape() { }
|
||||||
public Collider2DShapeBehaviour(Shape2D shape)
|
public Collider2DShape(Shape2D shape)
|
||||||
{
|
{
|
||||||
ShapeLocal = shape;
|
ShapeLocal = shape;
|
||||||
}
|
}
|
@ -25,11 +25,11 @@ public class PhysicsEngine2D : Behaviour, IPreUpdate, IPhysicsEngine2D
|
|||||||
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
|
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
|
||||||
protected BehaviourCollector<ICollider2D> colliderCollector = new();
|
protected BehaviourCollector<ICollider2D> colliderCollector = new();
|
||||||
|
|
||||||
private readonly ListPool<ICollider2D> colliderPool = new(() => new(32));
|
private readonly ListPool<ICollider2D> colliderPool = new();
|
||||||
private readonly ListPool<IPrePhysicsUpdate> prePhysicsUpdatePool = new(() => new(32));
|
private readonly ListPool<IPrePhysicsUpdate> prePhysicsUpdatePool = new();
|
||||||
private readonly ListPool<IPhysicsUpdate> physicsUpdatePool = new(() => new(32));
|
private readonly ListPool<IPhysicsUpdate> physicsUpdatePool = new();
|
||||||
private readonly ListPool<IPhysicsIteration> physicsIterationPool = new(() => new(32));
|
private readonly ListPool<IPhysicsIteration> physicsIterationPool = new();
|
||||||
private readonly ListPool<IPostPhysicsUpdate> postPhysicsUpdatePool = new(() => new(32));
|
private readonly ListPool<IPostPhysicsUpdate> postPhysicsUpdatePool = new();
|
||||||
|
|
||||||
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
|
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
|
||||||
public float IterationPeriod { get => _iterationPeriod; set => _iterationPeriod = value.Max(0.0001f); }
|
public float IterationPeriod { get => _iterationPeriod; set => _iterationPeriod = value.Max(0.0001f); }
|
||||||
|
@ -21,6 +21,12 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
|||||||
private readonly ICollisionResolver2D collisionResolver = null!;
|
private readonly ICollisionResolver2D collisionResolver = null!;
|
||||||
private readonly IRaycastResolver2D raycastResolver = null!;
|
private readonly IRaycastResolver2D raycastResolver = null!;
|
||||||
|
|
||||||
|
private readonly ListPool<ICollider2D> colliderPool = new();
|
||||||
|
private readonly ListPool<IPrePhysicsUpdate> prePhysicsUpdatePool = new();
|
||||||
|
private readonly ListPool<IPhysicsUpdate> physicsUpdatePool = new();
|
||||||
|
private readonly ListPool<IPhysicsIteration> physicsIterationPool = new();
|
||||||
|
private readonly ListPool<IPostPhysicsUpdate> postPhysicsUpdatePool = new();
|
||||||
|
|
||||||
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||||
|
|
||||||
public void AddRigidBody(IRigidBody2D rigidBody)
|
public void AddRigidBody(IRigidBody2D rigidBody)
|
||||||
@ -112,11 +118,11 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
|||||||
{
|
{
|
||||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||||
|
|
||||||
List<ICollider2D> childColliders = [];
|
List<ICollider2D> childColliders = colliderPool.Get();
|
||||||
List<IPrePhysicsUpdate> physicsPreUpdates = [];
|
List<IPrePhysicsUpdate> physicsPreUpdates = prePhysicsUpdatePool.Get();
|
||||||
List<IPhysicsUpdate> physicsUpdates = [];
|
List<IPhysicsUpdate> physicsUpdates = physicsUpdatePool.Get();
|
||||||
List<IPhysicsIteration> physicsIterations = [];
|
List<IPhysicsIteration> physicsIterations = physicsIterationPool.Get();
|
||||||
List<IPostPhysicsUpdate> physicsPostUpdates = [];
|
List<IPostPhysicsUpdate> physicsPostUpdates = postPhysicsUpdatePool.Get();
|
||||||
|
|
||||||
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
|
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
|
||||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
|
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
|
||||||
@ -160,6 +166,12 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
|||||||
|
|
||||||
for (int i = physicsPostUpdates.Count - 1; i >= 0; i--)
|
for (int i = physicsPostUpdates.Count - 1; i >= 0; i--)
|
||||||
physicsPostUpdates[i].PostPhysicsUpdate(deltaTime);
|
physicsPostUpdates[i].PostPhysicsUpdate(deltaTime);
|
||||||
|
|
||||||
|
colliderPool.Return(childColliders);
|
||||||
|
prePhysicsUpdatePool.Return(physicsPreUpdates);
|
||||||
|
physicsUpdatePool.Return(physicsUpdates);
|
||||||
|
physicsIterationPool.Return(physicsIterations);
|
||||||
|
postPhysicsUpdatePool.Return(physicsPostUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
|
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
|
||||||
|
@ -6,7 +6,7 @@ namespace Syntriax.Engine.Physics2D;
|
|||||||
|
|
||||||
public class RaycastResolver2D : IRaycastResolver2D
|
public class RaycastResolver2D : IRaycastResolver2D
|
||||||
{
|
{
|
||||||
private readonly ListPool<Line2D> lineCacheQueue = new(() => new(4));
|
private readonly ListPool<Line2D> lineCacheQueue = new(initialListCapacity: 4);
|
||||||
|
|
||||||
RaycastResult? IRaycastResolver2D.RaycastAgainst<T>(T shape, Ray2D ray, float length)
|
RaycastResult? IRaycastResolver2D.RaycastAgainst<T>(T shape, Ray2D ray, float length)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Systems.Time;
|
namespace Syntriax.Engine.Systems.Time;
|
||||||
|
|
||||||
public interface ITicker : IStopwatch
|
public interface ITicker
|
||||||
{
|
{
|
||||||
Event<ITicker> OnTick { get; }
|
Event<ITicker> OnTick { get; }
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Systems.Time;
|
namespace Syntriax.Engine.Systems.Time;
|
||||||
|
|
||||||
public class StopwatchBehaviour : Behaviour, IUpdate, IStopwatch
|
public class Stopwatch : Behaviour, IUpdate, IStopwatch
|
||||||
{
|
{
|
||||||
public Event<IReadOnlyStopwatch> OnStarted { get; } = new();
|
public Event<IReadOnlyStopwatch> OnStarted { get; } = new();
|
||||||
public Event<IReadOnlyStopwatch, IReadOnlyStopwatch.StopwatchDeltaArguments> OnDelta { get; } = new();
|
public Event<IReadOnlyStopwatch, IReadOnlyStopwatch.StopwatchDeltaArguments> OnDelta { get; } = new();
|
@ -2,14 +2,14 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Systems.Time;
|
namespace Syntriax.Engine.Systems.Time;
|
||||||
|
|
||||||
public class TickerBehaviour : StopwatchBehaviour, ITicker
|
public class TickerStopwatch : Stopwatch, ITicker
|
||||||
{
|
{
|
||||||
public Event<ITicker> OnTick { get; } = new();
|
public Event<ITicker> OnTick { get; } = new();
|
||||||
|
|
||||||
public double Period { get; set; } = 1f;
|
public double Period { get; set; } = 1f;
|
||||||
public int TickCounter { get; private set; } = 0;
|
public int TickCounter { get; private set; } = 0;
|
||||||
|
|
||||||
private double nextTick = 0f;
|
private double nextTick = double.MaxValue;
|
||||||
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
@ -22,7 +22,7 @@ public class TickerBehaviour : StopwatchBehaviour, ITicker
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
while (Time > nextTick)
|
while (Time >= nextTick)
|
||||||
{
|
{
|
||||||
nextTick += Period;
|
nextTick += Period;
|
||||||
TickCounter++;
|
TickCounter++;
|
40
Engine.Systems/Time/TickerTimer.cs
Normal file
40
Engine.Systems/Time/TickerTimer.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Systems.Time;
|
||||||
|
|
||||||
|
public class TickerTimer : Timer, ITicker
|
||||||
|
{
|
||||||
|
public Event<ITicker> OnTick { get; } = new();
|
||||||
|
|
||||||
|
public double Period { get; set; } = 1f;
|
||||||
|
public int TickCounter { get; private set; } = 0;
|
||||||
|
|
||||||
|
private double nextTick = double.MinValue;
|
||||||
|
|
||||||
|
public override void Start(double time)
|
||||||
|
{
|
||||||
|
TickCounter = 0;
|
||||||
|
base.Start(time);
|
||||||
|
nextTick = Remaining - Period;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
while (Remaining <= nextTick)
|
||||||
|
{
|
||||||
|
nextTick -= Period;
|
||||||
|
TickCounter++;
|
||||||
|
OnTick?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFinalize()
|
||||||
|
{
|
||||||
|
base.OnFinalize();
|
||||||
|
|
||||||
|
TickCounter = 0;
|
||||||
|
nextTick = 0f;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
|
|||||||
|
|
||||||
namespace Syntriax.Engine.Systems.Time;
|
namespace Syntriax.Engine.Systems.Time;
|
||||||
|
|
||||||
public class TimerBehaviour : Behaviour, IUpdate, ITimer
|
public class Timer : Behaviour, IUpdate, ITimer
|
||||||
{
|
{
|
||||||
public Event<IReadOnlyTimer> OnStarted { get; } = new();
|
public Event<IReadOnlyTimer> OnStarted { get; } = new();
|
||||||
public Event<IReadOnlyTimer, IReadOnlyTimer.TimerDeltaArguments> OnDelta { get; } = new();
|
public Event<IReadOnlyTimer, IReadOnlyTimer.TimerDeltaArguments> OnDelta { get; } = new();
|
||||||
@ -53,7 +53,7 @@ public class TimerBehaviour : Behaviour, IUpdate, ITimer
|
|||||||
OnStopped?.Invoke(this);
|
OnStopped?.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public virtual void Update()
|
||||||
{
|
{
|
||||||
if (State is not TimerState.Ticking)
|
if (State is not TimerState.Ticking)
|
||||||
return;
|
return;
|
@ -9,8 +9,8 @@ internal static class EaseConstants
|
|||||||
internal const float c1 = 1.70158f;
|
internal const float c1 = 1.70158f;
|
||||||
internal const float c2 = c1 * 1.525f;
|
internal const float c2 = c1 * 1.525f;
|
||||||
internal const float c3 = c1 + 1f;
|
internal const float c3 = c1 + 1f;
|
||||||
internal const float c4 = 2f * Math.PI / 3f;
|
internal const float c4 = 2f * Math.Pi / 3f;
|
||||||
internal const float c5 = 2f * Math.PI / 4.5f;
|
internal const float c5 = 2f * Math.Pi / 4.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class EasingBase<T> where T : IEasing, new() { public static readonly T Instance = new(); }
|
public abstract class EasingBase<T> where T : IEasing, new() { public static readonly T Instance = new(); }
|
||||||
@ -33,9 +33,9 @@ public class EaseInQuint : EasingBase<EaseInQuint>, IEasing { public float Evalu
|
|||||||
public class EaseOutQuint : EasingBase<EaseOutQuint>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 5f); }
|
public class EaseOutQuint : EasingBase<EaseOutQuint>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 5f); }
|
||||||
public class EaseInOutQuint : EasingBase<EaseInOutQuint>, IEasing { public float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 5f) * .5f; }
|
public class EaseInOutQuint : EasingBase<EaseInOutQuint>, IEasing { public float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 5f) * .5f; }
|
||||||
|
|
||||||
public class EaseInSine : EasingBase<EaseInSine>, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.PI * .5f); }
|
public class EaseInSine : EasingBase<EaseInSine>, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.Pi * .5f); }
|
||||||
public class EaseOutSine : EasingBase<EaseOutSine>, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.PI * .5f); }
|
public class EaseOutSine : EasingBase<EaseOutSine>, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.Pi * .5f); }
|
||||||
public class EaseInOutSine : EasingBase<EaseInOutSine>, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.PI * x) - 1f) * .5f; }
|
public class EaseInOutSine : EasingBase<EaseInOutSine>, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.Pi * x) - 1f) * .5f; }
|
||||||
|
|
||||||
public class EaseInExpo : EasingBase<EaseInExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : Math.Pow(2f, 10f * x - 10f); }
|
public class EaseInExpo : EasingBase<EaseInExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : Math.Pow(2f, 10f * x - 10f); }
|
||||||
public class EaseOutExpo : EasingBase<EaseOutExpo>, IEasing { public float Evaluate(float x) => x == 1f ? 1f : 1f - Math.Pow(2f, -10f * x); }
|
public class EaseOutExpo : EasingBase<EaseOutExpo>, IEasing { public float Evaluate(float x) => x == 1f ? 1f : 1f - Math.Pow(2f, -10f * x); }
|
||||||
|
@ -5,11 +5,14 @@ namespace Syntriax.Engine.Systems.Tween;
|
|||||||
public static class TweenColorExtensions
|
public static class TweenColorExtensions
|
||||||
{
|
{
|
||||||
public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action<ColorRGB> setMethod)
|
public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action<ColorRGB> setMethod)
|
||||||
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorRGB.Lerp(targetColorRGB, t)));
|
=> TweenColor((ColorHSV)initialColorRGB, tweenManager, duration, (ColorHSV)targetColorRGB, color => setMethod?.Invoke(color));
|
||||||
|
|
||||||
public static ITween TweenColor(this ColorRGBA initialColorRGBA, ITweenManager tweenManager, float duration, ColorRGBA targetColorRGBA, System.Action<ColorRGBA> setMethod)
|
public static ITween TweenColor(this ColorRGBA initialColorRGBA, ITweenManager tweenManager, float duration, ColorRGBA targetColorRGBA, System.Action<ColorRGBA> setMethod)
|
||||||
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorRGBA.Lerp(targetColorRGBA, t)));
|
=> TweenColor((ColorHSVA)initialColorRGBA, tweenManager, duration, (ColorHSVA)targetColorRGBA, color => setMethod?.Invoke(color));
|
||||||
|
|
||||||
public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action<ColorHSV> setMethod)
|
public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action<ColorHSV> setMethod)
|
||||||
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSV.Lerp(targetColorHSV, t)));
|
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSV.Lerp(targetColorHSV, t)));
|
||||||
|
|
||||||
|
public static ITween TweenColor(this ColorHSVA initialColorHSVA, ITweenManager tweenManager, float duration, ColorHSVA targetColorHSVA, System.Action<ColorHSVA> setMethod)
|
||||||
|
=> tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSVA.Lerp(targetColorHSVA, t)));
|
||||||
}
|
}
|
||||||
|
271
Engine.Tests/Core/BehaviourController_Tests.cs
Normal file
271
Engine.Tests/Core/BehaviourController_Tests.cs
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Engine.Tests;
|
||||||
|
|
||||||
|
public class BehaviourController_Tests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetRequiredBehaviour()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
Assert.NotNull(behaviourController.GetRequiredBehaviour<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetRequiredBehaviourRaisesExceptionIfNotFound()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
Assert.Throws<BehaviourNotFoundException>(behaviourController.GetRequiredBehaviour<BehaviourX>);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddSingleBehaviour()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
Assert.NotNull(behaviourController.AddBehaviour<BehaviourX>());
|
||||||
|
Assert.NotNull(behaviourController.GetBehaviour<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMultipleBehaviours()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
BehaviourX behaviourX = behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
BehaviourY behaviourY = behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
|
||||||
|
bool isXFound = false;
|
||||||
|
bool isYFound = false;
|
||||||
|
|
||||||
|
foreach (IBehaviour behaviour in behaviourController.GetBehaviours<IBehaviour>())
|
||||||
|
{
|
||||||
|
if (behaviour == behaviourX) isXFound = true;
|
||||||
|
if (behaviour == behaviourY) isYFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.True(isXFound && isYFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddThenRemoveBehaviour()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
behaviourController.RemoveBehaviour<BehaviourX>();
|
||||||
|
Assert.Null(behaviourController.GetBehaviour<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetOrAddBehaviourExtension()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
Assert.Null(behaviourController.GetBehaviour<BehaviourX>());
|
||||||
|
Assert.NotNull(behaviourController.GetOrAddBehaviour<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetOrAddBehaviourExtensionFallback()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
Assert.Null(behaviourController.GetBehaviour<IBehaviour>());
|
||||||
|
Assert.NotNull(behaviourController.GetOrAddBehaviour<IBehaviour, BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveSpecificBehaviourViaGeneric()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
behaviourController.RemoveBehaviour<BehaviourY>();
|
||||||
|
|
||||||
|
Assert.NotNull(behaviourController.GetBehaviour<BehaviourX>());
|
||||||
|
Assert.Null(behaviourController.GetBehaviour<BehaviourY>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveSpecificBehaviourViaReference()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
|
||||||
|
BehaviourY behaviourYFirst = behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
BehaviourY behaviourYSecond = behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
|
||||||
|
behaviourController.RemoveBehaviour(behaviourYFirst);
|
||||||
|
|
||||||
|
Assert.Equal(behaviourController.GetBehaviour<BehaviourY>(), behaviourYSecond);
|
||||||
|
Assert.NotNull(behaviourController.GetBehaviour<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddBehaviourSameTypeCreatesSeparateInstances()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
BehaviourY first = behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
BehaviourY second = behaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
|
||||||
|
Assert.NotSame(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourAddedEvents()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
IBehaviourController? sender = null;
|
||||||
|
IBehaviourController.BehaviourAddedArguments? arguments = null;
|
||||||
|
|
||||||
|
behaviourController.OnBehaviourAdded.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
BehaviourX behaviour = behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(behaviourController, sender);
|
||||||
|
Assert.Equal(behaviour, arguments.Value.BehaviourAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourRemovedEvents()
|
||||||
|
{
|
||||||
|
IBehaviourController behaviourController = CreateUniverseObject().BehaviourController;
|
||||||
|
|
||||||
|
IBehaviourController? sender = null;
|
||||||
|
IBehaviourController.BehaviourRemovedArguments? arguments = null;
|
||||||
|
|
||||||
|
behaviourController.OnBehaviourRemoved.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
BehaviourX behaviour = behaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
behaviourController.RemoveBehaviour<BehaviourX>();
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(behaviourController, sender);
|
||||||
|
Assert.Equal(behaviour, arguments.Value.BehaviourRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hierarchy Structure
|
||||||
|
|
||||||
|
private static IUniverseObject CreateUniverseObject()
|
||||||
|
{
|
||||||
|
UniverseObject universeObject = new();
|
||||||
|
BehaviourController behaviourController = new();
|
||||||
|
|
||||||
|
universeObject.Assign(new StateEnable());
|
||||||
|
universeObject.StateEnable.Assign(universeObject);
|
||||||
|
|
||||||
|
behaviourController.Assign(new StateEnable());
|
||||||
|
behaviourController.StateEnable.Assign(behaviourController);
|
||||||
|
|
||||||
|
behaviourController.Assign(universeObject);
|
||||||
|
universeObject.Assign(behaviourController);
|
||||||
|
|
||||||
|
universeObject.Initialize();
|
||||||
|
behaviourController.Initialize();
|
||||||
|
|
||||||
|
return universeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniverseObject CreateParentChildScenario()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject().SetUniverseObject("Parent");
|
||||||
|
IUniverseObject middleUO = CreateUniverseObject().SetUniverseObject("Middle", parentUO);
|
||||||
|
IUniverseObject childUO = CreateUniverseObject().SetUniverseObject("Child", middleUO);
|
||||||
|
|
||||||
|
parentUO.BehaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
middleUO.BehaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
childUO.BehaviourController.AddBehaviour<BehaviourY>();
|
||||||
|
|
||||||
|
return middleUO;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInParent()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.NotNull(universeObject.BehaviourController.GetBehaviourInParent<BehaviourY>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInChildren()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.NotNull(universeObject.BehaviourController.GetBehaviourInChildren<BehaviourY>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInChildrenOnSameObject()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.NotNull(universeObject.BehaviourController.GetBehaviourInChildren<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInParentOnSameObject()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.NotNull(universeObject.BehaviourController.GetBehaviourInParent<BehaviourX>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInParentRaisesExceptionIfNotFound()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.Throws<BehaviourNotFoundException>(universeObject.BehaviourController.GetRequiredBehaviourInParent<BehaviourZ>);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInChildrenRaisesExceptionIfNotFound()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.Throws<BehaviourNotFoundException>(universeObject.BehaviourController.GetRequiredBehaviourInChildren<BehaviourZ>);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInChildrenOnSameObjectRaisesExceptionIfNotFound()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.Throws<BehaviourNotFoundException>(universeObject.BehaviourController.GetRequiredBehaviourInChildren<BehaviourZ>);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParentChildScenarioGetBehaviourInParentOnSameObjectRaisesExceptionIfNotFound()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateParentChildScenario();
|
||||||
|
|
||||||
|
Assert.Throws<BehaviourNotFoundException>(universeObject.BehaviourController.GetRequiredBehaviourInParent<BehaviourZ>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private class BehaviourX : BehaviourBase;
|
||||||
|
private class BehaviourY : BehaviourBase;
|
||||||
|
private class BehaviourZ : BehaviourBase;
|
||||||
|
}
|
72
Engine.Tests/Core/Behaviour_Tests.cs
Normal file
72
Engine.Tests/Core/Behaviour_Tests.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Engine.Tests;
|
||||||
|
|
||||||
|
public class Behaviour_Tests
|
||||||
|
{
|
||||||
|
private static IUniverseObject CreateUniverseObject()
|
||||||
|
{
|
||||||
|
UniverseObject universeObject = new();
|
||||||
|
BehaviourController behaviourController = new();
|
||||||
|
|
||||||
|
universeObject.Assign(new StateEnable());
|
||||||
|
universeObject.StateEnable.Assign(universeObject);
|
||||||
|
|
||||||
|
behaviourController.Assign(new StateEnable());
|
||||||
|
behaviourController.StateEnable.Assign(behaviourController);
|
||||||
|
|
||||||
|
behaviourController.Assign(universeObject);
|
||||||
|
universeObject.Assign(behaviourController);
|
||||||
|
|
||||||
|
universeObject.Initialize();
|
||||||
|
behaviourController.Initialize();
|
||||||
|
|
||||||
|
return universeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourActive()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
|
||||||
|
BehaviourX behaviourX = universeObject.BehaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
behaviourX.StateEnable.Enabled = true;
|
||||||
|
|
||||||
|
Assert.True(behaviourX.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourNotActiveOnDisabled()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
|
||||||
|
BehaviourX behaviourX = universeObject.BehaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
behaviourX.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
Assert.False(behaviourX.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourNotActiveOnBehaviourControllerDisabled()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
|
||||||
|
BehaviourX behaviourX = universeObject.BehaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
universeObject.BehaviourController.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
Assert.False(behaviourX.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BehaviourNotActiveOnUniverseObjectDisabled()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
|
||||||
|
BehaviourX behaviourX = universeObject.BehaviourController.AddBehaviour<BehaviourX>();
|
||||||
|
universeObject.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
Assert.False(behaviourX.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BehaviourX : BehaviourBase;
|
||||||
|
}
|
272
Engine.Tests/Core/UniverseObject_Tests.cs
Normal file
272
Engine.Tests/Core/UniverseObject_Tests.cs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Engine.Tests;
|
||||||
|
|
||||||
|
public class UniverseObject_Tests
|
||||||
|
{
|
||||||
|
private static IUniverseObject CreateUniverseObject()
|
||||||
|
{
|
||||||
|
UniverseObject universeObject = new();
|
||||||
|
BehaviourController behaviourController = new();
|
||||||
|
|
||||||
|
universeObject.Assign(new StateEnable());
|
||||||
|
|
||||||
|
behaviourController.Assign(new StateEnable());
|
||||||
|
behaviourController.StateEnable.Assign(behaviourController);
|
||||||
|
|
||||||
|
behaviourController.Assign(universeObject);
|
||||||
|
universeObject.Assign(behaviourController);
|
||||||
|
|
||||||
|
universeObject.Initialize();
|
||||||
|
behaviourController.Initialize();
|
||||||
|
|
||||||
|
return universeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddChildren()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
parentUO.AddChild(childUO);
|
||||||
|
Assert.NotEmpty(parentUO.Children);
|
||||||
|
Assert.Equal(parentUO.Children[0], childUO);
|
||||||
|
Assert.Equal(childUO.Parent, parentUO);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveChildren()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
parentUO.AddChild(childUO);
|
||||||
|
Assert.NotEmpty(parentUO.Children);
|
||||||
|
Assert.Contains(childUO, parentUO.Children);
|
||||||
|
Assert.Equal(childUO.Parent, parentUO);
|
||||||
|
|
||||||
|
parentUO.RemoveChild(childUO);
|
||||||
|
Assert.Empty(parentUO.Children);
|
||||||
|
Assert.Null(childUO.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetParentValue()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
childUO.Parent = parentUO;
|
||||||
|
Assert.Contains(childUO, parentUO.Children);
|
||||||
|
Assert.Equal(childUO.Parent, parentUO);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetParentNull()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
parentUO.AddChild(childUO);
|
||||||
|
|
||||||
|
childUO.Parent = null;
|
||||||
|
Assert.Empty(parentUO.Children);
|
||||||
|
Assert.Null(childUO.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddChildrenEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
IUniverseObject? sender = null;
|
||||||
|
IUniverseObject.ChildrenAddedArguments? arguments = null;
|
||||||
|
|
||||||
|
parentUO.OnChildrenAdded.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
parentUO.AddChild(childUO);
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(parentUO, sender);
|
||||||
|
Assert.Equal(childUO, arguments.Value.ChildrenAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveChildrenEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
IUniverseObject? sender = null;
|
||||||
|
IUniverseObject.ChildrenRemovedArguments? arguments = null;
|
||||||
|
|
||||||
|
parentUO.AddChild(childUO);
|
||||||
|
|
||||||
|
parentUO.OnChildrenRemoved.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
parentUO.RemoveChild(childUO);
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(parentUO, sender);
|
||||||
|
Assert.Equal(childUO, arguments.Value.ChildrenRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetParentEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
IUniverseObject? sender = null;
|
||||||
|
IUniverseObject.ParentChangedArguments? arguments = null;
|
||||||
|
|
||||||
|
childUO.OnParentChanged.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
childUO.Parent = parentUO;
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(childUO, sender);
|
||||||
|
Assert.Null(arguments.Value.PreviousParent);
|
||||||
|
Assert.Equal(parentUO, arguments.Value.CurrentParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UnsetParentEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject parentUO = CreateUniverseObject();
|
||||||
|
IUniverseObject childUO = CreateUniverseObject();
|
||||||
|
|
||||||
|
IUniverseObject? sender = null;
|
||||||
|
IUniverseObject.ParentChangedArguments? arguments = null;
|
||||||
|
|
||||||
|
childUO.Parent = parentUO;
|
||||||
|
|
||||||
|
childUO.OnParentChanged.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
childUO.Parent = null;
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
Assert.Equal(childUO, sender);
|
||||||
|
Assert.Equal(parentUO, arguments.Value.PreviousParent);
|
||||||
|
Assert.Null(arguments.Value.CurrentParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Active()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.StateEnable.Enabled = true;
|
||||||
|
|
||||||
|
Assert.True(universeObject.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NotActiveWhenDisabled()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
Assert.False(universeObject.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ActiveEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.StateEnable.Enabled = true;
|
||||||
|
|
||||||
|
IActive? sender = null;
|
||||||
|
IActive.ActiveChangedArguments? arguments = null;
|
||||||
|
|
||||||
|
universeObject.OnActiveChanged.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
universeObject.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
|
||||||
|
Assert.False(universeObject.IsActive);
|
||||||
|
Assert.True(arguments.Value.PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NotActiveEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.StateEnable.Enabled = false;
|
||||||
|
|
||||||
|
IActive? sender = null;
|
||||||
|
IActive.ActiveChangedArguments? arguments = null;
|
||||||
|
|
||||||
|
universeObject.OnActiveChanged.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
universeObject.StateEnable.Enabled = true;
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
|
||||||
|
Assert.True(universeObject.IsActive);
|
||||||
|
Assert.False(arguments.Value.PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetName()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.Name = "Test";
|
||||||
|
Assert.Equal("Test", universeObject.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetNameEvents()
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = CreateUniverseObject();
|
||||||
|
universeObject.Name = "Previous";
|
||||||
|
|
||||||
|
INameable? sender = null;
|
||||||
|
INameable.NameChangedArguments? arguments = null;
|
||||||
|
|
||||||
|
universeObject.OnNameChanged.AddListener((s, a) =>
|
||||||
|
{
|
||||||
|
sender = s;
|
||||||
|
arguments = a;
|
||||||
|
});
|
||||||
|
|
||||||
|
universeObject.Name = "Test";
|
||||||
|
|
||||||
|
Assert.NotNull(sender);
|
||||||
|
Assert.NotNull(arguments);
|
||||||
|
|
||||||
|
Assert.Equal("Test", universeObject.Name);
|
||||||
|
Assert.Equal("Previous", arguments.Value.PreviousName);
|
||||||
|
}
|
||||||
|
}
|
28
Engine.Tests/Engine.Tests.csproj
Normal file
28
Engine.Tests/Engine.Tests.csproj
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Engine\Engine.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
16
Engine.sln
16
Engine.sln
@ -19,7 +19,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Serial
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{823D4020-332D-2C13-F261-6F510F11A57E}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{823D4020-332D-2C13-F261-6F510F11A57E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Tests", "Engine.Tests\Engine.Tests.csproj", "{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -115,6 +117,18 @@ Global
|
|||||||
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.Build.0 = Release|Any CPU
|
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.ActiveCfg = Release|Any CPU
|
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.Build.0 = Release|Any CPU
|
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5FAE9C0F-39B9-4470-9E6B-08F764A03A08}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user