70 Commits

Author SHA1 Message Date
0215f8ef1f chore: debumped .NET Core version to 8 2025-05-07 19:30:36 +03:00
0bf38234c6 feat: async serializer methods 2025-05-04 19:00:54 +03:00
ed6969c16a feat: progression trackers added 2025-05-04 18:57:26 +03:00
b0b421151f refactor: TypeFactory ReloadTypes made multithread friendly 2025-05-04 18:57:01 +03:00
41c5def097 refactor: renamed DelegateHelpers to DelegateExtensions 2025-05-04 18:52:47 +03:00
fbbdfb07fa chore: bumped .netcore version to 9 2025-05-04 18:46:21 +03:00
bf283d804c chore: updated Shape2D tween to look more aesthetic by choosing more linearly distributed vertices instead of the last vertex 2025-05-03 23:31:06 +03:00
063ea08707 feat: added RoundToInt RoundMode for midway values 2025-05-03 23:30:02 +03:00
fd11a94ddf refactor: easings have a singleton base so we don't create an unnecessary instance or cache everytime 2025-05-03 22:38:40 +03:00
be2295b92d feat: added engine member tween extensions 2025-05-03 22:23:52 +03:00
a93e55619c refactor: extracted interface from TweenManager 2025-05-03 22:23:28 +03:00
48ae24af47 chore: added safeguard value clamps for color operations 2025-05-03 22:21:58 +03:00
1366a417f1 feat: added Math.OneMinus method 2025-05-03 22:16:14 +03:00
4bfe98852c refactor: tween extensions method spacings fixed 2025-05-03 20:46:20 +03:00
98edbe1af5 chore: disabled all ImplicitUsings 2025-05-03 20:41:26 +03:00
3725a3b0fd feat: added preserver class & method to preserve assembly loading 2025-05-03 20:22:35 +03:00
f43ab36742 feat: added loggers 2025-05-03 17:01:58 +03:00
c7aafd85bc refactor: renamed assert helper and moved to Debug subfolder 2025-05-03 15:37:52 +03:00
5de08b8fe4 refactor: primitives now use Core.Math for math 2025-05-02 18:57:42 +03:00
16e4077d40 chore: HSV hue is normalized between 0 and 1 2025-05-02 18:54:08 +03:00
fc3c1ed1f9 refactor: Shape2D converted into a class as it has a reference type 2025-05-02 12:46:23 +03:00
b100b5c2fe feat: added color primitives 2025-05-02 00:51:58 +03:00
5e28ba8814 chore: updated README.md 2025-05-02 00:14:58 +03:00
4c235e3230 feat: added basic math operations as Math methods 2025-05-02 00:14:41 +03:00
131203d578 refactor: Yaml serialization moved from Core to own project 2025-05-02 00:00:03 +03:00
bd5eb432b7 feat: serialized state machine & states 2025-05-02 00:00:03 +03:00
d2ca85568f feat: entity register for serialized entity references 2025-05-02 00:00:03 +03:00
4c41870732 perf: made SerializedClass private and public fields optional 2025-05-02 00:00:03 +03:00
f77afa3632 chore: removed forgotten removed project reference 2025-05-02 00:00:03 +03:00
eb61598489 chore: reordered UniverserObjectSerializer fields for better readable yaml output 2025-05-02 00:00:03 +03:00
efe51b491d chore: universe serializer filters in only the root universe objects 2025-05-02 00:00:03 +03:00
fa3a4d1e0d feat: added universe serializer 2025-05-02 00:00:03 +03:00
6e7a0993f5 refactor: renamed converters to serializers 2025-05-02 00:00:03 +03:00
d70bee2c6b feat: serializable Transform2D 2025-05-02 00:00:03 +03:00
5812f43117 refactor: moved type container one directory up 2025-05-02 00:00:03 +03:00
d102c5471d feat: type container added back for field/property serialization 2025-05-02 00:00:03 +03:00
fb363970fc refactor: moved serialization into core project 2025-05-02 00:00:03 +03:00
791349686b chore: removed unused classes 2025-05-02 00:00:03 +03:00
3a0942ff46 fix: ignore serialization objects being included in serialization fixed 2025-05-02 00:00:03 +03:00
b002dd469a feat: behaviour & behaviour controller converters added 2025-05-02 00:00:03 +03:00
f92f36442c feat: state enable converted added 2025-05-02 00:00:03 +03:00
bb934b59f3 feat: wip universe object converter added 2025-05-02 00:00:03 +03:00
c704173183 feat: serialize all attribute 2025-05-02 00:00:03 +03:00
c3876add1e chore: added serialized entity class 2025-05-02 00:00:03 +03:00
35a75d993b chore: experimentations 2025-05-02 00:00:03 +03:00
2637f99456 fix: fixed fields/properties like behaviour controllers not being explored by entity finder 2025-05-02 00:00:03 +03:00
9581f5aa54 refactor: removed unnecessary logs 2025-05-02 00:00:02 +03:00
82cc25a9ef feat: entity finder added 2025-05-02 00:00:02 +03:00
336e7e16e7 chore: memberInfo.HasAttribute method added 2025-05-02 00:00:02 +03:00
a3a8fb4e84 chore: depth limit for debugging 2025-05-01 23:59:43 +03:00
35f6c3850e fix: GetTypeData not including base class proprety & fields 2025-04-28 22:29:08 +03:00
f51d5f342e chore: added a generic converter 2025-04-28 22:29:08 +03:00
9c129cefe2 feat: added state enable serialization 2025-04-28 22:29:08 +03:00
a254bb721b chore: changed entity reference order 2025-04-28 22:29:08 +03:00
5fa7420c04 feat: added entity converter 2025-04-28 22:29:08 +03:00
5bcc256777 feat: added type container serialization 2025-04-28 22:29:08 +03:00
680d718957 chore: moved primitive converters under subfolder 2025-04-28 22:29:08 +03:00
20bc6a1adb chore: updated to forked version of YamlDotNet that fixes sequence indentations 2025-04-28 22:29:08 +03:00
eb454a471c feat: added primitive serialization 2025-04-28 22:29:08 +03:00
c205e710bc chore: some experimentations with DotNetYaml 2025-04-28 22:29:08 +03:00
cddb30c631 refactor: optimized & added reload method for type factory 2025-04-28 22:26:33 +03:00
29f6c83bf0 chore: removed unnecessary partial keyword 2025-04-27 22:28:35 +03:00
c20f210b29 refactor: rewritten GetType in a more readable way 2025-04-27 22:28:21 +03:00
1ea1844677 fix: Transform2D not raising OnPositionChanged event with correct parameters 2025-04-26 14:26:17 +03:00
5b2c13f8bf fix: BehaviourController assigning a new state enable to all newly added behaviours fixed 2025-04-26 14:10:40 +03:00
c39ee44442 fix: behaviour controller initializing added behaviours when it itself is not initialized 2025-04-25 21:54:05 +03:00
4623b4861a fix: behaviour controllers of universe objects not being initialized 2025-04-25 21:26:01 +03:00
0a868b82e5 fix: behaviour controller not respecting it's own state enable 2025-04-25 21:05:20 +03:00
d92d16cfad refactor: IBehaviourController is now an IEntity as well 2025-04-22 15:50:26 +03:00
0184d1758c feat: added more methods for TypeFactory 2025-04-20 00:06:48 +03:00
100 changed files with 2861 additions and 172 deletions

3
.gitmodules vendored Normal file
View File

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

View File

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

View File

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

View File

@@ -53,7 +53,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreUpdate() { } protected virtual void OnPreUpdate() { }
protected virtual void PreUpdate(IBehaviourController _) protected virtual void PreUpdate(IBehaviourController _)
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
OnPreUpdatePreActiveCheck(); OnPreUpdatePreActiveCheck();
@@ -77,7 +77,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnUpdate() { } protected virtual void OnUpdate() { }
protected virtual void Update(IBehaviourController _) protected virtual void Update(IBehaviourController _)
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
OnUpdatePreActiveCheck(); OnUpdatePreActiveCheck();
@@ -91,7 +91,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreDraw() { } protected virtual void OnPreDraw() { }
protected virtual void PreDraw(IBehaviourController _) protected virtual void PreDraw(IBehaviourController _)
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
OnPreDrawPreActiveCheck(); OnPreDrawPreActiveCheck();

View File

@@ -66,8 +66,8 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
protected override void InitializeInternal() protected override void InitializeInternal()
{ {
Debug.AssertHelpers.AssertBehaviourControllerAssigned(this); Debug.Assert.AssertBehaviourControllerAssigned(this);
Debug.AssertHelpers.AssertStateEnableAssigned(this); Debug.Assert.AssertStateEnableAssigned(this);
} }
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive(); private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();

View File

@@ -6,7 +6,7 @@ using System.Linq;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] [System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
public class BehaviourController : IBehaviourController public class BehaviourController : BaseEntity, IBehaviourController
{ {
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null; public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
public event IBehaviourController.UpdateEventHandler? OnUpdate = null; public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
@@ -16,42 +16,20 @@ public class BehaviourController : IBehaviourController
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null; public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IHasUniverseObject.UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned = null; public event IHasUniverseObject.UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL); private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
private IUniverseObject _universeObject = null!; private IUniverseObject _universeObject = null!;
private bool _initialized = false;
public IUniverseObject UniverseObject => _universeObject; public IUniverseObject UniverseObject => _universeObject;
public bool IsInitialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.InvokeSafe(this);
else
OnFinalized?.InvokeSafe(this);
}
}
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
{ {
InsertBehaviourByPriority(behaviour); InsertBehaviourByPriority(behaviour);
behaviour.Assign(this); behaviour.Assign(this);
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
behaviour.Initialize(); if (IsInitialized)
behaviour.Initialize();
behaviour.OnPriorityChanged += OnPriorityChange; behaviour.OnPriorityChanged += OnPriorityChange;
OnBehaviourAdded?.InvokeSafe(this, behaviour); OnBehaviourAdded?.InvokeSafe(this, behaviour);
return behaviour; return behaviour;
@@ -133,45 +111,25 @@ public class BehaviourController : IBehaviourController
return true; return true;
} }
public bool Initialize() protected override void InitializeInternal()
{ {
if (IsInitialized) Debug.Assert.AssertUniverseObjectAssigned(this);
return false;
Debug.AssertHelpers.AssertUniverseObjectAssigned(this);
foreach (IBehaviour behaviour in behaviours) foreach (IBehaviour behaviour in behaviours)
behaviour.Initialize(); behaviour.Initialize();
IsInitialized = true;
return true;
} }
public bool Finalize() protected override void FinalizeInternal()
{ {
if (!IsInitialized)
return false;
foreach (IBehaviour behaviour in behaviours) foreach (IBehaviour behaviour in behaviours)
behaviour.Finalize(); behaviour.Finalize();
IsInitialized = false;
return true;
}
public bool Unassign()
{
if (IsInitialized)
return false;
_universeObject = null!;
OnUnassigned?.InvokeSafe(this);
return true;
} }
public void Update() public void Update()
{ {
if (!UniverseObject.StateEnable.Enabled) Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return; return;
OnPreUpdate?.InvokeSafe(this); OnPreUpdate?.InvokeSafe(this);
@@ -180,7 +138,9 @@ public class BehaviourController : IBehaviourController
public void UpdatePreDraw() public void UpdatePreDraw()
{ {
if (!UniverseObject.StateEnable.Enabled) Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return; return;
OnPreDraw?.InvokeSafe(this); OnPreDraw?.InvokeSafe(this);

View File

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

View File

@@ -0,0 +1,8 @@
using System;
namespace Syntriax.Engine.Core.Debug;
public class ConsoleLogger : LoggerBase
{
protected override void Write(string message) => Console.WriteLine(message);
}

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
using System;
namespace Syntriax.Engine.Core.Debug;
public abstract class LoggerBase : ILogger
{
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Info;
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
{
if (!force && level < FilterLevel)
return;
string timestamp = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss tt");
Write($"[{timestamp}] [{level}] \t{message}");
}
protected abstract void Write(string message);
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Diagnostics;
namespace Syntriax.Engine.Core.Debug;
public static class LoggerExtensions
{
public static void Log<T>(this ILogger logger, T caller, string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
{
string body = $"{caller?.GetType().Name ?? typeof(T).Name}: {message}";
logger.Log(body, level, force);
}
public static void LogWarning<T>(this ILogger logger, T caller, string message, bool force = false) => Log(logger, caller, message, ILogger.Level.Info, force);
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
{
Log(logger, caller, message, ILogger.Level.Error, force);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}");
}
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
{
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}");
}
}

View File

@@ -1,21 +1,59 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Syntriax.Engine.Core.Factory; namespace Syntriax.Engine.Core.Factory;
public static class TypeFactory public static class TypeFactory
{ {
public static T Get<T>(params object?[]? args) where T : class private static readonly ConcurrentDictionary<string, Type> registeredTypes = [];
public static string GetTypeName(Type type) => type.FullName ?? throw new ArgumentException($"{type.Name} must be a resolvable type");
public static T Get<T>(params object?[]? args) where T : class => (T)Get(typeof(T), args);
public static object Get(string fullName, params object?[]? args) => Get(GetType(fullName), args);
public static object Get(Type type, params object?[]? args)
{ {
T? result; object? result;
if (args is not null && args.Length != 0) if (args is not null && args.Length != 0)
result = Activator.CreateInstance(typeof(T), args) as T; result = Activator.CreateInstance(type, args);
else else
result = Activator.CreateInstance(typeof(T)) as T; result = Activator.CreateInstance(type);
if (result is null) if (result is null)
throw new Exception($"{typeof(T).Name} of type {typeof(T).Name} could not be created."); throw new Exception($"Type {type.Name} could not be created.");
return result; return result;
} }
public static Type GetType(string fullName)
{
if (registeredTypes.TryGetValue(fullName, out Type? result))
return result;
ReloadTypes();
if (registeredTypes.TryGetValue(fullName, out Type? reloadedType))
return reloadedType;
throw new Exception($"Type {fullName} could not be found in the current domain.");
}
public static void ReloadTypes()
{
registeredTypes.Clear();
IEnumerable<Type> domainTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes());
// TODO: Replace this
// There are some system & compiler generated types with duplicated names,
// it is ugly it will cause headaches in the future because it will not
// throw an error if there's a type with an unintended duplicate name
foreach (Type type in domainTypes)
registeredTypes.TryAdd(GetTypeName(type), type);
}
} }

View File

@@ -2,7 +2,7 @@ using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
public static class DelegateHelpers public static class DelegateExtensions
{ {
public static void InvokeSafe(this Delegate @delegate, params object?[] args) public static void InvokeSafe(this Delegate @delegate, params object?[] args)
{ {

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
namespace Syntriax.Engine.Core;
public class ProgressionTracker : IProgressionTracker
{
public event IReadOnlyProgressionTracker.ProgressionUpdatedEventHandler? OnUpdated = null;
public event IReadOnlyProgressionTracker.ProgressionEndedEventHandler? OnEnded = null;
public float Progression { get; private set; } = 0f;
public string Status { get; private set; } = "Default";
void IProgressionTracker.Set(float progression, string status)
{
if (Progression >= 1f)
return;
float previousProgression = Progression;
string previousStatus = Status;
Progression = progression.Clamp(Progression, 1f);
Status = status;
OnUpdated?.InvokeSafe(this, previousProgression, previousStatus);
if (progression >= 1f)
OnEnded?.InvokeSafe(this);
}
void IProgressionTracker.Reset()
{
Progression = 0f;
Status = "Default";
OnUpdated = null;
OnEnded = null;
}
}

View File

@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Syntriax.Engine.Core;
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
{
public static implicit operator (IReadOnlyProgressionTracker progressionTracker, Task<T> task)(ProgressiveTask<T> value) => (value.ProgressionTracker, value.Task);
public static implicit operator ProgressiveTask<T>((IReadOnlyProgressionTracker progressionTracker, Task<T> task) value) => new(value.progressionTracker, value.task);
}

View File

@@ -30,6 +30,64 @@ public static class Math
/// </summary> /// </summary>
public const float DegreeToRadian = PI / 180f; public const float DegreeToRadian = PI / 180f;
/// <summary>
/// Gets one minus of given <see cref="T"/>.
/// </summary>
/// <param name="value">The value <see cref="T"/>.</param>
/// <returns>One minus of given <see cref="T"/>.</returns>
public static T OneMinus<T>(T value) where T : INumber<T> => T.One - value;
/// <summary>
/// Adds two <see cref="T"/>s.
/// </summary>
/// <param name="left">The first <see cref="T"/>.</param>
/// <param name="value">The second <see cref="T"/>.</param>
/// <returns>The sum of the two <see cref="T"/>s.</returns>
public static T Add<T>(T left, T value) where T : INumber<T> => left + value;
/// <summary>
/// Subtracts one <see cref="T"/> from another.
/// </summary>
/// <param name="left">The <see cref="T"/> to subtract from.</param>
/// <param name="value">The <see cref="T"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="T"/> from the first.</returns>
public static T Subtract<T>(T left, T value) where T : INumber<T> => left - value;
/// <summary>
/// Multiplies a <see cref="T"/> by a scalar value.
/// </summary>
/// <param name="left">The <see cref="T"/>.</param>
/// <param name="multiplier">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="T"/> by the scalar value.</returns>
public static T Multiply<T>(T left, T multiplier) where T : INumber<T> => left * multiplier;
/// <summary>
/// Divides a <see cref="T"/> by a scalar value.
/// </summary>
/// <param name="left">The <see cref="T"/>.</param>
/// <param name="divider">The scalar value.</param>
/// <returns>The result of dividing the <see cref="T"/> by the scalar value.</returns>
public static T Divide<T>(T left, T divider) where T : INumber<T> => left / divider;
/// <summary>
/// Returns the true mathematical modulus of a <see cref="T"/> value.
/// Unlike the remainder operator (%), this result is always non-negative,
/// even when the <paramref name="value"/> operand is negative.
/// </summary>
/// <typeparam name="T">A numeric type that implements <see cref="INumber{T}"/>.</typeparam>
/// <param name="value">The dividend <see cref="T"/> value.</param>
/// <param name="modulus">The modulus <see cref="T"/> value (must be non-zero).</param>
/// <returns>
/// The non-negative remainder of <paramref name="value"/> divided by <paramref name="modulus"/>.
/// </returns>
public static T Mod<T>(T value, T modulus) where T : INumber<T>
{
T result = value % modulus;
if (result < T.Zero)
result += modulus;
return result;
}
/// <summary> /// <summary>
/// Returns the absolute value of a number. /// Returns the absolute value of a number.
/// </summary> /// </summary>
@@ -190,6 +248,15 @@ public static class Math
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns> /// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
/// <summary>
/// Rounds a number to an integer.
/// </summary>
/// <param name="x">The number to round.</param>
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
/// <returns></returns>
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
public enum RoundMode { Ceil, Floor };
/// <summary> /// <summary>
/// Returns the square of a number. /// Returns the square of a number.
/// </summary> /// </summary>

View File

@@ -5,6 +5,24 @@ namespace Syntriax.Engine.Core;
public static class MathExtensions public static class MathExtensions
{ {
/// <inheritdoc cref="Math.OneMinus{T}(T)" />
public static T OneMinus<T>(this T value) where T : INumber<T> => Math.OneMinus(value);
/// <inheritdoc cref="Math.Add{T}(T, T)" />
public static T Add<T>(this T left, T value) where T : INumber<T> => Math.Add(left, value);
/// <inheritdoc cref="Math.Subtract{T}(T, T)" />
public static T Subtract<T>(this T left, T value) where T : INumber<T> => Math.Subtract(left, value);
/// <inheritdoc cref="Math.Multiply{T}(T, T)" />
public static T Multiply<T>(this T left, T multiplier) where T : INumber<T> => Math.Multiply(left, multiplier);
/// <inheritdoc cref="Math.Divide{T}(T, T)" />
public static T Divide<T>(this T left, T divider) where T : INumber<T> => Math.Divide(left, divider);
/// <inheritdoc cref="Math.Mod{T}(T, T)" />
public static T Mod<T>(this T value, T modulus) where T : INumber<T> => Math.Mod(value, modulus);
/// <inheritdoc cref="Math.Abs{T}(T)" /> /// <inheritdoc cref="Math.Abs{T}(T)" />
public static T Abs<T>(this T x) where T : INumber<T> => Math.Abs(x); public static T Abs<T>(this T x) where T : INumber<T> => Math.Abs(x);
@@ -65,6 +83,9 @@ public static class MathExtensions
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" /> /// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode); public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
/// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
/// <inheritdoc cref="Math.Sqr{T}(T)" /> /// <inheritdoc cref="Math.Sqr{T}(T)" />
public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x); public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x);

10
Engine.Core/Preserver.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace Syntriax.Engine.Core
{
// This is pretty much so the assembly gets loaded automatically because
// the builds include the assembly but sometimes doesn't link load it at startup.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@@ -0,0 +1,185 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an HSV color.
/// </summary>
/// <param name="hue">Hue of the <see cref="ColorHSV"/>.</param>
/// <param name="saturation">Saturation of the <see cref="ColorHSV"/>.</param>
/// <param name="value">Value of the <see cref="ColorHSV"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorHSV(float hue, float saturation, float value)
{
/// <summary>
/// The Hue value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Hue = hue.Clamp(0f, 1f);
/// <summary>
/// The Saturation value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Saturation = saturation.Clamp(0f, 1f);
/// <summary>
/// The Value value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Value = value.Clamp(0f, 1f);
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f));
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f));
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f));
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value);
public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value);
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba;
public static implicit operator ColorHSV(ColorRGB rgb)
{
float hue;
float saturation;
float value;
float rd = rgb.R / 255f;
float gd = rgb.G / 255f;
float bd = rgb.B / 255f;
float max = Math.Max(rd, Math.Max(gd, bd));
float min = Math.Min(rd, Math.Min(gd, bd));
float delta = max - min;
if (delta.ApproximatelyEquals(0))
hue = 0f;
else if (max.ApproximatelyEquals(rd))
hue = 60f * ((gd - bd) / delta % 6f);
else if (max.ApproximatelyEquals(gd))
hue = 60f * (((bd - rd) / delta) + 2f);
else
hue = 60f * (((rd - gd) / delta) + 4f);
if (hue < 0f)
hue += 360f;
hue /= 360f;
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
value = max;
return new(hue, saturation, value);
}
/// <summary>
/// Inverts the given <see cref="ColorHSV"/>.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <returns>The inverted <see cref="ColorHSV"/>.</returns>
public static ColorHSV Invert(ColorHSV color) => -color;
/// <summary>
/// Adds two <see cref="ColorHSV"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
/// <returns>The sum of the two <see cref="ColorHSV"/>s.</returns>
public static ColorHSV Add(ColorHSV left, ColorHSV right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorHSV"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorHSV"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorHSV"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorHSV"/> from the first.</returns>
public static ColorHSV Subtract(ColorHSV left, ColorHSV right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorHSV"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorHSV"/> by the scalar value.</returns>
public static ColorHSV Multiply(ColorHSV color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorHSV"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorHSV"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorHSV"/> from the starting point to the ending point.</returns>
public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorHSV"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorHSV"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
/// <summary>
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
/// </summary>
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="ColorHSV"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(ColorHSV left, ColorHSV right, float epsilon = float.Epsilon)
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorHSV"/>.
/// </summary>
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
public override bool Equals(object? obj) => obj is ColorHSV objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value);
/// <summary>
/// Generates a hash code for the <see cref="ColorHSV"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
}
/// <summary>
/// Provides extension methods for <see cref="ColorHSV"/> type.
/// </summary>
public static class ColorHSVExtensions
{
/// <inheritdoc cref="ColorHSV.Add(ColorHSV, ColorHSV)" />
public static ColorHSV Add(this ColorHSV color, ColorHSV value) => ColorHSV.Add(color, value);
/// <inheritdoc cref="ColorHSV.Subtract(ColorHSV, ColorHSV)" />
public static ColorHSV Subtract(this ColorHSV color, ColorHSV value) => ColorHSV.Subtract(color, value);
/// <inheritdoc cref="ColorHSV.Multiply(ColorHSV, ColorHSV)" />
public static ColorHSV Multiply(this ColorHSV color, float value) => ColorHSV.Multiply(color, value);
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
/// <inheritdoc cref="ColorHSV.FromTo(ColorHSV, ColorHSV)" />
public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to);
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
/// <inheritdoc cref="ColorHSV.ApproximatelyEquals(ColorHSV, ColorHSV, float) " />
public static bool ApproximatelyEquals(this ColorHSV left, ColorHSV right, float epsilon = float.Epsilon) => ColorHSV.ApproximatelyEquals(left, right, epsilon);
}

View File

@@ -0,0 +1,164 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an RGB color.
/// </summary>
/// <param name="r">Red value of the <see cref="ColorRGB"/>.</param>
/// <param name="g">Green value of the <see cref="ColorRGB"/>.</param>
/// <param name="b">Blue value of the <see cref="ColorRGB"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorRGB(byte r, byte g, byte b)
{
/// <summary>
/// The Red value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte R = r;
/// <summary>
/// The Green value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte G = g;
/// <summary>
/// The Blue value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte B = b;
public static ColorRGB operator -(ColorRGB color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B));
public static ColorRGB operator +(ColorRGB left, ColorRGB right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255));
public static ColorRGB operator -(ColorRGB left, ColorRGB right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255));
public static ColorRGB operator *(ColorRGB left, ColorRGB right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255));
public static ColorRGB operator *(ColorRGB color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
public static ColorRGB operator *(float value, ColorRGB color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
public static ColorRGB operator /(ColorRGB color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255));
public static bool operator ==(ColorRGB left, ColorRGB right) => left.R == right.R && left.G == right.G && left.B == right.B;
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
public static implicit operator ColorRGB(ColorHSV hsv)
{
float hue = hsv.Hue * 360f;
float chroma = hsv.Value * hsv.Saturation;
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
float m = hsv.Value - chroma;
float r1 = 0f;
float g1 = 0f;
float b1 = 0f;
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
return new(r, g, b);
}
/// <summary>
/// Inverts the given <see cref="ColorRGB"/>.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <returns>The inverted <see cref="ColorRGB"/>.</returns>
public static ColorRGB Invert(ColorRGB color) => -color;
/// <summary>
/// Adds two <see cref="ColorRGB"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorRGB"/>.</param>
/// <param name="right">The second <see cref="ColorRGB"/>.</param>
/// <returns>The sum of the two <see cref="ColorRGB"/>s.</returns>
public static ColorRGB Add(ColorRGB left, ColorRGB right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorRGB"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorRGB"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorRGB"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorRGB"/> from the first.</returns>
public static ColorRGB Subtract(ColorRGB left, ColorRGB right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorRGB"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorRGB"/> by the scalar value.</returns>
public static ColorRGB Multiply(ColorRGB color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorRGB"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorRGB"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGB"/> from the starting point to the ending point.</returns>
public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorRGB"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorRGB"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
/// </summary>
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
public override bool Equals(object? obj) => obj is ColorRGB objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B);
/// <summary>
/// Generates a hash code for the <see cref="ColorRGB"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
}
/// <summary>
/// Provides extension methods for <see cref="ColorRGB"/> type.
/// </summary>
public static class ColorRGBExtensions
{
/// <inheritdoc cref="ColorRGB.Add(ColorRGB, ColorRGB)" />
public static ColorRGB Add(this ColorRGB color, ColorRGB value) => ColorRGB.Add(color, value);
/// <inheritdoc cref="ColorRGB.Subtract(ColorRGB, ColorRGB)" />
public static ColorRGB Subtract(this ColorRGB color, ColorRGB value) => ColorRGB.Subtract(color, value);
/// <inheritdoc cref="ColorRGB.Multiply(ColorRGB, ColorRGB)" />
public static ColorRGB Multiply(this ColorRGB color, float value) => ColorRGB.Multiply(color, value);
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
/// <inheritdoc cref="ColorRGB.FromTo(ColorRGB, ColorRGB)" />
public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to);
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
}

View File

@@ -0,0 +1,147 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an RGBA color.
/// </summary>
/// <param name="r">Red value of the <see cref="ColorRGBA"/>.</param>
/// <param name="g">Green value of the <see cref="ColorRGBA"/>.</param>
/// <param name="b">Blue value of the <see cref="ColorRGBA"/>.</param>
/// <param name="a">Alpha value of the <see cref="ColorRGBA"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
{
/// <summary>
/// The Red value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte R = r;
/// <summary>
/// The Green value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte G = g;
/// <summary>
/// The Blue value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte B = b;
/// <summary>
/// The Alpha value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte A = a;
public static ColorRGBA operator -(ColorRGBA color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B), color.A);
public static ColorRGBA operator +(ColorRGBA left, ColorRGBA right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255), (byte)(left.A + right.A).Clamp(0, 255));
public static ColorRGBA operator -(ColorRGBA left, ColorRGBA right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255), (byte)(left.A - right.A).Clamp(0, 255));
public static ColorRGBA operator *(ColorRGBA left, ColorRGBA right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255), (byte)(left.A * right.A).Clamp(0, 255));
public static ColorRGBA operator *(ColorRGBA color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
public static ColorRGBA operator *(float value, ColorRGBA color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
public static ColorRGBA operator /(ColorRGBA color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255), (byte)(color.A / value).Clamp(0, 255));
public static bool operator ==(ColorRGBA left, ColorRGBA right) => left.R == right.R && left.G == right.G && left.B == right.B && left.A == right.A;
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorRGB)hsv;
/// <summary>
/// Inverts the given <see cref="ColorRGBA"/>.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <returns>The inverted <see cref="ColorRGBA"/>.</returns>
public static ColorRGBA Invert(ColorRGBA color) => -color;
/// <summary>
/// Adds two <see cref="ColorRGBA"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorRGBA"/>.</param>
/// <param name="right">The second <see cref="ColorRGBA"/>.</param>
/// <returns>The sum of the two <see cref="ColorRGBA"/>s.</returns>
public static ColorRGBA Add(ColorRGBA left, ColorRGBA right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorRGBA"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorRGBA"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorRGBA"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorRGBA"/> from the first.</returns>
public static ColorRGBA Subtract(ColorRGBA left, ColorRGBA right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorRGBA"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorRGBA"/> by the scalar value.</returns>
public static ColorRGBA Multiply(ColorRGBA color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorRGBA"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorRGBA"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGBA"/> from the starting point to the ending point.</returns>
public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorRGBA"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorRGBA"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
/// </summary>
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
public override bool Equals(object? obj) => obj is ColorRGBA objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A);
/// <summary>
/// Generates a hash code for the <see cref="ColorRGBA"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
}
/// <summary>
/// Provides extension methods for <see cref="ColorRGBA"/> type.
/// </summary>
public static class ColorRGBAExtensions
{
/// <inheritdoc cref="ColorRGBA.Add(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Add(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Add(color, value);
/// <inheritdoc cref="ColorRGBA.Subtract(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Subtract(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Subtract(color, value);
/// <inheritdoc cref="ColorRGBA.Multiply(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Multiply(this ColorRGBA color, float value) => ColorRGBA.Multiply(color, value);
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
/// <inheritdoc cref="ColorRGBA.FromTo(ColorRGBA, ColorRGBA)" />
public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to);
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
@@ -68,12 +67,12 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary> /// </summary>
public static float GetT(Line2D line, Vector2D point) public static float GetT(Line2D line, Vector2D point)
{ {
float fromX = MathF.Abs(line.From.X); float fromX = Math.Abs(line.From.X);
float toX = MathF.Abs(line.To.X); float toX = Math.Abs(line.To.X);
float pointX = MathF.Abs(point.X); float pointX = Math.Abs(point.X);
float min = MathF.Min(fromX, toX); float min = Math.Min(fromX, toX);
float max = MathF.Max(fromX, toX) - min; float max = Math.Max(fromX, toX) - min;
pointX -= min; pointX -= min;
@@ -114,8 +113,8 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary> /// </summary>
public static bool OnSegment(Line2D line, Vector2D point) public static bool OnSegment(Line2D line, Vector2D point)
{ {
if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) && if (point.X <= Math.Max(line.From.X, line.To.X) && point.X >= Math.Min(line.From.X, line.To.X) &&
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y)) point.Y <= Math.Max(line.From.Y, line.To.Y) && point.Y >= Math.Min(line.From.Y, line.To.Y))
return true; return true;
return false; return false;
@@ -173,7 +172,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y); float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
t = MathF.Max(0, MathF.Min(1, t)); t = Math.Max(0, Math.Min(1, t));
float closestX = line.From.X + t * edgeVector.X; float closestX = line.From.X + t * edgeVector.X;
float closestY = line.From.Y + t * edgeVector.Y; float closestY = line.From.Y + t * edgeVector.Y;

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
@@ -178,11 +176,11 @@ public readonly struct Quaternion(float x, float y, float z, float w)
if (dot > 0.9995f) if (dot > 0.9995f)
return Lerp(from, to, t); return Lerp(from, to, t);
float angle = MathF.Acos(dot); float angle = Math.Acos(dot);
float sinAngle = MathF.Sin(angle); float sinAngle = Math.Sin(angle);
float fromWeight = MathF.Sin((1f - t) * angle) / sinAngle; float fromWeight = Math.Sin((1f - t) * angle) / sinAngle;
float toWeight = MathF.Sin(t * angle) / sinAngle; float toWeight = Math.Sin(t * angle) / sinAngle;
return from * fromWeight + to * toWeight; return from * fromWeight + to * toWeight;
} }
@@ -213,8 +211,8 @@ public readonly struct Quaternion(float x, float y, float z, float w)
public static Quaternion FromAxisAngle(Vector3D axis, float angle) public static Quaternion FromAxisAngle(Vector3D axis, float angle)
{ {
float halfAngle = angle * .5f; float halfAngle = angle * .5f;
float sinHalf = MathF.Sin(halfAngle); float sinHalf = Math.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, MathF.Cos(halfAngle)); return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
} }
/// <summary> /// <summary>
@@ -301,7 +299,7 @@ public readonly struct Quaternion(float x, float y, float z, float w)
/// Generates a hash code for the <see cref="Quaternion"/>. /// Generates a hash code for the <see cref="Quaternion"/>.
/// </summary> /// </summary>
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns> /// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z); public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
} }
/// <summary> /// <summary>

View File

@@ -11,19 +11,33 @@ namespace Syntriax.Engine.Core;
/// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices. /// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices.
/// </remarks> /// </remarks>
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")] [System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D> public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
{ {
public static readonly Shape2D Triangle = CreateNgon(3, Vector2D.Up); public static Shape2D Triangle => CreateNgon(3, Vector2D.Up);
public static readonly Shape2D Square = CreateNgon(4, Vector2D.One); public static Shape2D Square => CreateNgon(4, Vector2D.One);
public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up); public static Shape2D Pentagon => CreateNgon(5, Vector2D.Up);
public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right); public static Shape2D Hexagon => CreateNgon(6, Vector2D.Right);
private readonly List<Vector2D> _verticesList = vertices; public event ShapeUpdatedEventHandler? OnShapeUpdated = null;
private List<Vector2D> _vertices = vertices;
/// <summary> /// <summary>
/// Gets the vertices of the <see cref="Shape2D"/>. /// Gets the vertices of the <see cref="Shape2D"/>.
/// </summary> /// </summary>
public IReadOnlyList<Vector2D> Vertices => _verticesList; public IReadOnlyList<Vector2D> Vertices
{
get => _vertices;
set
{
_vertices.Clear();
foreach (Vector2D vertex in value)
_vertices.Add(vertex);
OnShapeUpdated?.InvokeSafe(this);
}
}
/// <summary> /// <summary>
/// The vertex at the specified index. /// The vertex at the specified index.
@@ -207,13 +221,15 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="from">The <see cref="Shape2D"/> to transform.</param> /// <param name="from">The <see cref="Shape2D"/> to transform.</param>
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param> /// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
/// <param name="to">The transformed <see cref="Shape2D"/>.</param> /// <param name="to">The transformed <see cref="Shape2D"/>.</param>
public static void Transform(Shape2D from, ITransform2D transform, ref Shape2D to) public static void Transform(Shape2D from, ITransform2D transform, Shape2D to)
{ {
to._verticesList.Clear(); to._vertices.Clear();
int count = from._verticesList.Count; int count = from._vertices.Count;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
to._verticesList.Add(transform.Transform(from[i])); to._vertices.Add(transform.Transform(from[i]));
to.OnShapeUpdated?.InvokeSafe(to);
} }
/// <summary> /// <summary>
@@ -240,6 +256,8 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <inheritdoc/> /// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
public delegate void ShapeUpdatedEventHandler(Shape2D shape2D);
} }
/// <summary> /// <summary>
@@ -275,13 +293,13 @@ public static class Shape2DExtensions
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform); public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" /> /// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" />
public static void Transform(this ITransform2D transform, Shape2D from, ref Shape2D to) => Shape2D.Transform(from, transform, ref to); public static void Transform(this ITransform2D transform, Shape2D from, Shape2D to) => Shape2D.Transform(from, transform, to);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" /> /// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" />
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform); public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, ref Shape2D)" /> /// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D,Shape2D)" />
public static void Transform(this Shape2D from, ITransform2D transform, ref Shape2D to) => Shape2D.Transform(from, transform, ref to); public static void Transform(this Shape2D from, ITransform2D transform, Shape2D to) => Shape2D.Transform(from, transform, to);
/// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" /> /// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" />
public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon); public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon);

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")] [System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
@@ -10,7 +8,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
public readonly Vector2D C { get; init; } = C; public readonly Vector2D C { get; init; } = C;
public readonly float Area public readonly float Area
=> .5f * MathF.Abs( => .5f * Math.Abs(
A.X * (B.Y - C.Y) + A.X * (B.Y - C.Y) +
B.X * (C.Y - A.Y) + B.X * (C.Y - A.Y) +
C.X * (A.Y - B.Y) C.X * (A.Y - B.Y)
@@ -25,7 +23,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X); float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
Vector2D center; Vector2D center;
if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon) if (Math.Abs(slopeAB - slopeBC) > float.Epsilon)
{ {
float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB)); float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB));
float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f; float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f;

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
@@ -316,7 +314,7 @@ public readonly struct Vector2D(float x, float y)
/// Generates a hash code for the <see cref="Vector2D"/>. /// Generates a hash code for the <see cref="Vector2D"/>.
/// </summary> /// </summary>
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns> /// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y); public override int GetHashCode() => System.HashCode.Combine(X, Y);
} }
/// <summary> /// <summary>

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
/// <summary> /// <summary>
@@ -290,7 +288,7 @@ public readonly struct Vector3D(float x, float y, float z)
/// Generates a hash code for the <see cref="Vector3D"/>. /// Generates a hash code for the <see cref="Vector3D"/>.
/// </summary> /// </summary>
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns> /// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z); public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
} }
/// <summary> /// <summary>

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Serialization;
public class EntityRegistry
{
public event EntityRegisteredEventHandler? OnEntityRegistered = null!;
private readonly Dictionary<string, Action<IEntity>?> assignCallbacks = [];
private readonly Dictionary<string, IEntity> registeredEntities = [];
public IReadOnlyDictionary<string, IEntity> RegisteredEntities => registeredEntities;
public void Add(IEntity entity)
{
if (registeredEntities.TryAdd(entity.Id, entity))
OnEntityRegistered?.InvokeSafe(this, entity);
}
public void QueueAssign(string id, Action<IEntity> setMethod)
{
assignCallbacks.TryAdd(id, null);
assignCallbacks[id] = assignCallbacks[id] + setMethod;
}
public void AssignAll()
{
foreach ((string id, Action<IEntity>? action) in assignCallbacks)
action?.InvokeSafe(registeredEntities[id]);
}
public void Reset()
{
assignCallbacks.Clear();
registeredEntities.Clear();
}
public delegate void EntityRegisteredEventHandler(EntityRegistry sender, IEntity entity);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
using Syntriax.Engine.Core.Serialization;
namespace Syntriax.Engine.Core; namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")] [System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
@@ -11,9 +13,9 @@ public class Transform2D : Behaviour, ITransform2D
private Vector2D _scale = Vector2D.One; private Vector2D _scale = Vector2D.One;
private float _rotation = 0f; private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero; [Serialize] private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One; [Serialize] private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f; [Serialize] private float _localRotation = 0f;
private ITransform2D? parentTransform = null; private ITransform2D? parentTransform = null;
@@ -29,7 +31,7 @@ public class Transform2D : Behaviour, ITransform2D
_position = value; _position = value;
UpdateLocalPosition(); UpdateLocalPosition();
OnPositionChanged?.InvokeSafe(this, _position); OnPositionChanged?.InvokeSafe(this, previousPosition);
} }
} }

View File

@@ -110,7 +110,7 @@ public class Universe : BaseEntity, IUniverse
public void Update(UniverseTime engineTime) public void Update(UniverseTime engineTime)
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
UnscaledTime = engineTime; UnscaledTime = engineTime;
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale))); Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
@@ -125,7 +125,7 @@ public class Universe : BaseEntity, IUniverse
public void PreDraw() public void PreDraw()
{ {
Debug.AssertHelpers.AssertInitialized(this); Debug.Assert.AssertInitialized(this);
for (int i = 0; i < UniverseObjects.Count; i++) for (int i = 0; i < UniverseObjects.Count; i++)
UniverseObjects[i].BehaviourController.UpdatePreDraw(); UniverseObjects[i].BehaviourController.UpdatePreDraw();

View File

@@ -156,6 +156,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
{ {
base.InitializeInternal(); base.InitializeInternal();
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this); _behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
_behaviourController.Initialize();
} }
public UniverseObject() public UniverseObject()

View File

@@ -18,7 +18,7 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2
private Shape2D _shapeWorld = Shape2D.Square.CreateCopy(); private Shape2D _shapeWorld = Shape2D.Square.CreateCopy();
private Shape2D _shapeLocal = Shape2D.Square; private Shape2D _shapeLocal = Shape2D.Square;
public override void CalculateCollider() => Transform.Transform(ShapeLocal, ref _shapeWorld); public override void CalculateCollider() => ShapeLocal.Transform(Transform, _shapeWorld);
public Collider2DShapeBehaviour() { } public Collider2DShapeBehaviour() { }
public Collider2DShapeBehaviour(Shape2D shape) public Collider2DShapeBehaviour(Shape2D shape)

View File

@@ -2,7 +2,7 @@ using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D; namespace Syntriax.Engine.Physics2D;
public static partial class Physics2D public static class Physics2D
{ {
public static bool Overlaps(this Shape2D shape, Vector2D point) => Overlaps(shape, point, out float _); public static bool Overlaps(this Shape2D shape, Vector2D point) => Overlaps(shape, point, out float _);
public static bool Overlaps(this Shape2D shape, Vector2D point, out float depth) public static bool Overlaps(this Shape2D shape, Vector2D point, out float depth)

View File

@@ -0,0 +1,10 @@
namespace Syntriax.Engine.Physics2D
{
// This is pretty much so the assembly gets loaded automatically because
// the builds include the assembly but sometimes doesn't link load it at startup.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@@ -0,0 +1,13 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public interface IEngineTypeYamlConverter : IYamlTypeConverter
{
YamlSerializer Serializer { get; set; }
EntityRegistry EntityRegistry { get; set; }
IProgressionTracker ProgressionTracker { get; set; }
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehaviourController>
{
private const string BEHAVIOURS_SCALAR_NAME = "Behaviours";
public override IBehaviourController? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour controller");
string id;
IBehaviourController behaviourController;
IStateEnable stateEnable;
List<IBehaviour> behaviours;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviourController.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviourController = (IBehaviourController)instanceSerializedClass.CreateInstance(EntityRegistry);
string value = parser.Consume<Scalar>().Value;
if (value.CompareTo(nameof(IBehaviourController.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
if (parser.Consume<Scalar>().Value.CompareTo(BEHAVIOURS_SCALAR_NAME) != 0)
throw new();
behaviours = (List<IBehaviour>)rootDeserializer(typeof(List<IBehaviour>))!;
parser.Consume<MappingEnd>();
behaviourController.Id = id;
stateEnable.Assign(behaviourController);
behaviourController.Assign(stateEnable);
foreach (IBehaviour behaviour in behaviours)
behaviourController.AddBehaviour(behaviour);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviourController;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IBehaviourController behaviourController = (IBehaviourController)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour controller");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviourController.Id)));
emitter.Emit(new Scalar(behaviourController.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(behaviourController));
emitter.Emit(new Scalar(nameof(IBehaviourController.StateEnable)));
serializer(behaviourController.StateEnable);
emitter.Emit(new Scalar(BEHAVIOURS_SCALAR_NAME));
serializer(behaviourController.GetBehaviours<IBehaviour>().Where(b => !b.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour controller");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,81 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
{
public override IBehaviour? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour information");
string id;
int priority;
IBehaviour behaviour;
IStateEnable stateEnable;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.Priority)) != 0)
throw new();
priority = int.Parse(parser.Consume<Scalar>().Value);
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviour = (IBehaviour)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
parser.Consume<MappingEnd>();
behaviour.Id = id;
behaviour.Priority = priority;
stateEnable.Assign(behaviour);
behaviour.Assign(stateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviour;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IBehaviour behaviour = (IBehaviour)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviour.Id)));
emitter.Emit(new Scalar(behaviour.Id));
emitter.Emit(new Scalar(nameof(IBehaviour.Priority)));
emitter.Emit(new Scalar(behaviour.Priority.ToString()));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(behaviour));
emitter.Emit(new Scalar(nameof(IBehaviour.StateEnable)));
serializer(behaviour.StateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,33 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public abstract class EngineTypeYamlSerializerBase<T> : IEngineTypeYamlConverter
{
protected const string SERIALIZED_SCALAR_NAME = "Properties";
public EntityRegistry EntityRegistry { get; set; } = null!;
public YamlSerializer Serializer { get; set; } = null!;
public IProgressionTracker ProgressionTracker { get; set; } = null!;
public bool Accepts(Type type) => typeof(T).IsAssignableFrom(type);
public abstract T? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer);
public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
T? result = Read(parser, type, rootDeserializer);
if (result is IEntity entity)
EntityRegistry.Add(entity);
return result;
}
public abstract void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer);
}

View File

@@ -0,0 +1,41 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class AABBConverter : EngineTypeYamlSerializerBase<AABB>
{
public override AABB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.LowerBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must start with {nameof(AABB.LowerBoundary)}");
Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.UpperBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must end with {nameof(AABB.UpperBoundary)}");
Vector2D upperBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new AABB(lowerBoundary, upperBoundary);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
AABB aabb = (AABB)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(AABB.LowerBoundary)));
serializer(aabb.LowerBoundary, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(AABB.UpperBoundary)));
serializer(aabb.UpperBoundary, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class CircleConverter : EngineTypeYamlSerializerBase<Circle>
{
public override Circle Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Circle.Center)) != 0)
throw new ArgumentException($"{nameof(Circle)} mapping must start with {nameof(Circle.Center)}");
Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Circle.Radius)) != 0)
throw new ArgumentException($"{nameof(Circle)} mapping must end with {nameof(Circle.Radius)}");
float radius = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Circle(lowerBoundary, radius);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Circle circle = (Circle)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Circle.Center)));
serializer(circle.Center, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Circle.Radius)));
serializer(circle.Radius, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorHSVConverter : EngineTypeYamlSerializerBase<ColorHSV>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorHSV).Length + 1;
public override ColorHSV Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorHSV(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorHSV hsv = (ColorHSV)value!;
emitter.Emit(new Scalar($"{nameof(ColorHSV)}({hsv.Hue}, {hsv.Saturation}, {hsv.Value})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorRGBAConverter : EngineTypeYamlSerializerBase<ColorRGBA>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGBA).Length + 1;
public override ColorRGBA Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGBA(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]), byte.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGBA rgb = (ColorRGBA)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGBA)}({rgb.R}, {rgb.G}, {rgb.B}, {rgb.A})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorRGBConverter : EngineTypeYamlSerializerBase<ColorRGB>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGB).Length + 1;
public override ColorRGB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGB(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGB rgb = (ColorRGB)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGB)}({rgb.R}, {rgb.G}, {rgb.B})"));
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Line2DConverter : EngineTypeYamlSerializerBase<Line2D>
{
public override Line2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2D.From)) != 0)
throw new ArgumentException($"{nameof(Line2D)} mapping must start with {nameof(Line2D.From)}");
Vector2D from = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2D.To)) != 0)
throw new ArgumentException($"{nameof(Line2D)} mapping must end with {nameof(Line2D.To)}");
Vector2D to = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new Line2D(from, to);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Line2D line2D = (Line2D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Line2D.From)));
serializer(line2D.From, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Line2D.To)));
serializer(line2D.To, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Line2DEquationConverter : EngineTypeYamlSerializerBase<Line2DEquation>
{
public override Line2DEquation Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2DEquation.Slope)) != 0)
throw new ArgumentException($"{nameof(Line2DEquation)} mapping must start with {nameof(Line2DEquation.Slope)}");
float slope = (float)rootDeserializer(typeof(float))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line2DEquation.OffsetY)) != 0)
throw new ArgumentException($"{nameof(Line2DEquation)} mapping must end with {nameof(Line2DEquation.OffsetY)}");
float offset = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Line2DEquation(slope, offset);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Line2DEquation line2DEquation = (Line2DEquation)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Line2DEquation.Slope)));
serializer(line2DEquation.Slope, typeof(float));
emitter.Emit(new Scalar(nameof(Line2DEquation.OffsetY)));
serializer(line2DEquation.OffsetY, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Projection1DConverter : EngineTypeYamlSerializerBase<Projection1D>
{
public override Projection1D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Projection1D.Min)) != 0)
throw new ArgumentException($"{nameof(Projection1D)} mapping must start with {nameof(Projection1D.Min)}");
float min = (float)rootDeserializer(typeof(float))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Projection1D.Max)) != 0)
throw new ArgumentException($"{nameof(Projection1D)} mapping must end with {nameof(Projection1D.Max)}");
float max = (float)rootDeserializer(typeof(float))!;
parser.Consume<MappingEnd>();
return new Projection1D(min, max);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Projection1D projection1D = (Projection1D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Projection1D.Min)));
serializer(projection1D.Min, typeof(float));
emitter.Emit(new Scalar(nameof(Projection1D.Max)));
serializer(projection1D.Max, typeof(float));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class QuaternionConverter : EngineTypeYamlSerializerBase<Quaternion>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Quaternion).Length + 1;
public override Quaternion Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Quaternion(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Quaternion quaternion = (Quaternion)value!;
emitter.Emit(new Scalar($"{nameof(Quaternion)}({quaternion.X}, {quaternion.Y}, {quaternion.Z}, {quaternion.W})"));
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Shape2DConverter : EngineTypeYamlSerializerBase<Shape2D>
{
public override Shape2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Shape2D.Vertices)) != 0)
throw new ArgumentException($"{nameof(Shape2D)} mapping have a {nameof(Shape2D.Vertices)}");
parser.Consume<SequenceStart>();
List<Vector2D> vertices = [];
while (!parser.TryConsume<SequenceEnd>(out _))
{
Vector2D vertex = (Vector2D)rootDeserializer(typeof(Vector2D))!;
vertices.Add(vertex);
}
parser.Consume<MappingEnd>();
return new Shape2D(vertices);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Shape2D shape2D = (Shape2D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Shape2D.Vertices)));
emitter.Emit(new SequenceStart(anchor: null, tag: null, isImplicit: false, style: SequenceStyle.Block));
foreach (Vector2D vertex in shape2D.Vertices)
serializer(vertex, typeof(Vector2D));
emitter.Emit(new SequenceEnd());
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,47 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class TriangleConverter : EngineTypeYamlSerializerBase<Triangle>
{
public override Triangle Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.A)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must start with {nameof(Triangle.A)}");
Vector2D a = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.B)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must have {nameof(Triangle.B)} after {nameof(Triangle.A)}");
Vector2D b = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Triangle.C)) != 0)
throw new ArgumentException($"{nameof(Triangle)} mapping must end with {nameof(Triangle.C)}");
Vector2D c = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new Triangle(a, b, c);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Triangle aabb = (Triangle)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Triangle.A)));
serializer(aabb.A, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Triangle.B)));
serializer(aabb.B, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(Triangle.C)));
serializer(aabb.C, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Vector2DConverter : EngineTypeYamlSerializerBase<Vector2D>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector2D).Length + 1;
public override Vector2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector2D(float.Parse(values[0]), float.Parse(values[1]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector2D vector2D = (Vector2D)value!;
emitter.Emit(new Scalar($"{nameof(Vector2D)}({vector2D.X}, {vector2D.Y})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class Vector3DConverter : EngineTypeYamlSerializerBase<Vector3D>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector3D).Length + 1;
public override Vector3D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector3D(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector3D vector3D = (Vector3D)value!;
emitter.Emit(new Scalar($"{nameof(Vector3D)}({vector3D.X}, {vector3D.Y}, {vector3D.Z})"));
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Factory;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedClass>
{
public override SerializedClass? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading class");
SerializedClass serializedClass = new();
Dictionary<string, TypeContainer> publicDictionary = [];
Dictionary<string, TypeContainer> privateDictionary = [];
parser.Consume<MappingStart>();
while (!parser.TryConsume<MappingEnd>(out _))
{
string key = parser.Consume<Scalar>().Value;
switch (key)
{
case nameof(SerializedClass.Type): serializedClass.Type = parser.Consume<Scalar>().Value; break;
case nameof(SerializedClass.Public): publicDictionary = (Dictionary<string, TypeContainer>)rootDeserializer(typeof(Dictionary<string, TypeContainer>))!; break;
case nameof(SerializedClass.Private): privateDictionary = (Dictionary<string, TypeContainer>)rootDeserializer(typeof(Dictionary<string, TypeContainer>))!; break;
}
}
foreach ((string key, TypeContainer typeContainer) in publicDictionary)
serializedClass.Public.Add(key, Serializer.InternalDeserialize(typeContainer.Value!.ToString()!, TypeFactory.GetType(typeContainer.Type)));
foreach ((string key, TypeContainer typeContainer) in privateDictionary)
serializedClass.Private.Add(key, Serializer.InternalDeserialize(typeContainer.Value!.ToString()!, TypeFactory.GetType(typeContainer.Type)));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Read {serializedClass.Type}");
return serializedClass;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
SerializedClass serializedClass = (SerializedClass)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing {serializedClass.Type}");
Dictionary<string, TypeContainer> publics = [];
Dictionary<string, TypeContainer> privates = [];
foreach ((string key, object? @object) in serializedClass.Public.Where(v => !v.GetType().HasAttribute<IgnoreSerializationAttribute>()))
publics.Add(key, new TypeContainer(@object));
foreach ((string key, object? @object) in serializedClass.Private.Where(v => !v.GetType().HasAttribute<IgnoreSerializationAttribute>()))
privates.Add(key, new TypeContainer(@object));
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(SerializedClass.Type)));
emitter.Emit(new Scalar(serializedClass.Type));
if (publics.Count > 0)
{
emitter.Emit(new Scalar(nameof(SerializedClass.Public)));
serializer(publics);
}
if (privates.Count > 0)
{
emitter.Emit(new Scalar(nameof(SerializedClass.Private)));
serializer(privates);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized {serializedClass.Type}");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,60 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
{
public override IStateEnable? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading state enable");
bool enabled;
IStateEnable stateEnable;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IStateEnable.Enabled)) != 0)
throw new();
enabled = bool.Parse(parser.Consume<Scalar>().Value);
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
stateEnable = (IStateEnable)instanceSerializedClass.CreateInstance(EntityRegistry);
parser.Consume<MappingEnd>();
stateEnable.Enabled = enabled;
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Read state enable");
return stateEnable;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IStateEnable stateEnable = (IStateEnable)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing state enable");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IStateEnable.Enabled)));
emitter.Emit(new Scalar(stateEnable.Enabled.ToString()));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(stateEnable));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized state enable");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,46 @@
using System;
using Syntriax.Engine.Core.Serialization;
using Syntriax.Engine.Core.Factory;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class TypeContainerConverter : EngineTypeYamlSerializerBase<TypeContainer>
{
public override TypeContainer Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(TypeContainer.Type)) != 0)
throw new ArgumentException($"{nameof(TypeContainer)} mapping must start with {nameof(TypeContainer.Type)}");
string typeFullName = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(TypeContainer.Value)) != 0)
throw new ArgumentException($"{nameof(TypeContainer)} mapping must end with {nameof(TypeContainer.Type)}");
object? value = rootDeserializer(TypeFactory.GetType(typeFullName));
parser.Consume<MappingEnd>();
return new TypeContainer() { Type = typeFullName, Value = value };
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
TypeContainer? typeContainer = (TypeContainer)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(TypeContainer.Type)));
emitter.Emit(new Scalar(typeContainer.Type));
emitter.Emit(new Scalar(nameof(TypeContainer.Value)));
serializer(typeContainer.Value, TypeFactory.GetType(typeContainer.Type));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
{
public override IUniverse? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? 0.1f : ProgressionTracker.Progression, "Reading universe");
string id;
IUniverse universe;
IStateEnable stateEnable;
List<IUniverseObject> universeObjects;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .2f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universe = (IUniverse)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Reading universe objects");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.UniverseObjects)) != 0)
throw new();
universeObjects = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
parser.Consume<MappingEnd>();
universe.Id = id;
stateEnable.Assign(universe);
universe.Assign(stateEnable);
ProgressionTracker.Set(isTrackingController ? .9f : ProgressionTracker.Progression, "Registering universe objects");
for (int i = 0; i < universeObjects.Count; i++)
{
IUniverseObject uo = universeObjects[i];
ProgressionTracker.Set(isTrackingController ? .9f + .1f * ((float)i / universeObjects.Count) : ProgressionTracker.Progression, $"Registering {uo.Name}");
universe.Register(uo);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return universe;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IUniverse universe = (IUniverse)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe");
IEnumerable<IUniverseObject> rootUniverseObjects = universe.UniverseObjects.Where(uo => uo.Parent is null);
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IUniverse.Id)));
emitter.Emit(new Scalar(universe.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(universe));
emitter.Emit(new Scalar(nameof(IUniverse.StateEnable)));
serializer(universe.StateEnable);
emitter.Emit(new Scalar(nameof(IUniverse.UniverseObjects)));
serializer(rootUniverseObjects);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized universe");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseObject>
{
public override IUniverseObject? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
string name;
string id;
IUniverseObject universeObject;
IStateEnable stateEnable;
IBehaviourController behaviourController;
List<IUniverseObject> children;
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Name)) != 0)
throw new();
name = parser.Consume<Scalar>().Value;
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading {name}");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Id)) != 0)
throw new();
id = parser.Consume<Scalar>().Value;
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universeObject = (IUniverseObject)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.BehaviourController)) != 0)
throw new();
behaviourController = (IBehaviourController)rootDeserializer(typeof(IBehaviourController))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Children)) != 0)
throw new();
children = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
parser.Consume<MappingEnd>();
universeObject.Id = id;
universeObject.Name = name;
stateEnable.Assign(universeObject);
universeObject.Assign(stateEnable);
behaviourController.Assign(universeObject);
universeObject.Assign(behaviourController);
foreach (IUniverseObject child in children)
universeObject.AddChild(child);
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Created {name}");
return universeObject;
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
IUniverseObject universeObject = (IUniverseObject)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IUniverseObject.Name)));
emitter.Emit(new Scalar(universeObject.Name));
emitter.Emit(new Scalar(nameof(IUniverseObject.Id)));
emitter.Emit(new Scalar(universeObject.Id));
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(universeObject));
emitter.Emit(new Scalar(nameof(IUniverseObject.StateEnable)));
serializer(universeObject.StateEnable);
emitter.Emit(new Scalar(nameof(IUniverseObject.BehaviourController)));
serializer(universeObject.BehaviourController);
emitter.Emit(new Scalar(nameof(IUniverseObject.Children)));
serializer(universeObject.Children.Where(c => !c.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Serializers.Yaml</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Engine\Engine.csproj" />
<ProjectReference Include="..\YamlDotNet\YamlDotNet\YamlDotNet.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
using System;
namespace Syntriax.Engine.Serializers.Yaml;
[Serializable]
public class SerializerInProgressException() : Exception("There's already a running deserialization in progress.");

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Syntriax.Engine.Serializers.Yaml;
public class YamlSerializer : Core.Serialization.ISerializer
{
private readonly YamlDotNet.Serialization.ISerializer serializer = null!;
private readonly YamlDotNet.Serialization.IDeserializer deserializer = null!;
private readonly EntityRegistry entityRegistry = null!;
private readonly IProgressionTracker progressionTracker = null!;
private readonly object Lock = new();
public YamlSerializer()
{
entityRegistry = new();
progressionTracker = new ProgressionTracker();
SerializerBuilder serializerBuilder = new SerializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.DisableAliases();
DeserializerBuilder deserializerBuilder = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance);
foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters())
{
typeConverter.Serializer = this;
typeConverter.EntityRegistry = entityRegistry;
typeConverter.ProgressionTracker = progressionTracker;
deserializerBuilder = deserializerBuilder.WithTypeConverter(typeConverter);
serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter);
}
serializer = serializerBuilder.Build();
deserializer = deserializerBuilder.Build();
}
private static IEnumerable<IEngineTypeYamlConverter> GetEngineYamlTypeConverters()
{
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(IEngineTypeYamlConverter).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract))
yield return (Activator.CreateInstance(type) as IEngineTypeYamlConverter)!;
}
public string Serialize(object instance)
{
lock (Lock)
{
return serializer.Serialize(instance);
}
}
public object Deserialize(string configuration)
{
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration)!;
entityRegistry.AssignAll();
return result;
}
}
public object Deserialize(string configuration, Type type)
{
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration, type)!;
entityRegistry.AssignAll();
return result;
}
}
public T Deserialize<T>(string configuration)
{
lock (Lock)
{
entityRegistry.Reset();
T result = deserializer.Deserialize<T>(configuration);
entityRegistry.AssignAll();
return result;
}
}
public ProgressiveTask<object> DeserializeAsync(string configuration)
{
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<object> DeserializeAsync(string configuration, Type type)
{
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration, type));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<T> DeserializeAsync<T>(string configuration)
{
lock (Lock)
{
progressionTracker.Reset();
Task<T> task = Task.Run(() => Deserialize<T>(configuration));
return new ProgressiveTask<T>(progressionTracker, task);
}
}
public ProgressiveTask<string> SerializeAsync(object instance)
{
lock (Lock)
{
progressionTracker.Reset();
Task<string> task = Task.Run(() => Serialize(instance));
return new ProgressiveTask<string>(progressionTracker, task);
}
}
internal object InternalDeserialize(string configuration, Type type)
{
return deserializer.Deserialize(configuration, type)!;
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Systems</RootNamespace> <RootNamespace>Syntriax.Engine.Systems</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@@ -0,0 +1,10 @@
namespace Syntriax.Engine.Systems
{
// This is pretty much so the assembly gets loaded automatically because
// the builds include the assembly but sometimes doesn't link load it at startup.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@@ -1,14 +1,14 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.StateMachine; namespace Syntriax.Engine.Systems.StateMachine;
public interface IState public interface IState : IEntity, INameable
{ {
event StateUpdateEventHandler? OnStateUpdate; event StateUpdateEventHandler? OnStateUpdate;
event StateTransitionedFromEventHandler? OnStateTransitionedFrom; event StateTransitionedFromEventHandler? OnStateTransitionedFrom;
event StateTransitionedToEventHandler? OnStateTransitionedTo; event StateTransitionedToEventHandler? OnStateTransitionedTo;
event StateTransitionReadyEventHandler? OnStateTransitionReady; event StateTransitionReadyEventHandler? OnStateTransitionReady;
string Name { get; }
IState? GetNextState(); IState? GetNextState();
void Update(); void Update();

View File

@@ -1,20 +1,36 @@
using System.Collections.Generic;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.StateMachine; namespace Syntriax.Engine.Systems.StateMachine;
public class State : IState public class State : BaseEntity, IState
{ {
public event IState.StateUpdateEventHandler? OnStateUpdate = null; public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null; public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null; public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public event IState.StateTransitionReadyEventHandler? OnStateTransitionReady = null; public event IState.StateTransitionReadyEventHandler? OnStateTransitionReady = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null;
private readonly List<StateTransition> transitions = []; private readonly List<StateTransition> transitions = [];
private readonly Dictionary<string, StateTransition> possibleTransitions = []; private readonly Dictionary<string, StateTransition> possibleTransitions = [];
private string _name = "Default State Name";
public string Name { get; set; } = "Default State Name";
public IReadOnlyList<StateTransition> Transitions => transitions; public IReadOnlyList<StateTransition> Transitions => transitions;
public IReadOnlyDictionary<string, StateTransition> PossibleTransitions => possibleTransitions; public IReadOnlyDictionary<string, StateTransition> PossibleTransitions => possibleTransitions;
public string Name
{
get => _name;
set
{
if (_name.CompareTo(value) == 0)
return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
}
}
public void RemoveTransition(string name) public void RemoveTransition(string name)
{ {

View File

@@ -7,10 +7,24 @@ public abstract class StateBehaviourBase : Behaviour, IState
public event IState.StateUpdateEventHandler? OnStateUpdate = null; public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null; public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null; public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null;
public abstract event IState.StateTransitionReadyEventHandler? OnStateTransitionReady; public abstract event IState.StateTransitionReadyEventHandler? OnStateTransitionReady;
public abstract string Name { get; } private string _name = string.Empty;
public string Name
{
get => _name;
set
{
if (_name.CompareTo(value) == 0)
return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
}
}
protected virtual void OnUpdateState() { } protected virtual void OnUpdateState() { }
public void Update() public void Update()

View File

@@ -1,4 +1,5 @@
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
namespace Syntriax.Engine.Systems.StateMachine; namespace Syntriax.Engine.Systems.StateMachine;
@@ -7,6 +8,7 @@ public class StateMachine : Behaviour
public event StateChangedEventHandler? OnStateChanged = null; public event StateChangedEventHandler? OnStateChanged = null;
private IState _state = new State(); private IState _state = new State();
[Serialize]
public IState State public IState State
{ {
get => _state; get => _state;

View File

@@ -1,3 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Syntriax.Engine.Systems.StateMachine; namespace Syntriax.Engine.Systems.StateMachine;
public readonly record struct StateTransition(IState State, IReadOnlyList<Func<bool>> Conditions) public readonly record struct StateTransition(IState State, IReadOnlyList<Func<bool>> Conditions)

View File

@@ -1,5 +1,7 @@
// Reference: https://easings.net // Reference: https://easings.net
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween; namespace Syntriax.Engine.Systems.Tween;
internal static class EaseConstants internal static class EaseConstants
@@ -7,52 +9,54 @@ 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 * Core.Math.PI / 3f; internal const float c4 = 2f * Math.PI / 3f;
internal const float c5 = 2f * Core.Math.PI / 4.5f; internal const float c5 = 2f * Math.PI / 4.5f;
} }
public readonly struct EaseLinear : IEasing { public readonly float Evaluate(float x) => x; } public abstract class EasingBase<T> where T : IEasing, new() { public static readonly T Instance = new(); }
public readonly struct EaseInQuad : IEasing { public readonly float Evaluate(float x) => x * x; } public class EaseLinear : EasingBase<EaseLinear>, IEasing { public float Evaluate(float x) => x; }
public readonly struct EaseOutQuad : IEasing { public readonly float Evaluate(float x) => 1f - (1f - x) * (1f - x); }
public readonly struct EaseInOutQuad : IEasing { public readonly float Evaluate(float x) => x < .5f ? 2f * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 2f) * .5f; }
public readonly struct EaseInCubic : IEasing { public readonly float Evaluate(float x) => x * x * x; } public class EaseInQuad : EasingBase<EaseInQuad>, IEasing { public float Evaluate(float x) => x * x; }
public readonly struct EaseOutCubic : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 3f); } public class EaseOutQuad : EasingBase<EaseOutQuad>, IEasing { public float Evaluate(float x) => 1f - (1f - x) * (1f - x); }
public readonly struct EaseInOutCubic : IEasing { public readonly float Evaluate(float x) => x < .5f ? 4f * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 3f) * .5f; } public class EaseInOutQuad : EasingBase<EaseInOutQuad>, IEasing { public float Evaluate(float x) => x < .5f ? 2f * x * x : 1f - Math.Pow(-2f * x + 2f, 2f) * .5f; }
public readonly struct EaseInQuart : IEasing { public readonly float Evaluate(float x) => x * x * x * x; } public class EaseInCubic : EasingBase<EaseInCubic>, IEasing { public float Evaluate(float x) => x * x * x; }
public readonly struct EaseOutQuart : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 4f); } public class EaseOutCubic : EasingBase<EaseOutCubic>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 3f); }
public readonly struct EaseInOutQuart : IEasing { public readonly float Evaluate(float x) => x < .5f ? 8f * x * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 4f) * .5f; } public class EaseInOutCubic : EasingBase<EaseInOutCubic>, IEasing { public float Evaluate(float x) => x < .5f ? 4f * x * x * x : 1f - Math.Pow(-2f * x + 2f, 3f) * .5f; }
public readonly struct EaseInQuint : IEasing { public readonly float Evaluate(float x) => x * x * x * x * x; } public class EaseInQuart : EasingBase<EaseInQuart>, IEasing { public float Evaluate(float x) => x * x * x * x; }
public readonly struct EaseOutQuint : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 5f); } public class EaseOutQuart : EasingBase<EaseOutQuart>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 4f); }
public readonly struct EaseInOutQuint : IEasing { public readonly float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 5f) * .5f; } public class EaseInOutQuart : EasingBase<EaseInOutQuart>, IEasing { public float Evaluate(float x) => x < .5f ? 8f * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 4f) * .5f; }
public readonly struct EaseInSine : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Cos(x * Core.Math.PI * .5f); } public class EaseInQuint : EasingBase<EaseInQuint>, IEasing { public float Evaluate(float x) => x * x * x * x * x; }
public readonly struct EaseOutSine : IEasing { public readonly float Evaluate(float x) => Core.Math.Sin(x * Core.Math.PI * .5f); } public class EaseOutQuint : EasingBase<EaseOutQuint>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 5f); }
public readonly struct EaseInOutSine : IEasing { public readonly float Evaluate(float x) => -(Core.Math.Cos(Core.Math.PI * x) - 1f) * .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 readonly struct EaseInExpo : IEasing { public readonly float Evaluate(float x) => x == 0f ? 0f : Core.Math.Pow(2f, 10f * x - 10f); } public class EaseInSine : EasingBase<EaseInSine>, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.PI * .5f); }
public readonly struct EaseOutExpo : IEasing { public readonly float Evaluate(float x) => x == 1f ? 1f : 1f - Core.Math.Pow(2f, -10f * x); } public class EaseOutSine : EasingBase<EaseOutSine>, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.PI * .5f); }
public readonly struct EaseInOutExpo : IEasing { public readonly float Evaluate(float x) => x == 0f ? 0f : x == 1f ? 1f : x < .5f ? Core.Math.Pow(2f, 20f * x - 10f) * .5f : (2f - Core.Math.Pow(2f, -20f * x + 10f)) * .5f; } public class EaseInOutSine : EasingBase<EaseInOutSine>, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.PI * x) - 1f) * .5f; }
public readonly struct EaseInCirc : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Sqrt(1f - Core.Math.Pow(x, 2f)); } public class EaseInExpo : EasingBase<EaseInExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : Math.Pow(2f, 10f * x - 10f); }
public readonly struct EaseOutCirc : IEasing { public readonly float Evaluate(float x) => Core.Math.Sqrt(1f - Core.Math.Pow(x - 1f, 2f)); } public class EaseOutExpo : EasingBase<EaseOutExpo>, IEasing { public float Evaluate(float x) => x == 1f ? 1f : 1f - Math.Pow(2f, -10f * x); }
public readonly struct EaseInOutCirc : IEasing { public readonly float Evaluate(float x) => x < .5f ? (1f - Core.Math.Sqrt(1f - Core.Math.Pow(2f * x, 2f))) * .5f : (Core.Math.Sqrt(1f - Core.Math.Pow(-2f * x + 2f, 2f)) + 1f) * .5f; } public class EaseInOutExpo : EasingBase<EaseInOutExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : x == 1f ? 1f : x < .5f ? Math.Pow(2f, 20f * x - 10f) * .5f : (2f - Math.Pow(2f, -20f * x + 10f)) * .5f; }
public readonly struct EaseInBack : IEasing { public readonly float Evaluate(float x) => EaseConstants.c3 * x * x * x - EaseConstants.c1 * x * x; } public class EaseInCirc : EasingBase<EaseInCirc>, IEasing { public float Evaluate(float x) => 1f - Math.Sqrt(1f - Math.Pow(x, 2f)); }
public readonly struct EaseOutBack : IEasing { public readonly float Evaluate(float x) => 1f + EaseConstants.c3 * Core.Math.Pow(x - 1f, 3f) + EaseConstants.c1 * Core.Math.Pow(x - 1f, 2f); } public class EaseOutCirc : EasingBase<EaseOutCirc>, IEasing { public float Evaluate(float x) => Math.Sqrt(1f - Math.Pow(x - 1f, 2f)); }
public readonly struct EaseInOutBack : IEasing { public readonly float Evaluate(float x) => x < .5f ? Core.Math.Pow(2f * x, 2f) * ((EaseConstants.c2 + 1f) * 2f * x - EaseConstants.c2) * .5f : (Core.Math.Pow(2f * x - 2f, 2f) * ((EaseConstants.c2 + 1f) * (x * 2f - 2f) + EaseConstants.c2) + 2f) * .5f; } public class EaseInOutCirc : EasingBase<EaseInOutCirc>, IEasing { public float Evaluate(float x) => x < .5f ? (1f - Math.Sqrt(1f - Math.Pow(2f * x, 2f))) * .5f : (Math.Sqrt(1f - Math.Pow(-2f * x + 2f, 2f)) + 1f) * .5f; }
public readonly struct EaseInElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : -Core.Math.Pow(2f, 10f * x - 10f) * Core.Math.Sin((x * 10f - 10.75f) * EaseConstants.c4); } public class EaseInBack : EasingBase<EaseInBack>, IEasing { public float Evaluate(float x) => EaseConstants.c3 * x * x * x - EaseConstants.c1 * x * x; }
public readonly struct EaseOutElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : Core.Math.Pow(2f, -10f * x) * Core.Math.Sin((x * 10f - .75f) * EaseConstants.c4) + 1f; } public class EaseOutBack : EasingBase<EaseOutBack>, IEasing { public float Evaluate(float x) => 1f + EaseConstants.c3 * Math.Pow(x - 1f, 3f) + EaseConstants.c1 * Math.Pow(x - 1f, 2f); }
public readonly struct EaseInOutElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : x < .5f ? -(Core.Math.Pow(2f, 20f * x - 10f) * Core.Math.Sin((20f * x - 11.125f) * EaseConstants.c5)) * .5f : Core.Math.Pow(2f, -20f * x + 10f) * Core.Math.Sin((20f * x - 11.125f) * EaseConstants.c5) * .5f + 1f; } public class EaseInOutBack : EasingBase<EaseInOutBack>, IEasing { public float Evaluate(float x) => x < .5f ? Math.Pow(2f * x, 2f) * ((EaseConstants.c2 + 1f) * 2f * x - EaseConstants.c2) * .5f : (Math.Pow(2f * x - 2f, 2f) * ((EaseConstants.c2 + 1f) * (x * 2f - 2f) + EaseConstants.c2) + 2f) * .5f; }
public readonly struct EaseInBounce : IEasing { public readonly float Evaluate(float x) => 1f - new EaseOutBounce().Evaluate(1f - x); } public class EaseInElastic : EasingBase<EaseInElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : -Math.Pow(2f, 10f * x - 10f) * Math.Sin((x * 10f - 10.75f) * EaseConstants.c4); }
public readonly struct EaseOutBounce : IEasing public class EaseOutElastic : EasingBase<EaseOutElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : Math.Pow(2f, -10f * x) * Math.Sin((x * 10f - .75f) * EaseConstants.c4) + 1f; }
public class EaseInOutElastic : EasingBase<EaseInOutElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : x < .5f ? -(Math.Pow(2f, 20f * x - 10f) * Math.Sin((20f * x - 11.125f) * EaseConstants.c5)) * .5f : Math.Pow(2f, -20f * x + 10f) * Math.Sin((20f * x - 11.125f) * EaseConstants.c5) * .5f + 1f; }
public class EaseInBounce : EasingBase<EaseInBounce>, IEasing { public float Evaluate(float x) => 1f - EaseOutBounce.Instance.Evaluate(1f - x); }
public class EaseOutBounce : EasingBase<EaseOutBounce>, IEasing
{ {
public readonly float Evaluate(float x) public float Evaluate(float x)
{ {
const float n1 = 7.5625f; const float n1 = 7.5625f;
const float d1 = 2.75f; const float d1 = 2.75f;
@@ -69,4 +73,4 @@ public readonly struct EaseOutBounce : IEasing
return n1 * (x -= 2.625f / d1) * x + .984375f; return n1 * (x -= 2.625f / d1) * x + .984375f;
} }
} }
public readonly struct EaseInOutBounce : IEasing { public readonly float Evaluate(float x) => x < .5f ? (1f - new EaseOutBounce().Evaluate(1f - 2f * x)) * .5f : (1f + new EaseOutBounce().Evaluate(2f * x - 1f)) * .5f; } public class EaseInOutBounce : IEasing { public float Evaluate(float x) => x < .5f ? (1f - EaseOutBounce.Instance.Evaluate(1f - 2f * x)) * .5f : (1f + EaseOutBounce.Instance.Evaluate(2f * x - 1f)) * .5f; }

View File

@@ -0,0 +1,10 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenAABBExtensions
{
public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, Action<AABB> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(new AABB(initialAABB.LowerBoundary.Lerp(targetAABB.LowerBoundary, t), initialAABB.UpperBoundary.Lerp(targetAABB.UpperBoundary, t))));
}

View File

@@ -0,0 +1,12 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenCamera2DExtensions
{
public static ITween TweenZoom(this ICamera2D camera2D, ITweenManager tweenManager, float duration, float targetZoom)
{
float initialZoom = camera2D.Zoom;
return tweenManager.StartTween(duration, t => camera2D.Zoom = initialZoom.Lerp(targetZoom, t));
}
}

View File

@@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenCircleExtensions
{
public static ITween TweenCircle(this Circle initialCircle, ITweenManager tweenManager, float duration, Circle targetCircle, System.Action<Circle> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Circle(
initialCircle.Center.Lerp(targetCircle.Center, t),
initialCircle.Diameter.Lerp(targetCircle.Diameter, t)
)
)
);
}

View File

@@ -0,0 +1,15 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenColorExtensions
{
public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action<ColorRGB> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorRGB.Lerp(targetColorRGB, t)));
public static ITween TweenColor(this ColorRGBA initialColorRGBA, ITweenManager tweenManager, float duration, ColorRGBA targetColorRGBA, System.Action<ColorRGBA> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorRGBA.Lerp(targetColorRGBA, t)));
public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action<ColorHSV> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorHSV.Lerp(targetColorHSV, t)));
}

View File

@@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenLine2DEquationExtensions
{
public static ITween TweenLine2DEquation(this Line2DEquation initialLine2DEquation, ITweenManager tweenManager, float duration, Line2DEquation targetLine2DEquation, System.Action<Line2DEquation> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Line2DEquation(
initialLine2DEquation.Slope.Lerp(targetLine2DEquation.Slope, t),
initialLine2DEquation.OffsetY.Lerp(targetLine2DEquation.OffsetY, t)
)
)
);
}

View File

@@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenLine2DExtensions
{
public static ITween TweenLine2D(this Line2D initialLine2D, ITweenManager tweenManager, float duration, Line2D targetLine2D, System.Action<Line2D> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Line2D(
initialLine2D.From.Lerp(targetLine2D.From, t),
initialLine2D.To.Lerp(targetLine2D.To, t)
)
)
);
}

View File

@@ -0,0 +1,17 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenProjection1DExtensions
{
public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, Action<Projection1D> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Projection1D(
initialProjection1D.Min.Lerp(targetProjection1D.Min, t),
initialProjection1D.Max.Lerp(targetProjection1D.Max, t)
)
)
);
}

View File

@@ -0,0 +1,10 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenQuaternionExtensions
{
public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, Action<Quaternion> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialQuaternion.SLerp(targetQuaternion, t)));
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenShape2DExtensions
{
public static ITween TweenShape2D(this Shape2D shape, ITweenManager tweenManager, float duration, Shape2D targetShape2D)
{
List<Vector2D> initialVertices = new(shape.Vertices);
List<Vector2D> shapeVertices = new(shape.Vertices);
return tweenManager.StartTween(duration,
t =>
{
shapeVertices.Clear();
int maxCount = initialVertices.Count.Max(targetShape2D.Vertices.Count);
for (int i = 0; i < maxCount; i++)
{
int initialIndex = (i * (initialVertices.Count / (float)maxCount)).RoundToInt(Math.RoundMode.Floor);
int targetIndex = (i * (targetShape2D.Vertices.Count / (float)maxCount)).RoundToInt(Math.RoundMode.Floor);
shapeVertices.Add(targetShape2D.Vertices[targetIndex].Lerp(initialVertices[initialIndex], 1f - t));
}
shape.Vertices = shapeVertices;
}
).OnComplete(() =>
{
shapeVertices.Clear();
for (int i = 0; i < targetShape2D.Vertices.Count; i++)
shapeVertices.Add(targetShape2D.Vertices[i]);
shape.Vertices = shapeVertices;
});
}
}

View File

@@ -0,0 +1,42 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenTransform2DExtensions
{
public static ITween TweenPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetPosition)
{
Vector2D initialPosition = transform2D.Position;
return tweenManager.StartTween(duration, t => transform2D.Position = initialPosition.Lerp(targetPosition, t));
}
public static ITween TweenScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetScale)
{
Vector2D initialScale = transform2D.Scale;
return tweenManager.StartTween(duration, t => transform2D.Scale = initialScale.Lerp(targetScale, t));
}
public static ITween TweenRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetRotation)
{
float initialRotation = transform2D.Rotation;
return tweenManager.StartTween(duration, t => transform2D.Rotation = initialRotation.Lerp(targetRotation, t));
}
public static ITween TweenLocalPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalPosition)
{
Vector2D initialLocalPosition = transform2D.LocalPosition;
return tweenManager.StartTween(duration, t => transform2D.LocalPosition = initialLocalPosition.Lerp(targetLocalPosition, t));
}
public static ITween TweenLocalScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalScale)
{
Vector2D initialLocalScale = transform2D.LocalScale;
return tweenManager.StartTween(duration, t => transform2D.LocalScale = initialLocalScale.Lerp(targetLocalScale, t));
}
public static ITween TweenLocalRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetLocalRotation)
{
float initialLocalRotation = transform2D.LocalRotation;
return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t));
}
}

View File

@@ -0,0 +1,18 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenTriangleExtensions
{
public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, Action<Triangle> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Triangle(
initialTriangle.A.Lerp(targetTriangle.A, t),
initialTriangle.B.Lerp(targetTriangle.B, t),
initialTriangle.C.Lerp(targetTriangle.C, t)
)
)
);
}

View File

@@ -0,0 +1,9 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenVector2DExtensions
{
public static ITween TweenVector2D(this Vector2D initialVector2D, ITweenManager tweenManager, float duration, Vector2D targetVector2D, System.Action<Vector2D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialVector2D.Lerp(targetVector2D, t)));
}

View File

@@ -0,0 +1,9 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenVector3DExtensions
{
public static ITween TweenVector3D(this Vector3D initialVector3D, ITweenManager tweenManager, float duration, Vector3D targetVector3D, System.Action<Vector3D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialVector3D.Lerp(targetVector3D, t)));
}

View File

@@ -0,0 +1,9 @@
namespace Syntriax.Engine.Systems.Tween;
public interface ITweenManager
{
ITween StartTween(float duration, TweenSetCallback? setCallback = null);
void CancelTween(ITween tween);
delegate void TweenSetCallback(float t);
}

View File

@@ -43,7 +43,7 @@ internal class Tween : ITween
public float Progress { get; internal set; } = 0f; public float Progress { get; internal set; } = 0f;
private float _counter = 0f; private float _counter = 0f;
public IEasing Easing { get; set; } = new EaseLinear(); public IEasing Easing { get; set; } = EaseLinear.Instance;
public float Value => Easing.Evaluate(Progress); public float Value => Easing.Evaluate(Progress);
public float Counter public float Counter

View File

@@ -1,3 +1,5 @@
using System;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween; namespace Syntriax.Engine.Systems.Tween;
@@ -47,42 +49,49 @@ public static class TweenExtensions
tweenConcrete.OnStarted += _ => callback.InvokeSafe(); tweenConcrete.OnStarted += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnPause(this ITween tween, Action callback) public static ITween OnPause(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnPaused += _ => callback.InvokeSafe(); tweenConcrete.OnPaused += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnResume(this ITween tween, Action callback) public static ITween OnResume(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnResumed += _ => callback.InvokeSafe(); tweenConcrete.OnResumed += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnCancel(this ITween tween, Action callback) public static ITween OnCancel(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCancelled += _ => callback.InvokeSafe(); tweenConcrete.OnCancelled += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnComplete(this ITween tween, Action callback) public static ITween OnComplete(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCompleted += _ => callback.InvokeSafe(); tweenConcrete.OnCompleted += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnEnd(this ITween tween, Action callback) public static ITween OnEnd(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnEnded += _ => callback.InvokeSafe(); tweenConcrete.OnEnded += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnUpdate(this ITween tween, Action callback) public static ITween OnUpdate(this ITween tween, Action callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnUpdated += _ => callback.InvokeSafe(); tweenConcrete.OnUpdated += _ => callback.InvokeSafe();
return tween; return tween;
} }
public static ITween OnDeltaUpdate(this ITween tween, Action<float> callback) public static ITween OnDeltaUpdate(this ITween tween, Action<float> callback)
{ {
Tween tweenConcrete = (Tween)tween; Tween tweenConcrete = (Tween)tween;

View File

@@ -1,16 +1,17 @@
using System.Collections; using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween; namespace Syntriax.Engine.Systems.Tween;
public class TweenManager : UniverseObject public class TweenManager : UniverseObject, ITweenManager
{ {
private CoroutineManager coroutineManager = null!; private CoroutineManager coroutineManager = null!;
private readonly Dictionary<ITween, IEnumerator> runningCoroutines = []; private readonly Dictionary<ITween, IEnumerator> runningCoroutines = [];
public ITween StartTween(float duration, TweenSetCallback? setCallback = null) public ITween StartTween(float duration, ITweenManager.TweenSetCallback? setCallback = null)
{ {
Tween tween = new(duration); Tween tween = new(duration);
tween.OnUpdated += tween => setCallback?.InvokeSafe(tween.Value); tween.OnUpdated += tween => setCallback?.InvokeSafe(tween.Value);
@@ -60,6 +61,4 @@ public class TweenManager : UniverseObject
{ {
coroutineManager = null!; coroutineManager = null!;
} }
public delegate void TweenSetCallback(float t);
} }

View File

@@ -11,14 +11,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine.Sy
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Serializers", "Engine.Serializers", "{F88E129A-9A47-4D27-96EE-6EC02F79594B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serializers.Yaml", "Engine.Serializers\Engine.Serializers.Yaml\Engine.Serializers.Yaml.csproj", "{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Serializers\YamlDotNet\YamlDotNet\YamlDotNet.csproj", "{3D852C92-BC14-4893-AEF2-50612DAFCD8F}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU {71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
@@ -36,5 +39,20 @@ Global
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU {58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU {58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU {58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|Any CPU.Build.0 = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176} = {F88E129A-9A47-4D27-96EE-6EC02F79594B}
{3D852C92-BC14-4893-AEF2-50612DAFCD8F} = {F88E129A-9A47-4D27-96EE-6EC02F79594B}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@@ -2,14 +2,15 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" /> <ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
<ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" /> <ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" />
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

15
Engine/Preserver.cs Normal file
View File

@@ -0,0 +1,15 @@
namespace Syntriax.Engine
{
// This is pretty much so the assembly gets loaded automatically because
// the builds include the assembly but sometimes doesn't link load it at startup.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve()
{
Core.Preserver.Preserve();
Physics2D.Preserver.Preserve();
Systems.Preserver.Preserve();
}
}
}

View File

@@ -1,15 +1,36 @@
# Work In Progress # Work In Progress
This engine is still in development so there **WILL** be breaking changes, but the implemented features include: This is a Universe(Game) engine written in C# .NET Core.
This engine is experimental and still in development so there ***WILL*** be breaking changes, but the implemented features include:
- Modular Systems - Modular Systems
- Timers
- Coroutines
- Tweening
- State Machines
- 2D Physics Engine(**Not Fully Completed, but usable**)
- Rigid Body Simulations
- Collision Detection (Convex Shape & Circle)
- Collision Resolution (**Not Fully Completed**)
- Serialization
- YAML
- JSON (Not added yet)
- Behaviour System - Behaviour System
- 2D Physics Engine(**Not Fully Completed, but usable**) - Hierarchy System
- Rigid Body Simulations
- Collision Detection (Convex Shape & Circle)
- Collision Resolution (**Not Fully Completed**)
- Vector2D, AABB, Circle, Line, LineEquation, Projection & Shape Data Types
- General Math - General Math
- Primitive Types
- AABB
- Circle
- Line2D
- Line2DEquation
- Projection1D
- Quaternion
- Shape2D
- Triangle
- Vector2D
- Vector3D
- ***And more to come!***
--- ---