Compare commits
No commits in common. "main" and "feat/physics2d" have entirely different histories.
main
...
feat/physi
225
.editorconfig
225
.editorconfig
@ -1,225 +0,0 @@
|
||||
# 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
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
@ -6,9 +8,9 @@ namespace Syntriax.Engine.Core.Abstract;
|
||||
public interface IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// Callback triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// </summary>
|
||||
event UnassignEventHandler? OnUnassigned;
|
||||
Action<IAssignable>? OnUnassigned { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
|
||||
@ -17,6 +19,4 @@ public interface IAssignable
|
||||
/// <see cref="true"/>, if the fields are unsigned successfully, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Unassign();
|
||||
|
||||
delegate void UnassignEventHandler(IAssignable sender);
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
||||
/// </summary>
|
||||
public interface IHasBehaviourController : IAssignable
|
||||
public interface IAssignableBehaviourController : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
|
||||
Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBehaviourController" />
|
||||
IBehaviourController BehaviourController { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object.
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="behaviourController">New <see cref="IBehaviourController"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IBehaviourController behaviourController);
|
||||
|
||||
delegate void BehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
||||
/// </summary>
|
||||
public interface IHasEntity : IAssignable
|
||||
public interface IAssignableEntity : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event EntityAssignedEventHandler? OnEntityAssigned;
|
||||
Action<IAssignableEntity>? OnEntityAssigned { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IEntity" />
|
||||
IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object.
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="entity">New <see cref="IEntity"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IEntity entity);
|
||||
|
||||
delegate void EntityAssignedEventHandler(IHasEntity sender);
|
||||
}
|
26
Engine.Core/Abstract/Assignable/IAssignableGameObject.cs
Normal file
26
Engine.Core/Abstract/Assignable/IAssignableGameObject.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IGameObject"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableGameObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; }
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
||||
/// </summary>
|
||||
public interface IHasStateEnable : IAssignable
|
||||
public interface IAssignableStateEnable : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event StateEnableAssignedEventHandler? OnStateEnableAssigned;
|
||||
Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IStateEnable" />
|
||||
IStateEnable StateEnable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object.
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="stateEnable">New <see cref="IStateEnable"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IStateEnable stateEnable);
|
||||
|
||||
delegate void StateEnableAssignedEventHandler(IHasStateEnable sender);
|
||||
}
|
26
Engine.Core/Abstract/Assignable/IAssignableTransform.cs
Normal file
26
Engine.Core/Abstract/Assignable/IAssignableTransform.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableTransform : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableTransform>? OnTransformAssigned { get; set; }
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform2D"/> field.
|
||||
/// </summary>
|
||||
public interface IHasGameManager : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event GameManagerAssignedEventHandler? OnGameManagerAssigned;
|
||||
|
||||
/// <inheritdoc cref="IGameManager" />
|
||||
IGameManager GameManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IGameManager"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="gameManager">New <see cref="IGameManager"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IGameManager gameManager);
|
||||
|
||||
delegate void GameManagerAssignedEventHandler(IHasGameManager sender);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IHierarchyObject"/> field.
|
||||
/// </summary>
|
||||
public interface IHasHierarchyObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IHierarchyObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned;
|
||||
|
||||
/// <inheritdoc cref="IHierarchyObject" />
|
||||
IHierarchyObject HierarchyObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IHierarchyObject"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">New <see cref="IHierarchyObject"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IHierarchyObject hierarchyObject);
|
||||
|
||||
delegate void HierarchyObjectAssignedEventHandler(IHasHierarchyObject sender);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,19 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
|
||||
/// Responsible for every behaviour an object in the game might have, controlled by <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
|
||||
public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignableStateEnable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
||||
/// Callback triggered when the <see cref="Priority"/> has changed.
|
||||
/// </summary>
|
||||
event PriorityChangedEventHandler? OnPriorityChanged;
|
||||
Action<IBehaviour>? OnPriorityChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the <see cref="IBehaviour"/>.
|
||||
/// Call priority of the <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
int Priority { get; set; }
|
||||
|
||||
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
|
||||
/// <summary>
|
||||
/// If the <see cref="IBehaviour"/> is active.
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IBehaviour2D : IBehaviour
|
||||
{
|
||||
ITransform2D Transform { get; }
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collector for the class type of <typeparamref name="T"/>.
|
||||
/// Provides mechanisms for tracking additions and removals, and notifies subscribers when such events occur on the assigned <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
|
||||
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 CollectedEventHandler? OnCollected;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when an object of type <typeparamref name="T"/> is removed from the collector.
|
||||
/// </summary>
|
||||
event RemovedEventHandler? OnRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for handling the <see cref="OnCollected"/> event.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
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="IBehaviourCollector{T}"/> that triggered the event.</param>
|
||||
/// <param name="behaviourRemoved">The object of type <typeparamref name="T"/> that was removed from the collector.</param>
|
||||
delegate void RemovedEventHandler(IBehaviourCollector<T> sender, T behaviourRemoved);
|
||||
}
|
@ -1,102 +1,93 @@
|
||||
using System;
|
||||
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="IHierarchyObject"/>.
|
||||
/// Responsible for controlling <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEnumerable<IBehaviour>
|
||||
public interface IBehaviourController : IAssignableGameObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered before the update of <see cref="IBehaviour"/>s.
|
||||
/// Callback triggered when the <see cref="Update()"/> is called but right before the <see cref="OnUpdate"/> action is triggered.
|
||||
/// </summary>
|
||||
event PreUpdateEventHandler? OnPreUpdate;
|
||||
Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="Update()"/> is called.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered during the update of <see cref="IBehaviour"/>s.
|
||||
/// Callback triggered when the <see cref="OnPreDraw()"/> is called.
|
||||
/// </summary>
|
||||
event UpdateEventHandler? OnUpdate;
|
||||
Action<IBehaviourController>? OnPreDraw { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered before the drawing phase.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been registered a new <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
event PreDrawEventHandler? OnPreDraw;
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been removed an existing <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
event BehaviourAddedEventHandler? OnBehaviourAdded;
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
||||
/// Registers the provided <see cref="IBehaviour"/> to be controlled by the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
event BehaviourRemovedEventHandler? OnBehaviourRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to add.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
/// <param name="behaviour">Uninitialized <see cref="IBehaviour"/> to be registered.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The provided <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
T AddBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IBehaviour"/> of the specified type to the <see cref="IBehaviourController"/>.
|
||||
/// Instantiates the provided <see cref="IBehaviour"/> type and registers it to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="args">Construction parameters for the <see cref="IBehaviour"/>.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
/// <param name="args">Constructor parameters for the given <see cref="IBehaviour"/> class.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The instantiated <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type.
|
||||
/// Looks up and tries to get the <see cref="IBehaviour"/> that is controlled by the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, <see cref="null"/>.</returns>
|
||||
T? GetBehaviour<T>();
|
||||
/// <param name="behaviour">If return value is <see cref="true"/> outputs the class found in the <see cref="IBehaviourController"/>. If the return value is falls, this parameter is <see cref="null"/></param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/></typeparam>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the type of <see cref="IBehaviour"/> is present in the <see cref="IBehaviourController"/>, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <returns>A list of <see cref="IBehaviour"/>s of the specified type.</returns>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/>.</typeparam>
|
||||
/// <returns>Returns a list of all the matching <see cref="IBehaviour"/>s found in the <see cref="IBehaviourController"/>.</returns>
|
||||
IList<T> GetBehaviours<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type and stores them in the provided list.
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <param name="results">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||
void GetBehaviours<T>(IList<T> results);
|
||||
|
||||
/// <summary>
|
||||
/// Removes <see cref="IBehaviour"/>s of the specified type from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to remove.</typeparam>
|
||||
/// <param name="removeAll">A flag indicating whether to remove all <see cref="IBehaviour"/>s of the specified type.</param>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified <see cref="IBehaviour"/> from the <see cref="IBehaviourController"/>.
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to remove.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Updates all <see cref="IBehaviour"/>s in the <see cref="IBehaviourController"/>.
|
||||
/// To be called in every frame of the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that a new frame is happening.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void Update();
|
||||
|
||||
/// <summary>
|
||||
/// Performs pre-draw operations.
|
||||
/// To be called before every draw call from the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that the engine is about to start drawing into the screen.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void UpdatePreDraw();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D camera in the engine.
|
||||
/// </summary>
|
||||
public interface ICamera2D : IBehaviour2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The zoom level of the camera.
|
||||
/// </summary>
|
||||
float Zoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from screen coordinates to world coordinates.
|
||||
/// </summary>
|
||||
/// <param name="screenPosition">The position in screen coordinates.</param>
|
||||
/// <returns>The position in world coordinates.</returns>
|
||||
Vector2D ScreenToWorldPosition(Vector2D screenPosition);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from world coordinates to screen coordinates.
|
||||
/// </summary>
|
||||
/// <param name="worldPosition">The position in world coordinates.</param>
|
||||
/// <returns>The position in screen coordinates.</returns>
|
||||
Vector2D WorldToScreenPosition(Vector2D worldPosition);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface ICoroutineYield
|
||||
{
|
||||
bool Yield();
|
||||
}
|
@ -1,20 +1,5 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic entity in the engine.
|
||||
/// </summary>
|
||||
public interface IEntity : IInitializable, IHasStateEnable
|
||||
public interface IEntity : IInitialize, IAssignableStateEnable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
||||
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
event IdChangedEventHandler? OnIdChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
|
||||
delegate void IdChangedEventHandler(IEntity sender, string previousId);
|
||||
}
|
||||
|
@ -1,76 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a game world responsible for managing <see cref="IHierarchyObject"/>s.
|
||||
/// </summary>
|
||||
public interface IGameManager : IEntity, IEnumerable<IHierarchyObject>
|
||||
public interface IGameManager : IEntity, IEnumerable<IGameObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="Update(double)"/> is called on the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event UpdateEventHandler? OnUpdate;
|
||||
Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; }
|
||||
Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event PreDawEventHandler? OnPreDraw;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IHierarchyObject"/> is registered to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
|
||||
IReadOnlyList<IGameObject> GameObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IHierarchyObject"/> is unregistered from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Contains time data related to this <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
EngineTime Time { get; }
|
||||
void RegisterGameObject(IGameObject gameObject);
|
||||
T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject;
|
||||
IGameObject RemoveGameObject(IGameObject gameObject);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of <see cref="IHierarchyObject"/>s managed by the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IHierarchyObject> HierarchyObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IHierarchyObject"/> to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to register.</param>
|
||||
void Register(IHierarchyObject hierarchyObject);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="IHierarchyObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IHierarchyObject"/> to instantiate.</typeparam>
|
||||
/// <param name="args">Constructor parameters for the given type of <see cref="IHierarchyObject"/>.</param>
|
||||
/// <returns>The instantiated <see cref="IHierarchyObject"/>.</returns>
|
||||
T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject;
|
||||
|
||||
/// <summary>
|
||||
/// Removes an <see cref="IHierarchyObject"/> from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">The <see cref="IHierarchyObject"/> to remove.</param>
|
||||
void Remove(IHierarchyObject hierarchyObject);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="IGameManager"/> with the given delta time.
|
||||
/// </summary>
|
||||
/// <param name="engineTime">Delta time.</param>
|
||||
void Update(EngineTime engineTime);
|
||||
|
||||
/// <summary>
|
||||
/// Performs operations that should be done before the draw calls.
|
||||
/// </summary>
|
||||
void Update(EngineTime time);
|
||||
void PreDraw();
|
||||
|
||||
delegate void UpdateEventHandler(IGameManager sender, EngineTime engineTime);
|
||||
delegate void PreDawEventHandler(IGameManager sender);
|
||||
|
||||
delegate void HierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
|
||||
delegate void HierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
|
||||
}
|
||||
|
10
Engine.Core/Abstract/IGameObject.cs
Normal file
10
Engine.Core/Abstract/IGameObject.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IGameObject : IEntity, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
|
||||
{
|
||||
Action<IGameObject>? OnUpdated { get; set; }
|
||||
|
||||
void Update();
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IEntity"/> that can enter and exit a hierarchy within the <see cref="IGameManager"/> system.
|
||||
/// This interface allows for tracking the object's presence in the hierarchy and provides events
|
||||
/// for notifying when the see enters or exits the hierarchy.
|
||||
/// </summary>
|
||||
public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy.
|
||||
/// </summary>
|
||||
event EnteredHierarchyEventHandler? OnEnteredHierarchy;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy.
|
||||
/// </summary>
|
||||
event ExitedHierarchyEventHandler? OnExitedHierarchy;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Parent"/> of the <see cref="IHierarchyObject"/> changes. The second parameter is the old <see cref="IHierarchyObject"/>.
|
||||
/// </summary>
|
||||
event ParentChangedEventHandler? OnParentChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a new <see cref="IHierarchyObject"/> is added to the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event ChildrenAddedEventHandler? OnChildrenAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when an <see cref="IHierarchyObject"/> is removed from the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event ChildrenRemovedEventHandler? OnChildrenRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IGameManager"/> this <see cref="IHierarchyObject"/> is connected to, if any.
|
||||
/// </summary>
|
||||
IGameManager GameManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the <see cref="IHierarchyObject"/> is currently in the hierarchy.
|
||||
/// </summary>
|
||||
bool IsInHierarchy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="IHierarchyObject"/> of the <see cref="IHierarchyObject"/>.
|
||||
/// </summary>
|
||||
IHierarchyObject? Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IHierarchyObject"/>s that have this <see cref="IHierarchyObject"/> as their <see cref="Parent"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IHierarchyObject> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle entering the hierarchy.
|
||||
/// This should be called by the system to properly manage hierarchy states.
|
||||
/// </summary>
|
||||
/// <param name="gameManager">The <see cref="IGameManager"/> that is managing this hierarchy.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully entered the hierarchy;
|
||||
/// <see cref="false"/> if it failed to do so.
|
||||
/// </returns>
|
||||
internal bool EnterHierarchy(IGameManager gameManager);
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle exiting the hierarchy.
|
||||
/// This should be called by the system to properly manage hierarchy states.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see cref="true"/> if the <see cref="IHierarchyObject"/> successfully exited the hierarchy;
|
||||
/// <see cref="false"/> if it failed to do so.
|
||||
/// </returns>
|
||||
internal bool ExitHierarchy();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent <see cref="IHierarchyObject"/> of this <see cref="IHierarchyObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">The parent <see cref="IHierarchyObject"/> to set.</param>
|
||||
void SetParent(IHierarchyObject? hierarchyObject);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a child <see cref="IHierarchyObject"/> to this <see cref="IHierarchyObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to add.</param>
|
||||
void AddChild(IHierarchyObject hierarchyObject);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a child <see cref="IHierarchyObject"/> from this <see cref="IHierarchyObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hierarchyObject">The child <see cref="IHierarchyObject"/> to remove.</param>
|
||||
void RemoveChild(IHierarchyObject hierarchyObject);
|
||||
|
||||
/// <summary>
|
||||
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy of a <see cref="IGameManager">.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that entered the hierarchy.</param>
|
||||
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has entered it's hierarchy.</param>
|
||||
delegate void EnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
|
||||
|
||||
/// <summary>
|
||||
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy of a <see cref="IGameManager">.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that exited the hierarchy.</param>
|
||||
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has exited it's hierarchy.</param>
|
||||
delegate void ExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when the <see cref="IHierarchyObject"/>'s parent changes.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IHierarchyObject"/> that the parent has changed.</param>
|
||||
/// <param name="previousParent">The previous <see cref="IHierarchyObject"/> the sender was a child of.</param>
|
||||
/// <param name="newParent">The new and current <see cref="IHierarchyObject"/> the sender is a child of.</param>
|
||||
delegate void ParentChangedEventHandler(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> added as a child.
|
||||
/// </summary>
|
||||
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
|
||||
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
|
||||
delegate void ChildrenAddedEventHandler(IHierarchyObject sender, IHierarchyObject childrenAdded);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> removed from being a child.
|
||||
/// </summary>
|
||||
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
|
||||
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
|
||||
delegate void ChildrenRemovedEventHandler(IHierarchyObject sender, IHierarchyObject childrenRemoved);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
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 IInitializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
||||
/// </summary>
|
||||
event InitializedEventHandler? OnInitialized;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
|
||||
/// </summary>
|
||||
event FinalizedEventHandler? OnFinalized;
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the entity has been initialized.
|
||||
/// </summary>
|
||||
bool IsInitialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the entity.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if initialization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the entity so it can either be recycled or garbage collected.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Finalize();
|
||||
|
||||
delegate void InitializedEventHandler(IInitializable sender);
|
||||
delegate void FinalizedEventHandler(IInitializable sender);
|
||||
}
|
13
Engine.Core/Abstract/IInitialize.cs
Normal file
13
Engine.Core/Abstract/IInitialize.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IInitialize
|
||||
{
|
||||
Action<IInitialize>? OnInitialized { get; set; }
|
||||
Action<IInitialize>? OnFinalized { get; set; }
|
||||
bool Initialized { get; }
|
||||
|
||||
bool Initialize();
|
||||
bool Finalize();
|
||||
}
|
@ -1,19 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with a name.
|
||||
/// </summary>
|
||||
public interface INameable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the name of the entity changes.
|
||||
/// </summary>
|
||||
event NameChangedEventHandler? OnNameChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the entity.
|
||||
/// </summary>
|
||||
Action<IEntity>? OnNameChanged { get; set; }
|
||||
string Name { get; set; }
|
||||
|
||||
delegate void NameChangedEventHandler(INameable sender, string previousName);
|
||||
}
|
||||
|
@ -1,19 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with an enable state that can be toggled.
|
||||
/// </summary>
|
||||
public interface IStateEnable : IHasEntity
|
||||
public interface IStateEnable : IAssignableEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
||||
/// </summary>
|
||||
event EnabledChangedEventHandler? OnEnabledChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
||||
/// </summary>
|
||||
Action<IStateEnable>? OnEnabledChanged { get; set; }
|
||||
bool Enabled { get; set; }
|
||||
|
||||
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
|
||||
}
|
||||
|
15
Engine.Core/Abstract/ITransform.cs
Normal file
15
Engine.Core/Abstract/ITransform.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface ITransform
|
||||
{
|
||||
Action<ITransform>? OnPositionChanged { get; set; }
|
||||
Action<ITransform>? OnScaleChanged { get; set; }
|
||||
Action<ITransform>? OnRotationChanged { get; set; }
|
||||
|
||||
Vector2D Position { get; set; }
|
||||
Vector2D Scale { get; set; }
|
||||
|
||||
float Rotation { get; set; }
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
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();
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
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) { }
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public abstract class BaseEntity : IEntity
|
||||
{
|
||||
public event IEntity.IdChangedEventHandler? OnIdChanged = 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!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _id = string.Empty;
|
||||
|
||||
public virtual IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
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;
|
||||
|
||||
string previousId = _id;
|
||||
|
||||
_id = value;
|
||||
OnIdChanged?.Invoke(this, previousId);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInitialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnAssign(IStateEnable stateEnable) { }
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnAssign(stateEnable);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void UnassignInternal() { }
|
||||
public bool Unassign()
|
||||
{
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
UnassignInternal();
|
||||
|
||||
_stateEnable = null!;
|
||||
_stateEnable.Unassign();
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal() { }
|
||||
public bool Initialize()
|
||||
{
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
_stateEnable ??= Factory.StateEnableFactory.Instantiate(this);
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
IsInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void FinalizeInternal() { }
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
return false;
|
||||
|
||||
FinalizeInternal();
|
||||
|
||||
IsInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected BaseEntity() => _id = Guid.NewGuid().ToString("D");
|
||||
protected BaseEntity(string id) => _id = id;
|
||||
}
|
@ -1,105 +1,113 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public abstract class Behaviour : BehaviourBase
|
||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||
public abstract class Behaviour : IBehaviour
|
||||
{
|
||||
private bool isInitializedThisFrame = false;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
|
||||
protected IGameManager GameManager => BehaviourController.HierarchyObject.GameManager;
|
||||
protected IHierarchyObject HierarchyObject => BehaviourController.HierarchyObject;
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IBehaviour>? OnPriorityChanged { get; set; } = null;
|
||||
|
||||
public Behaviour()
|
||||
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private int _priority = 0;
|
||||
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
OnInitialized += OnInitialize;
|
||||
OnFinalized += OnFinalize;
|
||||
OnUnassigned += OnUnassign;
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnUnassign() { }
|
||||
protected virtual void OnUnassign(IAssignable assignable) => OnUnassign();
|
||||
|
||||
protected virtual void OnInitialize() { }
|
||||
protected virtual void OnInitialize(IInitializable _)
|
||||
public int Priority
|
||||
{
|
||||
isInitializedThisFrame = true;
|
||||
get => _priority;
|
||||
set
|
||||
{
|
||||
if (value == _priority)
|
||||
return;
|
||||
|
||||
BehaviourController.OnPreUpdate += PreUpdate;
|
||||
BehaviourController.OnPreDraw += PreDraw;
|
||||
BehaviourController.OnUpdate += Update;
|
||||
BehaviourController.HierarchyObject.OnEnteredHierarchy += EnteredHierarchy;
|
||||
BehaviourController.HierarchyObject.OnExitedHierarchy += ExitedHierarchy;
|
||||
|
||||
OnInitialize();
|
||||
|
||||
if (HierarchyObject.IsInHierarchy)
|
||||
EnteredHierarchy(HierarchyObject, GameManager);
|
||||
_priority = value;
|
||||
OnPriorityChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnFinalize() { }
|
||||
protected virtual void OnFinalize(IInitializable _)
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
BehaviourController.OnPreUpdate -= PreUpdate;
|
||||
BehaviourController.OnPreDraw -= PreDraw;
|
||||
BehaviourController.OnUpdate -= Update;
|
||||
BehaviourController.HierarchyObject.OnEnteredHierarchy -= EnteredHierarchy;
|
||||
BehaviourController.HierarchyObject.OnExitedHierarchy -= ExitedHierarchy;
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
OnFinalize();
|
||||
|
||||
if (HierarchyObject.IsInHierarchy)
|
||||
ExitedHierarchy(HierarchyObject, GameManager);
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||
protected virtual void OnPreUpdate() { }
|
||||
protected virtual void PreUpdate(IBehaviourController _)
|
||||
public bool Assign(IBehaviourController behaviourController)
|
||||
{
|
||||
OnPreUpdatePreActiveCheck();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (isInitializedThisFrame)
|
||||
FirstActiveFrame();
|
||||
|
||||
OnPreUpdate();
|
||||
_behaviourController = behaviourController;
|
||||
OnBehaviourControllerAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnFirstActiveFrame() { }
|
||||
protected virtual void FirstActiveFrame()
|
||||
public bool Unassign()
|
||||
{
|
||||
OnFirstActiveFrame();
|
||||
isInitializedThisFrame = false;
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
_behaviourController = null!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnUpdatePreActiveCheck() { }
|
||||
protected virtual void OnUpdate() { }
|
||||
protected virtual void Update(IBehaviourController _)
|
||||
public bool Initialize()
|
||||
{
|
||||
OnUpdatePreActiveCheck();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
|
||||
OnUpdate();
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||
protected virtual void OnPreDraw() { }
|
||||
protected virtual void PreDraw(IBehaviourController _)
|
||||
public bool Finalize()
|
||||
{
|
||||
OnPreDrawPreActiveCheck();
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreDraw();
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnEnteredHierarchy(IGameManager gameManager) { }
|
||||
protected virtual void EnteredHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnEnteredHierarchy(gameManager);
|
||||
|
||||
protected virtual void OnExitedHierarchy(IGameManager gameManager) { }
|
||||
protected virtual void ExitedHierarchy(IHierarchyObject sender, IGameManager gameManager) => OnExitedHierarchy(gameManager);
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||
public abstract class BehaviourBase : BaseEntity, IBehaviour
|
||||
{
|
||||
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
|
||||
public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null;
|
||||
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
|
||||
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
private int _priority = 0;
|
||||
public int Priority
|
||||
{
|
||||
get => _priority;
|
||||
set
|
||||
{
|
||||
if (value == _priority)
|
||||
return;
|
||||
|
||||
int previousPriority = _priority;
|
||||
_priority = value;
|
||||
OnPriorityChanged?.Invoke(this, previousPriority);
|
||||
}
|
||||
}
|
||||
|
||||
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!;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
||||
{
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
|
||||
|
||||
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
|
||||
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
|
||||
|
||||
protected readonly List<T> behaviours = new(32);
|
||||
|
||||
public IReadOnlyList<T> Behaviours => behaviours;
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
|
||||
public T this[Index index] => behaviours[index];
|
||||
|
||||
public BehaviourCollector() { }
|
||||
public BehaviourCollector(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;
|
||||
|
||||
behaviours.Add(tBehaviour);
|
||||
OnBehaviourAdd(behaviour);
|
||||
OnCollected?.Invoke(this, tBehaviour);
|
||||
}
|
||||
|
||||
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not T tBehaviour)
|
||||
return;
|
||||
|
||||
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 (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
|
||||
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
|
||||
|
||||
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
|
||||
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
|
||||
|
||||
GameManager = gameManager;
|
||||
OnAssign(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() => behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
||||
{
|
||||
public Comparison<T>? SortBy { get; set; } = null;
|
||||
|
||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
||||
{
|
||||
if (SortBy is not null)
|
||||
behaviours.Sort(SortBy);
|
||||
}
|
||||
|
||||
public BehaviourCollectorSorted() { }
|
||||
public BehaviourCollectorSorted(IGameManager gameManager) : base(gameManager) { }
|
||||
}
|
@ -1,59 +1,36 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||
public class BehaviourController : IBehaviourController
|
||||
{
|
||||
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
|
||||
public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
|
||||
public event IBehaviourController.PreDrawEventHandler? OnPreDraw = null;
|
||||
public Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
public Action<IBehaviourController>? OnUpdate { get; set; } = null;
|
||||
public Action<IBehaviourController>? OnPreDraw { get; set; } = null;
|
||||
|
||||
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
|
||||
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
|
||||
public event IHasHierarchyObject.HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
|
||||
public Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; } = null;
|
||||
public Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; } = null;
|
||||
|
||||
public event IInitializable.InitializedEventHandler? OnInitialized = null;
|
||||
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
|
||||
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
|
||||
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
|
||||
|
||||
private IHierarchyObject _hierarchyObject = null!;
|
||||
private bool _initialized = false;
|
||||
private IGameObject _gameObject = null!;
|
||||
|
||||
public IHierarchyObject HierarchyObject => _hierarchyObject;
|
||||
|
||||
public bool IsInitialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
public IGameObject GameObject => _gameObject;
|
||||
|
||||
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
|
||||
{
|
||||
InsertBehaviourByPriority(behaviour);
|
||||
|
||||
behaviour.Assign(this);
|
||||
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
|
||||
|
||||
behaviour.Initialize();
|
||||
behaviour.OnPriorityChanged += OnPriorityChange;
|
||||
OnBehaviourAdded?.Invoke(this, behaviour);
|
||||
@ -61,21 +38,27 @@ public class BehaviourController : IBehaviourController
|
||||
}
|
||||
|
||||
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
||||
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_hierarchyObject, args));
|
||||
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_gameObject, args));
|
||||
|
||||
public T? GetBehaviour<T>()
|
||||
public bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
foreach (IBehaviour behaviourItem in behaviours)
|
||||
if (behaviourItem is T result)
|
||||
return result;
|
||||
foreach (var behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T result)
|
||||
continue;
|
||||
|
||||
return default;
|
||||
behaviour = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
behaviour = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IList<T> GetBehaviours<T>()
|
||||
{
|
||||
List<T>? behaviours = null;
|
||||
foreach (IBehaviour behaviourItem in this.behaviours)
|
||||
foreach (var behaviourItem in this.behaviours)
|
||||
{
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
@ -87,18 +70,6 @@ public class BehaviourController : IBehaviourController
|
||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||
}
|
||||
|
||||
public void GetBehaviours<T>(IList<T> results)
|
||||
{
|
||||
results.Clear();
|
||||
foreach (IBehaviour behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
|
||||
results.Add(behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||
{
|
||||
for (int i = behaviours.Count; i >= 0; i--)
|
||||
@ -116,7 +87,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 {HierarchyObject.Name}'s {nameof(IBehaviourController)}.");
|
||||
throw new Exception($"{behaviour.GetType().Name} does not exist in {GameObject.Name}'s {nameof(IBehaviourController)}.");
|
||||
|
||||
behaviour.OnPriorityChanged -= OnPriorityChange;
|
||||
behaviour.Finalize();
|
||||
@ -124,57 +95,29 @@ public class BehaviourController : IBehaviourController
|
||||
OnBehaviourRemoved?.Invoke(this, behaviour);
|
||||
}
|
||||
|
||||
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
|
||||
public bool Assign(IHierarchyObject hierarchyObject)
|
||||
public bool Assign(IGameObject gameObject)
|
||||
{
|
||||
if (HierarchyObject is not null && HierarchyObject.IsInitialized)
|
||||
if (GameObject is not null && GameObject.Initialized)
|
||||
return false;
|
||||
|
||||
_hierarchyObject = hierarchyObject;
|
||||
OnAssign(hierarchyObject);
|
||||
OnHierarchyObjectAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, _hierarchyObject);
|
||||
|
||||
foreach (IBehaviour behaviour in behaviours)
|
||||
behaviour.Initialize();
|
||||
|
||||
IsInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
return false;
|
||||
|
||||
foreach (IBehaviour behaviour in behaviours)
|
||||
behaviour.Finalize();
|
||||
|
||||
IsInitialized = false;
|
||||
_gameObject = gameObject;
|
||||
OnGameObjectAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (IsInitialized)
|
||||
if (GameObject is not null && GameObject.Initialized)
|
||||
return false;
|
||||
|
||||
_hierarchyObject = null!;
|
||||
_gameObject = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!HierarchyObject.StateEnable.Enabled)
|
||||
if (!GameObject.StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreUpdate?.Invoke(this);
|
||||
@ -183,14 +126,14 @@ public class BehaviourController : IBehaviourController
|
||||
|
||||
public void UpdatePreDraw()
|
||||
{
|
||||
if (!HierarchyObject.StateEnable.Enabled)
|
||||
if (!GameObject.StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreDraw?.Invoke(this);
|
||||
}
|
||||
|
||||
public BehaviourController() { }
|
||||
public BehaviourController(IHierarchyObject hierarchyObject) => Assign(hierarchyObject);
|
||||
public BehaviourController(IGameObject gameObject) => Assign(gameObject);
|
||||
|
||||
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
|
||||
{
|
||||
@ -209,12 +152,9 @@ public class BehaviourController : IBehaviourController
|
||||
behaviours.Add(behaviour);
|
||||
}
|
||||
|
||||
private void OnPriorityChange(IBehaviour sender, int previousPriority)
|
||||
private void OnPriorityChange(IBehaviour behaviour)
|
||||
{
|
||||
behaviours.Remove(sender);
|
||||
InsertBehaviourByPriority(sender);
|
||||
behaviours.Remove(behaviour);
|
||||
InsertBehaviourByPriority(behaviour);
|
||||
}
|
||||
|
||||
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
||||
}
|
||||
|
87
Engine.Core/BehaviourOverride.cs
Normal file
87
Engine.Core/BehaviourOverride.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public abstract class BehaviourOverride : Behaviour
|
||||
{
|
||||
private bool isInitializedThisFrame = false;
|
||||
|
||||
protected IGameObject GameObject => BehaviourController.GameObject;
|
||||
protected ITransform Transform => BehaviourController.GameObject.Transform;
|
||||
|
||||
public BehaviourOverride()
|
||||
{
|
||||
OnInitialized += OnInitialize;
|
||||
OnFinalized += OnFinalize;
|
||||
OnUnassigned += OnUnassign;
|
||||
}
|
||||
|
||||
protected virtual void OnUnassign() { }
|
||||
private void OnUnassign(IAssignable assignable) => OnUnassign();
|
||||
|
||||
protected virtual void OnInitialize() { }
|
||||
private void OnInitialize(IInitialize _)
|
||||
{
|
||||
isInitializedThisFrame = true;
|
||||
|
||||
BehaviourController.OnPreUpdate += PreUpdate;
|
||||
BehaviourController.OnPreDraw += PreDraw;
|
||||
BehaviourController.OnUpdate += Update;
|
||||
OnInitialize();
|
||||
}
|
||||
|
||||
protected virtual void OnFinalize() { }
|
||||
private void OnFinalize(IInitialize _)
|
||||
{
|
||||
BehaviourController.OnPreUpdate -= PreUpdate;
|
||||
BehaviourController.OnPreDraw -= PreDraw;
|
||||
BehaviourController.OnUpdate -= Update;
|
||||
OnFinalize();
|
||||
}
|
||||
|
||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||
protected virtual void OnPreUpdate() { }
|
||||
private void PreUpdate(IBehaviourController _)
|
||||
{
|
||||
OnPreUpdatePreActiveCheck();
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (isInitializedThisFrame)
|
||||
FirstActiveFrame();
|
||||
|
||||
OnPreUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnFirstActiveFrame() { }
|
||||
private void FirstActiveFrame()
|
||||
{
|
||||
OnFirstActiveFrame();
|
||||
isInitializedThisFrame = false;
|
||||
}
|
||||
|
||||
protected virtual void OnUpdatePreActiveCheck() { }
|
||||
protected virtual void OnUpdate() { }
|
||||
private void Update(IBehaviourController _)
|
||||
{
|
||||
OnUpdatePreActiveCheck();
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
OnUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||
protected virtual void OnPreDraw() { }
|
||||
private void PreDraw(IBehaviourController _)
|
||||
{
|
||||
OnPreDrawPreActiveCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreDraw();
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class CoroutineManager : HierarchyObject
|
||||
{
|
||||
private readonly List<IEnumerator> enumerators = [];
|
||||
|
||||
public IEnumerator StartCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
enumerators.Add(enumerator);
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
public void StopCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
enumerators.Remove(enumerator);
|
||||
}
|
||||
|
||||
protected override void OnEnteringHierarchy(IGameManager gameManager)
|
||||
{
|
||||
gameManager.OnUpdate += OnUpdate;
|
||||
}
|
||||
|
||||
protected override void OnExitingHierarchy(IGameManager gameManager)
|
||||
{
|
||||
gameManager.OnUpdate -= OnUpdate;
|
||||
}
|
||||
|
||||
private void OnUpdate(IGameManager sender, EngineTime time)
|
||||
{
|
||||
for (int i = enumerators.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (enumerators[i].Current is ICoroutineYield coroutineYield && coroutineYield.Yield())
|
||||
continue;
|
||||
|
||||
if (!enumerators[i].MoveNext())
|
||||
enumerators.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class CoroutineYield(Func<bool> condition) : ICoroutineYield
|
||||
{
|
||||
private readonly Func<bool> condition = condition;
|
||||
|
||||
public bool Yield() => condition.Invoke();
|
||||
}
|
@ -4,7 +4,5 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>false</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,46 +0,0 @@
|
||||
@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
|
@ -2,10 +2,8 @@ using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public readonly struct EngineTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
|
||||
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
|
||||
{
|
||||
public readonly TimeSpan TimeSinceStart = TimeSinceStart;
|
||||
public readonly TimeSpan DeltaSpan = TimeDelta;
|
||||
|
||||
public readonly float DeltaTime = (float)TimeDelta.TotalSeconds;
|
||||
public readonly TimeSpan Total { get; init; } = Total;
|
||||
public readonly TimeSpan Elapsed { get; init; } = Elapsed;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
@ -7,6 +8,13 @@ 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($"Assign operation has failed on T: {to?.GetType().FullName ?? "\"null\""}, value: {value?.GetType().ToString() ?? "\"null\""}");
|
||||
=> 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\""}");
|
||||
}
|
||||
// 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}");
|
||||
|
@ -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)
|
||||
=> new($"{value?.GetType().FullName ?? "\"null\""} has not been assigned to {to?.GetType().FullName ?? "\"null\""}");
|
||||
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 void Check<T1, T2>(T1 to, T2? value)
|
||||
public static void Check<T1, T2>(T1 to, T2? value) where T1 : IAssignable
|
||||
{
|
||||
if (value is not null)
|
||||
return;
|
||||
|
@ -2,7 +2,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static Vector2D TransformVector2D(this ITransform2D transform, Vector2D vector)
|
||||
public static Vector2D TransformVector2D(this ITransform transform, Vector2D vector)
|
||||
=> vector.Scale(transform.Scale)
|
||||
.Rotate(transform.Rotation * Math.DegreeToRadian)
|
||||
.Add(transform.Position);
|
||||
|
@ -1,96 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class BehaviourControllerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to search in.</param>
|
||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool TryGetBehaviour<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
behaviour = behaviourController.GetBehaviour<T>();
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns a new one if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get or add.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to search in.</param>
|
||||
/// <param name="args">Optional arguments to pass to the constructor of the <see cref="IBehaviour"/> if a new one is added.</param>
|
||||
/// <returns>The existing or newly added <see cref="IBehaviour"/> of the specified type.</returns>
|
||||
public static T GetOrAddBehaviour<T>(this IBehaviourController behaviourController, params object?[]? args) where T : class, IBehaviour
|
||||
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type in the parent hierarchy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the parent hierarchy; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool TryGetBehaviourInParent<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = GetBehaviourInParent<T>(behaviourController);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type in the parent hierarchy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, null.</returns>
|
||||
public static T? GetBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
||||
{
|
||||
IBehaviourController? controller = behaviourController;
|
||||
|
||||
while (controller is not null)
|
||||
{
|
||||
if (behaviourController.GetBehaviour<T>() is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
controller = controller.HierarchyObject.Parent?.BehaviourController;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type in the child hierarchy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, null.</param>
|
||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the child hierarchy; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool TryGetBehaviourInChildren<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = GetBehaviourInChildren<T>(behaviourController);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type in the child hierarchy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to start searching from.</param>
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, null.</returns>
|
||||
public static T? GetBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
||||
{
|
||||
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
|
||||
return localBehaviour;
|
||||
|
||||
foreach (IHierarchyObject child in behaviourController.HierarchyObject)
|
||||
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class BehaviourExtensions
|
||||
{
|
||||
public static T? FindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
|
||||
{
|
||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
||||
if (hierarchyObject.BehaviourController.GetBehaviour<T>() is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool TryFindBehaviour<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = FindBehaviour<T>(hierarchyObjects);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public static void FindBehaviours<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
|
||||
{
|
||||
behaviours.Clear();
|
||||
List<T> cache = [];
|
||||
|
||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
||||
{
|
||||
hierarchyObject.BehaviourController.GetBehaviours(cache);
|
||||
behaviours.AddRange(cache);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class GameManagerExtensions
|
||||
{
|
||||
public static IHierarchyObject InstantiateHierarchyObject(this IGameManager gameManager, params object?[]? args)
|
||||
=> gameManager.InstantiateHierarchyObject<HierarchyObject>(args);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class HierarchyObjectExtensions
|
||||
{
|
||||
public static 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)
|
||||
return @object;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool TryFindHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = FindHierarchyObject<T>(hierarchyObjects);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public static void FindHierarchyObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
|
||||
{
|
||||
behaviours.Clear();
|
||||
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
|
||||
if (hierarchyObject is T @object)
|
||||
behaviours.Add(@object);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (position.HasValue) transform.Position = position.Value;
|
||||
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||
if (scale.HasValue) transform.Scale = scale.Value;
|
||||
if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
|
||||
if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
|
||||
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||
return transform;
|
||||
}
|
||||
}
|
35
Engine.Core/Extensions/Vector2DExtensions.cs
Normal file
35
Engine.Core/Extensions/Vector2DExtensions.cs
Normal file
@ -0,0 +1,35 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class Vector2DExtensions
|
||||
{
|
||||
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
|
||||
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
||||
|
||||
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
||||
|
||||
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
|
||||
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||
public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value);
|
||||
|
||||
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
|
||||
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
||||
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||
|
||||
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
||||
public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
|
||||
|
||||
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
||||
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
||||
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
||||
|
||||
|
||||
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@ -5,19 +5,16 @@ namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class BehaviourControllerFactory
|
||||
{
|
||||
public static IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
|
||||
=> Instantiate<BehaviourController>(hierarchyObject);
|
||||
public IBehaviourController Instantiate(IGameObject gameObject)
|
||||
=> Instantiate<BehaviourController>(gameObject);
|
||||
|
||||
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
|
||||
public T Instantiate<T>(IGameObject gameObject, params object?[]? args)
|
||||
where T : class, IBehaviourController
|
||||
{
|
||||
T behaviourController = TypeFactory.Get<T>(args);
|
||||
|
||||
if (!hierarchyObject.Assign(behaviourController))
|
||||
throw AssignException.From(hierarchyObject, behaviourController);
|
||||
|
||||
if (!behaviourController.Assign(hierarchyObject))
|
||||
throw AssignException.From(behaviourController, hierarchyObject);
|
||||
if (!behaviourController.Assign(gameObject))
|
||||
throw AssignException.From(behaviourController, gameObject);
|
||||
|
||||
return behaviourController;
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class BehaviourFactory
|
||||
{
|
||||
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
|
||||
=> Instantiate<T>(hierarchyObject, stateEnable: null, args);
|
||||
public 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, IStateEnable? stateEnable, params object?[]? args)
|
||||
public T Instantiate<T>(IGameObject gameObject, 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;
|
||||
}
|
||||
|
39
Engine.Core/Factory/GameObjectFactory.cs
Normal file
39
Engine.Core/Factory/GameObjectFactory.cs
Normal file
@ -0,0 +1,39 @@
|
||||
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 (!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;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -5,15 +5,12 @@ namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class StateEnableFactory
|
||||
{
|
||||
public static IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
|
||||
public IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
|
||||
|
||||
public static T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
|
||||
public 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);
|
||||
|
||||
|
@ -4,7 +4,7 @@ namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class TransformFactory
|
||||
{
|
||||
public static ITransform2D Instantiate() => TypeFactory.Get<Transform2D>();
|
||||
public static T Instantiate<T>(params object?[]? args) where T : class, ITransform2D
|
||||
public ITransform Instantiate() => TypeFactory.Get<Transform>();
|
||||
public T Instantiate<T>(params object?[]? args) where T : class, ITransform
|
||||
=> TypeFactory.Get<T>(args);
|
||||
}
|
||||
|
@ -4,136 +4,167 @@ 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("HierarchyObject Count: {_hierarchyObjects.Count}")]
|
||||
public class GameManager : BaseEntity, IGameManager
|
||||
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
|
||||
public class GameManager : IGameManager
|
||||
{
|
||||
public event IGameManager.UpdateEventHandler? OnUpdate = null;
|
||||
public event IGameManager.PreDawEventHandler? OnPreDraw = null;
|
||||
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
|
||||
public event IGameManager.HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
|
||||
public event IGameManager.HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
|
||||
private readonly List<IHierarchyObject> _hierarchyObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
|
||||
public IReadOnlyList<IHierarchyObject> HierarchyObjects => _hierarchyObjects;
|
||||
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
|
||||
public override IStateEnable StateEnable
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private GameObjectFactory _gameObjectFactory = null!;
|
||||
private bool _initialized = false;
|
||||
|
||||
private GameObjectFactory GameObjectFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.StateEnable is null)
|
||||
{
|
||||
Assign(Factory.StateEnableFactory.Instantiate(this));
|
||||
if (base.StateEnable is null)
|
||||
throw NotAssignedException.From(this, base.StateEnable);
|
||||
}
|
||||
|
||||
return base.StateEnable;
|
||||
if (_gameObjectFactory is null)
|
||||
_gameObjectFactory = new GameObjectFactory();
|
||||
return _gameObjectFactory;
|
||||
}
|
||||
}
|
||||
|
||||
public EngineTime Time { get; private set; } = new();
|
||||
public bool Initialized => _initialized;
|
||||
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||
|
||||
public void Register(IHierarchyObject hierarchyObject)
|
||||
public IStateEnable StateEnable
|
||||
{
|
||||
if (_hierarchyObjects.Contains(hierarchyObject))
|
||||
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is already registered to the {nameof(GameManager)}.");
|
||||
get
|
||||
{
|
||||
if (_stateEnable is null)
|
||||
{
|
||||
Assign(new StateEnableFactory().Instantiate(this));
|
||||
if (_stateEnable is null)
|
||||
throw NotAssignedException.From(this, _stateEnable);
|
||||
}
|
||||
|
||||
hierarchyObject.OnFinalized += OnHierarchyObjectFinalize;
|
||||
hierarchyObject.OnExitedHierarchy += OnHierarchyObjectExitedHierarchy;
|
||||
|
||||
if (!hierarchyObject.Initialize())
|
||||
throw new Exception($"{hierarchyObject.Name} can't be initialized");
|
||||
|
||||
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);
|
||||
return _stateEnable;
|
||||
}
|
||||
}
|
||||
|
||||
public T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject
|
||||
public void RegisterGameObject(IGameObject gameObject)
|
||||
{
|
||||
T hierarchyObject = Factory.HierarchyObjectFactory.Instantiate<T>(args);
|
||||
Register(hierarchyObject);
|
||||
return hierarchyObject;
|
||||
if (_gameObjects.Contains(gameObject))
|
||||
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is already registered to the {nameof(GameManager)}.");
|
||||
|
||||
Register(gameObject);
|
||||
}
|
||||
|
||||
public void Remove(IHierarchyObject hierarchyObject)
|
||||
public T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject
|
||||
{
|
||||
if (!_hierarchyObjects.Contains(hierarchyObject))
|
||||
throw new Exception($"{nameof(IHierarchyObject)} named {hierarchyObject.Name} is not registered to the {nameof(GameManager)}.");
|
||||
|
||||
hierarchyObject.OnFinalized -= OnHierarchyObjectFinalize;
|
||||
hierarchyObject.OnExitedHierarchy -= OnHierarchyObjectExitedHierarchy;
|
||||
|
||||
foreach (IHierarchyObject child in hierarchyObject.Children)
|
||||
Remove(child);
|
||||
|
||||
_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);
|
||||
T gameObject = GameObjectFactory.Instantiate<T>(args);
|
||||
Register(gameObject);
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
public IGameObject RemoveGameObject(IGameObject gameObject)
|
||||
{
|
||||
base.InitializeInternal();
|
||||
if (!_gameObjects.Contains(gameObject))
|
||||
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is not registered to the {nameof(GameManager)}.");
|
||||
|
||||
Unregister(gameObject);
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
|
||||
foreach (IHierarchyObject hierarchyObject in HierarchyObjects)
|
||||
hierarchyObject.Initialize();
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.Initialize();
|
||||
|
||||
_initialized = true;
|
||||
OnInitialized?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
public bool Finalize()
|
||||
{
|
||||
base.FinalizeInternal();
|
||||
for (int i = HierarchyObjects.Count; i >= 0; i--)
|
||||
HierarchyObjects[i].Finalize();
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
for (int i = GameObjects.Count; i >= 0; i--)
|
||||
GameObjects[i].Finalize();
|
||||
|
||||
OnFinalized?.Invoke(this);
|
||||
_initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(EngineTime engineTime)
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
Time = engineTime;
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < HierarchyObjects.Count; i++)
|
||||
HierarchyObjects[i].BehaviourController.Update();
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnUpdate?.Invoke(this, engineTime);
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(EngineTime time)
|
||||
{
|
||||
Time.SetTime(time);
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.BehaviourController.Update();
|
||||
}
|
||||
|
||||
public void PreDraw()
|
||||
{
|
||||
for (int i = 0; i < HierarchyObjects.Count; i++)
|
||||
HierarchyObjects[i].BehaviourController.UpdatePreDraw();
|
||||
|
||||
OnPreDraw?.Invoke(this);
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.BehaviourController.UpdatePreDraw();
|
||||
}
|
||||
|
||||
private void OnHierarchyObjectFinalize(IInitializable initializable)
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
private void Register(IGameObject gameObject)
|
||||
{
|
||||
if (initializable is IHierarchyObject hierarchyObject)
|
||||
Remove(hierarchyObject);
|
||||
gameObject.OnFinalized += OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Add(gameObject);
|
||||
OnGameObjectRegistered?.Invoke(this, gameObject);
|
||||
}
|
||||
|
||||
private void OnHierarchyObjectExitedHierarchy(IHierarchyObject sender, IGameManager gameManager)
|
||||
private void Unregister(IGameObject gameObject)
|
||||
{
|
||||
if (sender is IHierarchyObject hierarchyObject)
|
||||
Remove(hierarchyObject);
|
||||
gameObject.OnFinalized -= OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Remove(gameObject);
|
||||
OnGameObjectUnRegistered?.Invoke(this, gameObject);
|
||||
}
|
||||
|
||||
public IEnumerator<IHierarchyObject> GetEnumerator() => _hierarchyObjects.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _hierarchyObjects.GetEnumerator();
|
||||
private void OnGameObjectFinalize(IInitialize initialize)
|
||||
{
|
||||
if (initialize is IGameObject gameObject)
|
||||
Unregister(gameObject);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
public IEnumerator<IGameObject> GetEnumerator() => _gameObjects.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _gameObjects.GetEnumerator();
|
||||
}
|
||||
|
150
Engine.Core/GameObject.cs
Normal file
150
Engine.Core/GameObject.cs
Normal file
@ -0,0 +1,150 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||
public class GameObject : IGameObject
|
||||
{
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
|
||||
public Action<IEntity>? OnNameChanged { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
|
||||
public Action<IGameObject>? OnUpdated { get; set; } = null;
|
||||
|
||||
|
||||
private ITransform _transform = null!;
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private string _name = nameof(GameObject);
|
||||
private bool _initialized = false;
|
||||
|
||||
public ITransform Transform => _transform;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
if (value == _name) return;
|
||||
|
||||
_name = value;
|
||||
OnNameChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, _transform);
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_stateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnUpdated?.Invoke(this);
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
|
||||
System.Threading.Tasks.Parallel.ForEach(
|
||||
_behaviourController.GetBehaviours<IBehaviour>(),
|
||||
behaviour => behaviour.Finalize()
|
||||
);
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(ITransform transform)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_transform = transform;
|
||||
OnTransformAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IBehaviourController behaviourController)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_behaviourController = behaviourController;
|
||||
OnBehaviourControllerAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
_transform = null!;
|
||||
_behaviourController = null!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
|
||||
private void ConnectBehaviourController(IAssignableBehaviourController controller)
|
||||
{
|
||||
controller.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
controller.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
private void OnBehaviourRemoved(IBehaviourController _, IBehaviour behaviour) { if (Initialized) behaviour.Initialize(); }
|
||||
private void OnBehaviourAdded(IBehaviourController _, IBehaviour behaviour) { if (Initialized) behaviour.Finalize(); }
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
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();
|
||||
}
|
@ -5,210 +5,31 @@ namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class Math
|
||||
{
|
||||
/// <summary>
|
||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
||||
/// </summary>
|
||||
public const float PI = 3.1415926535897932f;
|
||||
|
||||
/// <summary>
|
||||
/// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319.
|
||||
/// </summary>
|
||||
public const float Tau = 2f * PI;
|
||||
|
||||
/// <summary>
|
||||
/// The base of the natural logarithm, approximately equal to 2.71828.
|
||||
/// </summary>
|
||||
public const float E = 2.718281828459045f;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from radians to degrees.
|
||||
/// </summary>
|
||||
public const float RadianToDegree = 180f / PI;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from degrees to radians.
|
||||
/// </summary>
|
||||
public const float DegreeToRadian = PI / 180f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute value of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The absolute value of <paramref name="x"/>.</returns>
|
||||
public const float E = 2.718281828459045f;
|
||||
public const float PI = 3.1415926535897932f;
|
||||
public const float Tau = 2f * PI;
|
||||
|
||||
public static T Abs<T>(T x) where T : INumber<T> => x > T.Zero ? x : -x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cosine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The cosine of <paramref name="x"/>.</returns>
|
||||
public static float Cos(float x) => MathF.Cos(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The sine of <paramref name="x"/>.</returns>
|
||||
public static float Sin(float x) => MathF.Sin(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arccosine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arccosine of <paramref name="x"/>.</returns>
|
||||
public static float Acos(float x) => MathF.Acos(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arcsine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arcsine of <paramref name="x"/>.</returns>
|
||||
public static float Asin(float x) => MathF.Asin(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle whose tangent is the quotient of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="y">The y-coordinate of a point.</param>
|
||||
/// <param name="x">The x-coordinate of a point.</param>
|
||||
/// <returns>The angle, measured in radians.</returns>
|
||||
public static float Atan2(float y, float x) => MathF.Atan2(y, x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hyperbolic arctangent of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The hyperbolic arctangent of <paramref name="x"/>.</returns>
|
||||
public static float Atanh(float x) => MathF.Atanh(x);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a number between a minimum and maximum value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static T Clamp<T>(T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smallest integral value that is greater than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The smallest integral value that is greater than or equal to <paramref name="x"/>.</returns>
|
||||
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||
public static float Ceiling(float x) => MathF.Ceiling(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The magnitude value.</param>
|
||||
/// <param name="y">The sign value.</param>
|
||||
/// <returns>A value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.</returns>
|
||||
public static float CopySign(float x, float y) => MathF.CopySign(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the largest integral value that is less than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The largest integral value that is less than or equal to <paramref name="x"/>.</returns>
|
||||
public static float Floor(float x) => MathF.Floor(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remainder of the division of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="x">The dividend.</param>
|
||||
/// <param name="y">The divisor.</param>
|
||||
/// <returns>The remainder of the division of <paramref name="x"/> by <paramref name="y"/>.</returns>
|
||||
public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the natural (base e) logarithm of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <param name="y">The base.</param>
|
||||
/// <returns>The natural logarithm of <paramref name="x"/> with base <paramref name="y"/>.</returns>
|
||||
public static float Log(float x, float y) => MathF.Log(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the larger of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The larger of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Max<T>(T x, T y) where T : INumber<T> => (x > y) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is larger.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is larger.</returns>
|
||||
public static T AbsMax<T>(T x, T y) where T : INumber<T> => (Abs(x) > Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smaller of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The smaller of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y);
|
||||
public static T Min<T>(T x, T y) where T : INumber<T> => (x < y) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is smaller.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is smaller.</returns>
|
||||
public static T AbsMin<T>(T x, T y) where T : INumber<T> => (Abs(x) < Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specified number raised to the specified power.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to raise to a power.</param>
|
||||
/// <param name="y">The power to raise <paramref name="x"/> to.</param>
|
||||
/// <returns>The number <paramref name="x"/> raised to the power <paramref name="y"/>.</returns>
|
||||
public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y);
|
||||
public static float Pow(float x, float y) => MathF.Pow(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two specified values.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the values, which must implement <see cref="IFloatingPoint{T}"/>.</typeparam>
|
||||
/// <param name="x">The starting value of the interpolation.</param>
|
||||
/// <param name="y">The ending value of the interpolation.</param>
|
||||
/// <param name="t">The interpolation factor, typically in the range [0, 1].</param>
|
||||
/// <returns>A value that represents the linear interpolation between <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Lerp<T>(T x, T y, T t) where T : IFloatingPoint<T> => x + (y - x) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Rounds a number to a specified number of fractional digits.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to round.</param>
|
||||
/// <param name="digits">The number of fractional digits in the return value.</param>
|
||||
/// <param name="mode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
|
||||
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
|
||||
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to square.</param>
|
||||
/// <returns>The square of <paramref name="x"/>.</returns>
|
||||
public static T Sqr<T>(T x) where T : INumber<T> => x * x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square root of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The square root of <paramref name="x"/>.</returns>
|
||||
public static float Sqrt(float x) => MathF.Sqrt(x);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the integral part of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The integral part of <paramref name="x"/>.</returns>
|
||||
public static float Truncate(float x) => MathF.Truncate(x);
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class MathExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Math.Abs{T}(T)" />
|
||||
public static T Abs<T>(this T x) where T : INumber<T> => Math.Abs(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Cos(float)" />
|
||||
public static float Cos(this float x) => Math.Cos(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Sin(float)" />
|
||||
public static float Sin(this float x) => Math.Sin(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Acos(float)" />
|
||||
public static float Acos(this float x) => Math.Acos(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Asin(float)" />
|
||||
public static float Asin(this float x) => Math.Asin(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Atan2(float, float)" />
|
||||
public static float Atan2(this float y, float x) => Math.Atan2(y, x);
|
||||
|
||||
/// <inheritdoc cref="Math.Atanh(float)" />
|
||||
public static float Atanh(this float x) => Math.Atanh(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Clamp{T}(T, T, T)" />
|
||||
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => Math.Clamp(x, min, max);
|
||||
|
||||
/// <inheritdoc cref="Math.Ceiling(float)" />
|
||||
public static float Ceiling(this float x) => Math.Ceiling(x);
|
||||
|
||||
/// <inheritdoc cref="Math.CopySign(float, float)" />
|
||||
public static float CopySign(this float x, float y) => Math.CopySign(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Floor(float)" />
|
||||
public static float Floor(this float x) => Math.Floor(x);
|
||||
|
||||
/// <inheritdoc cref="Math.IEEERemainder(float, float)" />
|
||||
public static float IEEERemainder(this float x, float y) => Math.IEEERemainder(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Log(float, float)" />
|
||||
public static float Log(this float x, float y) => Math.Log(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Max{T}(T, T)" />
|
||||
public static T Max<T>(this T x, T y) where T : INumber<T> => Math.Max(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.AbsMax{T}(T, T)" />
|
||||
public static T AbsMax<T>(this T x, T y) where T : INumber<T> => Math.AbsMax(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Min{T}(T, T)" />
|
||||
public static T Min<T>(this T x, T y) where T : INumber<T> => Math.Min(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.AbsMin{T}(T, T)" />
|
||||
public static T AbsMin<T>(this T x, T y) where T : INumber<T> => Math.AbsMin(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Pow(float, float)" />
|
||||
public static float Pow(this float x, float y) => Math.Pow(x, y);
|
||||
|
||||
/// <inheritdoc cref="Math.Lerp{T}(T, T, T)" />
|
||||
public static T Lerp<T>(this T x, T y, T t) where T : IFloatingPoint<T> => Math.Lerp(x, y, t);
|
||||
|
||||
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
|
||||
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
|
||||
|
||||
/// <inheritdoc cref="Math.Sqr{T}(T)" />
|
||||
public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Sqrt(float)" />
|
||||
public static float Sqrt(this float x) => Math.Sqrt(x);
|
||||
|
||||
/// <inheritdoc cref="Math.Truncate(float)" />
|
||||
public static float Truncate(this float x) => Math.Truncate(x);
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
||||
/// </summary>
|
||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB"/>.</param>
|
||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="AABB"/> struct with the specified lower and upper boundaries.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||
public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
{
|
||||
/// <summary>
|
||||
/// The lower boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D LowerBoundary = lowerBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// The upper boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D UpperBoundary = upperBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the center point of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets half the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D SizeHalf => Size * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
||||
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
||||
|
||||
foreach (Vector2D vector in vectors)
|
||||
{
|
||||
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
||||
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter < 2)
|
||||
throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||
|
||||
return new(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="AABB"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
||||
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <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, float epsilon = float.Epsilon)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="AABB"/> struct.
|
||||
/// </summary>
|
||||
public static class AABBExtensions
|
||||
{
|
||||
/// <inheritdoc cref="AABB.ToAABB" />
|
||||
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
||||
|
||||
/// <inheritdoc cref="AABB.ApproximatelyEquals" />
|
||||
public static bool ApproximatelyEquals(this AABB left, AABB right, float epsilon = float.Epsilon) => AABB.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D circle.
|
||||
/// </summary>
|
||||
/// <param name="center">The center of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <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)
|
||||
{
|
||||
/// <summary>
|
||||
/// The center of the circle.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center = center;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Radius = radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float RadiusSquared => Radius * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diameter of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Diameter => 2f * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// A predefined unit <see cref="Circle"/> with a center at the origin and a radius of 1.
|
||||
/// </summary>
|
||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius);
|
||||
|
||||
/// <summary>
|
||||
/// Displaces the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static 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="ITransform2D"/>.
|
||||
/// </summary>
|
||||
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>
|
||||
/// <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>
|
||||
/// Provides extension methods for the <see cref="Circle"/> struct.
|
||||
/// </summary>
|
||||
public static class CircleExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Circle.SetCenter(Circle, Vector2D)" />
|
||||
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
|
||||
|
||||
/// <inheritdoc cref="Circle.SetRadius(Circle, float)" />
|
||||
public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius);
|
||||
|
||||
/// <inheritdoc cref="Circle.Displace(Circle, Vector2D)" />
|
||||
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||
|
||||
/// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
|
||||
public static Projection1D ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||
|
||||
/// <inheritdoc cref="Circle.TransformCircle(ITransform2D, Circle)" />
|
||||
public static Circle TransformCircle(this ITransform2D transform, Circle circle) => Circle.TransformCircle(transform, circle);
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
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 <see cref="Line2D"/> struct with the specified endpoints.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
||||
public readonly struct Line2D(Vector2D from, Vector2D to)
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting point of the <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D From = from;
|
||||
|
||||
/// <summary>
|
||||
/// The ending point of the <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D To = to;
|
||||
|
||||
/// <summary>
|
||||
/// The reversed <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public readonly Line2D Reversed => new(To, From);
|
||||
|
||||
/// <summary>
|
||||
/// 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="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public readonly float Length => From.FromTo(To).Length();
|
||||
|
||||
/// <summary>
|
||||
/// 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="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
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 Line2DEquation(slope, yOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.
|
||||
/// </summary>
|
||||
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="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public static float GetT(Line2D line, Vector2D point)
|
||||
{
|
||||
float fromX = MathF.Abs(line.From.X);
|
||||
float toX = MathF.Abs(line.To.X);
|
||||
float pointX = MathF.Abs(point.X);
|
||||
|
||||
float min = MathF.Min(fromX, toX);
|
||||
float max = MathF.Max(fromX, toX) - min;
|
||||
|
||||
pointX -= min;
|
||||
|
||||
float t = pointX / max;
|
||||
|
||||
// FIXME
|
||||
// I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line
|
||||
// Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason
|
||||
// too unmotivated to find a solution. Future me, find a better way if possible, please.
|
||||
if (!Lerp(line, t).ApproximatelyEquals(point))
|
||||
return 1f - t;
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Line2D"/> segment intersects with another <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
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);
|
||||
int o3 = Vector2D.Orientation(right.From, right.To, left.From);
|
||||
int o4 = Vector2D.Orientation(right.From, right.To, left.To);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
return true;
|
||||
|
||||
if (o1 == 0 && OnSegment(left, right.From)) return true;
|
||||
if (o2 == 0 && OnSegment(left, right.To)) return true;
|
||||
if (o3 == 0 && OnSegment(right, left.From)) return true;
|
||||
if (o4 == 0 && OnSegment(right, left.To)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the point lies within the <see cref="Line2D"/> segment.
|
||||
/// </summary>
|
||||
public static bool OnSegment(Line2D line, Vector2D point)
|
||||
{
|
||||
if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) &&
|
||||
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="Line2D"/> segments intersect.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point)
|
||||
{
|
||||
point = null;
|
||||
|
||||
bool result = Intersects(left, right);
|
||||
|
||||
if (result)
|
||||
point = IntersectionPoint(left, right);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the point of intersection between two <see cref="Line2D"/> segments.
|
||||
/// </summary>
|
||||
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="Line2D"/> segment.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
// Lines are parallel
|
||||
if (denominator == 0)
|
||||
return float.NaN;
|
||||
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between the two endpoints of the <see cref="Line2D"/> segment using parameter 't'.
|
||||
/// </summary>
|
||||
public static Vector2D Lerp(Line2D line, float t)
|
||||
=> Vector2D.Lerp(line.From, line.To, t);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point on the <see cref="Line2D"/> segment to the specified point.
|
||||
/// </summary>
|
||||
public static Vector2D ClosestPointTo(Line2D line, Vector2D point)
|
||||
{
|
||||
Vector2D edgeVector = line.From.FromTo(line.To);
|
||||
Vector2D pointVector = point - line.From;
|
||||
|
||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
||||
|
||||
t = MathF.Max(0, MathF.Min(1, t));
|
||||
|
||||
float closestX = line.From.X + t * edgeVector.X;
|
||||
float closestY = line.From.Y + t * edgeVector.Y;
|
||||
|
||||
return new Vector2D((float)closestX, (float)closestY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Line2D"/> segments are approximately equal.
|
||||
/// </summary>
|
||||
/// <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 <see cref="Line2D"/> struct.
|
||||
/// </summary>
|
||||
public static class Line2DExtensions
|
||||
{
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D)" />
|
||||
public static bool Intersects(this Line2D left, Line2D right) => Line2D.Intersects(left, right);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range of values along a single axis.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum value of the projection.</param>
|
||||
/// <param name="max">The maximum value of the projection.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||
public readonly struct Projection1D(float min, float max)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the minimum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Min = min;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Max = max;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection1D left, Projection1D right) => Overlaps(left, right, out float _);
|
||||
|
||||
/// <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(Projection1D left, Projection1D right, out float depth)
|
||||
{
|
||||
// TODO Try to improve this
|
||||
bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
|
||||
if (rightMinInLeft)
|
||||
{
|
||||
depth = left.Max - right.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rightMaxInLeft = right.Max < left.Max && right.Max > left.Min;
|
||||
if (rightMaxInLeft)
|
||||
{
|
||||
depth = left.Min - right.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMinInRight = left.Min > right.Min && left.Min < right.Max;
|
||||
if (leftMinInRight)
|
||||
{
|
||||
depth = right.Max - left.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMaxInRight = left.Max < right.Max && left.Max > right.Min;
|
||||
if (leftMaxInRight)
|
||||
{
|
||||
depth = right.Min - left.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
depth = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Projection1D"/> struct.
|
||||
/// </summary>
|
||||
public static class Projection1DExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D)" />
|
||||
public static bool Overlaps(this Projection1D left, Projection1D right) => Projection1D.Overlaps(left, right);
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,359 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
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="Shape2D"/> struct with the specified vertices.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||
public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
||||
{
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertices of the shape.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Vector2D> Vertices => _verticesList;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the vertex to get or set.</param>
|
||||
/// <returns>The vertex at the specified index.</returns>
|
||||
public Vector2D this[System.Index index] => Vertices[index];
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the current shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to copy.</param>
|
||||
/// <returns>A copy of the input shape.</returns>
|
||||
public static 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 Shape2D CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regular polygon (ngon) with the specified number of vertices and a rotation position.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">The number of vertices in the polygon.</param>
|
||||
/// <param name="positionToRotate">The position to use for rotation.</param>
|
||||
/// <returns>A regular polygon with the specified number of vertices and rotation position.</returns>
|
||||
public static Shape2D CreateNgon(int vertexCount, Vector2D positionToRotate)
|
||||
{
|
||||
if (vertexCount < 3)
|
||||
throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2.");
|
||||
|
||||
List<Vector2D> vertices = new(vertexCount);
|
||||
|
||||
float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian;
|
||||
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices.Add(positionToRotate.Rotate(i * radiansPerVertex));
|
||||
|
||||
return new(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the super triangle that encloses the given shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the given shape.</returns>
|
||||
public static Triangle GetSuperTriangle(Shape2D shape)
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
float maxX = float.MinValue, maxY = float.MinValue;
|
||||
|
||||
foreach (Vector2D point in shape.Vertices)
|
||||
{
|
||||
minX = Math.Min(minX, point.X);
|
||||
minY = Math.Min(minY, point.Y);
|
||||
maxX = Math.Max(maxX, point.X);
|
||||
maxY = Math.Max(maxY, point.Y);
|
||||
}
|
||||
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
float deltaMax = Math.Max(dx, dy);
|
||||
float midX = (minX + maxX) / 2;
|
||||
float midY = (minY + maxY) / 2;
|
||||
|
||||
Vector2D p1 = new((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
Vector2D p2 = new((float)midX, (float)midY + 20 * (float)deltaMax);
|
||||
Vector2D p3 = new((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
|
||||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <param name="lines">The list to populate with lines.</param>
|
||||
public static void GetLines(Shape2D shape, IList<Line2D> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
for (int i = 0; i < shape.Vertices.Count - 1; i++)
|
||||
lines.Add(new(shape.Vertices[i], shape.Vertices[i + 1]));
|
||||
lines.Add(new(shape.Vertices[^1], shape.Vertices[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <returns>A list of lines that form the edges of the shape.</returns>
|
||||
public static List<Line2D> GetLines(Shape2D shape)
|
||||
{
|
||||
List<Line2D> lines = new(shape.Vertices.Count - 1);
|
||||
GetLines(shape, lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <param name="list">The list to populate with projected values.</param>
|
||||
public static void Project(Shape2D shape, Vector2D projectionVector, IList<float> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
int count = shape.Vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
list.Add(projectionVector.Dot(shape[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the shape onto the vector.</returns>
|
||||
public static Projection1D Project(Shape2D shape, Vector2D projectionVector)
|
||||
{
|
||||
float min = float.MaxValue;
|
||||
float max = float.MinValue;
|
||||
|
||||
for (int i = 0; i < shape.Vertices.Count; i++)
|
||||
{
|
||||
float projectedLength = projectionVector.Dot(shape.Vertices[i]);
|
||||
min = Math.Min(projectedLength, min);
|
||||
max = Math.Max(projectedLength, max);
|
||||
}
|
||||
|
||||
return new(min, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <returns>The transformed shape.</returns>
|
||||
public static Shape2D TransformShape(Shape2D shape, ITransform2D transform)
|
||||
{
|
||||
List<Vector2D> vertices = new(shape.Vertices.Count);
|
||||
|
||||
int count = shape.Vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
vertices.Add(transform.TransformVector2D(shape[i]));
|
||||
|
||||
return new Shape2D(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="from">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="to">The transformed shape.</param>
|
||||
public static void TransformShape(Shape2D from, ITransform2D transform, ref Shape2D to)
|
||||
{
|
||||
to._verticesList.Clear();
|
||||
|
||||
int count = from._verticesList.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
to._verticesList.Add(transform.TransformVector2D(from[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two shapes are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first shape to compare.</param>
|
||||
/// <param name="right">The second shape to compare.</param>
|
||||
/// <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(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], epsilon))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Shape2D"/> struct.
|
||||
/// </summary>
|
||||
public static class Shape2DExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Shape2D.CreateCopy(Shape2D)" />
|
||||
public static Shape2D CreateCopy(this Shape2D shape) => Shape2D.CreateCopy(shape);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.GetSuperTriangle(Shape2D)" />
|
||||
public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.GetLines(Shape2D, IList{Line2D})" />
|
||||
public static void ToLines(this Shape2D shape, IList<Line2D> lines) => Shape2D.GetLines(shape, lines);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.GetLines(Shape2D)" />
|
||||
public static List<Line2D> ToLines(this Shape2D shape) => Shape2D.GetLines(shape);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.Project(Shape2D, Vector2D)" />
|
||||
public static Projection1D ToProjection(this Shape2D shape, Vector2D projectionVector) => Shape2D.Project(shape, projectionVector);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.TransformShape(Shape2D, ITransform2D)" />
|
||||
public static Shape2D TransformShape(this ITransform2D transform, Shape2D shape) => Shape2D.TransformShape(shape, transform);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,384 +0,0 @@
|
||||
using System;
|
||||
|
||||
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)
|
||||
{
|
||||
/// <summary>
|
||||
/// The X coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float X = x;
|
||||
|
||||
/// <summary>
|
||||
/// The Y coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float Y = y;
|
||||
|
||||
/// <summary>
|
||||
/// The magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float Magnitude => Length(this);
|
||||
|
||||
/// <summary>
|
||||
/// The squared magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
||||
/// </summary>
|
||||
public Vector2D Normalized => Normalize(this);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing upwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Up = new(0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing downwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Down = new(0f, -1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing leftwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Left = new(-1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing rightwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Right = new(1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the zero <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Zero = new(0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the <see cref="Vector2D"/> with both components equal to 1.
|
||||
/// </summary>
|
||||
public readonly static Vector2D One = new(1f, 1f);
|
||||
|
||||
public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
|
||||
public static Vector2D operator +(Vector2D left, Vector2D right) => new(left.X + right.X, left.Y + right.Y);
|
||||
public static Vector2D operator -(Vector2D left, Vector2D right) => new(left.X - right.X, left.Y - right.Y);
|
||||
public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float Length(Vector2D vector) => Math.Sqrt(LengthSquared(vector));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The start <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The end <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the direction of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector2D"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="Vector2D"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="Vector2D"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="Vector2D"/> from the first.</returns>
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Divide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the absolute value of each component of the vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> with each component's absolute value.</returns>
|
||||
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector2D"/> (creates a unit <see cref="Vector2D"/> with the same direction).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to normalize.</param>
|
||||
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The incident <see cref="Vector2D"/>.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector2D"/> of the surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Vector2D"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> from the starting point to the ending point.</returns>
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
|
||||
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
|
||||
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(Math.Cos(angleInRadian) * vector.X - Math.Sin(angleInRadian) * vector.Y, Math.Sin(angleInRadian) * vector.X + Math.Cos(angleInRadian) * vector.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
|
||||
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
|
||||
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector2D"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="Vector2D"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float Angle(Vector2D left, Vector2D right) => Math.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the orientation of three points represented by <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="middle">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The third <see cref="Vector2D"/>.</param>
|
||||
/// <returns>
|
||||
/// <para>0 - Collinear.</para>
|
||||
/// <para>1 - Clockwise.</para>
|
||||
/// <para>2 - Counterclockwise.</para>
|
||||
/// </returns>
|
||||
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
|
||||
{
|
||||
Vector2D leftToMiddle = left.FromTo(middle);
|
||||
Vector2D middleToRight = middle.FromTo(right);
|
||||
|
||||
float value = leftToMiddle.Y * middleToRight.X -
|
||||
leftToMiddle.X * middleToRight.Y;
|
||||
|
||||
if (value > 0) return 1;
|
||||
if (value < 0) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Vector2D"/>s are approximately equal within a specified epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="Vector2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Vector2D"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
||||
public override int GetHashCode() => HashCode.Combine(X, Y);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,366 +0,0 @@
|
||||
using System;
|
||||
|
||||
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)
|
||||
{
|
||||
/// <summary>
|
||||
/// The X coordinate of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public readonly float X = x;
|
||||
|
||||
/// <summary>
|
||||
/// The Y coordinate of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public readonly float Y = y;
|
||||
|
||||
/// <summary>
|
||||
/// The Z coordinate of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public readonly float Z = z;
|
||||
|
||||
/// <summary>
|
||||
/// The magnitude (length) of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public float Magnitude => Length(this);
|
||||
|
||||
/// <summary>
|
||||
/// The squared magnitude (length) of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized form of the <see cref="Vector3D"/> (a <see cref="Vector3D"/> with the same direction and a magnitude of 1).
|
||||
/// </summary>
|
||||
public Vector3D Normalized => Normalize(this);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing upwards.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Up = new(0f, 1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing downwards.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Down = new(0f, -1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing leftwards.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Left = new(-1f, 0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing rightwards.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Right = new(1f, 0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing forwards.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Forward = new(0f, 0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector3D"/> pointing backwards.
|
||||
public readonly static Vector3D Backward = new(0f, 0f, -1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the zero <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
public readonly static Vector3D Zero = new(0f, 0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the <see cref="Vector3D"/> with both components equal to 1.
|
||||
/// </summary>
|
||||
public readonly static Vector3D One = new(1f, 1f, 1f);
|
||||
|
||||
public static Vector3D operator -(Vector3D vector) => new(0f - vector.X, 0f - vector.Y, 0f - vector.Z);
|
||||
public static Vector3D operator +(Vector3D left, Vector3D right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||
public static Vector3D operator -(Vector3D left, Vector3D right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
public static Vector3D operator *(Vector3D vector, float value) => new(vector.X * value, vector.Y * value, vector.Z * value);
|
||||
public static Vector3D operator *(float value, Vector3D vector) => new(vector.X * value, vector.Y * value, vector.Z * value);
|
||||
public static Vector3D operator /(Vector3D vector, float value) => new(vector.X / value, vector.Y / value, vector.Z / value);
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector3D"/>.</returns>
|
||||
public static float Length(Vector3D vector) => Math.Sqrt(LengthSquared(vector));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector3D"/>.</returns>
|
||||
public static float LengthSquared(Vector3D vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance 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>
|
||||
/// <returns>The distance between the two <see cref="Vector3D"/>s.</returns>
|
||||
public static float Distance(Vector3D from, Vector3D to) => Length(FromTo(from, to));
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the direction of the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector3D"/>.</returns>
|
||||
public static Vector3D Invert(Vector3D vector) => -vector;
|
||||
|
||||
/// <summary>
|
||||
/// Adds 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 sum of the two <see cref="Vector3D"/>s.</returns>
|
||||
public static Vector3D Add(Vector3D left, Vector3D right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector3D"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="Vector3D"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="Vector3D"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="Vector3D"/> from the first.</returns>
|
||||
public static Vector3D Subtract(Vector3D left, Vector3D right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector3D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="Vector3D"/> by the scalar value.</returns>
|
||||
public static Vector3D Multiply(Vector3D vector, float value) => vector * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector3D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="Vector3D"/> by the scalar value.</returns>
|
||||
public static Vector3D Divide(Vector3D vector, float value) => vector / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the absolute value of each component of the vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/>.</param>
|
||||
/// <returns>The <see cref="Vector3D"/> with each component's absolute value.</returns>
|
||||
public static Vector3D Abs(Vector3D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z));
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector3D"/> (creates a unit <see cref="Vector3D"/> with the same direction).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector3D"/> to normalize.</param>
|
||||
/// <returns>The normalized <see cref="Vector3D"/>.</returns>
|
||||
public static Vector3D Normalize(Vector3D vector) => vector / Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector3D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The incident <see cref="Vector3D"/>.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector3D"/> of the surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector3D"/>.</returns>
|
||||
public static Vector3D Reflect(Vector3D vector, Vector3D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Vector3D"/> 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"/> from the starting point to the ending point.</returns>
|
||||
public static Vector3D FromTo(Vector3D from, Vector3D to) => to - from;
|
||||
|
||||
/// <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(Vector3D vector, Vector3D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector3D"/> around a normal 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(Vector3D vector, Vector3D normal, float angleInRadian) => vector * Math.Cos(angleInRadian) + Cross(normal, vector) * Math.Sin(angleInRadian) + normal * Dot(normal, vector) * (1f - Math.Cos(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(Vector3D left, Vector3D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y, (left.Z < right.Z) ? left.Z : right.Z);
|
||||
|
||||
/// <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(Vector3D left, Vector3D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y, (left.Z > right.Z) ? left.Z : right.Z);
|
||||
|
||||
/// <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>A <see cref="Vector3D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector3D"/>s.</returns>
|
||||
public static Vector3D Clamp(Vector3D vector, Vector3D min, Vector3D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y), Math.Clamp(vector.Z, min.Z, max.Z));
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="Vector3D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector3D"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="Vector3D"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="Vector3D"/>.</returns>
|
||||
public static Vector3D Lerp(Vector3D from, Vector3D to, float t) => from + FromTo(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(Vector3D left, Vector3D right) => new(left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle 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 Angle(Vector3D left, Vector3D right) => Math.Acos(Dot(left, right) / (Length(left) * Length(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(Vector3D left, Vector3D right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Vector3D"/>s are approximately equal within a specified 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 epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="Vector3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon)
|
||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Vector3D"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="Vector3D"/>.</returns>
|
||||
public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is Vector3D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="Vector3D"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
|
||||
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class StateEnable : IStateEnable
|
||||
{
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
public event IHasEntity.EntityAssignedEventHandler? OnEntityAssigned = null;
|
||||
public event IStateEnable.EnabledChangedEventHandler? OnEnabledChanged = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableEntity>? OnEntityAssigned { get; set; } = null;
|
||||
public Action<IStateEnable>? OnEnabledChanged { get; set; } = null;
|
||||
|
||||
private bool _enabled = true;
|
||||
private IEntity _entity = null!;
|
||||
@ -21,20 +23,17 @@ public class StateEnable : IStateEnable
|
||||
if (value == _enabled)
|
||||
return;
|
||||
|
||||
bool previousState = _enabled;
|
||||
_enabled = value;
|
||||
OnEnabledChanged?.Invoke(this, previousState);
|
||||
OnEnabledChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnAssign(IEntity entity) { }
|
||||
public bool Assign(IEntity entity)
|
||||
{
|
||||
if (_entity is not null && _entity.IsInitialized)
|
||||
if (_entity is not null && _entity.Initialized)
|
||||
return false;
|
||||
|
||||
_entity = entity;
|
||||
OnAssign(entity);
|
||||
OnEntityAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
13
Engine.Core/Time.cs
Normal file
13
Engine.Core/Time.cs
Normal file
@ -0,0 +1,13 @@
|
||||
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 void SetTime(EngineTime engineTime) => _engineTime = engineTime;
|
||||
}
|
56
Engine.Core/Transform.cs
Normal file
56
Engine.Core/Transform.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
||||
public class Transform : ITransform
|
||||
{
|
||||
public Action<ITransform>? OnPositionChanged { get; set; } = null;
|
||||
public Action<ITransform>? OnScaleChanged { get; set; } = null;
|
||||
public Action<ITransform>? OnRotationChanged { get; set; } = null;
|
||||
|
||||
private Vector2D _position = Vector2D.Zero;
|
||||
private Vector2D _scale = Vector2D.One;
|
||||
private float _rotation = 0f;
|
||||
|
||||
public Vector2D Position
|
||||
{
|
||||
get => _position;
|
||||
set
|
||||
{
|
||||
if (value == _position)
|
||||
return;
|
||||
|
||||
_position = value;
|
||||
OnPositionChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2D Scale
|
||||
{
|
||||
get => _scale;
|
||||
set
|
||||
{
|
||||
if (value == _scale)
|
||||
return;
|
||||
|
||||
_scale = value;
|
||||
OnScaleChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
if (value == _rotation)
|
||||
return;
|
||||
|
||||
_rotation = value;
|
||||
OnRotationChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
83
Engine.Core/Vector2D.cs
Normal file
83
Engine.Core/Vector2D.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||
public readonly struct Vector2D(float X, float Y)
|
||||
{
|
||||
public readonly float X { get; init; } = X;
|
||||
public readonly float Y { get; init; } = Y;
|
||||
|
||||
public readonly float Magnitude => Length(this);
|
||||
public readonly float MagnitudeSquared => LengthSquared(this);
|
||||
public readonly Vector2D Normalized => Normalize(this);
|
||||
|
||||
public readonly static Vector2D Up = new(0f, 1f);
|
||||
public readonly static Vector2D Down = new(0f, -1f);
|
||||
public readonly static Vector2D Left = new(-1f, 0f);
|
||||
public readonly static Vector2D Right = new(1f, 0f);
|
||||
public readonly static Vector2D Zero = new(0f, 0f);
|
||||
public readonly static Vector2D One = new(1f, 1f);
|
||||
|
||||
public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
|
||||
public static Vector2D operator +(Vector2D left, Vector2D right) => new(left.X + right.X, left.Y + right.Y);
|
||||
public static Vector2D operator -(Vector2D left, Vector2D right) => new(left.X - right.X, left.Y - right.Y);
|
||||
public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
|
||||
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 float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||
|
||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||
|
||||
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
public static Vector2D Subdivide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||
public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
|
||||
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the Orientation of 3 <see cref="Vector2D"/>s
|
||||
/// </summary>
|
||||
/// <returns>0 -> Collinear, 1 -> Clockwise, 2 -> Counterclockwise</returns>
|
||||
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
|
||||
{
|
||||
Vector2D leftToMiddle = left.FromTo(middle);
|
||||
Vector2D middleToRight = middle.FromTo(right);
|
||||
|
||||
float value = leftToMiddle.Y * middleToRight.X -
|
||||
leftToMiddle.X * middleToRight.Y;
|
||||
|
||||
if (value > 0) return 1;
|
||||
if (value < 0) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||
|
||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||
|
||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
||||
public override int GetHashCode() => HashCode.Combine(X, Y);
|
||||
}
|
484
Engine.Input/.gitignore
vendored
Normal file
484
Engine.Input/.gitignore
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
## 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
|
14
Engine.Input/Abstract/IButtonInputs.cs
Normal file
14
Engine.Input/Abstract/IButtonInputs.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Input;
|
||||
|
||||
public interface IButtonInputs<T> : IAssignableStateEnable
|
||||
{
|
||||
void RegisterOnPress(T button, Action<IButtonInputs<T>, T> callback);
|
||||
void UnregisterOnPress(T button, Action<IButtonInputs<T>, T> callback);
|
||||
void RegisterOnRelease(T button, Action<IButtonInputs<T>, T> callback);
|
||||
void UnregisterOnRelease(T button, Action<IButtonInputs<T>, T> callback);
|
||||
|
||||
bool IsPressed(T button);
|
||||
bool WasPressed(T button);
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<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>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,19 +1,9 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with the shape of a <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public interface ICircleCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The local <see cref="Circle"/> shape of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Circle CircleLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The world space representation of the <see cref="Circle"/> shape.
|
||||
/// </summary>
|
||||
Circle CircleWorld { get; }
|
||||
}
|
||||
|
@ -1,50 +1,18 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collider.
|
||||
/// </summary>
|
||||
public interface ICollider2D : IBehaviour
|
||||
public interface ICollider2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is detected.
|
||||
/// </summary>
|
||||
event CollisionDetectedEventHandler? OnCollisionDetected;
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is resolved.
|
||||
/// </summary>
|
||||
event CollisionResolvedEventHandler? OnCollisionResolved;
|
||||
Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
event TriggeredEventHandler? OnTriggered;
|
||||
|
||||
/// <inheritdoc cref="ITransform2D" />
|
||||
ITransform2D Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRigidBody2D"/> associated with the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
IRigidBody2D? RigidBody2D { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="ICollider2D"/> is a trigger.
|
||||
/// </summary>
|
||||
bool IsTrigger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates <see cref="ICollider2D"/> properties.
|
||||
/// </summary>
|
||||
void Recalculate();
|
||||
|
||||
void Detect(CollisionDetectionInformation collisionDetectionInformation);
|
||||
void Resolve(CollisionDetectionInformation collisionDetectionInformation);
|
||||
void Trigger(ICollider2D initiator);
|
||||
|
||||
delegate void CollisionDetectedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
|
||||
delegate void CollisionResolvedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
|
||||
delegate void TriggeredEventHandler(ICollider2D sender, ICollider2D initiatorCollider);
|
||||
}
|
||||
|
@ -2,19 +2,7 @@ using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision detector.
|
||||
/// </summary>
|
||||
public interface ICollisionDetector2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to detect a collision between two <see cref="ICollider2D"/>s.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of the first <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <param name="left">The first <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
/// <returns><see cref="true"/> if a collision is detected, otherwise <see cref="false"/>.</returns>
|
||||
bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
|
||||
}
|
||||
|
@ -1,13 +1,6 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision resolver.
|
||||
/// </summary>
|
||||
public interface ICollisionResolver2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves collisions based on collision detection information provided.
|
||||
/// </summary>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
void Resolve(CollisionDetectionInformation collisionInformation);
|
||||
}
|
||||
|
@ -1,31 +1,11 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics engine.
|
||||
/// </summary>
|
||||
public interface IPhysicsEngine2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a single physics iteration.
|
||||
/// </summary>
|
||||
event PhysicsIterationEventHandler? OnPhysicsIteration;
|
||||
int IterationCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a full physics step/>.
|
||||
/// </summary>
|
||||
event PhysicsStepEventHandler? OnPhysicsStep;
|
||||
void AddRigidBody(IRigidBody2D rigidBody);
|
||||
void RemoveRigidBody(IRigidBody2D rigidBody);
|
||||
|
||||
/// <summary>
|
||||
/// The number of iterations the <see cref="IPhysicsEngine2D"/> performs per step.
|
||||
/// </summary>
|
||||
int IterationPerStep { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Advances the physics simulation by the specified time.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The time step.</param>
|
||||
void Step(float deltaTime);
|
||||
|
||||
delegate void PhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
|
||||
delegate void PhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
|
||||
}
|
||||
|
@ -1,17 +1,7 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics object's responsive attributes.
|
||||
/// </summary>
|
||||
public interface IPhysicsMaterial2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The friction coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Friction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The restitution (bounciness) coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Restitution { get; }
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="IBehaviour"/> that listens to physics simulation update phase.
|
||||
/// </summary>
|
||||
public interface IPhysicsUpdate : IBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the physics state of the object based on the elapsed time since the last update.
|
||||
/// </summary>
|
||||
/// <param name="delta">The time elapsed since the last physics update, typically in seconds.</param>
|
||||
void PhysicsUpdate(float delta);
|
||||
}
|
@ -3,33 +3,13 @@ using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D rigid body in the engine.
|
||||
/// </summary>
|
||||
public interface IRigidBody2D : IBehaviour2D
|
||||
public interface IRigidBody2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// The physics material of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
IPhysicsMaterial2D Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The velocity of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
Vector2D Velocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The angular velocity (rotation rate) of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float AngularVelocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float Mass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IRigidBody2D"/> is static/immovable.
|
||||
/// </summary>
|
||||
bool IsStatic { get; set; }
|
||||
}
|
||||
|
@ -1,19 +1,9 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with a custom <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
public interface IShapeCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the local <see cref="Shape2D"/> of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Shape2D ShapeLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world space representation of the <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
Shape2D ShapeWorld { get; }
|
||||
Shape ShapeLocal { get; set; }
|
||||
Shape ShapeWorld { get; }
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
||||
public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
|
||||
{
|
||||
public event ICollider2D.CollisionDetectedEventHandler? OnCollisionDetected = null;
|
||||
public event ICollider2D.CollisionResolvedEventHandler? OnCollisionResolved = null;
|
||||
public event ICollider2D.TriggeredEventHandler? OnTriggered = null;
|
||||
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; } = null;
|
||||
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; } = null;
|
||||
public Action<ICollider2D, ICollider2D>? OnTriggered { get; set; } = null;
|
||||
|
||||
|
||||
protected bool NeedsRecalculation { get; private set; } = true;
|
||||
protected IRigidBody2D? _rigidBody2D = null;
|
||||
@ -16,6 +19,11 @@ public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
||||
public IRigidBody2D? RigidBody2D => _rigidBody2D;
|
||||
public bool IsTrigger { get; set; } = false;
|
||||
|
||||
ITransform IAssignableTransform.Transform => Transform;
|
||||
Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
|
||||
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
|
||||
public void Recalculate()
|
||||
{
|
||||
if (!NeedsRecalculation)
|
||||
@ -29,20 +37,14 @@ public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
BehaviourController.TryGetBehaviourInParent(out _rigidBody2D);
|
||||
BehaviourController.TryGetBehaviour(out _rigidBody2D);
|
||||
|
||||
BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
|
||||
|
||||
Transform.OnPositionChanged += SetNeedsRecalculationFromPosition;
|
||||
Transform.OnRotationChanged += SetNeedsRecalculationFromRotation;
|
||||
Transform.OnScaleChanged += SetNeedsRecalculationFromScale;
|
||||
HierarchyObject.OnParentChanged += UpdateRigidBody2D;
|
||||
}
|
||||
|
||||
private void UpdateRigidBody2D(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent)
|
||||
{
|
||||
BehaviourController.TryGetBehaviourInParent(out _rigidBody2D);
|
||||
Transform.OnPositionChanged += SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged += SetNeedsRecalculation;
|
||||
Transform.OnScaleChanged += SetNeedsRecalculation;
|
||||
}
|
||||
|
||||
private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour)
|
||||
@ -57,21 +59,15 @@ public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
|
||||
_rigidBody2D = null;
|
||||
}
|
||||
|
||||
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;
|
||||
private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true;
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController;
|
||||
Transform.OnScaleChanged -= SetNeedsRecalculationFromScale;
|
||||
Transform.OnScaleChanged -= SetNeedsRecalculation;
|
||||
|
||||
Transform.OnPositionChanged -= SetNeedsRecalculationFromPosition;
|
||||
Transform.OnRotationChanged -= SetNeedsRecalculationFromRotation;
|
||||
Transform.OnPositionChanged -= SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged -= SetNeedsRecalculation;
|
||||
}
|
||||
|
||||
public void Detect(CollisionDetectionInformation collisionDetectionInformation) => OnCollisionDetected?.Invoke(this, collisionDetectionInformation);
|
||||
public void Resolve(CollisionDetectionInformation collisionDetectionInformation) => OnCollisionResolved?.Invoke(this, collisionDetectionInformation);
|
||||
public void Trigger(ICollider2D initiator) => OnTriggered?.Invoke(this, initiator);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
@ -8,8 +8,10 @@ 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;
|
||||
}
|
||||
|
@ -1,17 +1,18 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D
|
||||
{
|
||||
public Shape2D ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
|
||||
public Shape2D ShapeLocal { get; set; } = Shape2D.Box;
|
||||
public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
|
||||
public Shape ShapeLocal { get; set; } = Shape.Box;
|
||||
|
||||
private Shape2D _shapeWorld = Shape2D.Box.CreateCopy();
|
||||
private Shape _shapeWorld = Shape.Box.CreateCopy();
|
||||
|
||||
public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld);
|
||||
|
||||
|
||||
public Collider2DShapeBehaviour() { }
|
||||
public Collider2DShapeBehaviour(Shape2D shape) => ShapeLocal = shape;
|
||||
public Collider2DShapeBehaviour(Shape shape) => ShapeLocal = shape;
|
||||
}
|
||||
|
@ -6,16 +6,14 @@ namespace Syntriax.Engine.Physics2D;
|
||||
[System.Diagnostics.DebuggerDisplay("Normal: {Normal.ToString(), nq}, Penetration: {Penetration}")]
|
||||
public readonly struct CollisionDetectionInformation
|
||||
(
|
||||
ICollider2D Detector,
|
||||
ICollider2D Detected,
|
||||
ICollider2D Left,
|
||||
ICollider2D Right,
|
||||
Vector2D Normal,
|
||||
float Penetration
|
||||
)
|
||||
{
|
||||
public ICollider2D Detector { get; init; } = Detector;
|
||||
public ICollider2D Detected { get; init; } = Detected;
|
||||
public ICollider2D Left { get; init; } = Left;
|
||||
public ICollider2D Right { get; init; } = Right;
|
||||
public Vector2D Normal { get; init; } = Normal;
|
||||
public float Penetration { get; init; } = Penetration;
|
||||
|
||||
public CollisionDetectionInformation Reverse() => new(Detected, Detector, -Normal, Penetration);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
@ -41,20 +42,20 @@ public class CollisionDetector2D : ICollisionDetector2D
|
||||
|
||||
private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
System.Collections.Generic.IReadOnlyList<Vector2D> vertices = left.ShapeWorld.Vertices;
|
||||
var 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;
|
||||
|
||||
Projection1D leftProjection = left.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection1D rightProjection = right.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector);
|
||||
|
||||
if (!leftProjection.Overlaps(rightProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Detector is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(left, right, projectionVector, depth);
|
||||
}
|
||||
|
||||
@ -65,33 +66,33 @@ public class CollisionDetector2D : ICollisionDetector2D
|
||||
{
|
||||
collisionInformation = default;
|
||||
|
||||
System.Collections.Generic.IReadOnlyList<Vector2D> vertices = shapeCollider.ShapeWorld.Vertices;
|
||||
var 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;
|
||||
|
||||
Projection1D shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection1D circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
|
||||
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
|
||||
|
||||
if (!shapeProjection.Overlaps(circleProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Detector is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth);
|
||||
}
|
||||
|
||||
{
|
||||
Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized;
|
||||
|
||||
Projection1D shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
Projection1D circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
|
||||
if (!shapeProjection.Overlaps(circleProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Detector is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth);
|
||||
}
|
||||
|
||||
@ -104,8 +105,8 @@ public class CollisionDetector2D : ICollisionDetector2D
|
||||
|
||||
Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
|
||||
|
||||
Projection1D leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
Projection1D rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
|
||||
bool collision = leftProjection.Overlaps(rightProjection, out float depth);
|
||||
|
||||
|
@ -9,8 +9,8 @@ public class CollisionResolver2D : ICollisionResolver2D
|
||||
{
|
||||
Vector2D displacementVector = collisionInformation.Normal * collisionInformation.Penetration;
|
||||
|
||||
ICollider2D left = collisionInformation.Detector;
|
||||
ICollider2D right = collisionInformation.Detected;
|
||||
ICollider2D left = collisionInformation.Left;
|
||||
ICollider2D right = collisionInformation.Right;
|
||||
|
||||
bool isLeftStatic = left.RigidBody2D?.IsStatic ?? true;
|
||||
bool isRightStatic = right.RigidBody2D?.IsStatic ?? true;
|
||||
@ -38,7 +38,7 @@ public class CollisionResolver2D : ICollisionResolver2D
|
||||
left.Recalculate();
|
||||
right.Recalculate();
|
||||
|
||||
left.Resolve(collisionInformation);
|
||||
right.Resolve(collisionInformation);
|
||||
left.OnCollisionResolved?.Invoke(collisionInformation.Left, collisionInformation);
|
||||
right.OnCollisionResolved?.Invoke(right, collisionInformation);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<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>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,32 +1,13 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Engine.Physics2D;
|
||||
|
||||
public static partial class Physics2D
|
||||
{
|
||||
public static bool Overlaps(this Shape2D shape, Vector2D point) => Overlaps(shape, point, out float _);
|
||||
public static bool Overlaps(this Shape2D shape, Vector2D point, out float depth)
|
||||
{
|
||||
depth = float.MaxValue;
|
||||
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;
|
||||
|
||||
Projection1D shapeProjection = shape.ToProjection(projectionVector);
|
||||
float projectedPoint = point.Dot(projectionVector);
|
||||
|
||||
if (shapeProjection.Max < projectedPoint || shapeProjection.Min > projectedPoint)
|
||||
return false;
|
||||
|
||||
depth = Math.Min(depth, Math.Abs(Math.AbsMin(shapeProjection.Max - projectedPoint, shapeProjection.Min - projectedPoint)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Overlaps(this Circle left, Circle right)
|
||||
{
|
||||
float distanceSquared = left.Center.FromTo(right.Center).LengthSquared();
|
||||
@ -46,7 +27,7 @@ public static partial class Physics2D
|
||||
normal = distanceVector.Normalized;
|
||||
|
||||
if (isOverlapping)
|
||||
depth = Math.Sqrt(radiusSumSquared - distanceSquared);
|
||||
depth = MathF.Sqrt(radiusSumSquared - distanceSquared);
|
||||
|
||||
return isOverlapping;
|
||||
}
|
||||
@ -63,7 +44,7 @@ public static partial class Physics2D
|
||||
normal = distanceVector.Normalized;
|
||||
|
||||
if (isOverlapping)
|
||||
depth = Math.Sqrt(radiusSquared - distanceSquared);
|
||||
depth = MathF.Sqrt(radiusSquared - distanceSquared);
|
||||
|
||||
return isOverlapping;
|
||||
}
|
||||
@ -89,5 +70,5 @@ public static partial class Physics2D
|
||||
return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f);
|
||||
}
|
||||
|
||||
public static bool LaysOn(this Vector2D point, Line2D line) => Line2D.Intersects(line, point);
|
||||
public static bool LaysOn(this Vector2D point, Line line) => Line.Intersects(line, point);
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsCoroutineManager : HierarchyObject
|
||||
{
|
||||
private readonly List<IEnumerator> enumerators = [];
|
||||
private IPhysicsEngine2D? physicsEngine = null;
|
||||
|
||||
public IEnumerator CreateCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
enumerators.Add(enumerator);
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
public void StopCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
enumerators.Remove(enumerator);
|
||||
}
|
||||
|
||||
protected override void OnEnteringHierarchy(IGameManager gameManager)
|
||||
{
|
||||
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
|
||||
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
|
||||
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;
|
||||
else
|
||||
gameManager.OnUpdate += OnUpdate;
|
||||
}
|
||||
|
||||
private void OnPhysicsStep(IPhysicsEngine2D sender, float stepDeltaTime)
|
||||
{
|
||||
for (int i = enumerators.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (enumerators[i].Current is ICoroutineYield coroutineYield && coroutineYield.Yield())
|
||||
continue;
|
||||
|
||||
if (!enumerators[i].MoveNext())
|
||||
enumerators.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExitingHierarchy(IGameManager gameManager)
|
||||
{
|
||||
if (physicsEngine is IPhysicsEngine2D existingPhysicsEngine)
|
||||
existingPhysicsEngine.OnPhysicsStep -= OnPhysicsStep;
|
||||
gameManager.OnUpdate -= OnUpdate;
|
||||
}
|
||||
|
||||
private void OnUpdate(IGameManager sender, EngineTime engineTime)
|
||||
{
|
||||
if (GameManager is not IGameManager gameManager)
|
||||
return;
|
||||
|
||||
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
|
||||
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
|
||||
{
|
||||
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;
|
||||
gameManager.OnUpdate -= OnUpdate;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
@ -7,18 +8,14 @@ namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
{
|
||||
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);
|
||||
|
||||
private int _iterationCount = 1;
|
||||
private ICollisionDetector2D collisionDetector = new CollisionDetector2D();
|
||||
private ICollisionResolver2D collisionResolver = new CollisionResolver2D();
|
||||
|
||||
private readonly ICollisionDetector2D collisionDetector = null!;
|
||||
private readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
|
||||
public void AddRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
@ -27,7 +24,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
|
||||
rigidBodies.Add(rigidBody);
|
||||
|
||||
foreach (ICollider2D collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
|
||||
foreach (var collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
|
||||
colliders.Add(collider2D);
|
||||
|
||||
rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
@ -41,16 +38,16 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
float intervalDeltaTime = deltaTime / IterationCount;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
for (int i = 0; i < rigidBodies.Count; i++)
|
||||
StepRigidBody(rigidBodies[i], intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
foreach (ICollider2D collider in colliders)
|
||||
foreach (var collider in colliders)
|
||||
collider.Recalculate();
|
||||
|
||||
// Can Parallel
|
||||
@ -74,7 +71,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
if (bothCollidersAreTriggers)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true);
|
||||
bool bothCollidersAreStatic = colliderX.RigidBody2D?.IsStatic ?? true && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic;
|
||||
if (bothCollidersAreStatic)
|
||||
continue;
|
||||
|
||||
@ -82,38 +79,28 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
{
|
||||
if (colliderX.IsTrigger)
|
||||
{
|
||||
colliderX.Trigger(colliderY);
|
||||
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
|
||||
continue;
|
||||
}
|
||||
else if (colliderY.IsTrigger)
|
||||
{
|
||||
colliderY.Trigger(colliderY);
|
||||
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (information.Detector == colliderX)
|
||||
{
|
||||
colliderX.Detect(information);
|
||||
colliderY.Detect(information.Reverse());
|
||||
}
|
||||
else
|
||||
{
|
||||
colliderX.Detect(information.Reverse());
|
||||
colliderY.Detect(information);
|
||||
}
|
||||
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
|
||||
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
|
||||
|
||||
collisionResolver?.Resolve(information);
|
||||
}
|
||||
}
|
||||
}
|
||||
OnPhysicsIteration?.Invoke(this, intervalDeltaTime);
|
||||
}
|
||||
OnPhysicsStep?.Invoke(this, deltaTime);
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
if (rigidBody.IsStatic)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
@ -135,16 +122,4 @@ public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
|
||||
colliders.Remove(collider2D);
|
||||
}
|
||||
|
||||
public PhysicsEngine2D()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
||||
|
@ -1,133 +0,0 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2DCollector : HierarchyObject, IPhysicsEngine2D
|
||||
{
|
||||
public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null;
|
||||
public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null;
|
||||
|
||||
private int _iterationPerStep = 1;
|
||||
|
||||
protected readonly ICollisionDetector2D collisionDetector = null!;
|
||||
protected readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
|
||||
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)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
foreach (IRigidBody2D rigidBody in rigidBodyCollector)
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
foreach (ICollider2D collider in colliderCollector)
|
||||
collider.Recalculate();
|
||||
|
||||
// Can Parallel
|
||||
for (int x = 0; x < colliderCollector.Behaviours.Count; x++)
|
||||
{
|
||||
ICollider2D? colliderX = colliderCollector.Behaviours[x];
|
||||
if (!colliderX.IsActive)
|
||||
return;
|
||||
|
||||
for (int y = x + 1; y < colliderCollector.Behaviours.Count; y++)
|
||||
{
|
||||
ICollider2D? colliderY = colliderCollector.Behaviours[y];
|
||||
|
||||
if (!colliderY.IsActive)
|
||||
return;
|
||||
|
||||
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
|
||||
if (bothCollidersAreTriggers)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true);
|
||||
if (bothCollidersAreStatic)
|
||||
continue;
|
||||
|
||||
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
|
||||
{
|
||||
if (colliderX.IsTrigger)
|
||||
{
|
||||
colliderX.Trigger(colliderY);
|
||||
continue;
|
||||
}
|
||||
else if (colliderY.IsTrigger)
|
||||
{
|
||||
colliderY.Trigger(colliderY);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (information.Detector == colliderX)
|
||||
{
|
||||
colliderX.Detect(information);
|
||||
colliderY.Detect(information.Reverse());
|
||||
}
|
||||
else
|
||||
{
|
||||
colliderX.Detect(information.Reverse());
|
||||
colliderY.Detect(information);
|
||||
}
|
||||
|
||||
collisionResolver?.Resolve(information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnPhysicsIteration?.Invoke(this, intervalDeltaTime);
|
||||
}
|
||||
|
||||
foreach (IPhysicsUpdate physicsUpdate in physicsUpdateCollector)
|
||||
physicsUpdate.PhysicsUpdate(deltaTime);
|
||||
|
||||
OnPhysicsStep?.Invoke(this, deltaTime);
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
|
||||
}
|
||||
protected override void OnEnteringHierarchy(IGameManager gameManager)
|
||||
{
|
||||
physicsUpdateCollector.Assign(gameManager);
|
||||
colliderCollector.Assign(gameManager);
|
||||
rigidBodyCollector.Assign(gameManager);
|
||||
}
|
||||
|
||||
protected override void OnExitingHierarchy(IGameManager gameManager)
|
||||
{
|
||||
physicsUpdateCollector.Unassign();
|
||||
colliderCollector.Unassign();
|
||||
rigidBodyCollector.Unassign();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCollector()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCollector(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
46
Engine.Physics2D/Primitives/AABB.cs
Normal file
46
Engine.Physics2D/Primitives/AABB.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||
public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
|
||||
{
|
||||
public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary;
|
||||
public readonly Vector2D UpperBoundary { get; init; } = UpperBoundary;
|
||||
|
||||
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||
public readonly Vector2D SizeHalf => Size * .5f;
|
||||
|
||||
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
||||
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
||||
|
||||
foreach (Vector2D vector in vectors)
|
||||
{
|
||||
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
||||
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter < 2)
|
||||
throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||
|
||||
return new(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
public static bool ApproximatelyEquals(AABB left, AABB right)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
|
||||
}
|
||||
|
||||
public static class AABBExtensions
|
||||
{
|
||||
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
||||
|
||||
public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user