Development Merge 2025.04.01 #1

Merged
Syntriax merged 32 commits from development into main 2025-04-01 19:20:18 +03:00
120 changed files with 3205 additions and 2532 deletions

225
.editorconfig Normal file
View File

@ -0,0 +1,225 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
spelling_exclusion_path = SpellingExclusions.dic
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
charset = utf-8-bom
# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# XML config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# Powershell files
[*.ps1]
indent_size = 2
# Shell script files
[*.sh]
end_of_line = lf
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:none
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Whitespace options
dotnet_style_allow_multiple_blank_lines_experimental = false:error
# IDE0055: Fix formatting
# Workaround for https://github.com/dotnet/roslyn/issues/70570
dotnet_diagnostic.IDE0055.severity = suggestion
# https://github.com/dotnet/roslyn-analyzers/issues/7436 - False positives from valid GetDeclaredSymbol calls
dotnet_diagnostic.RS1039.severity = none
# CSharp code style settings:
# IDE0029: Use coalesce expression
dotnet_diagnostic.IDE0029.severity = none
# IDE0040: Add accessibility modifiers
dotnet_diagnostic.IDE0040.severity = warning
[*.cs]
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Whitespace options
csharp_style_allow_embedded_statements_on_same_line_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_elsewhere = false:warning
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Blocks are allowed
csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = warning
[src/{Compilers,ExpressionEvaluator,Scripting}/**Test**/*.{cs,vb}]
# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = none
[src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures,VisualStudio}/**/*.{cs,vb}]
# IDE0011: Add braces
csharp_prefer_braces = false:warning
# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201
dotnet_diagnostic.IDE0011.severity = warning
# IDE0040: Add accessibility modifiers
dotnet_diagnostic.IDE0040.severity = warning
# IDE0052: Remove unread private member
dotnet_diagnostic.IDE0052.severity = warning
# IDE0059: Unnecessary assignment to a value
dotnet_diagnostic.IDE0059.severity = warning
# CA1012: Abstract types should not have public constructors
dotnet_diagnostic.CA1012.severity = warning
# CA1822: Make member static
dotnet_diagnostic.CA1822.severity = warning
# Prefer "var" everywhere
dotnet_diagnostic.IDE0007.severity = warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_elsewhere = false:warning
# csharp_style_allow_embedded_statements_on_same_line_experimental
dotnet_diagnostic.IDE2001.severity = warning
# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
dotnet_diagnostic.IDE2004.severity = warning
# csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental
dotnet_diagnostic.IDE2005.severity = warning
# csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental
dotnet_diagnostic.IDE2006.severity = warning
[src/{VisualStudio}/**/*.{cs,vb}]
# CA1822: Make member static
# There is a risk of accidentally breaking an internal API that partners rely on though IVT.
dotnet_code_quality.CA1822.api_surface = private
[**/{ExternalAccess}/**/*.{cs,vb}]
# RS0016: Only enable if API files are present
dotnet_public_api_analyzer.require_api_files = true
dotnet_diagnostic.RS0051.severity = error
dotnet_diagnostic.RS0052.severity = error
dotnet_diagnostic.RS0053.severity = error
dotnet_diagnostic.RS0054.severity = error
dotnet_diagnostic.RS0055.severity = error
dotnet_diagnostic.RS0056.severity = error
dotnet_diagnostic.RS0057.severity = error
dotnet_diagnostic.RS0058.severity = error
dotnet_diagnostic.RS0059.severity = error
dotnet_diagnostic.RS0060.severity = error
dotnet_diagnostic.RS0061.severity = error

View File

@ -8,7 +8,7 @@ public interface IAssignable
/// <summary>
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
/// </summary>
event OnUnassignedEventHandler? OnUnassigned;
event UnassignEventHandler? OnUnassigned;
/// <summary>
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
@ -18,5 +18,5 @@ public interface IAssignable
/// </returns>
bool Unassign();
delegate void OnUnassignedEventHandler(IAssignable sender);
delegate void UnassignEventHandler(IAssignable sender);
}

View File

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

View File

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

View File

@ -3,12 +3,12 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
/// </summary>
public interface IAssignableBehaviourController : IAssignable
public interface IHasBehaviourController : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
/// </summary>
event OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
event BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
/// <inheritdoc cref="IBehaviourController" />
IBehaviourController BehaviourController { get; }
@ -22,5 +22,5 @@ public interface IAssignableBehaviourController : IAssignable
/// </returns>
bool Assign(IBehaviourController behaviourController);
delegate void OnBehaviourControllerAssignedEventHandler(IAssignableBehaviourController sender);
delegate void BehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
}

View File

@ -3,12 +3,12 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
/// </summary>
public interface IAssignableEntity : IAssignable
public interface IHasEntity : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
/// </summary>
event OnEntityAssignedEventHandler? OnEntityAssigned;
event EntityAssignedEventHandler? OnEntityAssigned;
/// <inheritdoc cref="IEntity" />
IEntity Entity { get; }
@ -22,5 +22,5 @@ public interface IAssignableEntity : IAssignable
/// </returns>
bool Assign(IEntity entity);
delegate void OnEntityAssignedEventHandler(IAssignableEntity sender);
delegate void EntityAssignedEventHandler(IHasEntity sender);
}

View File

@ -1,14 +1,14 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform2D"/> field.
/// </summary>
public interface IAssignableGameManager : IAssignable
public interface IHasGameManager : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
/// </summary>
event OnGameManagerAssignedEventHandler? OnGameManagerAssigned;
event GameManagerAssignedEventHandler? OnGameManagerAssigned;
/// <inheritdoc cref="IGameManager" />
IGameManager GameManager { get; }
@ -22,5 +22,5 @@ public interface IAssignableGameManager : IAssignable
/// </returns>
bool Assign(IGameManager gameManager);
delegate void OnGameManagerAssignedEventHandler(IAssignableGameManager sender);
delegate void GameManagerAssignedEventHandler(IHasGameManager sender);
}

View File

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

View File

@ -3,12 +3,12 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
/// </summary>
public interface IAssignableStateEnable : IAssignable
public interface IHasStateEnable : IAssignable
{
/// <summary>
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
/// </summary>
event OnStateEnableAssignedEventHandler? OnStateEnableAssigned;
event StateEnableAssignedEventHandler? OnStateEnableAssigned;
/// <inheritdoc cref="IStateEnable" />
IStateEnable StateEnable { get; }
@ -22,5 +22,5 @@ public interface IAssignableStateEnable : IAssignable
/// </returns>
bool Assign(IStateEnable stateEnable);
delegate void OnStateEnableAssignedEventHandler(IAssignableStateEnable sender);
delegate void StateEnableAssignedEventHandler(IHasStateEnable sender);
}

View File

@ -1,64 +0,0 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an <see cref="IEntity"/> that can enter and exit a hierarchy within the <see cref="IGameManager"/> system.
/// This interface allows for tracking the object's presence in the hierarchy and provides events
/// for notifying when the see enters or exits the hierarchy.
/// </summary>
public interface IHierarchyObject : IEntity, INameable
{
/// <summary>
/// Event triggered when the <see cref="IEntity"/> enters the hierarchy.
/// </summary>
event OnEnteredHierarchyEventHandler? OnEnteredHierarchy;
/// <summary>
/// Event triggered when the <see cref="IEntity"/> exits the hierarchy.
/// </summary>
event OnExitedHierarchyEventHandler? OnExitedHierarchy;
/// <summary>
/// Gets the <see cref="IGameManager"/> associated with this <see cref="IEntity"/> , if any.
/// </summary>
IGameManager GameManager { get; }
/// <summary>
/// Indicates whether the <see cref="IEntity"/> is currently in the hierarchy.
/// </summary>
bool IsInHierarchy { get; }
/// <summary>
/// Internal method to handle entering the hierarchy.
/// This should be called by the system to properly manage hierarchy states.
/// </summary>
/// <param name="gameManager">The game manager that is managing this hierarchy.</param>
/// <returns>
/// <see cref="true"/> if the <see cref="IEntity"/> successfully entered the hierarchy;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool EnterHierarchy(IGameManager gameManager);
/// <summary>
/// Internal method to handle exiting the hierarchy.
/// This should be called by the system to properly manage hierarchy states.
/// </summary>
/// <returns>
/// <see cref="true"/> if the <see cref="IEntity"/> successfully exited the hierarchy;
/// <see cref="false"/> if it failed to do so.
/// </returns>
internal bool ExitHierarchy();
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IEntity"/> enters the hierarchy of a <see cref="IGameManager">.
/// </summary>
/// <param name="sender">The <see cref="IEntity"/> that entered the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IEntity"/> has entered it's hierarchy.</param>
delegate void OnEnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IEntity"/> exits the hierarchy of a <see cref="IGameManager">.
/// </summary>
/// <param name="sender">The <see cref="IEntity"/> that exited the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IEntity"/> has exited it's hierarchy.</param>
delegate void OnExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
}

View File

@ -0,0 +1,19 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an entity which can be active or not.
/// </summary>
public interface IActive
{
/// <summary>
/// Event triggered when the <see cref="IsActive"/> state of the <see cref="IActive"/> changes.
/// </summary>
event ActiveChangedEventHandler? OnActiveChanged;
/// <summary>
/// The value indicating whether the <see cref="IActive"/> is enabled.
/// </summary>
bool IsActive { get; }
delegate void ActiveChangedEventHandler(IActive sender, bool previousState);
}

View File

@ -3,22 +3,17 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
/// </summary>
public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignableStateEnable
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
{
/// <summary>
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
/// </summary>
event OnPriorityChangedEventHandler? OnPriorityChanged;
event PriorityChangedEventHandler? OnPriorityChanged;
/// <summary>
/// The priority of the <see cref="IBehaviour"/>.
/// </summary>
int Priority { get; set; }
/// <summary>
/// The value indicating whether the <see cref="IBehaviour"/> is active.
/// </summary>
bool IsActive { get; }
delegate void OnPriorityChangedEventHandler(IBehaviour sender, int previousPriority);
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
}

View File

@ -0,0 +1,6 @@
namespace Syntriax.Engine.Core.Abstract;
public interface IBehaviour2D : IBehaviour
{
ITransform2D Transform { get; }
}

View File

@ -7,29 +7,29 @@ namespace Syntriax.Engine.Core.Abstract;
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IGameManager"/>.
/// </summary>
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
public interface IBehaviourCollector<T> : IAssignableGameManager, IEnumerable<T> where T : class
public interface IBehaviourCollector<T> : IHasGameManager, IEnumerable<T> where T : class
{
/// <summary>
/// Event triggered when an object of type <typeparamref name="T"/> is added to the collector.
/// </summary>
event OnCollectedEventHandler? OnCollected;
event CollectedEventHandler? OnCollected;
/// <summary>
/// Event triggered when an object of type <typeparamref name="T"/> is removed from the collector.
/// </summary>
event OnRemovedEventHandler? OnRemoved;
event RemovedEventHandler? OnRemoved;
/// <summary>
/// Delegate for handling the <see cref="OnCollected"/> event.
/// </summary>
/// <param name="sender">The instance of the <see cref="BehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="behaviourCollected">The object of type <typeparamref name="T"/> that was added to the collector.</param>
public delegate void OnCollectedEventHandler(BehaviourCollector<T> sender, T behaviourCollected);
delegate void CollectedEventHandler(IBehaviourCollector<T> sender, T behaviourCollected);
/// <summary>
/// Delegate for handling the <see cref="OnRemoved"/> event.
/// </summary>
/// <param name="sender">The instance of the <see cref="BehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="behaviourRemoved">The object of type <typeparamref name="T"/> that was removed from the collector.</param>
public delegate void OnRemovedEventHandler(BehaviourCollector<T> sender, T behaviourRemoved);
delegate void RemovedEventHandler(IBehaviourCollector<T> sender, T behaviourRemoved);
}

View File

@ -1,37 +1,36 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IHierarchyObject"/>.
/// </summary>
public interface IBehaviourController : IInitialize, IAssignableGameObject, IEnumerable<IBehaviour>
public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEnumerable<IBehaviour>
{
/// <summary>
/// Event triggered before the update of <see cref="IBehaviour"/>s.
/// </summary>
event OnPreUpdateEventHandler? OnPreUpdate;
event PreUpdateEventHandler? OnPreUpdate;
/// <summary>
/// Event triggered during the update of <see cref="IBehaviour"/>s.
/// </summary>
event OnUpdateEventHandler? OnUpdate;
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered before the drawing phase.
/// </summary>
event OnPreDrawEventHandler? OnPreDraw;
event PreDrawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
/// </summary>
event OnBehaviourAddedEventHandler? OnBehaviourAdded;
event BehaviourAddedEventHandler? OnBehaviourAdded;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
/// </summary>
event OnBehaviourRemovedEventHandler? OnBehaviourRemoved;
event BehaviourRemovedEventHandler? OnBehaviourRemoved;
/// <summary>
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
@ -94,10 +93,10 @@ public interface IBehaviourController : IInitialize, IAssignableGameObject, IEnu
/// </summary>
void UpdatePreDraw();
delegate void OnPreUpdateEventHandler(IBehaviourController sender);
delegate void OnUpdateEventHandler(IBehaviourController sender);
delegate void OnPreDrawEventHandler(IBehaviourController sender);
delegate void OnBehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
delegate void OnBehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
delegate void PreUpdateEventHandler(IBehaviourController sender);
delegate void UpdateEventHandler(IBehaviourController sender);
delegate void PreDrawEventHandler(IBehaviourController sender);
delegate void BehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
delegate void BehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
}

View File

@ -3,7 +3,7 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a 2D camera in the engine.
/// </summary>
public interface ICamera2D : IBehaviour, IAssignableTransform
public interface ICamera2D : IBehaviour2D
{
/// <summary>
/// The zoom level of the camera.

View File

@ -1,5 +1,3 @@
using System.Collections;
namespace Syntriax.Engine.Core.Abstract;
public interface ICoroutineYield

View File

@ -3,18 +3,18 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a basic entity in the engine.
/// </summary>
public interface IEntity : IInitialize, IAssignableStateEnable
public interface IEntity : IInitializable, IHasStateEnable
{
/// <summary>
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
/// </summary>
event OnIdChangedEventHandler? OnIdChanged;
event IdChangedEventHandler? OnIdChanged;
/// <summary>
/// The ID of the <see cref="IEntity"/>.
/// </summary>
string Id { get; set; }
delegate void OnIdChangedEventHandler(IEntity sender, string previousId);
delegate void IdChangedEventHandler(IEntity sender, string previousId);
}

View File

@ -3,44 +3,34 @@ using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a game world responsible for managing <see cref="IGameObject"/>s.
/// Represents a game world responsible for managing <see cref="IHierarchyObject"/>s.
/// </summary>
public interface IGameManager : IEntity
public interface IGameManager : IEntity, IEnumerable<IHierarchyObject>
{
/// <summary>
/// Event triggered when <see cref="Update(EngineTime)"/> is called on the <see cref="IGameManager"/>.
/// Event triggered when <see cref="Update(double)"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event OnUpdateEventHandler? OnUpdate;
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event OnPreDawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IGameObject"/> is registered to the <see cref="IGameManager"/>.
/// </summary>
event OnGameObjectRegisteredEventHandler? OnGameObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IGameObject"/> is unregistered from the <see cref="IGameManager"/>.
/// </summary>
event OnGameObjectUnRegisteredEventHandler? OnGameObjectUnRegistered;
event PreDawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is registered to the <see cref="IGameManager"/>.
/// </summary>
event OnHierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
event HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is unregistered from the <see cref="IGameManager"/>.
/// </summary>
event OnHierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
event HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
/// <summary>
/// Gets a read-only list of <see cref="IGameObject"/>s managed by the <see cref="IGameManager"/>.
/// Contains time data related to this <see cref="IGameManager"/>.
/// </summary>
IReadOnlyList<IGameObject> GameObjects { get; }
EngineTime Time { get; }
/// <summary>
/// Gets a read-only list of <see cref="IHierarchyObject"/>s managed by the <see cref="IGameManager"/>.
@ -54,12 +44,12 @@ public interface IGameManager : IEntity
void Register(IHierarchyObject hierarchyObject);
/// <summary>
/// Instantiates a <see cref="IGameObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
/// Instantiates a <see cref="IHierarchyObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
/// </summary>
/// <typeparam name="T">The type of <see cref="IGameObject"/> to instantiate.</typeparam>
/// <param name="args">Constructor parameters for the given type of <see cref="IGameObject"/>.</param>
/// <returns>The instantiated <see cref="IGameObject"/>.</returns>
T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject;
/// <typeparam name="T">The type of <see cref="IHierarchyObject"/> to instantiate.</typeparam>
/// <param name="args">Constructor parameters for the given type of <see cref="IHierarchyObject"/>.</param>
/// <returns>The instantiated <see cref="IHierarchyObject"/>.</returns>
T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject;
/// <summary>
/// Removes an <see cref="IHierarchyObject"/> from the <see cref="IGameManager"/>.
@ -68,21 +58,19 @@ public interface IGameManager : IEntity
void Remove(IHierarchyObject hierarchyObject);
/// <summary>
/// Updates the <see cref="IGameManager"/> with the given engine time data.
/// Updates the <see cref="IGameManager"/> with the given delta time.
/// </summary>
/// <param name="time">The engine time.</param>
void Update(EngineTime time);
/// <param name="engineTime">Delta time.</param>
void Update(EngineTime engineTime);
/// <summary>
/// Performs operations that should be done before the draw calls.
/// </summary>
void PreDraw();
delegate void OnUpdateEventHandler(IGameManager sender, EngineTime time);
delegate void OnPreDawEventHandler(IGameManager sender);
delegate void UpdateEventHandler(IGameManager sender, EngineTime engineTime);
delegate void PreDawEventHandler(IGameManager sender);
delegate void OnGameObjectRegisteredEventHandler(IGameManager sender, IGameObject gameObjectRegistered);
delegate void OnGameObjectUnRegisteredEventHandler(IGameManager sender, IGameObject gameObjectUnregistered);
delegate void OnHierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
delegate void OnHierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
delegate void HierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
delegate void HierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
}

View File

@ -1,19 +0,0 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a game object with various properties and functionalities.
/// </summary>
public interface IGameObject : IEntity, IHierarchyObject, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
{
/// <summary>
/// Event triggered when the <see cref="Update"/> method is called.
/// </summary>
event OnUpdatedEventHandler? OnUpdated;
/// <summary>
/// Updates the game object.
/// </summary>
void Update();
delegate void OnUpdatedEventHandler(IGameObject sender);
}

View File

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

View File

@ -3,17 +3,17 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
/// </summary>
public interface IInitialize
public interface IInitializable
{
/// <summary>
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
/// </summary>
event OnInitializedEventHandler? OnInitialized;
event InitializedEventHandler? OnInitialized;
/// <summary>
/// Event triggered when the <see cref="Finalize"/> method is called successfully.
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
/// </summary>
event OnFinalizedEventHandler? OnFinalized;
event FinalizedEventHandler? OnFinalized;
/// <summary>
/// The value indicating whether the entity has been initialized.
@ -32,6 +32,6 @@ public interface IInitialize
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
bool Finalize();
delegate void OnInitializedEventHandler(IInitialize sender);
delegate void OnFinalizedEventHandler(IInitialize sender);
delegate void InitializedEventHandler(IInitializable sender);
delegate void FinalizedEventHandler(IInitializable sender);
}

View File

@ -8,12 +8,12 @@ public interface INameable
/// <summary>
/// Event triggered when the name of the entity changes.
/// </summary>
event OnNameChangedEventHandler? OnNameChanged;
event NameChangedEventHandler? OnNameChanged;
/// <summary>
/// The name of the entity.
/// </summary>
string Name { get; set; }
delegate void OnNameChangedEventHandler(INameable sender, string previousName);
delegate void NameChangedEventHandler(INameable sender, string previousName);
}

View File

@ -3,17 +3,17 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an entity with an enable state that can be toggled.
/// </summary>
public interface IStateEnable : IAssignableEntity
public interface IStateEnable : IHasEntity
{
/// <summary>
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
/// </summary>
event OnNameChangedEventHandler? OnEnabledChanged;
event EnabledChangedEventHandler? OnEnabledChanged;
/// <summary>
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
/// </summary>
bool Enabled { get; set; }
delegate void OnNameChangedEventHandler(IStateEnable sender, bool previousState);
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
}

View File

@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents the transformation properties of an object such as position, scale, and rotation.
/// </summary>
public interface ITransform : IAssignableGameObject, IEnumerable<ITransform>
{
/// <summary>
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
/// </summary>
event OnPositionChangedEventHandler? OnPositionChanged;
/// <summary>
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform"/> changes.
/// </summary>
event OnScaleChangedEventHandler? OnScaleChanged;
/// <summary>
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
/// </summary>
event OnRotationChangedEventHandler? OnRotationChanged;
/// <summary>
/// Event triggered when the <see cref="Parent"/> of the <see cref="ITransform"/> changes. The second parameter is the old <see cref="ITransform"/>.
/// </summary>
event OnParentChangedEventHandler? OnParentChanged;
/// <summary>
/// Event triggered when a new <see cref="ITransform"/> is added to the <see cref="Children"/>.
/// </summary>
event OnChildrenAddedEventHandler? OnChildrenAdded;
/// <summary>
/// Event triggered when an <see cref="ITransform"/> is removed from the <see cref="Children"/>.
/// </summary>
event OnChildrenRemovedEventHandler? OnChildrenRemoved;
/// <summary>
/// The world position of the <see cref="ITransform"/> in 2D space.
/// </summary>
Vector2D Position { get; set; }
/// <summary>
/// The world scale of the <see cref="ITransform"/>.
/// </summary>
Vector2D Scale { get; set; }
/// <summary>
/// The world rotation of the <see cref="ITransform"/> in degrees.
/// </summary>
float Rotation { get; set; }
/// <summary>
/// The local position of the <see cref="ITransform"/> in 2D space.
/// </summary>
Vector2D LocalPosition { get; set; }
/// <summary>
/// The local scale of the <see cref="ITransform"/>.
/// </summary>
Vector2D LocalScale { get; set; }
/// <summary>
/// The local rotation of the <see cref="ITransform"/> in degrees.
/// </summary>
float LocalRotation { get; set; }
/// <summary>
/// The parent <see cref="ITransform"/> of the <see cref="ITransform"/>.
/// </summary>
ITransform? Parent { get; }
/// <summary>
/// The <see cref="ITransform"/>s that have this <see cref="ITransform"/> as their <see cref="Parent"/>.
/// </summary>
IReadOnlyList<ITransform> Children { get; }
/// <summary>
/// Sets the parent <see cref="ITransform"/> of this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The parent <see cref="ITransform"/> to set.</param>
void SetParent(ITransform? transform);
/// <summary>
/// Adds a child <see cref="ITransform"/> to this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The child <see cref="ITransform"/> to add.</param>
void AddChild(ITransform transform);
/// <summary>
/// Removes a child <see cref="ITransform"/> from this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The child <see cref="ITransform"/> to remove.</param>
void RemoveChild(ITransform transform);
delegate void OnPositionChangedEventHandler(ITransform sender);
delegate void OnScaleChangedEventHandler(ITransform sender);
delegate void OnRotationChangedEventHandler(ITransform sender);
delegate void OnParentChangedEventHandler(ITransform sender, ITransform? previousParent, ITransform? newParent);
delegate void OnChildrenAddedEventHandler(ITransform sender, ITransform childrenAdded);
delegate void OnChildrenRemovedEventHandler(ITransform sender, ITransform childrenRemoved);
}

View File

@ -0,0 +1,73 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.
/// </summary>
public interface ITransform2D : IBehaviour
{
/// <summary>
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform2D"/> changes.
/// </summary>
event PositionChangedEventHandler? OnPositionChanged;
/// <summary>
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform2D"/> changes.
/// </summary>
event ScaleChangedEventHandler? OnScaleChanged;
/// <summary>
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
/// </summary>
event RotationChangedEventHandler? OnRotationChanged;
/// <summary>
/// The world position of the <see cref="ITransform2D"/> in 2D space.
/// </summary>
Vector2D Position { get; set; }
/// <summary>
/// The world scale of the <see cref="ITransform2D"/>.
/// </summary>
Vector2D Scale { get; set; }
/// <summary>
/// The world rotation of the <see cref="ITransform2D"/> in degrees.
/// </summary>
float Rotation { get; set; }
/// <summary>
/// The local position of the <see cref="ITransform2D"/> in 2D space.
/// </summary>
Vector2D LocalPosition { get; set; }
/// <summary>
/// The local scale of the <see cref="ITransform2D"/>.
/// </summary>
Vector2D LocalScale { get; set; }
/// <summary>
/// The local rotation of the <see cref="ITransform2D"/> in degrees.
/// </summary>
float LocalRotation { get; set; }
/// <summary>
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousPosition">The previous <see cref="Position"/> of the <see cref="ITransform2D"/>.</param>
delegate void PositionChangedEventHandler(ITransform2D sender, Vector2D previousPosition);
/// <summary>
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousScale">The previous <see cref="Scale"/> of the <see cref="ITransform2D"/>.</param>
delegate void ScaleChangedEventHandler(ITransform2D sender, Vector2D previousScale);
/// <summary>
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform2D"/>.</param>
delegate void RotationChangedEventHandler(ITransform2D sender, float previousRotation);
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
{
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
private readonly List<T> monitoringBehaviours = new(32);
protected readonly List<T> activeBehaviours = new(32);
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public IReadOnlyList<T> Behaviours => activeBehaviours;
public IGameManager GameManager { get; private set; } = null!;
public T this[Index index] => activeBehaviours[index];
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IGameManager gameManager) => Assign(gameManager);
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
}
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged += OnBehaviourStateChanged;
OnBehaviourStateChanged(tBehaviour, !tBehaviour.IsActive);
}
private void OnBehaviourStateChanged(IActive sender, bool previousState)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
activeBehaviours.Add(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, behaviour);
}
else if (activeBehaviours.Remove(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, behaviour);
}
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged -= OnBehaviourStateChanged;
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
}
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
return false;
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
return false;
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public IEnumerator<T> GetEnumerator() => activeBehaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => activeBehaviours.GetEnumerator();
}

View File

@ -0,0 +1,19 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
{
public Comparison<T>? SortBy { get; set; } = null;
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
if (SortBy is not null)
activeBehaviours.Sort(SortBy);
}
public ActiveBehaviourCollectorSorted() { }
public ActiveBehaviourCollectorSorted(IGameManager gameManager) : base(gameManager) { }
}

View File

@ -1,19 +1,16 @@
using System;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Abstract;
public abstract class BaseEntity : IEntity
{
public event IEntity.OnIdChangedEventHandler? OnIdChanged = null;
public event IEntity.IdChangedEventHandler? OnIdChanged = null;
public event IInitialize.OnInitializedEventHandler? OnInitialized = null;
public event IInitialize.OnFinalizedEventHandler? OnFinalized = null;
public event IAssignableStateEnable.OnStateEnableAssignedEventHandler? OnStateEnableAssigned = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IHasStateEnable.StateEnableAssignedEventHandler? OnStateEnableAssigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private IStateEnable _stateEnable = null!;
@ -22,13 +19,14 @@ public abstract class BaseEntity : IEntity
public virtual IStateEnable StateEnable => _stateEnable;
public virtual bool IsActive => StateEnable.Enabled;
public string Id
{
get => _id;
set
{
if (IsInitialized)
throw new($"Can't change {nameof(Id)} of {_id} because it's initialized");
if (value == _id)
return;
@ -55,6 +53,7 @@ public abstract class BaseEntity : IEntity
}
}
protected virtual void OnAssign(IStateEnable stateEnable) { }
public bool Assign(IStateEnable stateEnable)
{
if (IsInitialized)
@ -62,6 +61,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnAssign(stateEnable);
OnStateEnableAssigned?.Invoke(this);
return true;
}
@ -86,7 +86,7 @@ public abstract class BaseEntity : IEntity
if (IsInitialized)
return false;
NotAssignedException.Check(this, _stateEnable);
_stateEnable ??= Factory.StateEnableFactory.Instantiate(this);
InitializeInternal();

View File

@ -6,9 +6,8 @@ public abstract class Behaviour : BehaviourBase
{
private bool isInitializedThisFrame = false;
protected IGameManager GameManager => BehaviourController.GameObject.GameManager;
protected IGameObject GameObject => BehaviourController.GameObject;
protected ITransform Transform => BehaviourController.GameObject.Transform;
protected IGameManager GameManager => BehaviourController.HierarchyObject.GameManager;
protected IHierarchyObject HierarchyObject => BehaviourController.HierarchyObject;
public Behaviour()
{
@ -18,43 +17,43 @@ public abstract class Behaviour : BehaviourBase
}
protected virtual void OnUnassign() { }
private void OnUnassign(IAssignable assignable) => OnUnassign();
protected virtual void OnUnassign(IAssignable assignable) => OnUnassign();
protected virtual void OnInitialize() { }
private void OnInitialize(IInitialize _)
protected virtual void OnInitialize(IInitializable _)
{
isInitializedThisFrame = true;
BehaviourController.OnPreUpdate += PreUpdate;
BehaviourController.OnPreDraw += PreDraw;
BehaviourController.OnUpdate += Update;
BehaviourController.GameObject.OnEnteredHierarchy += EnteredHierarchy;
BehaviourController.GameObject.OnExitedHierarchy += ExitedHierarchy;
BehaviourController.HierarchyObject.OnEnteredHierarchy += EnteredHierarchy;
BehaviourController.HierarchyObject.OnExitedHierarchy += ExitedHierarchy;
OnInitialize();
if (GameObject.IsInHierarchy)
EnteredHierarchy(GameObject, GameManager);
if (HierarchyObject.IsInHierarchy)
EnteredHierarchy(HierarchyObject, GameManager);
}
protected virtual void OnFinalize() { }
private void OnFinalize(IInitialize _)
protected virtual void OnFinalize(IInitializable _)
{
BehaviourController.OnPreUpdate -= PreUpdate;
BehaviourController.OnPreDraw -= PreDraw;
BehaviourController.OnUpdate -= Update;
BehaviourController.GameObject.OnEnteredHierarchy -= EnteredHierarchy;
BehaviourController.GameObject.OnExitedHierarchy -= ExitedHierarchy;
BehaviourController.HierarchyObject.OnEnteredHierarchy -= EnteredHierarchy;
BehaviourController.HierarchyObject.OnExitedHierarchy -= ExitedHierarchy;
OnFinalize();
if (GameObject.IsInHierarchy)
ExitedHierarchy(GameObject, GameManager);
if (HierarchyObject.IsInHierarchy)
ExitedHierarchy(HierarchyObject, GameManager);
}
protected virtual void OnPreUpdatePreActiveCheck() { }
protected virtual void OnPreUpdate() { }
private void PreUpdate(IBehaviourController _)
protected virtual void PreUpdate(IBehaviourController _)
{
OnPreUpdatePreActiveCheck();
@ -68,7 +67,7 @@ public abstract class Behaviour : BehaviourBase
}
protected virtual void OnFirstActiveFrame() { }
private void FirstActiveFrame()
protected virtual void FirstActiveFrame()
{
OnFirstActiveFrame();
isInitializedThisFrame = false;
@ -76,7 +75,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnUpdatePreActiveCheck() { }
protected virtual void OnUpdate() { }
private void Update(IBehaviourController _)
protected virtual void Update(IBehaviourController _)
{
OnUpdatePreActiveCheck();
@ -88,7 +87,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreDrawPreActiveCheck() { }
protected virtual void OnPreDraw() { }
private void PreDraw(IBehaviourController _)
protected virtual void PreDraw(IBehaviourController _)
{
OnPreDrawPreActiveCheck();
@ -99,8 +98,8 @@ public abstract class Behaviour : BehaviourBase
}
protected virtual void OnEnteredHierarchy(IGameManager gameManager) { }
private void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnEnteredHierarchy(gameManager);
protected virtual void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnEnteredHierarchy(gameManager);
protected virtual void OnExitedHierarchy(IGameManager gameManager) { }
private void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnExitedHierarchy(gameManager);
protected virtual void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnExitedHierarchy(gameManager);
}

View File

@ -0,0 +1,28 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public abstract class Behaviour2D : Behaviour, IBehaviour2D
{
public ITransform2D Transform { get; private set; } = null!;
protected sealed override void OnInitialize(IInitializable _)
{
Transform = BehaviourController.GetBehaviourInChildren<ITransform2D>() ?? throw new($"{HierarchyObject.Name} does not contain any {nameof(ITransform2D)}");
base.OnInitialize(_);
}
protected sealed override void OnFinalize(IInitializable _)
{
Transform = null!;
base.OnFinalize(_);
}
protected sealed override void OnUnassign(IAssignable assignable) => base.OnUnassign(assignable);
protected sealed override void PreUpdate(IBehaviourController behaviourController) => base.PreUpdate(behaviourController);
protected sealed override void FirstActiveFrame() => base.FirstActiveFrame();
protected sealed override void Update(IBehaviourController behaviourController) => base.Update(behaviourController);
protected sealed override void PreDraw(IBehaviourController behaviourController) => base.PreDraw(behaviourController);
protected sealed override void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.EnteredHierarchy(sender, gameManager);
protected sealed override void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => base.ExitedHierarchy(sender, gameManager);
}

View File

@ -6,20 +6,14 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
public abstract class BehaviourBase : BaseEntity, IBehaviour
{
public event IAssignableBehaviourController.OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event IBehaviour.OnPriorityChangedEventHandler? OnPriorityChanged = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private IBehaviourController _behaviourController = null!;
private int _priority = 0;
public IBehaviourController BehaviourController => _behaviourController;
public override bool IsActive => base.IsActive && BehaviourController.GameObject.StateEnable.Enabled;
private int _priority = 0;
public int Priority
{
get => _priority;
@ -34,18 +28,41 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
}
}
private bool _isActive = false;
public bool IsActive => _isActive;
protected virtual void OnAssign(IBehaviourController behaviourController) { }
public bool Assign(IBehaviourController behaviourController)
{
if (IsInitialized)
return false;
_behaviourController = behaviourController;
OnAssign(behaviourController);
behaviourController.OnHierarchyObjectAssigned += OnHierarchyObjectAssigned;
if (behaviourController.HierarchyObject is not null)
OnHierarchyObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
return true;
}
private void OnHierarchyObjectAssigned(IHasHierarchyObject sender)
{
sender.HierarchyObject.OnActiveChanged += OnHierarchyObjectActiveChanged;
UpdateActive();
}
protected override void OnAssign(IStateEnable stateEnable)
{
base.OnAssign(stateEnable);
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
}
protected override void UnassignInternal()
{
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
BehaviourController.OnHierarchyObjectAssigned -= OnHierarchyObjectAssigned;
base.UnassignInternal();
_behaviourController = null!;
}
@ -56,4 +73,16 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
NotAssignedException.Check(this, _behaviourController);
NotAssignedException.Check(this, StateEnable);
}
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
private void OnHierarchyObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void UpdateActive()
{
bool previousActive = IsActive;
_isActive = StateEnable.Enabled && _behaviourController.HierarchyObject.IsActive;
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
}
}

View File

@ -8,38 +8,38 @@ namespace Syntriax.Engine.Core;
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
{
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IAssignableGameManager.OnGameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IBehaviourCollector<T>.OnCollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.OnRemovedEventHandler? OnRemoved = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
protected readonly List<T> _behaviours = new(32);
protected readonly List<T> behaviours = new(32);
public IReadOnlyList<T> Behaviours => _behaviours;
public IReadOnlyList<T> Behaviours => behaviours;
public IGameManager GameManager { get; private set; } = null!;
public T this[Index index] => _behaviours[index];
public T this[Index index] => behaviours[index];
public BehaviourCollector() { }
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
gameObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
gameObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in gameObject.BehaviourController)
OnBehaviourAdded(gameObject.BehaviourController, item);
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
}
private void OnGameObjectUnregistered(IGameManager manager, IGameObject gameObject)
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
gameObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
gameObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in gameObject.BehaviourController)
OnBehaviourRemoved(gameObject.BehaviourController, item);
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
@ -48,7 +48,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
if (behaviour is not T tBehaviour)
return;
_behaviours.Add(tBehaviour);
behaviours.Add(tBehaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, tBehaviour);
}
@ -59,25 +59,27 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
if (behaviour is not T tBehaviour)
return;
if (!_behaviours.Remove(tBehaviour))
if (!behaviours.Remove(tBehaviour))
return;
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
protected virtual void OnAssign(IGameManager gameManager) { }
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
return false;
foreach (IGameObject gameObject in gameManager.GameObjects)
OnGameObjectRegistered(gameManager, gameObject);
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
gameManager.OnGameObjectRegistered += OnGameObjectRegistered;
gameManager.OnGameObjectUnRegistered += OnGameObjectUnregistered;
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
GameManager = gameManager;
OnAssign(gameManager);
OnGameManagerAssigned?.Invoke(this);
return true;
@ -88,17 +90,17 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
if (GameManager is null)
return false;
foreach (IGameObject gameObject in GameManager.GameObjects)
OnGameObjectUnregistered(GameManager, gameObject);
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
GameManager.OnGameObjectRegistered -= OnGameObjectRegistered;
GameManager.OnGameObjectUnRegistered -= OnGameObjectUnregistered;
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
public IEnumerator<T> GetEnumerator() => behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
}

View File

@ -11,7 +11,7 @@ public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
if (SortBy is not null)
_behaviours.Sort(SortBy);
behaviours.Sort(SortBy);
}
public BehaviourCollectorSorted() { }

View File

@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Syntriax.Engine.Core.Abstract;
@ -12,26 +11,25 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
public class BehaviourController : IBehaviourController
{
public event IBehaviourController.OnPreUpdateEventHandler? OnPreUpdate = null;
public event IBehaviourController.OnUpdateEventHandler? OnUpdate = null;
public event IBehaviourController.OnPreDrawEventHandler? OnPreDraw = null;
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
public event IBehaviourController.PreDrawEventHandler? OnPreDraw = null;
public event IBehaviourController.OnBehaviourAddedEventHandler? OnBehaviourAdded = null;
public event IBehaviourController.OnBehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IAssignableGameObject.OnGameObjectAssignedEventHandler? OnGameObjectAssigned = null;
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IHasHierarchyObject.HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
public event IInitialize.OnInitializedEventHandler? OnInitialized = null;
public event IInitialize.OnFinalizedEventHandler? OnFinalized = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
private IGameObject _gameObject = null!;
private IHierarchyObject _hierarchyObject = null!;
private bool _initialized = false;
public IGameObject GameObject => _gameObject;
public IHierarchyObject HierarchyObject => _hierarchyObject;
public bool IsInitialized
{
@ -54,7 +52,7 @@ public class BehaviourController : IBehaviourController
InsertBehaviourByPriority(behaviour);
behaviour.Assign(this);
behaviour.Assign(GameObject.StateEnable);
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
behaviour.Initialize();
behaviour.OnPriorityChanged += OnPriorityChange;
@ -63,11 +61,11 @@ public class BehaviourController : IBehaviourController
}
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_gameObject, args));
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_hierarchyObject, args));
public T? GetBehaviour<T>()
{
foreach (var behaviourItem in behaviours)
foreach (IBehaviour behaviourItem in behaviours)
if (behaviourItem is T result)
return result;
@ -77,7 +75,7 @@ public class BehaviourController : IBehaviourController
public IList<T> GetBehaviours<T>()
{
List<T>? behaviours = null;
foreach (var behaviourItem in this.behaviours)
foreach (IBehaviour behaviourItem in this.behaviours)
{
if (behaviourItem is not T behaviour)
continue;
@ -92,7 +90,7 @@ public class BehaviourController : IBehaviourController
public void GetBehaviours<T>(IList<T> results)
{
results.Clear();
foreach (var behaviourItem in behaviours)
foreach (IBehaviour behaviourItem in behaviours)
{
if (behaviourItem is not T behaviour)
continue;
@ -118,7 +116,7 @@ public class BehaviourController : IBehaviourController
public void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour
{
if (!behaviours.Contains(behaviour))
throw new Exception($"{behaviour.GetType().Name} does not exist in {GameObject.Name}'s {nameof(IBehaviourController)}.");
throw new Exception($"{behaviour.GetType().Name} does not exist in {HierarchyObject.Name}'s {nameof(IBehaviourController)}.");
behaviour.OnPriorityChanged -= OnPriorityChange;
behaviour.Finalize();
@ -126,23 +124,24 @@ public class BehaviourController : IBehaviourController
OnBehaviourRemoved?.Invoke(this, behaviour);
}
public bool Assign(IGameObject gameObject)
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
public bool Assign(IHierarchyObject hierarchyObject)
{
if (GameObject is not null && GameObject.IsInitialized)
if (HierarchyObject is not null && HierarchyObject.IsInitialized)
return false;
_gameObject = gameObject;
OnGameObjectAssigned?.Invoke(this);
_hierarchyObject = hierarchyObject;
OnAssign(hierarchyObject);
OnHierarchyObjectAssigned?.Invoke(this);
return true;
}
public bool Initialize()
{
if (IsInitialized)
return false;
NotAssignedException.Check(this, _gameObject);
NotAssignedException.Check(this, _hierarchyObject);
foreach (IBehaviour behaviour in behaviours)
behaviour.Initialize();
@ -168,14 +167,14 @@ public class BehaviourController : IBehaviourController
if (IsInitialized)
return false;
_gameObject = null!;
_hierarchyObject = null!;
OnUnassigned?.Invoke(this);
return true;
}
public void Update()
{
if (!GameObject.StateEnable.Enabled)
if (!HierarchyObject.StateEnable.Enabled)
return;
OnPreUpdate?.Invoke(this);
@ -184,14 +183,14 @@ public class BehaviourController : IBehaviourController
public void UpdatePreDraw()
{
if (!GameObject.StateEnable.Enabled)
if (!HierarchyObject.StateEnable.Enabled)
return;
OnPreDraw?.Invoke(this);
}
public BehaviourController() { }
public BehaviourController(IGameObject gameObject) => Assign(gameObject);
public BehaviourController(IHierarchyObject hierarchyObject) => Assign(hierarchyObject);
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
{
@ -210,7 +209,6 @@ public class BehaviourController : IBehaviourController
behaviours.Add(behaviour);
}
private void OnPriorityChange(IBehaviour sender, int previousPriority)
{
behaviours.Remove(sender);

View File

@ -5,7 +5,7 @@ using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class CoroutineManager : HierarchyObjectBase
public class CoroutineManager : HierarchyObject
{
private readonly List<IEnumerator> enumerators = [];

View File

@ -4,5 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>false</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
@startuml "Core Engine Relations"
top to bottom direction
skinparam linetype ortho
skinparam nodesep 100
title Core Engine Relations
interface Engine.Core.Abstract.IEntity extends Engine.Core.Abstract.IInitializable {}
interface Engine.Core.Abstract.IHierarchyObject extends Engine.Core.Abstract.IEntity, Engine.Core.Abstract.INameable {}
interface Engine.Core.Abstract.INameable {}
Engine.Core.Abstract.IHierarchyObject --> Engine.Core.Abstract.IBehaviourController: has
Engine.Core.Abstract.IBehaviourController "1" --> "0..*" Engine.Core.Abstract.IBehaviour: has
interface Engine.Core.Abstract.IBehaviourController {}
interface Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.Abstract.IBehaviour2D extends Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.Abstract.IBehaviour3D extends Engine.Core.Abstract.IBehaviour {}
interface Engine.Core.Abstract.IGameManager {}
Engine.Core.Abstract.IGameManager "1" -r-> "0..*" Engine.Core.Abstract.IHierarchyObject: has
' together {
' interface Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasStateEnable extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasGameManager extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasHierarchyObject extends Engine.Core.Abstract.IAssignable {}
' interface Engine.Core.Abstract.IHasBehaviourController extends Engine.Core.Abstract.IAssignable {}
' ' Engine.Core.Abstract.IHasStateEnable --> Engine.Core.Abstract.IStateEnable: has
' ' Engine.Core.Abstract.IHasGameManager --> Engine.Core.Abstract.IGameManager: has
' ' Engine.Core.Abstract.IHasHierarchyObject --> Engine.Core.Abstract.IHierarchyObject: has
' ' Engine.Core.Abstract.IHasBehaviourController --> Engine.Core.Abstract.IBehaviourController: has
' }
together {
interface Engine.Core.Abstract.ITransform2D {}
interface Engine.Core.Abstract.ICamera2D {}
interface Engine.Core.Abstract.ICoroutineYield {}
interface Engine.Core.Abstract.IStateEnable {}
interface Engine.Core.Abstract.IInitializable {}
interface Engine.Core.Abstract.IBehaviourCollector {}
}
@enduml

View File

@ -2,10 +2,10 @@ using System;
namespace Syntriax.Engine.Core;
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
public readonly struct EngineTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
{
public readonly TimeSpan Total = Total;
public readonly TimeSpan Elapsed = Elapsed;
public readonly TimeSpan TimeSinceStart = TimeSinceStart;
public readonly TimeSpan DeltaSpan = TimeDelta;
public readonly float DeltaTimeFrame = (float)Elapsed.TotalMilliseconds * .001f;
public readonly float DeltaTime = (float)TimeDelta.TotalSeconds;
}

View File

@ -1,5 +1,4 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core.Exceptions;
@ -8,13 +7,6 @@ public class AssignException : Exception
public AssignException() : base("Assign operation has failed.") { }
public AssignException(string? message) : base(message) { }
// public static AssignException FromStateEnable(IStateEnable? stateEnable)
// => new AssignException($"{nameof(IGameObject.AssignStateEnable)} failed on type {stateEnable?.GetType().ToString() ?? "\"null\""}");
public static AssignException From<T, T2>(T to, T2? value)
=> new AssignException($"Assign operation has failed on T: {typeof(T).FullName}, value: {value?.GetType().ToString() ?? "\"null\""}");
// public static AssignException FromBehaviourController(IBehaviourController? behaviourController)
// => new AssignException($"{nameof(IGameObject.AssignBehaviourController)} failed on type {behaviourController?.GetType().ToString() ?? "\"null\""}");
=> new($"Assign operation has failed on T: {to?.GetType().FullName ?? "\"null\""}, value: {value?.GetType().ToString() ?? "\"null\""}");
}
// throw new Exception($"{nameof(IGameObject.AssignTransform)} failed on type {transform?.GetType().ToString() ?? "null"} for type {typeof(T).FullName}");
// throw new Exception($"{nameof(IGameObject.AssignBehaviourController)} failed on type {behaviourController?.GetType().ToString() ?? "null"} for type {typeof(T).FullName}");
// throw new Exception($"{nameof(IGameObject.AssignStateEnable)} failed on type {stateEnable?.GetType().ToString() ?? "null"} for type {typeof(T).FullName}");

View File

@ -8,10 +8,10 @@ public class NotAssignedException : Exception
public NotAssignedException() : base("The object has not been assigned.") { }
public NotAssignedException(string? message) : base(message) { }
public static NotAssignedException From<T1, T2>(T1 to, T2? value) where T1 : IAssignable
=> new NotAssignedException($"{typeof(T2).Name} has not been assigned to {typeof(T1).Name}");
public static NotAssignedException From<T1, T2>(T1 to, T2? value)
=> new($"{value?.GetType().FullName ?? "\"null\""} has not been assigned to {to?.GetType().FullName ?? "\"null\""}");
public static void Check<T1, T2>(T1 to, T2? value) where T1 : IAssignable
public static void Check<T1, T2>(T1 to, T2? value)
{
if (value is not null)
return;

View File

@ -2,7 +2,7 @@ namespace Syntriax.Engine.Core.Abstract;
public static class TransformExtensions
{
public static Vector2D TransformVector2D(this ITransform transform, Vector2D vector)
public static Vector2D TransformVector2D(this ITransform2D transform, Vector2D vector)
=> vector.Scale(transform.Scale)
.Rotate(transform.Rotation * Math.DegreeToRadian)
.Add(transform.Position);

View File

@ -57,7 +57,7 @@ public static class BehaviourControllerExtensions
if (behaviourController.GetBehaviour<T>() is T behaviour)
return behaviour;
controller = controller.GameObject.Transform.Parent?.GameObject.BehaviourController;
controller = controller.HierarchyObject.Parent?.BehaviourController;
}
return default;
@ -87,8 +87,8 @@ public static class BehaviourControllerExtensions
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
return localBehaviour;
foreach (ITransform transform in behaviourController.GameObject.Transform)
if (GetBehaviourInChildren<T>(transform.GameObject.BehaviourController) is T behaviour)
foreach (IHierarchyObject child in behaviourController.HierarchyObject)
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
return behaviour;
return default;

View File

@ -7,29 +7,29 @@ namespace Syntriax.Engine.Core;
public static class BehaviourExtensions
{
public static T? FindBehaviour<T>(this IEnumerable<IGameObject> gameObjects) where T : class
public static T? FindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
{
foreach (IGameObject gameObject in gameObjects)
if (gameObject.BehaviourController.GetBehaviour<T>() is T behaviour)
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject.BehaviourController.GetBehaviour<T>() is T behaviour)
return behaviour;
return default;
}
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
public static bool TryFindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = FindBehaviour<T>(gameObjects);
behaviour = FindBehaviour<T>(hierarchyObjects);
return behaviour is not null;
}
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours) where T : class
public static void FindBehaviours<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
List<T> cache = [];
foreach (IGameObject gameObject in gameObjects)
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
{
gameObject.BehaviourController.GetBehaviours(cache);
hierarchyObject.BehaviourController.GetBehaviours(cache);
behaviours.AddRange(cache);
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Syntriax.Engine.Core;
public static class EnumExtensions
{
public static bool CheckFlag(this Enum left, Enum right)
=> ((int)(object)left & (int)(object)right) != 0;
}

View File

@ -4,6 +4,6 @@ namespace Syntriax.Engine.Core;
public static class GameManagerExtensions
{
public static IGameObject InstantiateGameObject(this IGameManager gameManager, params object?[]? args)
=> gameManager.InstantiateGameObject<GameObject>(args);
public static IHierarchyObject InstantiateHierarchyObject(this IGameManager gameManager, params object?[]? args)
=> gameManager.InstantiateHierarchyObject<HierarchyObject>(args);
}

View File

@ -1,12 +0,0 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class GameObjectExtensions
{
public static IGameObject SetGameObject(this IGameObject gameObject, string name)
{
gameObject.Name = name;
return gameObject;
}
}

View File

@ -7,7 +7,13 @@ namespace Syntriax.Engine.Core;
public static class HierarchyObjectExtensions
{
public static T? FindObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
public static IHierarchyObject SetHierarchyObject(this IHierarchyObject hierarchyObject, string name)
{
hierarchyObject.Name = name;
return hierarchyObject;
}
public static T? FindHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
{
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject is T @object)
@ -16,13 +22,13 @@ public static class HierarchyObjectExtensions
return default;
}
public static bool TryFindObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
public static bool TryFindHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = FindObject<T>(hierarchyObjects);
behaviour = FindHierarchyObject<T>(hierarchyObjects);
return behaviour is not null;
}
public static void FindObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
public static void FindHierarchyObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)

View File

@ -4,7 +4,7 @@ namespace Syntriax.Engine.Core;
public static class TransformExtensions
{
public static ITransform SetTransform(this ITransform transform,
public static ITransform2D SetTransform(this ITransform2D transform,
Vector2D? position = null, float? rotation = null, Vector2D? scale = null,
Vector2D? localPosition = null, float? localRotation = null, Vector2D? localScale = null)
{

View File

@ -1,195 +0,0 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Provides extension methods for <see cref="Vector2D"/> type.
/// </summary>
public static class Vector2DExtensions
{
/// <summary>
/// Returns the <see cref="Vector3D"/> representation of the <see cref="Vector2D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The <see cref="Vector3D"/> representation of the provided <see cref="Vector2D"/>.</returns>
public static Vector3D As3D(this Vector2D vector) => new(vector.X, vector.Y, 0f);
/// <summary>
/// Calculates the length of the <see cref="Vector2D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
/// <summary>
/// Calculates the squared length of the <see cref="Vector2D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
/// <summary>
/// Calculates the distance between two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Vector2D"/>.</param>
/// <param name="to">The ending <see cref="Vector2D"/>.</param>
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
/// <summary>
/// Returns the <see cref="Vector2D"/> with its components inverted.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
/// <summary>
/// Adds two <see cref="Vector2D"/>s component-wise.
/// </summary>
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
/// <param name="vectorToAdd">The vector <see cref="Vector2D"/> to be added.</param>
/// <returns>The result of the addition.</returns>
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
/// <summary>
/// Subtracts one <see cref="Vector2D"/> from another component-wise.
/// </summary>
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
/// <param name="vectorToSubtract">The <see cref="Vector2D"/> to be subtracted.</param>
/// <returns>The result of the subtraction.</returns>
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
/// <summary>
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to multiply.</param>
/// <param name="value">The scalar value to multiply with.</param>
/// <returns>The result of the multiplication.</returns>
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
/// <summary>
/// Divides a <see cref="Vector2D"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to divide.</param>
/// <param name="value">The scalar value to divide with.</param>
/// <returns>The result of the division.</returns>
public static Vector2D Divide(this Vector2D vector, float value) => Vector2D.Divide(vector, value);
/// <summary>
/// Returns a <see cref="Vector2D"/> with the absolute values of each component.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The <see cref="Vector2D"/> with absolute values.</returns>
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
/// <summary>
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to reflect.</param>
/// <param name="normal">The normal <see cref="Vector2D"/> of the reflecting surface.</param>
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
/// <summary>
/// Normalizes the <see cref="Vector2D"/> (creates a <see cref="Vector2D"/> with the same direction but with a length of 1).
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
/// <summary>
/// Creates a <see cref="Vector2D"/> pointing from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="Vector2D"/> pointing from <paramref name="from"/> to <paramref name="to"/>.</returns>
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
/// <summary>
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
/// <summary>
/// Calculates the perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
/// <summary>
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
/// <summary>
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
/// <summary>
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
/// <summary>
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
/// </summary>
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
/// <returns>The clamped <see cref="Vector2D"/>.</returns>
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
/// <summary>
/// Linearly interpolates between two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="from">The start <see cref="Vector2D"/>.</param>
/// <param name="to">The end <see cref="Vector2D"/>.</param>
/// <param name="t">The interpolation parameter (between 0 and 1).</param>
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
/// <summary>
/// Calculates the cross product of two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
/// <summary>
/// Calculates the angle in radians between two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
/// <summary>
/// Calculates the dot product of two <see cref="Vector2D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
/// <summary>
/// Checks whether two <see cref="Vector2D"/>s are approximately equal within a certain epsilon range.
/// </summary>
/// <param name="left">The first <see cref="Vector2D"/>.</param>
/// <param name="right">The second <see cref="Vector2D"/>.</param>
/// <param name="epsilon">The maximum difference allowed between components.</param>
/// <returns>True if the <see cref="Vector2D"/>s are approximately equal, false otherwise.</returns>
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,189 +0,0 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Provides extension methods for <see cref="Vector3D"/> type.
/// </summary>
public static class Vector3DExtensions
{
/// <summary>
/// Returns the <see cref="Vector2D"/> representation of the <see cref="Vector3D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The <see cref="Vector2D"/> representation of the provided <see cref="Vector3D"/>.</returns>
public static Vector2D As2D(this Vector3D vector) => new(vector.X, vector.Y);
/// <summary>
/// Calculates the length of the <see cref="Vector3D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The length of the <see cref="Vector3D"/>.</returns>
public static float Length(this Vector3D vector) => Vector3D.Length(vector);
/// <summary>
/// Calculates the squared length of the <see cref="Vector3D"/>.
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The squared length of the <see cref="Vector3D"/>.</returns>
public static float LengthSquared(this Vector3D vector) => Vector3D.LengthSquared(vector);
/// <summary>
/// Calculates the distance between two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Vector3D"/>.</param>
/// <param name="to">The ending <see cref="Vector3D"/>.</param>
/// <returns>The distance between the two <see cref="Vector3D"/>s.</returns>
public static float Distance(this Vector3D from, Vector3D to) => Vector3D.Distance(from, to);
/// <summary>
/// Returns the <see cref="Vector3D"/> with its components inverted.
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The inverted <see cref="Vector3D"/>.</returns>
public static Vector3D Invert(this Vector3D vector) => Vector3D.Invert(vector);
/// <summary>
/// Adds two <see cref="Vector3D"/>s component-wise.
/// </summary>
/// <param name="vector">The first <see cref="Vector3D"/>.</param>
/// <param name="vectorToAdd">The vector <see cref="Vector3D"/> to be added.</param>
/// <returns>The result of the addition.</returns>
public static Vector3D Add(this Vector3D vector, Vector3D vectorToAdd) => Vector3D.Add(vector, vectorToAdd);
/// <summary>
/// Subtracts one <see cref="Vector3D"/> from another component-wise.
/// </summary>
/// <param name="vector">The first <see cref="Vector3D"/>.</param>
/// <param name="vectorToSubtract">The <see cref="Vector3D"/> to be subtracted.</param>
/// <returns>The result of the subtraction.</returns>
public static Vector3D Subtract(this Vector3D vector, Vector3D vectorToSubtract) => Vector3D.Subtract(vector, vectorToSubtract);
/// <summary>
/// Multiplies a <see cref="Vector3D"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to multiply.</param>
/// <param name="value">The scalar value to multiply with.</param>
/// <returns>The result of the multiplication.</returns>
public static Vector3D Multiply(this Vector3D vector, float value) => Vector3D.Multiply(vector, value);
/// <summary>
/// Divides a <see cref="Vector3D"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to divide.</param>
/// <param name="value">The scalar value to divide with.</param>
/// <returns>The result of the division.</returns>
public static Vector3D Divide(this Vector3D vector, float value) => Vector3D.Divide(vector, value);
/// <summary>
/// Returns a <see cref="Vector3D"/> with the absolute values of each component.
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The <see cref="Vector3D"/> with absolute values.</returns>
public static Vector3D Abs(this Vector3D vector) => Vector3D.Abs(vector);
/// <summary>
/// Reflects a <see cref="Vector3D"/> off a surface with the specified normal.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to reflect.</param>
/// <param name="normal">The normal <see cref="Vector3D"/> of the reflecting surface.</param>
/// <returns>The reflected <see cref="Vector3D"/>.</returns>
public static Vector3D Reflect(this Vector3D vector, Vector3D normal) => Vector3D.Reflect(vector, normal);
/// <summary>
/// Normalizes the <see cref="Vector3D"/> (creates a <see cref="Vector3D"/> with the same direction but with a length of 1).
/// </summary>
/// <param name="vector">The input <see cref="Vector3D"/>.</param>
/// <returns>The normalized <see cref="Vector3D"/>.</returns>
public static Vector3D Normalize(this Vector3D vector) => Vector3D.Normalize(vector);
/// <summary>
/// Creates a <see cref="Vector3D"/> pointing from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="Vector3D"/> pointing from <paramref name="from"/> to <paramref name="to"/>.</returns>
public static Vector3D FromTo(this Vector3D from, Vector3D to) => Vector3D.FromTo(from, to);
/// <summary>
/// Scales a <see cref="Vector3D"/> by another <see cref="Vector3D"/> component-wise.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to scale.</param>
/// <param name="scale">The <see cref="Vector3D"/> containing the scaling factors for each component.</param>
/// <returns>The scaled <see cref="Vector3D"/>.</returns>
public static Vector3D Scale(this Vector3D vector, Vector3D scale) => Vector3D.Scale(vector, scale);
/// <summary>
/// Rotates a <see cref="Vector3D"/> by the specified angle (in radians).
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to rotate.</param>
/// <param name="normal">The <see cref="Vector3D"/> to rotate around.</param>
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
/// <returns>The rotated <see cref="Vector3D"/>.</returns>
public static Vector3D Rotate(this Vector3D vector, Vector3D normal, float angleInRadian) => Vector3D.Rotate(vector, normal, angleInRadian);
/// <summary>
/// Returns the component-wise minimum of two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <returns>The <see cref="Vector3D"/> containing the minimum components from both input <see cref="Vector3D"/>s.</returns>
public static Vector3D Min(this Vector3D left, Vector3D right) => Vector3D.Min(left, right);
/// <summary>
/// Returns the component-wise maximum of two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <returns>The <see cref="Vector3D"/> containing the maximum components from both input <see cref="Vector3D"/>s.</returns>
public static Vector3D Max(this Vector3D left, Vector3D right) => Vector3D.Max(left, right);
/// <summary>
/// Clamps each component of a <see cref="Vector3D"/> between the corresponding component of two other <see cref="Vector3D"/>s.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to clamp.</param>
/// <param name="min">The <see cref="Vector3D"/> representing the minimum values for each component.</param>
/// <param name="max">The <see cref="Vector3D"/> representing the maximum values for each component.</param>
/// <returns>The clamped <see cref="Vector3D"/>.</returns>
public static Vector3D Clamp(this Vector3D vector, Vector3D min, Vector3D max) => Vector3D.Clamp(vector, min, max);
/// <summary>
/// Linearly interpolates between two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="from">The start <see cref="Vector3D"/>.</param>
/// <param name="to">The end <see cref="Vector3D"/>.</param>
/// <param name="t">The interpolation parameter (between 0 and 1).</param>
/// <returns>The interpolated <see cref="Vector3D"/>.</returns>
public static Vector3D Lerp(this Vector3D from, Vector3D to, float t) => Vector3D.Lerp(from, to, t);
/// <summary>
/// Calculates the cross product of two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <returns>The cross product of the two <see cref="Vector3D"/>s.</returns>
public static Vector3D Cross(this Vector3D left, Vector3D right) => Vector3D.Cross(left, right);
/// <summary>
/// Calculates the angle in radians between two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <returns>The angle between the two <see cref="Vector3D"/>s in radians.</returns>
public static float AngleBetween(this Vector3D left, Vector3D right) => Vector3D.Angle(left, right);
/// <summary>
/// Calculates the dot product of two <see cref="Vector3D"/>s.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <returns>The dot product of the two <see cref="Vector3D"/>s.</returns>
public static float Dot(this Vector3D left, Vector3D right) => Vector3D.Dot(left, right);
/// <summary>
/// Checks whether two <see cref="Vector3D"/>s are approximately equal within a certain epsilon range.
/// </summary>
/// <param name="left">The first <see cref="Vector3D"/>.</param>
/// <param name="right">The second <see cref="Vector3D"/>.</param>
/// <param name="epsilon">The maximum difference allowed between components.</param>
/// <returns>True if the <see cref="Vector3D"/>s are approximately equal, false otherwise.</returns>
public static bool ApproximatelyEquals(this Vector3D left, Vector3D right, float epsilon = float.Epsilon) => Vector3D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -5,16 +5,19 @@ namespace Syntriax.Engine.Core.Factory;
public class BehaviourControllerFactory
{
public IBehaviourController Instantiate(IGameObject gameObject)
=> Instantiate<BehaviourController>(gameObject);
public static IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
=> Instantiate<BehaviourController>(hierarchyObject);
public T Instantiate<T>(IGameObject gameObject, params object?[]? args)
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
where T : class, IBehaviourController
{
T behaviourController = TypeFactory.Get<T>(args);
if (!behaviourController.Assign(gameObject))
throw AssignException.From(behaviourController, gameObject);
if (!hierarchyObject.Assign(behaviourController))
throw AssignException.From(hierarchyObject, behaviourController);
if (!behaviourController.Assign(hierarchyObject))
throw AssignException.From(behaviourController, hierarchyObject);
return behaviourController;
}

View File

@ -5,10 +5,10 @@ namespace Syntriax.Engine.Core.Factory;
public class BehaviourFactory
{
public T Instantiate<T>(IGameObject gameObject, params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(gameObject, stateEnable: null, args);
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(hierarchyObject, stateEnable: null, args);
public T Instantiate<T>(IGameObject gameObject, IStateEnable? stateEnable, params object?[]? args)
public static T Instantiate<T>(IHierarchyObject hierarchyObject, IStateEnable? stateEnable, params object?[]? args)
where T : class, IBehaviour
{
T behaviour = TypeFactory.Get<T>(args);
@ -17,10 +17,10 @@ public class BehaviourFactory
if (!stateEnable.Assign(behaviour))
throw AssignException.From(stateEnable, behaviour);
if (!behaviour.Assign(gameObject.BehaviourController))
throw AssignException.From(behaviour, gameObject.BehaviourController);
if (!behaviour.Assign(stateEnable))
throw AssignException.From(behaviour, stateEnable);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignException.From(behaviour, hierarchyObject.BehaviourController);
return behaviour;
}

View File

@ -1,42 +0,0 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core.Factory;
public class GameObjectFactory
{
public T Instantiate<T>(params object?[]? args) where T : class, IGameObject
=> Instantiate<T>(transform: null, behaviourController: null, stateEnable: null, args);
public T Instantiate<T>(
ITransform? transform = null,
IBehaviourController? behaviourController = null,
IStateEnable? stateEnable = null,
params object?[]? args
)
where T : class, IGameObject
{
T gameObject = TypeFactory.Get<T>(args);
transform ??= TypeFactory.Get<Transform>();
behaviourController ??= TypeFactory.Get<BehaviourController>();
stateEnable ??= TypeFactory.Get<StateEnable>();
if (!transform.Assign(gameObject))
throw AssignException.From(transform, gameObject);
if (!behaviourController.Assign(gameObject))
throw AssignException.From(behaviourController, gameObject);
if (!stateEnable.Assign(gameObject))
throw AssignException.From(stateEnable, gameObject);
if (!gameObject.Assign(transform))
throw AssignException.From(gameObject, transform);
if (!gameObject.Assign(behaviourController))
throw AssignException.From(gameObject, behaviourController);
if (!gameObject.Assign(stateEnable))
throw AssignException.From(gameObject, stateEnable);
return gameObject;
}
}

View File

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

View File

@ -5,12 +5,15 @@ namespace Syntriax.Engine.Core.Factory;
public class StateEnableFactory
{
public IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
public static IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
public T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
public static T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
{
T stateEnable = TypeFactory.Get<T>(args);
if (!entity.Assign(stateEnable))
throw AssignException.From(entity, stateEnable);
if (!stateEnable.Assign(entity))
throw AssignException.From(stateEnable, entity);

View File

@ -4,7 +4,7 @@ namespace Syntriax.Engine.Core.Factory;
public class TransformFactory
{
public ITransform Instantiate() => TypeFactory.Get<Transform>();
public T Instantiate<T>(params object?[]? args) where T : class, ITransform
public static ITransform2D Instantiate() => TypeFactory.Get<Transform2D>();
public static T Instantiate<T>(params object?[]? args) where T : class, ITransform2D
=> TypeFactory.Get<T>(args);
}

View File

@ -4,37 +4,20 @@ using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
using Syntriax.Engine.Core.Factory;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
[System.Diagnostics.DebuggerDisplay("HierarchyObject Count: {_hierarchyObjects.Count}")]
public class GameManager : BaseEntity, IGameManager
{
public event IGameManager.OnUpdateEventHandler? OnUpdate = null;
public event IGameManager.OnPreDawEventHandler? OnPreDraw = null;
public event IGameManager.UpdateEventHandler? OnUpdate = null;
public event IGameManager.PreDawEventHandler? OnPreDraw = null;
public event IGameManager.OnGameObjectRegisteredEventHandler? OnGameObjectRegistered = null;
public event IGameManager.OnGameObjectUnRegisteredEventHandler? OnGameObjectUnRegistered = null;
public event IGameManager.OnHierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
public event IGameManager.OnHierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
public event IGameManager.HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
public event IGameManager.HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
private readonly List<IHierarchyObject> _hierarchyObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
private GameObjectFactory _gameObjectFactory = null!;
private GameObjectFactory GameObjectFactory
{
get
{
if (_gameObjectFactory is null)
_gameObjectFactory = new GameObjectFactory();
return _gameObjectFactory;
}
}
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
public IReadOnlyList<IHierarchyObject> HierarchyObjects => _hierarchyObjects;
public override IStateEnable StateEnable
@ -43,7 +26,7 @@ public class GameManager : BaseEntity, IGameManager
{
if (base.StateEnable is null)
{
Assign(new StateEnableFactory().Instantiate(this));
Assign(Factory.StateEnableFactory.Instantiate(this));
if (base.StateEnable is null)
throw NotAssignedException.From(this, base.StateEnable);
}
@ -52,30 +35,35 @@ public class GameManager : BaseEntity, IGameManager
}
}
public EngineTime Time { get; private set; } = new();
public void Register(IHierarchyObject hierarchyObject)
{
if (_hierarchyObjects.Contains(hierarchyObject))
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is already registered to the {nameof(GameManager)}.");
if (hierarchyObject is IGameObject gameObject)
Register(gameObject);
else
{
if (!hierarchyObject.Initialize())
throw new Exception($"{nameof(hierarchyObject)} can't be initialized");
hierarchyObject.OnFinalized += OnHierarchyObjectFinalize;
hierarchyObject.OnExitedHierarchy += OnHierarchyObjectExitedHierarchy;
_hierarchyObjects.Add(hierarchyObject);
hierarchyObject.EnterHierarchy(this);
if (!hierarchyObject.Initialize())
throw new Exception($"{hierarchyObject.Name} can't be initialized");
OnHierarchyObjectRegistered?.Invoke(this, hierarchyObject);
}
foreach (IHierarchyObject child in hierarchyObject.Children)
Register(child);
_hierarchyObjects.Add(hierarchyObject);
if (!hierarchyObject.EnterHierarchy(this))
throw new Exception($"{hierarchyObject.Name} can't enter the hierarchy");
OnHierarchyObjectRegistered?.Invoke(this, hierarchyObject);
}
public T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject
public T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject
{
T gameObject = GameObjectFactory.Instantiate<T>(args);
Register(gameObject);
return gameObject;
T hierarchyObject = Factory.HierarchyObjectFactory.Instantiate<T>(args);
Register(hierarchyObject);
return hierarchyObject;
}
public void Remove(IHierarchyObject hierarchyObject)
@ -83,18 +71,21 @@ public class GameManager : BaseEntity, IGameManager
if (!_hierarchyObjects.Contains(hierarchyObject))
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is not registered to the {nameof(GameManager)}.");
if (hierarchyObject is IGameObject gameObject)
Unregister(gameObject);
else
{
_hierarchyObjects.Remove(hierarchyObject);
hierarchyObject.ExitHierarchy();
hierarchyObject.OnFinalized -= OnHierarchyObjectFinalize;
hierarchyObject.OnExitedHierarchy -= OnHierarchyObjectExitedHierarchy;
if (!hierarchyObject.Finalize())
throw new Exception($"{nameof(hierarchyObject)} can't be finalized");
foreach (IHierarchyObject child in hierarchyObject.Children)
Remove(child);
OnHierarchyObjectUnRegistered?.Invoke(this, hierarchyObject);
}
_hierarchyObjects.Remove(hierarchyObject);
if (!hierarchyObject.ExitHierarchy())
throw new Exception($"{hierarchyObject.Name} can't exit the hierarchy");
if (!hierarchyObject.Finalize())
throw new Exception($"{hierarchyObject.Name} can't be finalized");
OnHierarchyObjectUnRegistered?.Invoke(this, hierarchyObject);
}
protected override void InitializeInternal()
@ -102,93 +93,47 @@ public class GameManager : BaseEntity, IGameManager
base.InitializeInternal();
NotAssignedException.Check(this, StateEnable);
foreach (var gameObject in GameObjects)
gameObject.Initialize();
foreach (IHierarchyObject hierarchyObject in HierarchyObjects)
hierarchyObject.Initialize();
}
protected override void FinalizeInternal()
{
base.FinalizeInternal();
for (int i = GameObjects.Count; i >= 0; i--)
GameObjects[i].Finalize();
for (int i = HierarchyObjects.Count; i >= 0; i--)
HierarchyObjects[i].Finalize();
}
public void Update(EngineTime time)
public void Update(EngineTime engineTime)
{
Time.SetTime(time);
for (int i = 0; i < GameObjects.Count; i++)
GameObjects[i].BehaviourController.Update();
Time = engineTime;
OnUpdate?.Invoke(this, time);
for (int i = 0; i < HierarchyObjects.Count; i++)
HierarchyObjects[i].BehaviourController.Update();
OnUpdate?.Invoke(this, engineTime);
}
public void PreDraw()
{
for (int i = 0; i < GameObjects.Count; i++)
GameObjects[i].BehaviourController.UpdatePreDraw();
for (int i = 0; i < HierarchyObjects.Count; i++)
HierarchyObjects[i].BehaviourController.UpdatePreDraw();
OnPreDraw?.Invoke(this);
}
/////////////////////////////////////////////////////////////////
private void Register(IGameObject gameObject)
private void OnHierarchyObjectFinalize(IInitializable initializable)
{
if (_gameObjects.Contains(gameObject))
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is already registered to the {nameof(GameManager)}.");
gameObject.OnFinalized += OnGameObjectFinalize;
gameObject.OnExitedHierarchy += OnGameObjectExitedHierarchy;
if (!gameObject.Initialize())
throw new Exception($"{nameof(gameObject)} can't be initialized");
foreach (ITransform child in gameObject.Transform.Children)
Register(child.GameObject);
_gameObjects.Add(gameObject);
_hierarchyObjects.Add(gameObject);
if (!gameObject.EnterHierarchy(this))
throw new Exception($"{nameof(gameObject)} can't enter the hierarchy");
OnHierarchyObjectRegistered?.Invoke(this, gameObject);
OnGameObjectRegistered?.Invoke(this, gameObject);
if (initializable is IHierarchyObject hierarchyObject)
Remove(hierarchyObject);
}
private void Unregister(IGameObject gameObject)
private void OnHierarchyObjectExitedHierarchy(IHierarchyObject sender, IGameManager gameManager)
{
if (!_gameObjects.Contains(gameObject))
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is not registered to the {nameof(GameManager)}.");
gameObject.OnFinalized -= OnGameObjectFinalize;
gameObject.OnExitedHierarchy -= OnGameObjectExitedHierarchy;
foreach (ITransform child in gameObject.Transform.Children)
Unregister(child.GameObject);
_gameObjects.Remove(gameObject);
_hierarchyObjects.Remove(gameObject);
if (!gameObject.ExitHierarchy())
throw new Exception($"{nameof(gameObject)} can't exit the hierarchy");
if (!gameObject.Finalize())
throw new Exception($"{nameof(gameObject)} can't be finalized");
OnHierarchyObjectUnRegistered?.Invoke(this, gameObject);
OnGameObjectUnRegistered?.Invoke(this, gameObject);
if (sender is IHierarchyObject hierarchyObject)
Remove(hierarchyObject);
}
private void OnGameObjectFinalize(IInitialize initialize)
{
if (initialize is IGameObject gameObject)
Unregister(gameObject);
}
private void OnGameObjectExitedHierarchy(IHierarchyObject sender, IGameManager gameManager)
{
if (sender is IGameObject gameObject)
Unregister(gameObject);
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _hierarchyObjects.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _hierarchyObjects.GetEnumerator();
}

View File

@ -1,151 +0,0 @@
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class GameObject : BaseEntity, IGameObject
{
public event IHierarchyObject.OnEnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.OnExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IAssignableTransform.OnTransformAssignedEventHandler? OnTransformAssigned = null;
public event IAssignableBehaviourController.OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.OnNameChangedEventHandler? OnNameChanged = null;
public event IGameObject.OnUpdatedEventHandler? OnUpdated = null;
private ITransform _transform = null!;
private IBehaviourController _behaviourController = null!;
private IGameManager _gameManager = null!;
private string _name = nameof(GameObject);
public ITransform Transform => _transform;
public IBehaviourController BehaviourController => _behaviourController;
public IGameManager GameManager => _gameManager;
public bool IsInHierarchy => GameManager is not null;
public string Name
{
get => _name;
set
{
if (value == _name) return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
}
}
protected override void InitializeInternal()
{
base.InitializeInternal();
NotAssignedException.Check(this, _transform);
NotAssignedException.Check(this, _behaviourController);
if (!_behaviourController.Initialize())
throw new System.Exception($"Failed to Initialize {BehaviourController.GetType().Name} on {Transform.GameObject.Name}");
}
public void Update()
{
if (!StateEnable.Enabled)
return;
OnUpdated?.Invoke(this);
}
protected override void FinalizeInternal()
{
base.FinalizeInternal();
if (!_behaviourController.Finalize())
throw new System.Exception($"Failed to Finalize {BehaviourController.GetType().Name} on {Transform.GameObject.Name}");
}
public bool Assign(ITransform transform)
{
if (IsInitialized)
return false;
_transform = transform;
OnTransformAssigned?.Invoke(this);
transform.OnParentChanged += OnParentChangedInternal;
OnParentChangedInternal(transform, null, transform.Parent);
return true;
}
public bool Assign(IBehaviourController behaviourController)
{
if (IsInitialized)
return false;
_behaviourController = behaviourController;
OnBehaviourControllerAssigned?.Invoke(this);
return true;
}
protected override void UnassignInternal()
{
base.UnassignInternal();
_transform.OnParentChanged -= OnParentChangedInternal;
OnParentChangedInternal(_transform, _transform.Parent, null);
_transform = null!;
_behaviourController = null!;
_gameManager = null!;
}
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
private void ConnectBehaviourController(IAssignableBehaviourController controller)
{
controller.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
controller.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
}
private void OnBehaviourRemoved(IBehaviourController _, IBehaviour behaviour) { if (IsInitialized) behaviour.Finalize(); }
private void OnBehaviourAdded(IBehaviourController _, IBehaviour behaviour) { if (!IsInitialized) behaviour.Initialize(); }
private void OnParentChangedInternal(ITransform sender, ITransform? previousParent, ITransform? newParent)
{
if (previousParent is not null)
{
previousParent.OnParentChanged -= OnParentChangedInternal;
previousParent.GameObject.OnEnteredHierarchy -= EnterHierarchyInternal;
previousParent.GameObject.OnExitedHierarchy -= ExitHierarchyInternal;
}
if (newParent is not null)
{
newParent.OnParentChanged += OnParentChangedInternal;
newParent.GameObject.OnEnteredHierarchy += EnterHierarchyInternal;
newParent.GameObject.OnExitedHierarchy += ExitHierarchyInternal;
}
}
private void EnterHierarchyInternal(IHierarchyObject sender, IGameManager gameManager) => ((IHierarchyObject)this).EnterHierarchy(gameManager);
private void ExitHierarchyInternal(IHierarchyObject sender, IGameManager gameManager) => ((IHierarchyObject)this).ExitHierarchy();
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{
if (IsInHierarchy)
return false;
_gameManager = gameManager;
OnEnteredHierarchy?.Invoke(this, gameManager);
return true;
}
bool IHierarchyObject.ExitHierarchy()
{
if (!IsInHierarchy || _gameManager is not IGameManager gameManager)
return false;
_gameManager = null!;
OnExitedHierarchy?.Invoke(this, gameManager);
return true;
}
}

View File

@ -0,0 +1,159 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class HierarchyObject : BaseEntity, IHierarchyObject
{
public event IHierarchyObject.EnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.ExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IHierarchyObject.ParentChangedEventHandler? OnParentChanged = null;
public event IHierarchyObject.ChildrenAddedEventHandler? OnChildrenAdded = null;
public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private string _name = nameof(HierarchyObject);
private IGameManager _gameManager = null!;
private IBehaviourController _behaviourController = null!;
private bool _isActive = false;
private readonly List<IHierarchyObject> _children = [];
public IHierarchyObject? Parent { get; private set; } = null;
public IReadOnlyList<IHierarchyObject> Children => _children;
public IBehaviourController BehaviourController => _behaviourController;
public IGameManager GameManager => _gameManager;
public bool IsInHierarchy => _gameManager is not null;
public bool IsActive => _isActive;
public string Name
{
get => _name;
set
{
if (value == _name) return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
}
}
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{
if (IsInHierarchy)
return false;
_gameManager = gameManager;
UpdateActive();
OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager);
return true;
}
protected virtual void OnExitingHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.ExitHierarchy()
{
if (!IsInHierarchy || _gameManager is not IGameManager gameManager)
return false;
_gameManager = null!;
OnExitingHierarchy(gameManager);
OnExitedHierarchy?.Invoke(this, gameManager);
return true;
}
public void SetParent(IHierarchyObject? parent)
{
if (parent == this || Parent == parent)
return;
IHierarchyObject? previousParent = Parent;
if (previousParent is not null)
{
previousParent.RemoveChild(this);
previousParent.OnActiveChanged -= OnParentActiveChanged;
}
Parent = parent;
if (parent is not null)
{
parent.AddChild(this);
parent.OnActiveChanged += OnParentActiveChanged;
}
UpdateActive();
OnParentChanged?.Invoke(this, previousParent, parent);
}
public void AddChild(IHierarchyObject parent)
{
if (_children.Contains(parent))
return;
_children.Add(parent);
parent.SetParent(this);
OnChildrenAdded?.Invoke(this, parent);
}
public void RemoveChild(IHierarchyObject parent)
{
if (!_children.Remove(parent))
return;
parent.SetParent(null);
OnChildrenRemoved?.Invoke(this, parent);
}
protected virtual void OnAssign(IBehaviourController behaviourController) { }
public bool Assign(IBehaviourController behaviourController)
{
if (IsInitialized)
return false;
_behaviourController = behaviourController;
OnAssign(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
return true;
}
protected override void OnAssign(IStateEnable stateEnable)
{
base.OnAssign(stateEnable);
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
}
private void OnParentActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
private void UpdateActive()
{
bool previousActive = IsActive;
_isActive = StateEnable.Enabled && (Parent?.IsActive ?? true);
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
}
protected override void UnassignInternal()
{
base.UnassignInternal();
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
}
protected override void InitializeInternal()
{
base.InitializeInternal();
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _children.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
}

View File

@ -1,142 +0,0 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public abstract class HierarchyObjectBase : IHierarchyObject
{
public event IHierarchyObject.OnEnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.OnExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IEntity.OnIdChangedEventHandler? OnIdChanged = null;
public event IInitialize.OnInitializedEventHandler? OnInitialized = null;
public event IInitialize.OnFinalizedEventHandler? OnFinalized = null;
public event IAssignableStateEnable.OnStateEnableAssignedEventHandler? OnStateEnableAssigned = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event INameable.OnNameChangedEventHandler? OnNameChanged = null;
private string _id = string.Empty;
private string _name = nameof(HierarchyObjectBase);
private bool _initialized = false;
private IStateEnable _stateEnable = null!;
private IGameManager _gameManager = null!;
public IGameManager GameManager => _gameManager;
public bool IsInHierarchy => _gameManager is not null;
public bool IsInitialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
else
OnFinalized?.Invoke(this);
}
}
public virtual IStateEnable StateEnable => _stateEnable;
public string Name
{
get => _name;
set
{
if (value == _name) return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this, previousName);
}
}
public string Id
{
get => _id;
set
{
if (value == _id)
return;
string previousId = _id;
_id = value;
OnIdChanged?.Invoke(this, previousId);
}
}
public bool Assign(IStateEnable stateEnable)
{
if (IsInitialized)
return false;
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnStateEnableAssigned?.Invoke(this);
return true;
}
protected virtual void UnassignInternal() { }
public bool Unassign()
{
if (IsInitialized)
return false;
UnassignInternal();
OnUnassigned?.Invoke(this);
return true;
}
protected virtual void InitializeInternal() { }
public bool Initialize()
{
if (IsInitialized)
return false;
InitializeInternal();
IsInitialized = true;
return true;
}
protected virtual void FinalizeInternal() { }
public bool Finalize()
{
if (!IsInitialized)
return false;
FinalizeInternal();
IsInitialized = false;
return true;
}
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{
if (IsInHierarchy)
return false;
_gameManager = gameManager;
OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager);
return true;
}
protected virtual void OnExitingHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.ExitHierarchy()
{
if (!IsInHierarchy || _gameManager is not IGameManager gameManager)
return false;
_gameManager = null!;
OnExitingHierarchy(gameManager);
OnExitedHierarchy?.Invoke(this, gameManager);
return true;
}
}

View File

@ -1,8 +1,6 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
@ -76,9 +74,10 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
/// </summary>
/// <param name="left">The first <see cref="AABB"/>.</param>
/// <param name="right">The second <see cref="AABB"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(AABB left, AABB right)
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
}
/// <summary>
@ -86,18 +85,9 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
/// </summary>
public static class AABBExtensions
{
/// <summary>
/// Converts a collection of <see cref="Vector2D"/>s to an <see cref="AABB"/>.
/// </summary>
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
/// <inheritdoc cref="AABB.ToAABB" />
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
/// <summary>
/// Checks if two <see cref="AABB"/>s are approximately equal.
/// </summary>
/// <param name="left">The first <see cref="AABB"/>.</param>
/// <param name="right">The second <see cref="AABB"/>.</param>
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right);
/// <inheritdoc cref="AABB.ApproximatelyEquals" />
public static bool ApproximatelyEquals(this AABB left, AABB right, float epsilon = float.Epsilon) => AABB.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,9 +1,7 @@
using System.Diagnostics;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a 2D circle.
@ -11,7 +9,7 @@ namespace Syntriax.Engine.Physics2D.Primitives;
/// <param name="center">The center of the circle.</param>
/// <param name="radius">The radius of the circle.</param>
/// <remarks>
/// Initializes a new instance of the Circle struct with the specified center and radius.
/// Initializes a new instance of the <see cref="Circle"/> struct with the specified center and radius.
/// </remarks>
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
public readonly struct Circle(Vector2D center, float radius)
@ -59,23 +57,27 @@ public readonly struct Circle(Vector2D center, float radius)
/// <summary>
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
/// </summary>
public static Projection Project(Circle circle, Vector2D projectionVector)
public static Projection1D Project(Circle circle, Vector2D projectionVector)
{
float projectedCenter = circle.Center.Dot(projectionVector);
return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
}
/// <summary>
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform2D"/>.
/// </summary>
public static Circle TransformCircle(ITransform transform, Circle circle)
public static Circle TransformCircle(ITransform2D transform, Circle circle)
=> new(transform.TransformVector2D(circle.Center), circle.Radius * (transform.Scale.Magnitude / Vector2D.One.Magnitude));
/// <summary>
/// Checks if two <see cref="Circle"/>s are approximately equal.
/// </summary>
public static bool ApproximatelyEquals(Circle left, Circle right)
=> left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius);
/// <param name="left">The first <see cref="Circle"/>.</param>
/// <param name="right">The second <see cref="Circle"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
}
/// <summary>
@ -83,33 +85,21 @@ public readonly struct Circle(Vector2D center, float radius)
/// </summary>
public static class CircleExtensions
{
/// <summary>
/// Sets the center of the <see cref="Circle"/>.
/// </summary>
/// <inheritdoc cref="Circle.SetCenter(Circle, Vector2D)" />
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
/// <summary>
/// Sets the radius of the <see cref="Circle"/>.
/// </summary>
/// <inheritdoc cref="Circle.SetRadius(Circle, float)" />
public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius);
/// <summary>
/// Moves the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
/// </summary>
/// <inheritdoc cref="Circle.Displace(Circle, Vector2D)" />
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
/// <summary>
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
/// </summary>
public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
/// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
public static Projection1D ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
/// <summary>
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
/// </summary>
public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
/// <inheritdoc cref="Circle.TransformCircle(ITransform2D, Circle)" />
public static Circle TransformCircle(this ITransform2D transform, Circle circle) => Circle.TransformCircle(transform, circle);
/// <summary>
/// Checks if two <see cref="Circle"/>s are approximately equal.
/// </summary>
public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
/// <inheritdoc cref="Circle.ApproximatelyEquals(Circle, Circle, float)" />
public static bool ApproximatelyEquals(this Circle left, Circle right, float epsilon = float.Epsilon) => Circle.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,74 +1,72 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a 2D line segment defined by two endpoints.
/// </summary>
/// <param name="from">The starting point of the <see cref="Line2D"/> segment.</param>
/// <param name="to">The ending point of the <see cref="Line2D"/> segment.</param>
/// <remarks>
/// Initializes a new instance of the Line struct with the specified endpoints.
/// Initializes a new instance of the <see cref="Line2D"/> struct with the specified endpoints.
/// </remarks>
/// <param name="from">The starting point of the <see cref="Line"/> segment.</param>
/// <param name="to">The ending point of the <see cref="Line"/> segment.</param>
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
public readonly struct Line(Vector2D from, Vector2D to)
public readonly struct Line2D(Vector2D from, Vector2D to)
{
/// <summary>
/// The starting point of the <see cref="Line"/> segment.
/// The starting point of the <see cref="Line2D"/> segment.
/// </summary>
public readonly Vector2D From = from;
/// <summary>
/// The ending point of the <see cref="Line"/> segment.
/// The ending point of the <see cref="Line2D"/> segment.
/// </summary>
public readonly Vector2D To = to;
/// <summary>
/// The reversed <see cref="Line"/> segment.
/// The reversed <see cref="Line2D"/> segment.
/// </summary>
public readonly Line Reversed => new(To, From);
public readonly Line2D Reversed => new(To, From);
/// <summary>
/// The normalized direction <see cref="Vector2D"/> of the <see cref="Line"/> segment.
/// The normalized direction <see cref="Vector2D"/> of the <see cref="Line2D"/> segment.
/// </summary>
public readonly Vector2D Direction => From.FromTo(To).Normalize();
/// <summary>
/// The length of the <see cref="Line"/> segment.
/// The length of the <see cref="Line2D"/> segment.
/// </summary>
public readonly float Length => From.FromTo(To).Length();
/// <summary>
/// The squared length of the <see cref="Line"/> segment.
/// The squared length of the <see cref="Line2D"/> segment.
/// </summary>
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
/// <summary>
/// The equation of the <see cref="Line"/> defined by this <see cref="Line"/> segment.
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
/// </summary>
public static LineEquation GetLineEquation(Line line)
public static Line2DEquation GetLineEquation(Line2D line)
{
Vector2D slopeVector = line.From.FromTo(line.To);
float slope = slopeVector.Y / slopeVector.X;
float yOffset = line.From.Y - (slope * line.From.X);
return new LineEquation(slope, yOffset);
return new Line2DEquation(slope, yOffset);
}
/// <summary>
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line"/>.
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.
/// </summary>
public static bool Intersects(Line line, Vector2D point)
=> LineEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y);
public static bool Intersects(Line2D line, Vector2D point)
=> Line2DEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y);
/// <summary>
/// Calculates the parameter 't' representing the point's position on the <see cref="Line"/> segment.
/// Calculates the parameter 't' representing the point's position on the <see cref="Line2D"/> segment.
/// </summary>
public static float GetT(Line line, Vector2D point)
public static float GetT(Line2D line, Vector2D point)
{
float fromX = MathF.Abs(line.From.X);
float toX = MathF.Abs(line.To.X);
@ -91,9 +89,9 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Checks if the <see cref="Line"/> segment intersects with another <see cref="Line"/> segment.
/// Checks if the <see cref="Line2D"/> segment intersects with another <see cref="Line2D"/> segment.
/// </summary>
public static bool Intersects(Line left, Line right)
public static bool Intersects(Line2D left, Line2D right)
{
int o1 = Vector2D.Orientation(left.From, left.To, right.From);
int o2 = Vector2D.Orientation(left.From, left.To, right.To);
@ -112,9 +110,9 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Checks if the point lies within the <see cref="Line"/> segment.
/// Checks if the point lies within the <see cref="Line2D"/> segment.
/// </summary>
public static bool OnSegment(Line 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) &&
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
@ -124,9 +122,9 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Determines whether two <see cref="Line"/> segments intersect.
/// Determines whether two <see cref="Line2D"/> segments intersect.
/// </summary>
public static bool Intersects(Line left, Line right, [NotNullWhen(returnValue: true)] out Vector2D? point)
public static bool Intersects(Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point)
{
point = null;
@ -139,15 +137,15 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Finds the point of intersection between two <see cref="Line"/> segments.
/// Finds the point of intersection between two <see cref="Line2D"/> segments.
/// </summary>
public static Vector2D IntersectionPoint(Line left, Line right)
public static Vector2D IntersectionPoint(Line2D left, Line2D right)
=> Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right));
/// <summary>
/// Calculates the parameter 't' representing the intersection point's position on the <see cref="Line"/> segment.
/// Calculates the parameter 't' representing the intersection point's position on the <see cref="Line2D"/> segment.
/// </summary>
public static float IntersectionParameterT(Line left, Line right)
public static float IntersectionParameterT(Line2D left, Line2D right)
{
float numerator = (left.From.X - right.From.X) * (right.From.Y - right.To.Y) - (left.From.Y - right.From.Y) * (right.From.X - right.To.X);
float denominator = (left.From.X - left.To.X) * (right.From.Y - right.To.Y) - (left.From.Y - left.To.Y) * (right.From.X - right.To.X);
@ -160,30 +158,23 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Linearly interpolates between the two endpoints of the <see cref="Line"/> segment using parameter 't'.
/// Linearly interpolates between the two endpoints of the <see cref="Line2D"/> segment using parameter 't'.
/// </summary>
public static Vector2D Lerp(Line line, float t)
=> new(
line.From.X + (line.To.X - line.From.X) * t,
line.From.Y + (line.To.Y - line.From.Y) * t
);
public static Vector2D Lerp(Line2D line, float t)
=> Vector2D.Lerp(line.From, line.To, t);
/// <summary>
/// Calculates the closest point on the <see cref="Line"/> segment to the specified point.
/// Calculates the closest point on the <see cref="Line2D"/> segment to the specified point.
/// </summary>
public static Vector2D ClosestPointTo(Line line, Vector2D point)
public static Vector2D ClosestPointTo(Line2D line, Vector2D point)
{
// Convert edge points to vectors
var edgeVector = new Vector2D(line.To.X - line.From.X, line.To.Y - line.From.Y);
var pointVector = new Vector2D(point.X - line.From.X, point.Y - line.From.Y);
Vector2D edgeVector = line.From.FromTo(line.To);
Vector2D pointVector = point - line.From;
// Calculate the projection of pointVector onto edgeVector
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
// Clamp t to the range [0, 1] to ensure the closest point is on the edge
t = MathF.Max(0, MathF.Min(1, t));
// Calculate the closest point on the edge
float closestX = line.From.X + t * edgeVector.X;
float closestY = line.From.Y + t * edgeVector.Y;
@ -191,53 +182,39 @@ public readonly struct Line(Vector2D from, Vector2D to)
}
/// <summary>
/// Checks if two <see cref="Line"/> segments are approximately equal.
/// Checks if two <see cref="Line2D"/> segments are approximately equal.
/// </summary>
public static bool ApproximatelyEquals(Line left, Line right)
=> left.From.ApproximatelyEquals(right.From) && left.To.ApproximatelyEquals(right.To);
/// <param name="left">The first <see cref="Line2D"/>.</param>
/// <param name="right">The second <see cref="Line2D"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="Line2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon)
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
}
/// <summary>
/// Provides extension methods for the Line struct.
/// Provides extension methods for the <see cref="Line2D"/> struct.
/// </summary>
public static class LineExtensions
public static class Line2DExtensions
{
/// <summary>
/// Linearly interpolates between the two endpoints of the <see cref="Line"/> segment using parameter 't'.
/// </summary>
public static Vector2D Lerp(this Line line, float t) => Line.Lerp(line, t);
/// <inheritdoc cref="Line2D.Lerp(Line2D, float)" />
public static Vector2D Lerp(this Line2D line, float t) => Line2D.Lerp(line, t);
/// <inheritdoc cref="Line2D.GetLineEquation(Line2D)" />
public static Line2DEquation ToLineEquation(this Line2D line) => Line2D.GetLineEquation(line);
/// <summary>
/// The equation of the <see cref="Line"/> defined by this <see cref="Line"/> segment.
/// </summary>
public static LineEquation ToLineEquation(this Line line) => Line.GetLineEquation(line);
/// <inheritdoc cref="Line2D.Intersects(Line2D, Vector2D)" />
public static bool Intersects(this Line2D line, Vector2D point) => Line2D.Intersects(line, point);
/// <inheritdoc cref="Line2D.GetT(Line2D, Vector2D)" />
public static float GetT(this Line2D line, Vector2D point) => Line2D.GetT(line, point);
/// <summary>
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line"/>.
/// </summary>
public static bool Intersects(this Line line, Vector2D point) => Line.Intersects(line, point);
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D)" />
public static bool Intersects(this Line2D left, Line2D right) => Line2D.Intersects(left, right);
/// <summary>
/// Calculates the parameter 't' representing the point's position on the <see cref="Line"/> segment.
/// </summary>
public static float GetT(this Line line, Vector2D point) => Line.GetT(line, point);
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D, out Vector2D?)" />
public static bool Intersects(this Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point) => Line2D.Intersects(left, right, out point);
/// <summary>
/// Checks if the <see cref="Line"/> segment intersects with another <see cref="Line"/> segment.
/// </summary>
public static bool Intersects(this Line left, Line right) => Line.Intersects(left, right);
/// <summary>
/// Determines whether two <see cref="Line"/> segments intersect.
/// </summary>
public static bool Intersects(this Line left, Line right, [NotNullWhen(returnValue: true)] out Vector2D? point) => Line.Intersects(left, right, out point);
/// <summary>
/// Checks if two <see cref="Line"/>s are approximately equal.
/// </summary>
public static bool ApproximatelyEquals(this Line left, Line right) => Line.ApproximatelyEquals(left, right);
/// <inheritdoc cref="Line2D.ApproximatelyEquals(Line2D, Line2D, float)" />
public static bool ApproximatelyEquals(this Line2D left, Line2D right, float epsilon = float.Epsilon) => Line2D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -0,0 +1,53 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a line equation in the form y = mx + b.
/// </summary>
/// <param name="slope">The slope of the line.</param>
/// <param name="offsetY">The y-intercept of the line.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and y-intercept.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
public readonly struct Line2DEquation(float slope, float offsetY)
{
/// <summary>
/// The slope of the line equation.
/// </summary>
public readonly float Slope = slope;
/// <summary>
/// The y-intercept of the line equation.
/// </summary>
public readonly float OffsetY = offsetY;
/// <summary>
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
/// </summary>
/// <param name="lineEquation">The line equation to resolve.</param>
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
/// <returns>The y-coordinate resolved using the line equation.</returns>
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
/// <summary>
/// Checks if two line equations are approximately equal.
/// </summary>
/// <param name="left">The first line equation to compare.</param>
/// <param name="right">The second line equation to compare.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
}
/// <summary>
/// Provides extension methods for the LineEquation struct.
/// </summary>
public static class Line2DEquationExtensions
{
/// <inheritdoc cref="Line2DEquation.Resolve(Line2DEquation, float)" />
public static float Resolve(this Line2DEquation lineEquation, float x) => Line2DEquation.Resolve(lineEquation, x);
/// <inheritdoc cref="Line2DEquation.ApproximatelyEquals(Line2DEquation, Line2DEquation, float)" />
public static bool ApproximatelyEquals(this Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon) => Line2DEquation.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,4 +1,4 @@
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a range of values along a single axis.
@ -6,10 +6,10 @@ namespace Syntriax.Engine.Physics2D.Primitives;
/// <param name="min">The minimum value of the projection.</param>
/// <param name="max">The maximum value of the projection.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Projection"/> struct with the specified minimum and maximum values.
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
public readonly struct Projection(float min, float max)
public readonly struct Projection1D(float min, float max)
{
/// <summary>
/// Gets the minimum value of the projection.
@ -27,7 +27,7 @@ public readonly struct Projection(float min, float max)
/// <param name="left">The first projection to check.</param>
/// <param name="right">The second projection to check.</param>
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
public static bool Overlaps(Projection1D left, Projection1D right) => Overlaps(left, right, out float _);
/// <summary>
/// Checks if two projections overlap and calculates the depth of the overlap.
@ -36,7 +36,7 @@ public readonly struct Projection(float min, float max)
/// <param name="right">The second projection to check.</param>
/// <param name="depth">The depth of the overlap, if any.</param>
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
public static bool Overlaps(Projection left, Projection right, out float depth)
public static bool Overlaps(Projection1D left, Projection1D right, out float depth)
{
// TODO Try to improve this
bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
@ -73,24 +73,13 @@ public readonly struct Projection(float min, float max)
}
/// <summary>
/// Provides extension methods for the <see cref="Projection"/> struct.
/// Provides extension methods for the <see cref="Projection1D"/> struct.
/// </summary>
public static class ProjectionExtensions
public static class Projection1DExtensions
{
/// <summary>
/// Checks if two projections overlap.
/// </summary>
/// <param name="left">The first projection to check.</param>
/// <param name="right">The second projection to check.</param>
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D)" />
public static bool Overlaps(this Projection1D left, Projection1D right) => Projection1D.Overlaps(left, right);
/// <summary>
/// Checks if two projections overlap and calculates the depth of the overlap.
/// </summary>
/// <param name="left">The first projection to check.</param>
/// <param name="right">The second projection to check.</param>
/// <param name="depth">The depth of the overlap, if any.</param>
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
}

View File

@ -0,0 +1,359 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a 3D space rotation.
/// </summary>
/// <param name="x">X(i) position of the <see cref="Quaternion"/>.</param>
/// <param name="y">Y(j) position of the <see cref="Quaternion"/>.</param>
/// <param name="z">Z(k) position of the <see cref="Quaternion"/>.</param>
/// <param name="w">W(a) position of the <see cref="Quaternion"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Quaternion"/> struct with the specified positions.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
public readonly struct Quaternion(float x, float y, float z, float w)
{
/// <summary>
/// The X(i) imaginary of the <see cref="Quaternion"/>.
/// </summary>
public readonly float X = x;
/// <summary>
/// The Y(j) imaginary of the <see cref="Quaternion"/>.
/// </summary>
public readonly float Y = y;
/// <summary>
/// The Z(k) imaginary of the <see cref="Quaternion"/>.
/// </summary>
public readonly float Z = z;
/// <summary>
/// The W(a) scalar of the <see cref="Quaternion"/>.
/// </summary>
public readonly float W = w;
/// <summary>
/// The magnitude (length) of the <see cref="Quaternion"/>.
/// </summary>
public float Magnitude => Length(this);
/// <summary>
/// The squared magnitude (length) of the <see cref="Quaternion"/>.
/// </summary>
public float MagnitudeSquared => LengthSquared(this);
/// <summary>
/// The normalized form of the <see cref="Quaternion"/> (a <see cref="Quaternion"/> with the same direction and a magnitude of 1).
/// </summary>
public Quaternion Normalized => Normalize(this);
/// <summary>
/// Represents the <see cref="Quaternion"/> with no rotation.
/// </summary>
public readonly static Quaternion Zero = new(0f, 0f, 0f, 0f);
/// <summary>
/// Represents the identity <see cref="Quaternion"/>.
/// </summary>
public readonly static Quaternion Identity = new(0f, 0f, 0f, 1f);
public static Quaternion operator -(Quaternion quaternion) => new(-quaternion.X, -quaternion.Y, -quaternion.Z, quaternion.W);
public static Quaternion operator +(Quaternion left, Quaternion right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Quaternion operator -(Quaternion left, Quaternion right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Quaternion operator *(Quaternion quaternion, float value) => new(quaternion.X * value, quaternion.Y * value, quaternion.Z * value, quaternion.W * value);
public static Quaternion operator *(float value, Quaternion quaternion) => new(quaternion.X * value, quaternion.Y * value, quaternion.Z * value, quaternion.W * value);
public static Quaternion operator *(Quaternion left, Quaternion right)
=> new(
left.W * right.X + left.X * right.W + left.Y * right.Z - left.Z * right.Y,
left.W * right.Y + left.Y * right.W + left.Z * right.X - left.X * right.Z,
left.W * right.Z + left.Z * right.W + left.X * right.Y - left.Y * right.X,
left.W * right.W - left.X * right.X - left.Y * right.Y - left.Z * right.Z
);
public static Quaternion operator /(Quaternion quaternion, float value) => new(quaternion.X / value, quaternion.Y / value, quaternion.Z / value, quaternion.W / value);
public static bool operator ==(Quaternion left, Quaternion right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator !=(Quaternion left, Quaternion right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public static implicit operator Quaternion(System.Numerics.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
public static implicit operator System.Numerics.Quaternion(Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
/// <summary>
/// Calculates the length of the <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <returns>The length of the <see cref="Quaternion"/>.</returns>
public static float Length(Quaternion quaternion) => Math.Sqrt(LengthSquared(quaternion));
/// <summary>
/// Calculates the squared length of the <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <returns>The squared length of the <see cref="Quaternion"/>.</returns>
public static float LengthSquared(Quaternion quaternion) => quaternion.X * quaternion.X + quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z + quaternion.Z * quaternion.Z + quaternion.W * quaternion.W;
/// <summary>
/// Adds two <see cref="Quaternion"/>s.
/// </summary>
/// <param name="left">The first <see cref="Quaternion"/>.</param>
/// <param name="right">The second <see cref="Quaternion"/>.</param>
/// <returns>The sum of the two <see cref="Quaternion"/>s.</returns>
public static Quaternion Add(Quaternion left, Quaternion right) => left + right;
/// <summary>
/// Subtracts one <see cref="Quaternion"/> from another.
/// </summary>
/// <param name="left">The <see cref="Quaternion"/> to subtract from.</param>
/// <param name="right">The <see cref="Quaternion"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="Quaternion"/> from the first.</returns>
public static Quaternion Subtract(Quaternion left, Quaternion right) => left - right;
/// <summary>
/// Multiplies a <see cref="Quaternion"/> by a scalar value.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="Quaternion"/> by the scalar value.</returns>
public static Quaternion Multiply(Quaternion quaternion, float value) => quaternion * value;
/// <summary>
/// Divides a <see cref="Quaternion"/> by a scalar value.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="Quaternion"/> by the scalar value.</returns>
public static Quaternion Divide(Quaternion quaternion, float value) => quaternion / value;
/// <summary>
/// Normalizes the <see cref="Quaternion"/> (creates a unit <see cref="Quaternion"/> with the same direction).
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/> to normalize.</param>
/// <returns>The normalized <see cref="Quaternion"/>.</returns>
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
/// <summary>
/// Inverts the direction of the <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <returns>The inverted <see cref="Quaternion"/>.</returns>
public static Quaternion Invert(Quaternion quaternion) => Conjugate(quaternion) / LengthSquared(quaternion);
/// <summary>
/// Conjugate of the <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion">The <see cref="Quaternion"/>.</param>
/// <returns>The inverted <see cref="Quaternion"/>.</returns>
public static Quaternion Conjugate(Quaternion quaternion) => -quaternion;
/// <summary>
/// Rotates a <see cref="Vector3D"/> by applying the provided <see cref="Quaternion"/>.
/// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to be rotated.</param>
/// <param name="quaternion">The <see cref="Quaternion"/> to used for applying rotation.</param>
/// <returns>The rotated <see cref="Vector3D"/>.</returns>
public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion)
{
Quaternion rotation = quaternion * new Quaternion(vector.X, vector.Y, vector.Z, 0) * Invert(quaternion);
return new(rotation.X, rotation.Y, rotation.Z);
}
/// <summary>
/// Performs spherical linear interpolation between two <see cref="Quaternion"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Quaternion"/> (t = 0).</param>
/// <param name="to">The target <see cref="Quaternion"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="Quaternion"/>.</returns>
public static Quaternion SLerp(Quaternion from, Quaternion to, float t)
{
float dot = Dot(from, to);
if (dot < 0.0f)
{
from = new Quaternion(-from.X, -from.Y, -from.Z, -from.W);
dot = -dot;
}
if (dot > 0.9995f)
return Lerp(from, to, t);
float angle = MathF.Acos(dot);
float sinAngle = MathF.Sin(angle);
float fromWeight = MathF.Sin((1f - t) * angle) / sinAngle;
float toWeight = MathF.Sin(t * angle) / sinAngle;
return from * fromWeight + to * toWeight;
}
/// <summary>
/// Performs linear interpolation between two <see cref="Quaternion"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Quaternion"/> (t = 0).</param>
/// <param name="to">The target <see cref="Quaternion"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="Quaternion"/>.</returns>
public static Quaternion Lerp(Quaternion from, Quaternion to, float t) => Normalize(new(from.X.Lerp(to.X, t), from.W.Lerp(to.W, t), from.Z.Lerp(to.Z, t), from.W.Lerp(to.W, t)));
/// <summary>
/// Calculates the dot product of two <see cref="Quaternion"/>s.
/// </summary>
/// <param name="left">The first <see cref="Quaternion"/>.</param>
/// <param name="right">The second <see cref="Quaternion"/>.</param>
/// <returns>The dot product of the two <see cref="Quaternion"/>s.</returns>
public static float Dot(Quaternion left, Quaternion right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
/// <summary>
/// Calculates the <see cref="Quaternion"/> from given axis and angle.
/// </summary>
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given parameters.</returns>
public static Quaternion FromAxisAngle(Vector3D axis, float angle)
{
float halfAngle = angle * .5f;
float sinHalf = MathF.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, MathF.Cos(halfAngle));
}
/// <summary>
/// Calculates the <see cref="Quaternion"/> from given yaw, pitch and roll values.
/// </summary>
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given parameters.</returns>
public static Quaternion FromAngles(float x, float y, float z)
{
float cosX = Math.Cos(x * .5f);
float sinX = Math.Sin(x * .5f);
float cosY = Math.Cos(y * .5f);
float sinY = Math.Sin(y * .5f);
float cozZ = Math.Cos(z * .5f);
float sinZ = Math.Sin(z * .5f);
return new Quaternion(
x: sinX * cosY * cozZ - cosX * sinY * sinZ,
y: cosX * sinY * cozZ + sinX * cosY * sinZ,
z: cosX * cosY * sinZ - sinX * sinY * cozZ,
w: cosX * cosY * cozZ + sinX * sinY * sinZ
);
}
/// <summary>
/// Calculates the <see cref="System.Numerics.Matrix4x4"/> from given <see cref="Quaternion"/>.
/// </summary>
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>The rotation <see cref="System.Numerics.Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
{
float m00 = 1 - 2 * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
float m01 = 2 * (quaternion.X * quaternion.Y - quaternion.W * quaternion.Z);
float m02 = 2 * (quaternion.X * quaternion.Z + quaternion.W * quaternion.Y);
float m03 = 0;
float m10 = 2 * (quaternion.X * quaternion.Y + quaternion.W * quaternion.Z);
float m11 = 1 - 2 * (quaternion.X * quaternion.X + quaternion.Z * quaternion.Z);
float m12 = 2 * (quaternion.Y * quaternion.Z - quaternion.W * quaternion.X);
float m13 = 0;
float m20 = 2 * (quaternion.X * quaternion.Z - quaternion.W * quaternion.Y);
float m21 = 2 * (quaternion.Y * quaternion.Z + quaternion.W * quaternion.X);
float m22 = 1 - 2 * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
float m23 = 0;
float m30 = 0;
float m31 = 0;
float m32 = 0;
float m33 = 1;
return new(
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33
);
}
/// <summary>
/// Checks if two <see cref="Quaternion"/>s are approximately equal within a specified epsilon range.
/// </summary>
/// <param name="left">The first <see cref="Quaternion"/>.</param>
/// <param name="right">The second <see cref="Quaternion"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="Quaternion"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon)
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon);
/// <summary>
/// Converts the <see cref="Quaternion"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="Quaternion"/>.
/// </summary>
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
public override bool Equals(object? obj) => obj is Quaternion objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z) && W.Equals(objVec.W);
/// <summary>
/// Generates a hash code for the <see cref="Quaternion"/>.
/// </summary>
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
}
/// <summary>
/// Provides extension methods for <see cref="Quaternion"/> type.
/// </summary>
public static class QuaternionExtensions
{
/// <inheritdoc cref="Quaternion.Length(Quaternion)" />
public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion);
/// <inheritdoc cref="Quaternion.LengthSquared(Quaternion)" />
public static float LengthSquared(this Quaternion quaternion) => Quaternion.LengthSquared(quaternion);
/// <inheritdoc cref="Quaternion.Add(Quaternion, Quaternion)" />
public static Quaternion Add(this Quaternion left, Quaternion right) => Quaternion.Add(left, right);
/// <inheritdoc cref="Quaternion.Subtract(Quaternion, Quaternion)" />
public static Quaternion Subtract(this Quaternion left, Quaternion right) => Quaternion.Subtract(left, right);
/// <inheritdoc cref="Quaternion.Multiply(Quaternion, float)" />
public static Quaternion Multiply(this Quaternion quaternion, float value) => Quaternion.Multiply(quaternion, value);
/// <inheritdoc cref="Quaternion.Divide(Quaternion, float)" />
public static Quaternion Divide(this Quaternion quaternion, float value) => Quaternion.Divide(quaternion, value);
/// <inheritdoc cref="Quaternion.Normalize(Quaternion)" />
public static Quaternion Normalize(this Quaternion quaternion) => Quaternion.Normalize(quaternion);
/// <inheritdoc cref="Quaternion.Invert(Quaternion)" />
public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion);
/// <inheritdoc cref="Quaternion.Conjugate(Quaternion)" />
public static Quaternion Conjugate(this Quaternion quaternion) => Quaternion.Conjugate(quaternion);
/// <inheritdoc cref="Quaternion.RotateVector(Vector3D, Quaternion)" />
public static Vector3D RotateVector(this Vector3D vector, Quaternion quaternion) => Quaternion.RotateVector(vector, quaternion);
/// <inheritdoc cref="Quaternion.SLerp(Quaternion, Quaternion, float)" />
public static Quaternion SLerp(this Quaternion from, Quaternion to, float t) => Quaternion.SLerp(from, to, t);
/// <inheritdoc cref="Quaternion.Lerp(Quaternion, Quaternion, float)" />
public static Quaternion Lerp(this Quaternion from, Quaternion to, float t) => Quaternion.Lerp(from, to, t);
/// <inheritdoc cref="Quaternion.Dot(Quaternion, Quaternion)" />
public static float Dot(this Quaternion left, Quaternion right) => Quaternion.Dot(left, right);
/// <inheritdoc cref="Quaternion.ToRotationMatrix4x4(Quaternion, Quaternion)" />
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
/// <inheritdoc cref="Quaternion.FromAxisAngle(Vector3D, float)" />
public static Quaternion CreateRotation(this Vector3D axis, float angle) => Quaternion.FromAxisAngle(axis, angle);
/// <inheritdoc cref="Quaternion.ApproximatelyEquals(Quaternion, Quaternion, float)" />
public static bool ApproximatelyEquals(this Quaternion left, Quaternion right, float epsilon = float.Epsilon) => Quaternion.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,25 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a shape defined by a collection of vertices.
/// </summary>
/// <param name="vertices">The vertices of the shape.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Shape"/> struct with the specified vertices.
/// Initializes a new instance of the <see cref="Shape2D"/> struct with the specified vertices.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
{
public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
public static readonly Shape Box = CreateNgon(4, Vector2D.One);
public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
public static readonly Shape2D Triangle = CreateNgon(3, Vector2D.Up);
public static readonly Shape2D Box = CreateNgon(4, Vector2D.One);
public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up);
public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right);
private readonly List<Vector2D> _verticesList = vertices;
@ -40,14 +38,14 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// </summary>
/// <param name="shape">The shape to copy.</param>
/// <returns>A copy of the input shape.</returns>
public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
public static Shape2D CreateCopy(Shape2D shape) => new(new List<Vector2D>(shape.Vertices));
/// <summary>
/// Creates a regular polygon (ngon) with the specified number of vertices.
/// </summary>
/// <param name="vertexCount">The number of vertices in the polygon.</param>
/// <returns>A regular polygon with the specified number of vertices.</returns>
public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
public static Shape2D CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
/// <summary>
/// Creates a regular polygon (ngon) with the specified number of vertices and a rotation position.
@ -55,7 +53,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="vertexCount">The number of vertices in the polygon.</param>
/// <param name="positionToRotate">The position to use for rotation.</param>
/// <returns>A regular polygon with the specified number of vertices and rotation position.</returns>
public static Shape CreateNgon(int vertexCount, Vector2D positionToRotate)
public static Shape2D CreateNgon(int vertexCount, Vector2D positionToRotate)
{
if (vertexCount < 3)
throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2.");
@ -75,7 +73,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// </summary>
/// <param name="shape">The shape to enclose.</param>
/// <returns>The super triangle that encloses the given shape.</returns>
public static Triangle GetSuperTriangle(Shape shape)
public static Triangle GetSuperTriangle(Shape2D shape)
{
float minX = float.MaxValue, minY = float.MaxValue;
float maxX = float.MinValue, maxY = float.MinValue;
@ -106,7 +104,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// </summary>
/// <param name="shape">The shape to get lines from.</param>
/// <param name="lines">The list to populate with lines.</param>
public static void GetLines(Shape shape, IList<Line> lines)
public static void GetLines(Shape2D shape, IList<Line2D> lines)
{
lines.Clear();
for (int i = 0; i < shape.Vertices.Count - 1; i++)
@ -119,9 +117,9 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// </summary>
/// <param name="shape">The shape to get lines from.</param>
/// <returns>A list of lines that form the edges of the shape.</returns>
public static List<Line> GetLines(Shape shape)
public static List<Line2D> GetLines(Shape2D shape)
{
List<Line> lines = new(shape.Vertices.Count - 1);
List<Line2D> lines = new(shape.Vertices.Count - 1);
GetLines(shape, lines);
return lines;
}
@ -132,7 +130,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="shape">The shape to project.</param>
/// <param name="projectionVector">The vector to project onto.</param>
/// <param name="list">The list to populate with projected values.</param>
public static void Project(Shape shape, Vector2D projectionVector, IList<float> list)
public static void Project(Shape2D shape, Vector2D projectionVector, IList<float> list)
{
list.Clear();
@ -147,7 +145,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="shape">The shape to project.</param>
/// <param name="projectionVector">The vector to project onto.</param>
/// <returns>The projection of the shape onto the vector.</returns>
public static Projection Project(Shape shape, Vector2D projectionVector)
public static Projection1D Project(Shape2D shape, Vector2D projectionVector)
{
float min = float.MaxValue;
float max = float.MinValue;
@ -168,7 +166,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="shape">The shape to transform.</param>
/// <param name="transform">The transform to apply.</param>
/// <returns>The transformed shape.</returns>
public static Shape TransformShape(Shape shape, ITransform transform)
public static Shape2D TransformShape(Shape2D shape, ITransform2D transform)
{
List<Vector2D> vertices = new(shape.Vertices.Count);
@ -176,7 +174,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
for (int i = 0; i < count; i++)
vertices.Add(transform.TransformVector2D(shape[i]));
return new Shape(vertices);
return new Shape2D(vertices);
}
/// <summary>
@ -185,7 +183,7 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="from">The shape to transform.</param>
/// <param name="transform">The transform to apply.</param>
/// <param name="to">The transformed shape.</param>
public static void TransformShape(Shape from, ITransform transform, ref Shape to)
public static void TransformShape(Shape2D from, ITransform2D transform, ref Shape2D to)
{
to._verticesList.Clear();
@ -199,14 +197,15 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// </summary>
/// <param name="left">The first shape to compare.</param>
/// <param name="right">The second shape to compare.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
public static bool ApproximatelyEquals(Shape left, Shape right)
public static bool ApproximatelyEquals(Shape2D left, Shape2D right, float epsilon = float.Epsilon)
{
if (left.Vertices.Count != right.Vertices.Count)
return false;
for (int i = 0; i < left.Vertices.Count; i++)
if (!left.Vertices[i].ApproximatelyEquals(right.Vertices[i]))
if (!left.Vertices[i].ApproximatelyEquals(right.Vertices[i], epsilon))
return false;
return true;
@ -220,75 +219,34 @@ public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
}
/// <summary>
/// Provides extension methods for the <see cref="Shape"/> struct.
/// Provides extension methods for the <see cref="Shape2D"/> struct.
/// </summary>
public static class ShapeExtensions
public static class Shape2DExtensions
{
/// <summary>
/// Creates a copy of the shape.
/// </summary>
/// <param name="shape">The shape to copy.</param>
/// <returns>A copy of the input shape.</returns>
public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape);
/// <inheritdoc cref="Shape2D.CreateCopy(Shape2D)" />
public static Shape2D CreateCopy(this Shape2D shape) => Shape2D.CreateCopy(shape);
/// <summary>
/// Gets the super triangle that encloses the shape.
/// </summary>
/// <param name="shape">The shape to enclose.</param>
/// <returns>The super triangle that encloses the shape.</returns>
public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
/// <inheritdoc cref="Shape2D.GetSuperTriangle(Shape2D)" />
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
/// <summary>
/// Gets the lines that form the edges of the shape.
/// </summary>
/// <param name="shape">The shape to get lines from.</param>
/// <param name="lines">The list to populate with lines.</param>
public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
/// <inheritdoc cref="Shape2D.GetLines(Shape2D, IList{Line2D})" />
public static void ToLines(this Shape2D shape, IList<Line2D> lines) => Shape2D.GetLines(shape, lines);
/// <summary>
/// Gets a list of lines that form the edges of the shape.
/// </summary>
/// <param name="shape">The shape to get lines from.</param>
/// <returns>A list of lines that form the edges of the shape.</returns>
public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
/// <inheritdoc cref="Shape2D.GetLines(Shape2D)" />
public static List<Line2D> ToLines(this Shape2D shape) => Shape2D.GetLines(shape);
/// <summary>
/// Projects the shape onto a vector.
/// </summary>
/// <param name="shape">The shape to project.</param>
/// <param name="projectionVector">The vector to project onto.</param>
/// <param name="list">The list to populate with projected values.</param>
public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
/// <inheritdoc cref="Shape2D.Project(Shape2D, Vector2D, IList{float})" />
public static void ToProjection(this Shape2D shape, Vector2D projectionVector, IList<float> list) => Shape2D.Project(shape, projectionVector, list);
/// <summary>
/// Projects the shape onto a vector.
/// </summary>
/// <param name="shape">The shape to project.</param>
/// <param name="projectionVector">The vector to project onto.</param>
/// <returns>The projection of the shape onto the vector.</returns>
public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
/// <inheritdoc cref="Shape2D.Project(Shape2D, Vector2D)" />
public static Projection1D ToProjection(this Shape2D shape, Vector2D projectionVector) => Shape2D.Project(shape, projectionVector);
/// <summary>
/// Transforms the shape using the specified transform.
/// </summary>
/// <param name="transform">The transform to apply.</param>
/// <param name="shape">The shape to transform.</param>
/// <returns>The transformed shape.</returns>
public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
/// <inheritdoc cref="Shape2D.TransformShape(Shape2D, ITransform2D)" />
public static Shape2D TransformShape(this ITransform2D transform, Shape2D shape) => Shape2D.TransformShape(shape, transform);
/// <summary>
/// Transforms the shape using the specified transform.
/// </summary>
/// <param name="transform">The transform to apply.</param>
/// <param name="from">The shape to transform.</param>
/// <param name="to">The transformed shape.</param>
public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
/// <inheritdoc cref="Shape2D.TransformShape(Shape2D, ITransform2D, Shape2D)" />
public static void TransformShape(this ITransform2D transform, Shape2D from, ref Shape2D to) => Shape2D.TransformShape(from, transform, ref to);
/// <summary>
/// Determines whether two shapes are approximately equal.
/// </summary>
/// <param name="left">The first shape to compare.</param>
/// <param name="right">The second shape to compare.</param>
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right);
/// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" />
public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -1,8 +1,6 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
@ -39,11 +37,19 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
return new(center, Vector2D.Distance(center, triangle.A));
}
public static bool ApproximatelyEquals(Triangle left, Triangle right)
=> left.A.ApproximatelyEquals(right.A) && left.B.ApproximatelyEquals(right.B) && left.C.ApproximatelyEquals(right.C);
/// <summary>
/// Determines whether two <see cref="Triangle"/>s are approximately equal.
/// </summary>
/// <param name="left">The first <see cref="Triangle"/> to compare.</param>
/// <param name="right">The second <see cref="Triangle"/> to compare.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
}
public static class TriangleExtensions
{
public static bool ApproximatelyEquals(this Triangle left, Triangle right) => Triangle.ApproximatelyEquals(left, right);
/// <inheritdoc cref="Triangle.ApproximatelyEquals(Triangle, Triangle, float)" />
public static bool ApproximatelyEquals(this Triangle left, Triangle right, float epsilon = float.Epsilon) => Triangle.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -5,6 +5,11 @@ namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a two-dimensional vector.
/// </summary>
/// <param name="x">X position of the <see cref="Vector2D"/>.</param>
/// <param name="y">Y position of the <see cref="Vector2D"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Vector2D"/> struct with the specified positions.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
public readonly struct Vector2D(float x, float y)
{
@ -72,6 +77,8 @@ public readonly struct Vector2D(float x, float y)
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
@ -300,3 +307,78 @@ public readonly struct Vector2D(float x, float y)
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y);
}
/// <summary>
/// Provides extension methods for <see cref="Vector2D"/> type.
/// </summary>
public static class Vector2DExtensions
{
/// <inheritdoc cref="Vector2D.Length(Vector2D)" />
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
/// <inheritdoc cref="Vector2D.LengthSquared(this vector) => Vector2D/>
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
/// <inheritdoc cref="Vector2D.Distance(Vector2D, Vector2D)" />
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
/// <inheritdoc cref="Vector2D.Invert(this vector) => Vector2D/>
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
/// <inheritdoc cref="Vector2D.Add(Vector2D, Vector2D)" />
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
/// <inheritdoc cref="Vector2D.Subtract(Vector2D, Vector2D)" />
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
/// <inheritdoc cref="Vector2D.Multiply(Vector2D, float)" />
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
/// <inheritdoc cref="Vector2D.Divide(Vector2D, float)" />
public static Vector2D Divide(this Vector2D vector, float value) => Vector2D.Divide(vector, value);
/// <inheritdoc cref="Vector2D.Abs(Vector2D)" />
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
/// <inheritdoc cref="Vector2D.Reflect(Vector2D, Vector2D)" />
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
/// <inheritdoc cref="Vector2D.Normalize(Vector2D)" />
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
/// <inheritdoc cref="Vector2D.FromTo(Vector2D, Vector2D)" />
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
/// <inheritdoc cref="Vector2D.Scale(Vector2D, Vector2D)" />
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
/// <inheritdoc cref="Vector2D.Perpendicular(Vector2D)" />
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
/// <inheritdoc cref="Vector2D.Rotate(Vector2D, float)" />
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
/// <inheritdoc cref="Vector2D.Min(Vector2D, Vector2D)" />
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
/// <inheritdoc cref="Vector2D.Max(Vector2D, Vector2D)" />
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
/// <inheritdoc cref="Vector2D.Clamp(Vector2D, Vector2D,Vector2D)" />
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
/// <inheritdoc cref="Vector2D.Lerp(Vector2D, Vector2D," />
public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
/// <inheritdoc cref="Vector2D.Cross(Vector2D, Vector2D)" />
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
/// <inheritdoc cref="Vector2D.Angle(Vector2D, Vector2D)" />
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
/// <inheritdoc cref="Vector2D.Dot(Vector2D, Vector2D)" />
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
/// <inheritdoc cref="Vector2D.ApproximatelyEquals(Vector2D, Vector2D, float) " />
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -5,6 +5,12 @@ namespace Syntriax.Engine.Core;
/// <summary>
/// Represents a three-dimensional vector.
/// </summary>
/// <param name="x">X position of the <see cref="Vector3D"/>.</param>
/// <param name="y">Y position of the <see cref="Vector3D"/>.</param>
/// <param name="z">Z position of the <see cref="Vector3D"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="Vector3D"/> struct with the specified positions.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
public readonly struct Vector3D(float x, float y, float z)
{
@ -19,7 +25,7 @@ public readonly struct Vector3D(float x, float y, float z)
public readonly float Y = y;
/// <summary>
/// The Y coordinate of the <see cref="Vector3D"/>.
/// The Z coordinate of the <see cref="Vector3D"/>.
/// </summary>
public readonly float Z = z;
@ -86,6 +92,8 @@ public readonly struct Vector3D(float x, float y, float z)
public static bool operator ==(Vector3D left, Vector3D right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator !=(Vector3D left, Vector3D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z);
public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z);
public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f);
public static implicit operator Vector3D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f);
@ -284,3 +292,75 @@ public readonly struct Vector3D(float x, float y, float z)
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
}
/// <summary>
/// Provides extension methods for <see cref="Vector3D"/> type.
/// </summary>
public static class Vector3DExtensions
{
/// <inheritdoc cref="Vector3D.Length(Vector3D)" />
public static float Length(this Vector3D vector) => Vector3D.Length(vector);
/// <inheritdoc cref="Vector3D.LengthSquared(Vector3D)" />
public static float LengthSquared(this Vector3D vector) => Vector3D.LengthSquared(vector);
/// <inheritdoc cref="Vector3D.Distance(Vector3D, Vector3D)" />
public static float Distance(this Vector3D from, Vector3D to) => Vector3D.Distance(from, to);
/// <inheritdoc cref="Vector3D.Invert(Vector3D)" />
public static Vector3D Invert(this Vector3D vector) => Vector3D.Invert(vector);
/// <inheritdoc cref="Vector3D.Add(Vector3D, Vector3D)" />
public static Vector3D Add(this Vector3D vector, Vector3D vectorToAdd) => Vector3D.Add(vector, vectorToAdd);
/// <inheritdoc cref="Vector3D.Subtract(Vector3D, Vector3D)" />
public static Vector3D Subtract(this Vector3D vector, Vector3D vectorToSubtract) => Vector3D.Subtract(vector, vectorToSubtract);
/// <inheritdoc cref="Vector3D.Multiply(Vector3D, float)" />
public static Vector3D Multiply(this Vector3D vector, float value) => Vector3D.Multiply(vector, value);
/// <inheritdoc cref="Vector3D.Divide(Vector3D, float)" />
public static Vector3D Divide(this Vector3D vector, float value) => Vector3D.Divide(vector, value);
/// <inheritdoc cref="Vector3D.Abs(Vector3D)" />
public static Vector3D Abs(this Vector3D vector) => Vector3D.Abs(vector);
/// <inheritdoc cref="Vector3D.Reflect(Vector3D, Vector3D)" />
public static Vector3D Reflect(this Vector3D vector, Vector3D normal) => Vector3D.Reflect(vector, normal);
/// <inheritdoc cref="Vector3D.Normalize(Vector3D)" />
public static Vector3D Normalize(this Vector3D vector) => Vector3D.Normalize(vector);
/// <inheritdoc cref="Vector3D.FromTo(Vector3D, Vector3D)" />
public static Vector3D FromTo(this Vector3D from, Vector3D to) => Vector3D.FromTo(from, to);
/// <inheritdoc cref="Vector3D.Scale(Vector3D, Vector3D)" />
public static Vector3D Scale(this Vector3D vector, Vector3D scale) => Vector3D.Scale(vector, scale);
/// <inheritdoc cref="Vector3D.Rotate(Vector3D, Vector3D, float)" />
public static Vector3D Rotate(this Vector3D vector, Vector3D normal, float angleInRadian) => Vector3D.Rotate(vector, normal, angleInRadian);
/// <inheritdoc cref="Vector3D.Min(Vector3D, Vector3D)" />
public static Vector3D Min(this Vector3D left, Vector3D right) => Vector3D.Min(left, right);
/// <inheritdoc cref="Vector3D.Max(Vector3D, Vector3D)" />
public static Vector3D Max(this Vector3D left, Vector3D right) => Vector3D.Max(left, right);
/// <inheritdoc cref="Vector3D.Clamp(Vector3D, Vector3D, Vector3D)" />
public static Vector3D Clamp(this Vector3D vector, Vector3D min, Vector3D max) => Vector3D.Clamp(vector, min, max);
/// <inheritdoc cref="Vector3D.Lerp(Vector3D, Vector3D, float)" />
public static Vector3D Lerp(this Vector3D from, Vector3D to, float t) => Vector3D.Lerp(from, to, t);
/// <inheritdoc cref="Vector3D.Cross(Vector3D, Vector3D)" />
public static Vector3D Cross(this Vector3D left, Vector3D right) => Vector3D.Cross(left, right);
/// <inheritdoc cref="Vector3D.Angle(Vector3D, Vector3D)" />
public static float AngleBetween(this Vector3D left, Vector3D right) => Vector3D.Angle(left, right);
/// <inheritdoc cref="Vector3D.Dot(Vector3D, Vector3D)" />
public static float Dot(this Vector3D left, Vector3D right) => Vector3D.Dot(left, right);
/// <inheritdoc cref="Vector3D.ApproximatelyEquals(Vector3D, Vector3D, float)" />
public static bool ApproximatelyEquals(this Vector3D left, Vector3D right, float epsilon = float.Epsilon) => Vector3D.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -4,9 +4,9 @@ namespace Syntriax.Engine.Core;
public class StateEnable : IStateEnable
{
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IAssignableEntity.OnEntityAssignedEventHandler? OnEntityAssigned = null;
public event IStateEnable.OnNameChangedEventHandler? OnEnabledChanged = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasEntity.EntityAssignedEventHandler? OnEntityAssigned = null;
public event IStateEnable.EnabledChangedEventHandler? OnEnabledChanged = null;
private bool _enabled = true;
private IEntity _entity = null!;
@ -27,12 +27,14 @@ public class StateEnable : IStateEnable
}
}
protected virtual void OnAssign(IEntity entity) { }
public bool Assign(IEntity entity)
{
if (_entity is not null && _entity.IsInitialized)
return false;
_entity = entity;
OnAssign(entity);
OnEntityAssigned?.Invoke(this);
return true;
}

View File

@ -1,15 +0,0 @@
using System;
namespace Syntriax.Engine.Core;
public static class Time
{
private static EngineTime _engineTime = new(TimeSpan.Zero, TimeSpan.Zero);
public static TimeSpan Total => _engineTime.Total;
public static TimeSpan Elapsed => _engineTime.Elapsed;
public static float DeltaTimeFrame => _engineTime.DeltaTimeFrame;
public static void SetTime(EngineTime engineTime) => _engineTime = engineTime;
}

View File

@ -1,282 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {GameObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
public class Transform : ITransform
{
public event IAssignableGameObject.OnGameObjectAssignedEventHandler? OnGameObjectAssigned = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event ITransform.OnPositionChangedEventHandler? OnPositionChanged = null;
public event ITransform.OnScaleChangedEventHandler? OnScaleChanged = null;
public event ITransform.OnRotationChangedEventHandler? OnRotationChanged = null;
public event ITransform.OnParentChangedEventHandler? OnParentChanged = null;
public event ITransform.OnChildrenAddedEventHandler? OnChildrenAdded = null;
public event ITransform.OnChildrenRemovedEventHandler? OnChildrenRemoved = null;
private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One;
private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f;
private readonly List<ITransform> _children = [];
public IGameObject GameObject { get; private set; } = null!;
public ITransform? Parent { get; private set; } = null;
public IReadOnlyList<ITransform> Children => _children;
public Vector2D Position
{
get => _position;
set
{
if (value == _position)
return;
_position = value;
UpdateLocalPosition();
OnPositionChanged?.Invoke(this);
}
}
public Vector2D Scale
{
get => _localScale.Scale(Parent?.Scale ?? Vector2D.One);
set
{
if (value == _scale)
return;
_scale = value;
UpdateLocalScale();
OnScaleChanged?.Invoke(this);
}
}
public float Rotation
{
get => _localRotation + (Parent?.Rotation ?? 0f);
set
{
if (value == _rotation)
return;
_rotation = value;
UpdateLocalPosition();
OnRotationChanged?.Invoke(this);
}
}
public Vector2D LocalPosition
{
get => _localPosition;
set
{
if (value == _localPosition)
return;
_localPosition = value;
UpdatePosition();
OnPositionChanged?.Invoke(this);
}
}
public Vector2D LocalScale
{
get => _localScale;
set
{
if (value == _localScale)
return;
_localScale = value;
UpdateScale();
OnScaleChanged?.Invoke(this);
}
}
public float LocalRotation
{
get => _localRotation;
set
{
if (value == _localRotation)
return;
_localRotation = value;
UpdateRotation();
OnRotationChanged?.Invoke(this);
}
}
public void SetParent(ITransform? transform)
{
if (transform == this || Parent == transform)
return;
ITransform? previousParent = Parent;
if (previousParent is not null)
{
previousParent.RemoveChild(this);
previousParent.OnPositionChanged -= RecalculatePosition;
previousParent.OnScaleChanged -= RecalculateScale;
previousParent.OnRotationChanged -= RecalculateRotation;
previousParent.OnParentChanged -= NotifyChildrenOnParentChange;
}
Parent = transform;
if (transform is not null)
{
transform.AddChild(this);
transform.OnPositionChanged += RecalculatePosition;
transform.OnScaleChanged += RecalculateScale;
transform.OnRotationChanged += RecalculateRotation;
transform.OnParentChanged += NotifyChildrenOnParentChange;
}
UpdateLocalPosition();
UpdateLocalScale();
UpdateLocalRotation();
OnParentChanged?.Invoke(this, previousParent, transform);
}
public void AddChild(ITransform transform)
{
if (_children.Contains(transform))
return;
_children.Add(transform);
transform.SetParent(this);
OnChildrenAdded?.Invoke(this, transform);
}
public void RemoveChild(ITransform transform)
{
if (!_children.Remove(transform))
return;
transform.SetParent(null);
OnChildrenRemoved?.Invoke(this, transform);
}
public IEnumerator<ITransform> GetEnumerator() => _children.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
private void NotifyChildrenOnParentChange(ITransform transform, ITransform? previousParent, ITransform? newParent)
{
// TODO No idea how logical this is to propagate this to the children the way I'm doing right now.
// I was originally gonna just call `child.OnParentChanged?.Invoke(child, child.Parent);` but seems an unnecessary call too?
foreach (var child in Children) // TODO CHECK ERRORS
child.SetParent(this);
}
private void RecalculatePosition(ITransform _)
{
if (Parent is null)
return;
UpdatePosition();
OnPositionChanged?.Invoke(this);
}
private void RecalculateScale(ITransform _)
{
if (Parent is null)
return;
UpdateScale();
OnScaleChanged?.Invoke(this);
}
private void RecalculateRotation(ITransform _)
{
if (Parent is null)
return;
UpdatePosition();
UpdateRotation();
OnPositionChanged?.Invoke(this);
OnRotationChanged?.Invoke(this);
}
private void UpdateLocalPosition()
{
if (Parent is null)
_localPosition = Position;
else
_localPosition = Parent.Position.FromTo(Position).Scale(Parent.Scale);
}
private void UpdateLocalScale()
{
if (Parent is null)
_localScale = Scale;
else
_localScale = Scale.Scale(new(1f / Parent.Scale.X, 1f / Parent.Scale.Y));
}
private void UpdateLocalRotation()
{
if (Parent is null)
_localRotation = Rotation;
else
_localRotation = Rotation - Parent.Rotation;
}
private void UpdatePosition()
{
if (Parent is null)
_position = LocalPosition.Rotate(0f * Math.DegreeToRadian);
else
_position = Parent.Position + LocalPosition.Scale(new(Parent.Scale.X, Parent.Scale.Y)).Rotate(Parent.Rotation * Math.DegreeToRadian);
}
private void UpdateScale()
{
if (Parent is null)
_scale = LocalScale;
else
_scale = Vector2D.Scale(Parent.Scale, LocalScale);
}
private void UpdateRotation()
{
if (Parent is null)
_rotation = LocalRotation;
else
_rotation = Parent.Rotation + LocalRotation;
}
public bool Assign(IGameObject gameObject)
{
if (GameObject is not null && GameObject.IsInitialized)
return false;
GameObject = gameObject;
OnGameObjectAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameObject is not null && GameObject.IsInitialized)
return false;
GameObject = null!;
OnUnassigned?.Invoke(this);
return true;
}
}

255
Engine.Core/Transform2D.cs Normal file
View File

@ -0,0 +1,255 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {HierarchyObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
public class Transform2D : Behaviour, ITransform2D
{
public event ITransform2D.PositionChangedEventHandler? OnPositionChanged = null;
public event ITransform2D.ScaleChangedEventHandler? OnScaleChanged = null;
public event ITransform2D.RotationChangedEventHandler? OnRotationChanged = null;
private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One;
private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f;
private ITransform2D? parentTransform = null;
public Vector2D Position
{
get => _position;
set
{
if (value == _position)
return;
Vector2D previousPosition = _position;
_position = value;
UpdateLocalPosition();
OnPositionChanged?.Invoke(this, _position);
}
}
public Vector2D Scale
{
get => _scale;
set
{
if (value == _scale)
return;
Vector2D previousScale = _scale;
_scale = value;
UpdateLocalScale();
OnScaleChanged?.Invoke(this, previousScale);
}
}
public float Rotation
{
get => _rotation;
set
{
if (value == _rotation)
return;
float previousRotation = _rotation;
_rotation = value;
UpdateLocalRotation();
OnRotationChanged?.Invoke(this, previousRotation);
}
}
public Vector2D LocalPosition
{
get => _localPosition;
set
{
if (value == _localPosition)
return;
Vector2D previousPosition = _position;
_localPosition = value;
UpdatePosition();
OnPositionChanged?.Invoke(this, previousPosition);
}
}
public Vector2D LocalScale
{
get => _localScale;
set
{
if (value == _localScale)
return;
Vector2D previousScale = _scale;
_localScale = value;
UpdateScale();
OnScaleChanged?.Invoke(this, previousScale);
}
}
public float LocalRotation
{
get => _localRotation;
set
{
if (value == _localRotation)
return;
float previousRotation = _rotation;
_localRotation = value;
UpdateRotation();
OnRotationChanged?.Invoke(this, previousRotation);
}
}
private void RecalculatePosition(ITransform2D _, Vector2D previousPosition)
{
if (parentTransform is null)
return;
float previousRotation = Rotation;
UpdatePosition();
UpdateRotation();
OnPositionChanged?.Invoke(this, previousPosition);
OnRotationChanged?.Invoke(this, previousRotation);
}
private void RecalculateScale(ITransform2D _, Vector2D previousScale)
{
if (parentTransform is null)
return;
UpdateScale();
OnScaleChanged?.Invoke(this, previousScale);
}
private void RecalculateRotation(ITransform2D _, float previousRotation)
{
if (parentTransform is null)
return;
Vector2D previousPosition = Position;
UpdatePosition();
UpdateRotation();
OnPositionChanged?.Invoke(this, previousPosition);
OnRotationChanged?.Invoke(this, previousRotation);
}
private void UpdateLocalPosition()
{
if (parentTransform is null)
_localPosition = Position;
else
_localPosition = parentTransform.Position.FromTo(Position).Scale(parentTransform.Scale);
}
private void UpdateLocalScale()
{
if (parentTransform is null)
_localScale = Scale;
else
_localScale = Scale.Scale(new(1f / parentTransform.Scale.X, 1f / parentTransform.Scale.Y));
}
private void UpdateLocalRotation()
{
if (parentTransform is null)
_localRotation = Rotation;
else
_localRotation = Rotation - parentTransform.Rotation;
}
private void UpdatePosition()
{
if (parentTransform is null)
_position = LocalPosition.Rotate(0f * Math.DegreeToRadian);
else
_position = parentTransform.Position + LocalPosition.Scale(new(parentTransform.Scale.X, parentTransform.Scale.Y)).Rotate(parentTransform.Rotation * Math.DegreeToRadian);
}
private void UpdateScale()
{
if (parentTransform is null)
_scale = LocalScale;
else
_scale = (parentTransform?.Scale ?? Vector2D.One).Scale(_localScale);
}
private void UpdateRotation()
{
if (parentTransform is null)
_rotation = LocalRotation;
else
_rotation = parentTransform.Rotation + LocalRotation;
}
protected override void InitializeInternal()
{
UpdateParent(BehaviourController.HierarchyObject.Parent);
BehaviourController.HierarchyObject.OnParentChanged += OnParentChanged;
}
protected override void FinalizeInternal()
{
BehaviourController.HierarchyObject.OnParentChanged -= OnParentChanged;
}
private void UpdateParent(IHierarchyObject? parent)
{
ITransform2D? previousParent = parentTransform;
if (previousParent is not null)
{
previousParent.BehaviourController.HierarchyObject.RemoveChild(HierarchyObject);
previousParent.OnPositionChanged -= RecalculatePosition;
previousParent.OnScaleChanged -= RecalculateScale;
previousParent.OnRotationChanged -= RecalculateRotation;
previousParent.BehaviourController.HierarchyObject.OnParentChanged -= OnParentChanged;
}
parentTransform = parent?.BehaviourController.GetBehaviour<ITransform2D>();
if (parentTransform is not null)
{
parentTransform.BehaviourController.HierarchyObject.AddChild(HierarchyObject);
parentTransform.OnPositionChanged += RecalculatePosition;
parentTransform.OnScaleChanged += RecalculateScale;
parentTransform.OnRotationChanged += RecalculateRotation;
parentTransform.BehaviourController.HierarchyObject.OnParentChanged += OnParentChanged;
UpdatePosition();
UpdateScale();
UpdateRotation();
}
UpdateLocalPosition();
UpdateLocalScale();
UpdateLocalRotation();
OnPositionChanged?.Invoke(this, Position);
OnScaleChanged?.Invoke(this, Scale);
OnRotationChanged?.Invoke(this, Rotation);
}
private void OnParentChanged(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent)
{
UpdateParent(newParent);
}
}

View File

@ -1,484 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
# dotenv files
.env
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
.idea
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp

View File

@ -1,4 +1,4 @@
using Syntriax.Engine.Physics2D.Primitives;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Abstract;

View File

@ -5,22 +5,25 @@ namespace Syntriax.Engine.Physics2D.Abstract;
/// <summary>
/// Represents a 2D collider.
/// </summary>
public interface ICollider2D : IBehaviour, IAssignableTransform
public interface ICollider2D : IBehaviour
{
/// <summary>
/// Event triggered when a collision is detected.
/// </summary>
event OnCollisionDetectedEventHandler? OnCollisionDetected;
event CollisionDetectedEventHandler? OnCollisionDetected;
/// <summary>
/// Event triggered when a collision is resolved.
/// </summary>
event OnCollisionResolvedEventHandler? OnCollisionResolved;
event CollisionResolvedEventHandler? OnCollisionResolved;
/// <summary>
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
/// </summary>
event OnTriggeredEventHandler? OnTriggered;
event TriggeredEventHandler? OnTriggered;
/// <inheritdoc cref="ITransform2D" />
ITransform2D Transform { get; }
/// <summary>
/// The <see cref="IRigidBody2D"/> associated with the <see cref="ICollider2D"/>.
@ -41,7 +44,7 @@ public interface ICollider2D : IBehaviour, IAssignableTransform
void Resolve(CollisionDetectionInformation collisionDetectionInformation);
void Trigger(ICollider2D initiator);
delegate void OnCollisionDetectedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnCollisionResolvedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnTriggeredEventHandler(ICollider2D sender, ICollider2D initiatorCollider);
delegate void CollisionDetectedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void CollisionResolvedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void TriggeredEventHandler(ICollider2D sender, ICollider2D initiatorCollider);
}

View File

@ -8,12 +8,12 @@ public interface IPhysicsEngine2D
/// <summary>
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a single physics iteration.
/// </summary>
event OnPhysicsIterationEventHandler? OnPhysicsIteration;
event PhysicsIterationEventHandler? OnPhysicsIteration;
/// <summary>
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a full physics step/>.
/// </summary>
event OnPhysicsStepEventHandler? OnPhysicsStep;
event PhysicsStepEventHandler? OnPhysicsStep;
/// <summary>
/// The number of iterations the <see cref="IPhysicsEngine2D"/> performs per step.
@ -26,6 +26,6 @@ public interface IPhysicsEngine2D
/// <param name="deltaTime">The time step.</param>
void Step(float deltaTime);
delegate void OnPhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
delegate void OnPhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
delegate void PhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
delegate void PhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
}

View File

@ -6,7 +6,7 @@ namespace Syntriax.Engine.Physics2D.Abstract;
/// <summary>
/// Represents a 2D rigid body in the engine.
/// </summary>
public interface IRigidBody2D : IBehaviour, IAssignableTransform
public interface IRigidBody2D : IBehaviour2D
{
/// <summary>
/// The physics material of the <see cref="IRigidBody2D"/>.

View File

@ -1,20 +1,19 @@
using Syntriax.Engine.Physics2D.Primitives;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Abstract;
/// <summary>
/// Represents a <see cref="ICollider2D"/> with a custom <see cref="Shape"/>.
/// Represents a <see cref="ICollider2D"/> with a custom <see cref="Shape2D"/>.
/// </summary>
public interface IShapeCollider2D : ICollider2D
{
/// <summary>
/// Gets or sets the local <see cref="Shape"/> of the <see cref="ICollider2D"/>.
/// Gets or sets the local <see cref="Shape2D"/> of the <see cref="ICollider2D"/>.
/// </summary>
Shape ShapeLocal { get; set; }
Shape2D ShapeLocal { get; set; }
/// <summary>
/// Gets the world space representation of the <see cref="Shape"/>.
/// Gets the world space representation of the <see cref="Shape2D"/>.
/// </summary>
Shape ShapeWorld { get; }
Shape2D ShapeWorld { get; }
}

View File

@ -4,12 +4,11 @@ using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
public abstract class Collider2DBehaviourBase : Behaviour, ICollider2D
public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
{
public event ICollider2D.OnCollisionDetectedEventHandler? OnCollisionDetected = null;
public event ICollider2D.OnCollisionResolvedEventHandler? OnCollisionResolved = null;
public event ICollider2D.OnTriggeredEventHandler? OnTriggered = null;
public event ICollider2D.CollisionDetectedEventHandler? OnCollisionDetected = null;
public event ICollider2D.CollisionResolvedEventHandler? OnCollisionResolved = null;
public event ICollider2D.TriggeredEventHandler? OnTriggered = null;
protected bool NeedsRecalculation { get; private set; } = true;
protected IRigidBody2D? _rigidBody2D = null;
@ -17,11 +16,6 @@ public abstract class Collider2DBehaviourBase : Behaviour, ICollider2D
public IRigidBody2D? RigidBody2D => _rigidBody2D;
public bool IsTrigger { get; set; } = false;
ITransform IAssignableTransform.Transform => Transform;
public event IAssignableTransform.OnTransformAssignedEventHandler? OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; }
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
public void Recalculate()
{
if (!NeedsRecalculation)
@ -40,13 +34,13 @@ public abstract class Collider2DBehaviourBase : Behaviour, ICollider2D
BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
Transform.OnPositionChanged += SetNeedsRecalculation;
Transform.OnRotationChanged += SetNeedsRecalculation;
Transform.OnScaleChanged += SetNeedsRecalculation;
Transform.OnParentChanged += UpdateRigidBody2D;
Transform.OnPositionChanged += SetNeedsRecalculationFromPosition;
Transform.OnRotationChanged += SetNeedsRecalculationFromRotation;
Transform.OnScaleChanged += SetNeedsRecalculationFromScale;
HierarchyObject.OnParentChanged += UpdateRigidBody2D;
}
private void UpdateRigidBody2D(ITransform sender, ITransform? previousParent, ITransform? newParent)
private void UpdateRigidBody2D(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent)
{
BehaviourController.TryGetBehaviourInParent(out _rigidBody2D);
}
@ -63,16 +57,18 @@ public abstract class Collider2DBehaviourBase : Behaviour, ICollider2D
_rigidBody2D = null;
}
private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true;
private void SetNeedsRecalculationFromScale(ITransform2D sender, Vector2D previousScale) => NeedsRecalculation = true;
private void SetNeedsRecalculationFromPosition(ITransform2D sender, Vector2D previousPosition) => NeedsRecalculation = true;
private void SetNeedsRecalculationFromRotation(ITransform2D sender, float previousRotation) => NeedsRecalculation = true;
protected override void OnFinalize()
{
BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController;
BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController;
Transform.OnScaleChanged -= SetNeedsRecalculation;
Transform.OnScaleChanged -= SetNeedsRecalculationFromScale;
Transform.OnPositionChanged -= SetNeedsRecalculation;
Transform.OnRotationChanged -= SetNeedsRecalculation;
Transform.OnPositionChanged -= SetNeedsRecalculationFromPosition;
Transform.OnRotationChanged -= SetNeedsRecalculationFromRotation;
}
public void Detect(CollisionDetectionInformation collisionDetectionInformation) => OnCollisionDetected?.Invoke(this, collisionDetectionInformation);

View File

@ -1,5 +1,5 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D.Abstract;
using Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Physics2D;
@ -8,10 +8,8 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide
public Circle CircleWorld { get; protected set; } = Circle.UnitCircle;
public Circle CircleLocal { get; set; } = Circle.UnitCircle;
public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal);
public Collider2DCircleBehaviour() { }
public Collider2DCircleBehaviour(Circle circle) => CircleLocal = circle;
}

View File

@ -1,18 +1,17 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D.Abstract;
using Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Physics2D;
public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D
{
public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
public Shape ShapeLocal { get; set; } = Shape.Box;
public Shape2D ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
public Shape2D ShapeLocal { get; set; } = Shape2D.Box;
private Shape _shapeWorld = Shape.Box.CreateCopy();
private Shape2D _shapeWorld = Shape2D.Box.CreateCopy();
public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld);
public Collider2DShapeBehaviour() { }
public Collider2DShapeBehaviour(Shape shape) => ShapeLocal = shape;
public Collider2DShapeBehaviour(Shape2D shape) => ShapeLocal = shape;
}

View File

@ -1,6 +1,5 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D.Abstract;
using Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Physics2D;
@ -42,15 +41,15 @@ public class CollisionDetector2D : ICollisionDetector2D
private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation collisionInformation)
{
var vertices = left.ShapeWorld.Vertices;
System.Collections.Generic.IReadOnlyList<Vector2D> vertices = left.ShapeWorld.Vertices;
int count = vertices.Count;
for (int indexProjection = 0; indexProjection < count; indexProjection++)
{
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector);
Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector);
Projection1D leftProjection = left.ShapeWorld.ToProjection(projectionVector);
Projection1D rightProjection = right.ShapeWorld.ToProjection(projectionVector);
if (!leftProjection.Overlaps(rightProjection, out float depth))
return false;
@ -66,15 +65,15 @@ public class CollisionDetector2D : ICollisionDetector2D
{
collisionInformation = default;
var vertices = shapeCollider.ShapeWorld.Vertices;
System.Collections.Generic.IReadOnlyList<Vector2D> vertices = shapeCollider.ShapeWorld.Vertices;
int count = vertices.Count;
for (int indexProjection = 0; indexProjection < count; indexProjection++)
{
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
Projection1D shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
Projection1D circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
if (!shapeProjection.Overlaps(circleProjection, out float depth))
return false;
@ -86,8 +85,8 @@ public class CollisionDetector2D : ICollisionDetector2D
{
Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized;
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
Projection1D shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
Projection1D circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
if (!shapeProjection.Overlaps(circleProjection, out float depth))
return false;
@ -105,8 +104,8 @@ public class CollisionDetector2D : ICollisionDetector2D
Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
Projection1D leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
Projection1D rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
bool collision = leftProjection.Overlaps(rightProjection, out float depth);

View File

@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Physics2D</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,22 +1,21 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D.Primitives;
namespace Syntriax.Engine.Physics2D;
public static partial class Physics2D
{
public static bool Overlaps(this Shape shape, Vector2D point) => Overlaps(shape, point, out var _);
public static bool Overlaps(this Shape shape, Vector2D point, out float depth)
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)
{
depth = float.MaxValue;
var vertices = shape.Vertices;
System.Collections.Generic.IReadOnlyList<Vector2D> vertices = shape.Vertices;
int count = vertices.Count;
for (int indexProjection = 0; indexProjection < count; indexProjection++)
{
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
Projection shapeProjection = shape.ToProjection(projectionVector);
Projection1D shapeProjection = shape.ToProjection(projectionVector);
float projectedPoint = point.Dot(projectionVector);
if (shapeProjection.Max < projectedPoint || shapeProjection.Min > projectedPoint)
@ -90,5 +89,5 @@ public static partial class Physics2D
return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f);
}
public static bool LaysOn(this Vector2D point, Line line) => Line.Intersects(line, point);
public static bool LaysOn(this Vector2D point, Line2D line) => Line2D.Intersects(line, point);
}

View File

@ -7,7 +7,7 @@ using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
public class PhysicsCoroutineManager : HierarchyObjectBase
public class PhysicsCoroutineManager : HierarchyObject
{
private readonly List<IEnumerator> enumerators = [];
private IPhysicsEngine2D? physicsEngine = null;
@ -25,7 +25,7 @@ public class PhysicsCoroutineManager : HierarchyObjectBase
protected override void OnEnteringHierarchy(IGameManager gameManager)
{
physicsEngine = gameManager.HierarchyObjects.FindObject<IPhysicsEngine2D>();
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;
else
@ -51,12 +51,12 @@ public class PhysicsCoroutineManager : HierarchyObjectBase
gameManager.OnUpdate -= OnUpdate;
}
private void OnUpdate(IGameManager sender, EngineTime time)
private void OnUpdate(IGameManager sender, EngineTime engineTime)
{
if (GameManager is not IGameManager gameManager)
return;
physicsEngine = gameManager.HierarchyObjects.FindObject<IPhysicsEngine2D>();
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
{
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;

View File

@ -7,8 +7,8 @@ namespace Syntriax.Engine.Physics2D;
public class PhysicsEngine2D : IPhysicsEngine2D
{
public event IPhysicsEngine2D.OnPhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.OnPhysicsStepEventHandler? OnPhysicsStep = null;
public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null;
private readonly List<IRigidBody2D> rigidBodies = new(32);
private readonly List<ICollider2D> colliders = new(64);
@ -27,7 +27,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
rigidBodies.Add(rigidBody);
foreach (var collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
foreach (ICollider2D collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
colliders.Add(collider2D);
rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
@ -50,7 +50,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
StepRigidBody(rigidBodies[i], intervalDeltaTime);
// Can Parallel
foreach (var collider in colliders)
foreach (ICollider2D collider in colliders)
collider.Recalculate();
// Can Parallel

View File

@ -4,10 +4,10 @@ using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
public class PhysicsEngine2DCollector : HierarchyObjectBase, IPhysicsEngine2D
public class PhysicsEngine2DCollector : HierarchyObject, IPhysicsEngine2D
{
public event IPhysicsEngine2D.OnPhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.OnPhysicsStepEventHandler? OnPhysicsStep = null;
public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null;
private int _iterationPerStep = 1;
@ -18,7 +18,6 @@ public class PhysicsEngine2DCollector : HierarchyObjectBase, IPhysicsEngine2D
protected BehaviourCollector<ICollider2D> colliderCollector = new();
protected BehaviourCollector<IPhysicsUpdate> physicsUpdateCollector = new();
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
public void Step(float deltaTime)
@ -28,11 +27,11 @@ public class PhysicsEngine2DCollector : HierarchyObjectBase, IPhysicsEngine2D
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
{
// Can Parallel
foreach (var rigidBody in rigidBodyCollector)
foreach (IRigidBody2D rigidBody in rigidBodyCollector)
StepRigidBody(rigidBody, intervalDeltaTime);
// Can Parallel
foreach (var collider in colliderCollector)
foreach (ICollider2D collider in colliderCollector)
collider.Recalculate();
// Can Parallel

View File

@ -1,64 +0,0 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Physics2D.Primitives;
/// <summary>
/// Represents a line equation in the form y = mx + b.
/// </summary>
/// <param name="slope">The slope of the line.</param>
/// <param name="offsetY">The y-intercept of the line.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="LineEquation"/> struct with the specified slope and y-intercept.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
public readonly struct LineEquation(float slope, float offsetY)
{
/// <summary>
/// The slope of the line equation.
/// </summary>
public readonly float Slope = slope;
/// <summary>
/// The y-intercept of the line equation.
/// </summary>
public readonly float OffsetY = offsetY;
/// <summary>
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
/// </summary>
/// <param name="lineEquation">The line equation to resolve.</param>
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
/// <returns>The y-coordinate resolved using the line equation.</returns>
public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
/// <summary>
/// Checks if two line equations are approximately equal.
/// </summary>
/// <param name="left">The first line equation to compare.</param>
/// <param name="right">The second line equation to compare.</param>
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
public static bool ApproximatelyEquals(LineEquation left, LineEquation right)
=> left.Slope.ApproximatelyEquals(right.Slope) && left.OffsetY.ApproximatelyEquals(right.OffsetY);
}
/// <summary>
/// Provides extension methods for the LineEquation struct.
/// </summary>
public static class LineEquationExtensions
{
/// <summary>
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
/// </summary>
/// <param name="lineEquation">The line equation to resolve.</param>
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
/// <returns>The y-coordinate resolved using the line equation.</returns>
public static float Resolve(this LineEquation lineEquation, float x) => LineEquation.Resolve(lineEquation, x);
/// <summary>
/// Checks if two line equations are approximately equal.
/// </summary>
/// <param name="left">The first line equation to compare.</param>
/// <param name="right">The second line equation to compare.</param>
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
public static bool ApproximatelyEquals(this LineEquation left, LineEquation right) => LineEquation.ApproximatelyEquals(left, right);
}

View File

@ -1,17 +1,13 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
public class RigidBody2D : Behaviour, IRigidBody2D
public class RigidBody2D : Behaviour2D, IRigidBody2D
{
event IAssignableTransform.OnTransformAssignedEventHandler? IAssignableTransform.OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; }
private const float LOWEST_ALLOWED_MASS = 0.00001f;
private float _mass = 1f;
public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault();
public Vector2D Velocity { get; set; } = Vector2D.Zero;
@ -19,9 +15,4 @@ public class RigidBody2D : Behaviour, IRigidBody2D
public bool IsStatic { get; set; } = false;
public float Mass { get => _mass; set => _mass = Core.Math.Max(value, LOWEST_ALLOWED_MASS); }
ITransform IAssignableTransform.Transform => Transform;
public bool Assign(ITransform transform) => GameObject.Assign(transform);
}

View File

@ -4,9 +4,11 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Systems</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,8 +1,8 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Input;
namespace Syntriax.Engine.Systems.Input;
public interface IButtonInputs<T> : IAssignableStateEnable
public interface IButtonInputs<T> : IHasStateEnable
{
event ButtonCallbackEventHandler? OnAnyButtonPressed;
event ButtonCallbackEventHandler? OnAnyButtonReleased;

View File

@ -0,0 +1,22 @@
namespace Syntriax.Engine.StateMachine;
public interface IState
{
event StateUpdateEventHandler? OnStateUpdate;
event StateTransitionedFromEventHandler? OnStateTransitionedFrom;
event StateTransitionedToEventHandler? OnStateTransitionedTo;
event StateTransitionReadyEventHandler? OnStateTransitionReady;
string Name { get; }
IState? GetNextState();
void Update();
void TransitionTo(IState from);
void TransitionFrom(IState to);
delegate void StateUpdateEventHandler(IState sender);
delegate void StateTransitionedFromEventHandler(IState sender, IState toState);
delegate void StateTransitionedToEventHandler(IState sender, IState fromState);
delegate void StateTransitionReadyEventHandler(IState sender, IState toState);
}

View File

@ -0,0 +1,52 @@
namespace Syntriax.Engine.StateMachine;
public class State : IState
{
public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public event IState.StateTransitionReadyEventHandler? OnStateTransitionReady = null;
private readonly List<StateTransition> transitions = [];
private readonly Dictionary<string, StateTransition> possibleTransitions = [];
public string Name { get; set; } = "Default State Name";
public IReadOnlyList<StateTransition> Transitions => transitions;
public IReadOnlyDictionary<string, StateTransition> PossibleTransitions => possibleTransitions;
public void RemoveTransition(string name)
{
if (!possibleTransitions.TryGetValue(name, out StateTransition stateTransition))
return;
transitions.Remove(stateTransition);
possibleTransitions.Remove(name);
}
public void AddTransition(string name, StateTransition stateTransition)
{
if (transitions.Contains(stateTransition))
return;
transitions.Add(stateTransition);
possibleTransitions.Add(name, stateTransition);
}
public void Update()
{
if (GetNextState() is IState transitionState)
OnStateTransitionReady?.Invoke(this, transitionState);
OnStateUpdate?.Invoke(this);
}
public void TransitionTo(IState from) => OnStateTransitionedTo?.Invoke(this, from);
public void TransitionFrom(IState to) => OnStateTransitionedFrom?.Invoke(this, to);
public IState? GetNextState()
{
foreach (StateTransition stateTransition in transitions)
if (stateTransition.CanTransition)
return stateTransition.State;
return null;
}
}

View File

@ -0,0 +1,37 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.StateMachine;
public abstract class StateBehaviourBase : Behaviour, IState
{
public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public abstract event IState.StateTransitionReadyEventHandler? OnStateTransitionReady;
public abstract string Name { get; }
protected virtual void OnUpdateState() { }
public void Update()
{
OnUpdateState();
OnStateUpdate?.Invoke(this);
}
protected virtual void OnTransitionedToState(IState from) { }
public void TransitionTo(IState from)
{
OnTransitionedToState(from);
OnStateTransitionedTo?.Invoke(this, from);
}
protected virtual void OnTransitionedFromState(IState to) { }
public void TransitionFrom(IState to)
{
OnTransitionedFromState(to);
OnStateTransitionedFrom?.Invoke(this, to);
}
public abstract IState? GetNextState();
}

View File

@ -0,0 +1,49 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.StateMachine;
public class StateMachine : Behaviour
{
public event StateChangedEventHandler? OnStateChanged = null;
private IState _state = new State();
public IState State
{
get => _state;
set
{
if (_state == value)
return;
IState previousState = _state;
previousState.OnStateTransitionReady -= OnStateTransitionReady;
_state = value;
previousState.TransitionFrom(value);
value.TransitionTo(_state);
OnStateChanged?.Invoke(this, previousState, value);
value.OnStateTransitionReady += OnStateTransitionReady;
}
}
private void OnStateTransitionReady(IState sender, IState toState)
{
State = toState;
while (State.GetNextState() is IState nextState)
State = nextState;
}
protected override void OnUpdate()
{
if (State is null)
return;
while (State.GetNextState() is IState nextState)
State = nextState;
State.Update();
}
public delegate void StateChangedEventHandler(StateMachine sender, IState previousState, IState newState);
}

View File

@ -0,0 +1,11 @@
namespace Syntriax.Engine.StateMachine;
public readonly record struct StateTransition(IState State, IReadOnlyList<Func<bool>> Conditions)
{
public static implicit operator (IState state, IReadOnlyList<Func<bool>> conditions)(StateTransition value)
=> (value.State, value.Conditions);
public static implicit operator StateTransition((IState state, IReadOnlyList<Func<bool>> conditions) value)
=> new(value.state, value.conditions);
public bool CanTransition => !Conditions.Any(c => !c.Invoke());
}

View File

@ -0,0 +1,18 @@
namespace Syntriax.Engine.Systems.Time;
public interface IReadOnlyStopwatch
{
event StopwatchEventHandler? OnStarted;
event StopwatchDeltaEventHandler? OnDelta;
event StopwatchEventHandler? OnStopped;
double Time { get; }
TimerState State { get; }
event StopwatchEventHandler? OnPaused;
event StopwatchEventHandler? OnResumed;
delegate void StopwatchEventHandler(IReadOnlyStopwatch sender);
delegate void StopwatchDeltaEventHandler(IReadOnlyStopwatch sender, double delta);
}

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