Compare commits
196 Commits
ef21cdf213
...
net8
Author | SHA1 | Date | |
---|---|---|---|
0215f8ef1f | |||
0bf38234c6 | |||
ed6969c16a | |||
b0b421151f | |||
41c5def097 | |||
fbbdfb07fa | |||
bf283d804c | |||
063ea08707 | |||
fd11a94ddf | |||
be2295b92d | |||
a93e55619c | |||
48ae24af47 | |||
1366a417f1 | |||
4bfe98852c | |||
98edbe1af5 | |||
3725a3b0fd | |||
f43ab36742 | |||
c7aafd85bc | |||
5de08b8fe4 | |||
16e4077d40 | |||
fc3c1ed1f9 | |||
b100b5c2fe | |||
5e28ba8814 | |||
4c235e3230 | |||
131203d578 | |||
bd5eb432b7 | |||
d2ca85568f | |||
4c41870732 | |||
f77afa3632 | |||
eb61598489 | |||
efe51b491d | |||
fa3a4d1e0d | |||
6e7a0993f5 | |||
d70bee2c6b | |||
5812f43117 | |||
d102c5471d | |||
fb363970fc | |||
791349686b | |||
3a0942ff46 | |||
b002dd469a | |||
f92f36442c | |||
bb934b59f3 | |||
c704173183 | |||
c3876add1e | |||
35a75d993b | |||
2637f99456 | |||
9581f5aa54 | |||
82cc25a9ef | |||
336e7e16e7 | |||
a3a8fb4e84 | |||
35f6c3850e | |||
f51d5f342e | |||
9c129cefe2 | |||
a254bb721b | |||
5fa7420c04 | |||
5bcc256777 | |||
680d718957 | |||
20bc6a1adb | |||
eb454a471c | |||
c205e710bc | |||
cddb30c631 | |||
29f6c83bf0 | |||
c20f210b29 | |||
1ea1844677 | |||
5b2c13f8bf | |||
c39ee44442 | |||
4623b4861a | |||
0a868b82e5 | |||
d92d16cfad | |||
0184d1758c | |||
6e5b805803 | |||
8293c58f9f | |||
94d01521d4 | |||
5c1c025fe3 | |||
1d292a104e | |||
70c884acfe | |||
a9f5974568 | |||
dae72b11c5 | |||
58eb373c79 | |||
00f7b1aaab | |||
9e4c74ed1d | |||
2e2306c5bb | |||
86b8cd9b55 | |||
bfbcfdce4f | |||
80202d4a07 | |||
2be99d2142 | |||
4081693d32 | |||
193d23c877 | |||
c135035d5b | |||
fabc485689 | |||
48710b0a7f | |||
bf34e52dc8 | |||
e3845a2f5c | |||
26a80452bc | |||
2535a1d6ec | |||
3a385900fb | |||
b94bbc8ed7 | |||
3f7a646bf0 | |||
f119a23d2b | |||
61488aa0e5 | |||
66b46e3d36 | |||
1ee07b41f8 | |||
6f425776cc | |||
98c9dde98a | |||
04d325f38b | |||
901585d4bb | |||
33a452a62e | |||
906edf096e | |||
d1129c95df | |||
40f483974d | |||
4b856420f9 | |||
7f93d95f6b | |||
5756b96002 | |||
c71bf71fb3 | |||
417ddca972 | |||
d4c6288b38 | |||
21600b856f | |||
803b670433 | |||
067bc51487 | |||
9f3e39e337 | |||
cd2cd89eae | |||
90c1dd9348 | |||
e8ef41af41 | |||
d1a289885b | |||
6170de4a73 | |||
e2fdf1f538 | |||
7a1dd7eb1a | |||
b73c9ed0ae | |||
82705ba378 | |||
f9785462b0 | |||
5c3e0f6581 | |||
b9ee1ec232 | |||
4ec1a32db2 | |||
d825bb55d3 | |||
5fc8c012b3 | |||
95ddba0230 | |||
30caa202dc | |||
b2a286b5e5 | |||
5c542039ed | |||
25043bbcde | |||
183966d239 | |||
9ecf0b900f | |||
62b43025b9 | |||
9af44d48b3 | |||
d71c135491 | |||
e73c076243 | |||
7743ccadbf | |||
d0ab442f7f | |||
cdfe655ac4 | |||
b4659def55 | |||
58a9ada345 | |||
eb5345dc77 | |||
4416f64287 | |||
1b3f40be5f | |||
e725a4e89c | |||
981db0190f | |||
c0c48c7d51 | |||
cc4068fa2e | |||
4d59dcb9ab | |||
1b3b999a95 | |||
1545291942 | |||
81625abd25 | |||
ea94bed00d | |||
12cb144688 | |||
85f0555c59 | |||
55ed8b84f6 | |||
4856800f5f | |||
cc44e1ea69 | |||
cb60c71184 | |||
eb445604e8 | |||
8eb34a0a6d | |||
9f522bdb66 | |||
d7f0b76485 | |||
90370a2b43 | |||
0f3f1594d0 | |||
24b50eba12 | |||
6bc9043a86 | |||
43f1749b04 | |||
62e50aefc1 | |||
9c2b098821 | |||
91aa26e15a | |||
cf8a5de580 | |||
1f8fa78b76 | |||
fdc38fc800 | |||
fc34a60f30 | |||
eca23c5b89 | |||
f08f721f52 | |||
fb402acc30 | |||
923b25e26e | |||
2bcd1c5a89 | |||
40d1ce7c68 | |||
e7c80871fe | |||
c51eda49bf | |||
0f8a7db567 | |||
ffa0128813 | |||
15984bcc06 |
225
.editorconfig
Normal file
225
.editorconfig
Normal file
@@ -0,0 +1,225 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Don't use tabs for indentation.
|
||||
[*]
|
||||
indent_style = space
|
||||
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
||||
spelling_exclusion_path = SpellingExclusions.dic
|
||||
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
charset = utf-8-bom
|
||||
|
||||
# XML project files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
|
||||
# XML config files
|
||||
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
|
||||
indent_size = 2
|
||||
|
||||
# JSON files
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
# Powershell files
|
||||
[*.ps1]
|
||||
indent_size = 2
|
||||
|
||||
# Shell script files
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
|
||||
# Dotnet code style settings:
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_separate_import_directive_groups = false
|
||||
# Avoid "this." and "Me." if not necessary
|
||||
dotnet_style_qualification_for_field = false:refactoring
|
||||
dotnet_style_qualification_for_property = false:refactoring
|
||||
dotnet_style_qualification_for_method = false:refactoring
|
||||
dotnet_style_qualification_for_event = false:refactoring
|
||||
|
||||
# Use language keywords instead of framework type names for type references
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# Suggest more modern language features when available
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:none
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
|
||||
# Whitespace options
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false:error
|
||||
|
||||
# IDE0055: Fix formatting
|
||||
# Workaround for https://github.com/dotnet/roslyn/issues/70570
|
||||
dotnet_diagnostic.IDE0055.severity = suggestion
|
||||
|
||||
# https://github.com/dotnet/roslyn-analyzers/issues/7436 - False positives from valid GetDeclaredSymbol calls
|
||||
dotnet_diagnostic.RS1039.severity = none
|
||||
|
||||
# CSharp code style settings:
|
||||
|
||||
# IDE0029: Use coalesce expression
|
||||
dotnet_diagnostic.IDE0029.severity = none
|
||||
|
||||
# IDE0040: Add accessibility modifiers
|
||||
dotnet_diagnostic.IDE0040.severity = warning
|
||||
|
||||
[*.cs]
|
||||
# Newline settings
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
|
||||
# Whitespace options
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
|
||||
# Prefer "var" everywhere
|
||||
csharp_style_var_for_built_in_types = false:warning
|
||||
csharp_style_var_when_type_is_apparent = false:warning
|
||||
csharp_style_var_elsewhere = false:warning
|
||||
|
||||
# Prefer method-like constructs to have a block body
|
||||
csharp_style_expression_bodied_methods = false:none
|
||||
csharp_style_expression_bodied_constructors = false:none
|
||||
csharp_style_expression_bodied_operators = false:none
|
||||
|
||||
# Prefer property-like constructs to have an expression-body
|
||||
csharp_style_expression_bodied_properties = true:none
|
||||
csharp_style_expression_bodied_indexers = true:none
|
||||
csharp_style_expression_bodied_accessors = true:none
|
||||
|
||||
# Suggest more modern language features when available
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Blocks are allowed
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
# IDE0060: Remove unused parameter
|
||||
dotnet_diagnostic.IDE0060.severity = warning
|
||||
|
||||
[src/{Compilers,ExpressionEvaluator,Scripting}/**Test**/*.{cs,vb}]
|
||||
|
||||
# IDE0060: Remove unused parameter
|
||||
dotnet_diagnostic.IDE0060.severity = none
|
||||
|
||||
[src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures,VisualStudio}/**/*.{cs,vb}]
|
||||
|
||||
# IDE0011: Add braces
|
||||
csharp_prefer_braces = false:warning
|
||||
# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201
|
||||
dotnet_diagnostic.IDE0011.severity = warning
|
||||
|
||||
# IDE0040: Add accessibility modifiers
|
||||
dotnet_diagnostic.IDE0040.severity = warning
|
||||
|
||||
# IDE0052: Remove unread private member
|
||||
dotnet_diagnostic.IDE0052.severity = warning
|
||||
|
||||
# IDE0059: Unnecessary assignment to a value
|
||||
dotnet_diagnostic.IDE0059.severity = warning
|
||||
|
||||
# CA1012: Abstract types should not have public constructors
|
||||
dotnet_diagnostic.CA1012.severity = warning
|
||||
|
||||
# CA1822: Make member static
|
||||
dotnet_diagnostic.CA1822.severity = warning
|
||||
|
||||
# Prefer "var" everywhere
|
||||
dotnet_diagnostic.IDE0007.severity = warning
|
||||
csharp_style_var_for_built_in_types = false:warning
|
||||
csharp_style_var_when_type_is_apparent = false:warning
|
||||
csharp_style_var_elsewhere = false:warning
|
||||
|
||||
# csharp_style_allow_embedded_statements_on_same_line_experimental
|
||||
dotnet_diagnostic.IDE2001.severity = warning
|
||||
|
||||
# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
|
||||
dotnet_diagnostic.IDE2004.severity = warning
|
||||
|
||||
# csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental
|
||||
dotnet_diagnostic.IDE2005.severity = warning
|
||||
|
||||
# csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental
|
||||
dotnet_diagnostic.IDE2006.severity = warning
|
||||
|
||||
[src/{VisualStudio}/**/*.{cs,vb}]
|
||||
# CA1822: Make member static
|
||||
# There is a risk of accidentally breaking an internal API that partners rely on though IVT.
|
||||
dotnet_code_quality.CA1822.api_surface = private
|
||||
|
||||
[**/{ExternalAccess}/**/*.{cs,vb}]
|
||||
|
||||
# RS0016: Only enable if API files are present
|
||||
dotnet_public_api_analyzer.require_api_files = true
|
||||
|
||||
dotnet_diagnostic.RS0051.severity = error
|
||||
dotnet_diagnostic.RS0052.severity = error
|
||||
dotnet_diagnostic.RS0053.severity = error
|
||||
dotnet_diagnostic.RS0054.severity = error
|
||||
dotnet_diagnostic.RS0055.severity = error
|
||||
dotnet_diagnostic.RS0056.severity = error
|
||||
dotnet_diagnostic.RS0057.severity = error
|
||||
dotnet_diagnostic.RS0058.severity = error
|
||||
dotnet_diagnostic.RS0059.severity = error
|
||||
dotnet_diagnostic.RS0060.severity = error
|
||||
dotnet_diagnostic.RS0061.severity = error
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "Engine.Serializers/YamlDotNet"]
|
||||
path = Engine.Serializers/YamlDotNet
|
||||
url = git@github.com:Syntriax/YamlDotNet.git
|
2
Engine.Core/.gitignore
vendored
2
Engine.Core/.gitignore
vendored
@@ -482,3 +482,5 @@ $RECYCLE.BIN/
|
||||
|
||||
# Vim temporary swap files
|
||||
*.swp
|
||||
|
||||
!Debug
|
||||
|
@@ -1,4 +1,4 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.
|
||||
@@ -8,7 +8,7 @@ public interface IAssignable
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// </summary>
|
||||
event OnUnassignedDelegate? OnUnassigned;
|
||||
event UnassignEventHandler? OnUnassigned;
|
||||
|
||||
/// <summary>
|
||||
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
|
||||
@@ -18,5 +18,5 @@ public interface IAssignable
|
||||
/// </returns>
|
||||
bool Unassign();
|
||||
|
||||
delegate void OnUnassignedDelegate(IAssignable sender);
|
||||
delegate void UnassignEventHandler(IAssignable sender);
|
||||
}
|
||||
|
@@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableGameManager : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnGameManagerAssignedDelegate? 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 OnGameManagerAssignedDelegate(IAssignableGameManager sender);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IGameObject"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableGameObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnGameObjectAssignedDelegate? OnGameObjectAssigned;
|
||||
|
||||
/// <inheritdoc cref="IGameObject" />
|
||||
IGameObject GameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IGameObject"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">New <see cref="IGameObject"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IGameObject gameObject);
|
||||
|
||||
delegate void OnGameObjectAssignedDelegate(IAssignableGameObject sender);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableTransform : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnTransformAssignedDelegate? OnTransformAssigned;
|
||||
|
||||
/// <inheritdoc cref="ITransform" />
|
||||
ITransform Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="ITransform"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="transform">New <see cref="ITransform"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(ITransform transform);
|
||||
|
||||
delegate void OnTransformAssignedDelegate(IAssignableTransform sender);
|
||||
}
|
@@ -1,14 +1,14 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableBehaviourController : IAssignable
|
||||
public interface IHasBehaviourController : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned;
|
||||
event BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
|
||||
|
||||
/// <inheritdoc cref="IBehaviourController" />
|
||||
IBehaviourController BehaviourController { get; }
|
||||
@@ -22,5 +22,5 @@ public interface IAssignableBehaviourController : IAssignable
|
||||
/// </returns>
|
||||
bool Assign(IBehaviourController behaviourController);
|
||||
|
||||
delegate void OnBehaviourControllerAssignedDelegate(IAssignableBehaviourController sender);
|
||||
delegate void BehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
|
||||
}
|
@@ -1,14 +1,14 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableEntity : IAssignable
|
||||
public interface IHasEntity : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnEntityAssignedDelegate? OnEntityAssigned;
|
||||
event EntityAssignedEventHandler? OnEntityAssigned;
|
||||
|
||||
/// <inheritdoc cref="IEntity" />
|
||||
IEntity Entity { get; }
|
||||
@@ -22,5 +22,5 @@ public interface IAssignableEntity : IAssignable
|
||||
/// </returns>
|
||||
bool Assign(IEntity entity);
|
||||
|
||||
delegate void OnEntityAssignedDelegate(IAssignableEntity sender);
|
||||
delegate void EntityAssignedEventHandler(IHasEntity sender);
|
||||
}
|
@@ -1,14 +1,14 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableStateEnable : IAssignable
|
||||
public interface IHasStateEnable : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event OnStateEnableAssignedDelegate? OnStateEnableAssigned;
|
||||
event StateEnableAssignedEventHandler? OnStateEnableAssigned;
|
||||
|
||||
/// <inheritdoc cref="IStateEnable" />
|
||||
IStateEnable StateEnable { get; }
|
||||
@@ -22,5 +22,5 @@ public interface IAssignableStateEnable : IAssignable
|
||||
/// </returns>
|
||||
bool Assign(IStateEnable stateEnable);
|
||||
|
||||
delegate void OnStateEnableAssignedDelegate(IAssignableStateEnable sender);
|
||||
delegate void StateEnableAssignedEventHandler(IHasStateEnable sender);
|
||||
}
|
26
Engine.Core/Abstract/Assignable/IHasUniverse.cs
Normal file
26
Engine.Core/Abstract/Assignable/IHasUniverse.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
|
||||
/// </summary>
|
||||
public interface IHasUniverse : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IUniverse"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event UniverseAssignedEventHandler? OnUniverseAssigned;
|
||||
|
||||
/// <inheritdoc cref="IUniverse" />
|
||||
IUniverse Universe { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IUniverse"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="universe">New <see cref="IUniverse"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IUniverse universe);
|
||||
|
||||
delegate void UniverseAssignedEventHandler(IHasUniverse sender);
|
||||
}
|
26
Engine.Core/Abstract/Assignable/IHasUniverseObject.cs
Normal file
26
Engine.Core/Abstract/Assignable/IHasUniverseObject.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
|
||||
/// </summary>
|
||||
public interface IHasUniverseObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IUniverseObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
event UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned;
|
||||
|
||||
/// <inheritdoc cref="IUniverseObject" />
|
||||
IUniverseObject UniverseObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IUniverseObject"/> field of this object.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">New <see cref="IUniverseObject"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(IUniverseObject universeObject);
|
||||
|
||||
delegate void UniverseObjectAssignedEventHandler(IHasUniverseObject sender);
|
||||
}
|
19
Engine.Core/Abstract/IActive.cs
Normal file
19
Engine.Core/Abstract/IActive.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <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,24 +1,19 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
|
||||
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.
|
||||
/// </summary>
|
||||
public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignableStateEnable, IInitialize
|
||||
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
||||
/// </summary>
|
||||
event OnPriorityChangedDelegate? OnPriorityChanged;
|
||||
event PriorityChangedEventHandler? OnPriorityChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IBehaviour"/> is active.
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
|
||||
delegate void OnPriorityChangedDelegate(IBehaviour sender, int previousPriority);
|
||||
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
|
||||
}
|
||||
|
6
Engine.Core/Abstract/IBehaviour2D.cs
Normal file
6
Engine.Core/Abstract/IBehaviour2D.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IBehaviour2D : IBehaviour
|
||||
{
|
||||
ITransform2D Transform { get; }
|
||||
}
|
35
Engine.Core/Abstract/IBehaviourCollector.cs
Normal file
35
Engine.Core/Abstract/IBehaviourCollector.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <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="IUniverse"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of objects tracked by the collector.</typeparam>
|
||||
public interface IBehaviourCollector<T> : IHasUniverse, 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,37 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBehaviour>
|
||||
public interface IBehaviourController : IEntity, IHasUniverseObject, IEnumerable<IBehaviour>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered before the update of <see cref="IBehaviour"/>s.
|
||||
/// </summary>
|
||||
event OnPreUpdateDelegate? OnPreUpdate;
|
||||
event PreUpdateEventHandler? OnPreUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered during the update of <see cref="IBehaviour"/>s.
|
||||
/// </summary>
|
||||
event OnUpdateDelegate? OnUpdate;
|
||||
event UpdateEventHandler? OnUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered before the drawing phase.
|
||||
/// </summary>
|
||||
event OnPreDrawDelegate? OnPreDraw;
|
||||
event PreDrawEventHandler? OnPreDraw;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
event OnBehaviourAddedDelegate? OnBehaviourAdded;
|
||||
event BehaviourAddedEventHandler? OnBehaviourAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
event OnBehaviourRemovedDelegate? OnBehaviourRemoved;
|
||||
event BehaviourRemovedEventHandler? OnBehaviourRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
||||
@@ -56,27 +55,19 @@ public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBeha
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, <see cref="null"/>.</returns>
|
||||
T? GetBehaviour<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <param name="behaviour">When this method returns, contains the <see cref="IBehaviour"/> of the specified type, if found; otherwise, see.</param>
|
||||
/// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found; otherwise, <see cref="false"/>.</returns>
|
||||
bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <returns>A list of <see cref="IBehaviour"/>s of the specified type.</returns>
|
||||
IList<T> GetBehaviours<T>();
|
||||
IReadOnlyList<T> GetBehaviours<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type and stores them in the provided list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <param name="behaviours">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||
void GetBehaviours<T>(List<T> behaviours);
|
||||
/// <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"/>.
|
||||
@@ -102,10 +93,10 @@ public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBeha
|
||||
/// </summary>
|
||||
void UpdatePreDraw();
|
||||
|
||||
delegate void OnPreUpdateDelegate(IBehaviourController sender);
|
||||
delegate void OnUpdateDelegate(IBehaviourController sender);
|
||||
delegate void OnPreDrawDelegate(IBehaviourController sender);
|
||||
delegate void OnBehaviourAddedDelegate(IBehaviourController sender, IBehaviour behaviourAdded);
|
||||
delegate void OnBehaviourRemovedDelegate(IBehaviourController sender, IBehaviour behaviourRemoved);
|
||||
delegate void PreUpdateEventHandler(IBehaviourController sender);
|
||||
delegate void UpdateEventHandler(IBehaviourController sender);
|
||||
delegate void PreDrawEventHandler(IBehaviourController sender);
|
||||
delegate void BehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
|
||||
delegate void BehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D camera in the engine.
|
||||
/// </summary>
|
||||
public interface ICamera2D : IBehaviour, IAssignableTransform
|
||||
public interface ICamera2D : IBehaviour2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The zoom level of the camera.
|
||||
|
6
Engine.Core/Abstract/ICoroutineYield.cs
Normal file
6
Engine.Core/Abstract/ICoroutineYield.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface ICoroutineYield
|
||||
{
|
||||
bool Yield();
|
||||
}
|
@@ -1,20 +1,20 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic entity in the engine.
|
||||
/// </summary>
|
||||
public interface IEntity : IInitialize, IAssignableStateEnable
|
||||
public interface IEntity : IInitializable, IHasStateEnable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
||||
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
event OnIdChangedDelegate? OnIdChanged;
|
||||
event IdChangedEventHandler? OnIdChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
|
||||
delegate void OnIdChangedDelegate(IEntity sender, string previousId);
|
||||
delegate void IdChangedEventHandler(IEntity sender, string previousId);
|
||||
}
|
||||
|
@@ -1,59 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a game world responsible for managing <see cref="IGameObject"/>s.
|
||||
/// </summary>
|
||||
public interface IGameManager : IEntity, IEnumerable<IGameObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IGameObject"/> is registered to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event OnGameObjectRegisteredDelegate? OnGameObjectRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IGameObject"/> is unregistered from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
event OnGameObjectUnRegisteredDelegate? OnGameObjectUnRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of <see cref="IGameObject"/>s managed by the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IGameObject> GameObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IGameObject"/> to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The <see cref="IGameObject"/> to register.</param>
|
||||
void RegisterGameObject(IGameObject gameObject);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="IGameObject"/> of type T with the given arguments and registers it to the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IGameObject"/> to instantiate.</typeparam>
|
||||
/// <param name="args">Constructor parameters for the given type of <see cref="IGameObject"/>.</param>
|
||||
/// <returns>The instantiated <see cref="IGameObject"/>.</returns>
|
||||
T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="IGameObject"/> from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The <see cref="IGameObject"/> to remove.</param>
|
||||
/// <returns>The removed <see cref="IGameObject"/>.</returns>
|
||||
IGameObject RemoveGameObject(IGameObject gameObject);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="IGameManager"/> with the given engine time data.
|
||||
/// </summary>
|
||||
/// <param name="time">The engine time.</param>
|
||||
void Update(EngineTime time);
|
||||
|
||||
/// <summary>
|
||||
/// Performs operations that should be done before the draw calls.
|
||||
/// </summary>
|
||||
void PreDraw();
|
||||
|
||||
delegate void OnGameObjectRegisteredDelegate(IGameManager sender, IGameObject gameObjectRegistered);
|
||||
delegate void OnGameObjectUnRegisteredDelegate(IGameManager sender, IGameObject gameObjectUnregistered);
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a game object with various properties and functionalities.
|
||||
/// </summary>
|
||||
public interface IGameObject : IEntity, IAssignableGameManager, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Update"/> method is called.
|
||||
/// </summary>
|
||||
event OnUpdatedDelegate? OnUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the game object.
|
||||
/// </summary>
|
||||
void Update();
|
||||
|
||||
delegate void OnUpdatedDelegate(IGameObject sender);
|
||||
}
|
@@ -1,24 +1,24 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
||||
/// </summary>
|
||||
public interface IInitialize
|
||||
public interface IInitializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
||||
/// </summary>
|
||||
event OnInitializedDelegate? OnInitialized;
|
||||
event InitializedEventHandler? OnInitialized;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Finalize"/> method is called successfully.
|
||||
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
|
||||
/// </summary>
|
||||
event OnFinalizedDelegate? OnFinalized;
|
||||
event FinalizedEventHandler? OnFinalized;
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the entity has been initialized.
|
||||
/// </summary>
|
||||
bool Initialized { get; }
|
||||
bool IsInitialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the entity.
|
||||
@@ -32,6 +32,6 @@ public interface IInitialize
|
||||
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Finalize();
|
||||
|
||||
delegate void OnInitializedDelegate(IInitialize sender);
|
||||
delegate void OnFinalizedDelegate(IInitialize sender);
|
||||
delegate void InitializedEventHandler(IInitializable sender);
|
||||
delegate void FinalizedEventHandler(IInitializable sender);
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with a name.
|
||||
@@ -8,12 +8,12 @@ public interface INameable
|
||||
/// <summary>
|
||||
/// Event triggered when the name of the entity changes.
|
||||
/// </summary>
|
||||
event OnNameChangedDelegate? OnNameChanged;
|
||||
event NameChangedEventHandler? OnNameChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the entity.
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
delegate void OnNameChangedDelegate(INameable sender, string previousName);
|
||||
delegate void NameChangedEventHandler(INameable sender, string previousName);
|
||||
}
|
||||
|
@@ -1,19 +1,19 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with an enable state that can be toggled.
|
||||
/// </summary>
|
||||
public interface IStateEnable : IAssignableEntity
|
||||
public interface IStateEnable : IHasEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
||||
/// </summary>
|
||||
event OnNameChangedDelegate? OnEnabledChanged;
|
||||
event EnabledChangedEventHandler? OnEnabledChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
||||
/// </summary>
|
||||
bool Enabled { get; set; }
|
||||
|
||||
delegate void OnNameChangedDelegate(IStateEnable sender, bool previousState);
|
||||
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
|
||||
}
|
||||
|
@@ -1,105 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the transformation properties of an object such as position, scale, and rotation.
|
||||
/// </summary>
|
||||
public interface ITransform : IAssignableGameObject, IEnumerable<ITransform>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
event OnPositionChangedDelegate? OnPositionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
event OnScaleChangedDelegate? OnScaleChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
event OnRotationChangedDelegate? OnRotationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Parent"/> of the <see cref="ITransform"/> changes. The second parameter is the old <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
event OnParentChangedDelegate? OnParentChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a new <see cref="ITransform"/> is added to the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event OnChildrenAddedDelegate? OnChildrenAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when an <see cref="ITransform"/> is removed from the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event OnChildrenRemovedDelegate? OnChildrenRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// The world position of the <see cref="ITransform"/> in 2D space.
|
||||
/// </summary>
|
||||
Vector2D Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The world scale of the <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
Vector2D Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The world rotation of the <see cref="ITransform"/> in degrees.
|
||||
/// </summary>
|
||||
float Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The local position of the <see cref="ITransform"/> in 2D space.
|
||||
/// </summary>
|
||||
Vector2D LocalPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The local scale of the <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
Vector2D LocalScale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The local rotation of the <see cref="ITransform"/> in degrees.
|
||||
/// </summary>
|
||||
float LocalRotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="ITransform"/> of the <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
ITransform? Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ITransform"/>s that have this <see cref="ITransform"/> as their <see cref="Parent"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<ITransform> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent <see cref="ITransform"/> of this <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
/// <param name="transform">The parent <see cref="ITransform"/> to set.</param>
|
||||
void SetParent(ITransform? transform);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a child <see cref="ITransform"/> to this <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
/// <param name="transform">The child <see cref="ITransform"/> to add.</param>
|
||||
void AddChild(ITransform transform);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a child <see cref="ITransform"/> from this <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
/// <param name="transform">The child <see cref="ITransform"/> to remove.</param>
|
||||
void RemoveChild(ITransform transform);
|
||||
|
||||
delegate void OnPositionChangedDelegate(ITransform sender);
|
||||
delegate void OnScaleChangedDelegate(ITransform sender);
|
||||
delegate void OnRotationChangedDelegate(ITransform sender);
|
||||
delegate void OnParentChangedDelegate(ITransform sender, ITransform? newParent);
|
||||
delegate void OnChildrenAddedDelegate(ITransform sender, ITransform childrenAdded);
|
||||
delegate void OnChildrenRemovedDelegate(ITransform sender, ITransform childrenRemoved);
|
||||
}
|
73
Engine.Core/Abstract/ITransform2D.cs
Normal file
73
Engine.Core/Abstract/ITransform2D.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <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);
|
||||
}
|
98
Engine.Core/Abstract/IUniverse.cs
Normal file
98
Engine.Core/Abstract/IUniverse.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
|
||||
/// </summary>
|
||||
public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="Update(UniverseTime)"/> is about to be called called on the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event UpdateEventHandler? OnPreUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="Update(UniverseTime)"/> is called on the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event UpdateEventHandler? OnUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event PreDrawEventHandler? OnPreDraw;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IUniverseObject"/> is registered to the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IUniverseObject"/> is unregistered from the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="TimeScale"/> is changed on the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
event TimeScaleChangedEventHandler? OnTimeScaleChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Current time scale the <see cref="IUniverse"/> operates on.
|
||||
/// </summary>
|
||||
float TimeScale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains time data related to this <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
UniverseTime Time { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains unscaled time data related to this <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
UniverseTime UnscaledTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of <see cref="IUniverseObject"/>s managed by the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IUniverseObject> UniverseObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IUniverseObject"/> to the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">The <see cref="IUniverseObject"/> to register.</param>
|
||||
void Register(IUniverseObject universeObject);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="IUniverseObject"/> of type T with the given arguments and registers it to the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IUniverseObject"/> to instantiate.</typeparam>
|
||||
/// <param name="args">Constructor parameters for the given type of <see cref="IUniverseObject"/>.</param>
|
||||
/// <returns>The instantiated <see cref="IUniverseObject"/>.</returns>
|
||||
T InstantiateUniverseObject<T>(params object?[]? args) where T : class, IUniverseObject;
|
||||
|
||||
/// <summary>
|
||||
/// Removes an <see cref="IUniverseObject"/> from the <see cref="IUniverse"/>.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">The <see cref="IUniverseObject"/> to remove.</param>
|
||||
void Remove(IUniverseObject universeObject);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="IUniverse"/> with the given delta time.
|
||||
/// </summary>
|
||||
/// <param name="universeTime">Delta time.</param>
|
||||
void Update(UniverseTime universeTime);
|
||||
|
||||
/// <summary>
|
||||
/// Performs operations that should be done before the draw calls.
|
||||
/// </summary>
|
||||
void PreDraw();
|
||||
|
||||
delegate void TimeScaleChangedEventHandler(IUniverse sender, float previousTimeScale);
|
||||
|
||||
delegate void UpdateEventHandler(IUniverse sender, UniverseTime engineTime);
|
||||
delegate void PreDrawEventHandler(IUniverse sender);
|
||||
|
||||
delegate void UniverseObjectRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectRegistered);
|
||||
delegate void UniverseObjectUnRegisteredEventHandler(IUniverse sender, IUniverseObject universeObjectUnregistered);
|
||||
}
|
131
Engine.Core/Abstract/IUniverseObject.cs
Normal file
131
Engine.Core/Abstract/IUniverseObject.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
|
||||
/// This interface allows for tracking the object's presence in the universe and provides events
|
||||
/// for notifying when the see enters or exits the universe.
|
||||
/// </summary>
|
||||
public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IUniverseObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IUniverseObject"/> enters the universe.
|
||||
/// </summary>
|
||||
event EnteredUniverseEventHandler? OnEnteredUniverse;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IUniverseObject"/> exits the universe.
|
||||
/// </summary>
|
||||
event ExitedUniverseEventHandler? OnExitedUniverse;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Parent"/> of the <see cref="IUniverseObject"/> changes. The second parameter is the old <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
event ParentChangedEventHandler? OnParentChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a new <see cref="IUniverseObject"/> is added to the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event ChildrenAddedEventHandler? OnChildrenAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when an <see cref="IUniverseObject"/> is removed from the <see cref="Children"/>.
|
||||
/// </summary>
|
||||
event ChildrenRemovedEventHandler? OnChildrenRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IUniverse"/> this <see cref="IUniverseObject"/> is connected to, if any.
|
||||
/// </summary>
|
||||
IUniverse Universe { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the <see cref="IUniverseObject"/> is currently in the universe.
|
||||
/// </summary>
|
||||
bool IsInUniverse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="IUniverseObject"/> of the <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
IUniverseObject? Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IUniverseObject"/>s that have this <see cref="IUniverseObject"/> as their <see cref="Parent"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IUniverseObject> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle entering the universe.
|
||||
/// This should be called by the system to properly manage universe states.
|
||||
/// </summary>
|
||||
/// <param name="universe">The <see cref="IUniverse"/> that is managing this universe.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully entered the universe;
|
||||
/// <see cref="false"/> if it failed to do so.
|
||||
/// </returns>
|
||||
internal bool EnterUniverse(IUniverse universe);
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle exiting the universe.
|
||||
/// This should be called by the system to properly manage universe states.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see cref="true"/> if the <see cref="IUniverseObject"/> successfully exited the universe;
|
||||
/// <see cref="false"/> if it failed to do so.
|
||||
/// </returns>
|
||||
internal bool ExitUniverse();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the parent <see cref="IUniverseObject"/> of this <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">The parent <see cref="IUniverseObject"/> to set.</param>
|
||||
void SetParent(IUniverseObject? universeObject);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a child <see cref="IUniverseObject"/> to this <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to add.</param>
|
||||
void AddChild(IUniverseObject universeObject);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a child <see cref="IUniverseObject"/> from this <see cref="IUniverseObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="universeObject">The child <see cref="IUniverseObject"/> to remove.</param>
|
||||
void RemoveChild(IUniverseObject universeObject);
|
||||
|
||||
/// <summary>
|
||||
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> enters the universe of a <see cref="IUniverse">.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IUniverseObject"/> that entered the universe.</param>
|
||||
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has entered it's universe.</param>
|
||||
delegate void EnteredUniverseEventHandler(IUniverseObject sender, IUniverse universe);
|
||||
|
||||
/// <summary>
|
||||
/// EventHandler delegate for the event triggered when the <see cref="IUniverseObject"/> exits the universe of a <see cref="IUniverse">.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IUniverseObject"/> that exited the universe.</param>
|
||||
/// <param name="universe">The <see cref="IUniverse"/> that the <see cref="IUniverseObject"/> has exited it's universe.</param>
|
||||
delegate void ExitedUniverseEventHandler(IUniverseObject sender, IUniverse universe);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when the <see cref="IUniverseObject"/>'s parent changes.
|
||||
/// </summary>
|
||||
/// <param name="sender">The <see cref="IUniverseObject"/> that the parent has changed.</param>
|
||||
/// <param name="previousParent">The previous <see cref="IUniverseObject"/> the sender was a child of.</param>
|
||||
/// <param name="newParent">The new and current <see cref="IUniverseObject"/> the sender is a child of.</param>
|
||||
delegate void ParentChangedEventHandler(IUniverseObject sender, IUniverseObject? previousParent, IUniverseObject? newParent);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> added as a child.
|
||||
/// </summary>
|
||||
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
|
||||
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
|
||||
delegate void ChildrenAddedEventHandler(IUniverseObject sender, IUniverseObject childrenAdded);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the event triggered when a new <see cref="IUniverseObject"/> removed from being a child.
|
||||
/// </summary>
|
||||
/// <param name="sender">The parent <see cref="IUniverseObject"/> this event is being called from.</param>
|
||||
/// <param name="childrenAdded">The <see cref="IUniverseObject"/> that got removed as a children of the sender <see cref="IUniverseObject"/>.</param>
|
||||
delegate void ChildrenRemovedEventHandler(IUniverseObject sender, IUniverseObject childrenRemoved);
|
||||
}
|
125
Engine.Core/ActiveBehaviourCollector.cs
Normal file
125
Engine.Core/ActiveBehaviourCollector.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
|
||||
{
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
public event IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = 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 IUniverse Universe { get; private set; } = null!;
|
||||
|
||||
public T this[Index index] => activeBehaviours[index];
|
||||
|
||||
public ActiveBehaviourCollector() { }
|
||||
public ActiveBehaviourCollector(IUniverse universe) => Assign(universe);
|
||||
|
||||
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
|
||||
{
|
||||
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
|
||||
foreach (IBehaviour item in universeObject.BehaviourController)
|
||||
OnBehaviourAdded(universeObject.BehaviourController, item);
|
||||
}
|
||||
|
||||
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject)
|
||||
{
|
||||
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
||||
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
||||
|
||||
foreach (IBehaviour item in universeObject.BehaviourController)
|
||||
OnBehaviourRemoved(universeObject.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?.InvokeSafe(this, behaviour);
|
||||
}
|
||||
else if (activeBehaviours.Remove(behaviour))
|
||||
{
|
||||
OnBehaviourRemove(behaviour);
|
||||
OnRemoved?.InvokeSafe(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?.InvokeSafe(this, tBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Assign(IUniverse universe)
|
||||
{
|
||||
if (Universe is not null)
|
||||
return false;
|
||||
|
||||
foreach (IUniverseObject universeObject in universe.UniverseObjects)
|
||||
OnUniverseObjectRegistered(universe, universeObject);
|
||||
|
||||
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered;
|
||||
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered;
|
||||
|
||||
Universe = universe;
|
||||
OnUniverseAssigned?.InvokeSafe(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Universe is null)
|
||||
return false;
|
||||
|
||||
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||
OnUniverseObjectUnregistered(Universe, universeObject);
|
||||
|
||||
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered;
|
||||
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered;
|
||||
|
||||
Universe = null!;
|
||||
OnUnassigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => activeBehaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => activeBehaviours.GetEnumerator();
|
||||
}
|
28
Engine.Core/ActiveBehaviourCollectorSorted.cs
Normal file
28
Engine.Core/ActiveBehaviourCollectorSorted.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
|
||||
{
|
||||
private Comparison<T>? _sortBy = null;
|
||||
public Comparison<T>? SortBy
|
||||
{
|
||||
get => _sortBy;
|
||||
set
|
||||
{
|
||||
_sortBy = value;
|
||||
|
||||
if (value is not null)
|
||||
activeBehaviours.Sort(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
||||
{
|
||||
if (SortBy is not null)
|
||||
activeBehaviours.Sort(SortBy);
|
||||
}
|
||||
|
||||
public ActiveBehaviourCollectorSorted() { }
|
||||
public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe) => SortBy = sortBy;
|
||||
}
|
@@ -1,17 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public abstract class BaseEntity : IEntity
|
||||
{
|
||||
public event IEntity.OnIdChangedDelegate? OnIdChanged = null;
|
||||
public event IEntity.IdChangedEventHandler? OnIdChanged = null;
|
||||
|
||||
public event IInitialize.OnInitializedDelegate? OnInitialized = null;
|
||||
public event IInitialize.OnFinalizedDelegate? OnFinalized = null;
|
||||
|
||||
public event IAssignableStateEnable.OnStateEnableAssignedDelegate? OnStateEnableAssigned = null;
|
||||
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
|
||||
public event IInitializable.InitializedEventHandler? OnInitialized = null;
|
||||
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
|
||||
|
||||
public event IHasStateEnable.StateEnableAssignedEventHandler? OnStateEnableAssigned = null;
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
@@ -20,24 +19,25 @@ public abstract class BaseEntity : IEntity
|
||||
|
||||
public virtual IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public virtual bool IsActive => StateEnable.Enabled;
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
if (IsInitialized)
|
||||
throw new($"Can't change {nameof(Id)} of {_id} because it's initialized");
|
||||
|
||||
if (value == _id)
|
||||
return;
|
||||
|
||||
string previousId = _id;
|
||||
|
||||
_id = value;
|
||||
OnIdChanged?.Invoke(this, previousId);
|
||||
OnIdChanged?.InvokeSafe(this, previousId);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Initialized
|
||||
public bool IsInitialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
@@ -47,56 +47,62 @@ public abstract class BaseEntity : IEntity
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
OnInitialized?.InvokeSafe(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
OnFinalized?.InvokeSafe(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnAssign(IStateEnable stateEnable) { }
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
OnAssign(stateEnable);
|
||||
OnStateEnableAssigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void UnassignInternal() { }
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
UnassignInternal();
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
_stateEnable = null!;
|
||||
_stateEnable.Unassign();
|
||||
OnUnassigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal() { }
|
||||
public bool Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
if (IsInitialized)
|
||||
return false;
|
||||
|
||||
_stateEnable ??= Factory.StateEnableFactory.Instantiate(this);
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
Initialized = true;
|
||||
IsInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void FinalizeInternal() { }
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
if (!IsInitialized)
|
||||
return false;
|
||||
|
||||
FinalizeInternal();
|
||||
|
||||
Initialized = false;
|
||||
IsInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,59 +1,109 @@
|
||||
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 Behaviour : BaseEntity, IBehaviour
|
||||
public abstract class Behaviour : BehaviourBase
|
||||
{
|
||||
public event IAssignableBehaviourController.OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned = null;
|
||||
private bool isInitializedThisFrame = false;
|
||||
|
||||
public event IBehaviour.OnPriorityChangedDelegate? OnPriorityChanged = null;
|
||||
protected IUniverse Universe => BehaviourController.UniverseObject.Universe;
|
||||
protected IUniverseObject UniverseObject => BehaviourController.UniverseObject;
|
||||
|
||||
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
|
||||
private int _priority = 0;
|
||||
|
||||
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
public override bool IsActive => base.IsActive && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
|
||||
public int Priority
|
||||
public Behaviour()
|
||||
{
|
||||
get => _priority;
|
||||
set
|
||||
{
|
||||
if (value == _priority)
|
||||
return;
|
||||
|
||||
int previousPriority = _priority;
|
||||
_priority = value;
|
||||
OnPriorityChanged?.Invoke(this, previousPriority);
|
||||
}
|
||||
OnInitialized += OnInitialize;
|
||||
OnFinalized += OnFinalize;
|
||||
OnUnassigned += OnUnassign;
|
||||
}
|
||||
|
||||
public bool Assign(IBehaviourController behaviourController)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
protected virtual void OnUnassign() { }
|
||||
protected virtual void OnUnassign(IAssignable assignable) => OnUnassign();
|
||||
|
||||
_behaviourController = behaviourController;
|
||||
OnBehaviourControllerAssigned?.Invoke(this);
|
||||
return true;
|
||||
protected virtual void OnInitialize() { }
|
||||
protected virtual void OnInitialize(IInitializable _)
|
||||
{
|
||||
isInitializedThisFrame = true;
|
||||
|
||||
BehaviourController.OnPreUpdate += PreUpdate;
|
||||
BehaviourController.OnPreDraw += PreDraw;
|
||||
BehaviourController.OnUpdate += Update;
|
||||
BehaviourController.UniverseObject.OnEnteredUniverse += EnteredUniverse;
|
||||
BehaviourController.UniverseObject.OnExitedUniverse += ExitedUniverse;
|
||||
|
||||
OnInitialize();
|
||||
|
||||
if (UniverseObject.IsInUniverse)
|
||||
EnteredUniverse(UniverseObject, Universe);
|
||||
}
|
||||
|
||||
protected override void UnassignInternal()
|
||||
protected virtual void OnFinalize() { }
|
||||
protected virtual void OnFinalize(IInitializable _)
|
||||
{
|
||||
base.UnassignInternal();
|
||||
_behaviourController = null!;
|
||||
BehaviourController.OnPreUpdate -= PreUpdate;
|
||||
BehaviourController.OnPreDraw -= PreDraw;
|
||||
BehaviourController.OnUpdate -= Update;
|
||||
BehaviourController.UniverseObject.OnEnteredUniverse -= EnteredUniverse;
|
||||
BehaviourController.UniverseObject.OnExitedUniverse -= ExitedUniverse;
|
||||
|
||||
OnFinalize();
|
||||
|
||||
if (UniverseObject.IsInUniverse)
|
||||
ExitedUniverse(UniverseObject, Universe);
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||
protected virtual void OnPreUpdate() { }
|
||||
protected virtual void PreUpdate(IBehaviourController _)
|
||||
{
|
||||
base.InitializeInternal();
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
Debug.Assert.AssertInitialized(this);
|
||||
|
||||
OnPreUpdatePreActiveCheck();
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (isInitializedThisFrame)
|
||||
FirstActiveFrame();
|
||||
|
||||
OnPreUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnFirstActiveFrame() { }
|
||||
protected virtual void FirstActiveFrame()
|
||||
{
|
||||
OnFirstActiveFrame();
|
||||
isInitializedThisFrame = false;
|
||||
}
|
||||
|
||||
protected virtual void OnUpdatePreActiveCheck() { }
|
||||
protected virtual void OnUpdate() { }
|
||||
protected virtual void Update(IBehaviourController _)
|
||||
{
|
||||
Debug.Assert.AssertInitialized(this);
|
||||
|
||||
OnUpdatePreActiveCheck();
|
||||
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
OnUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||
protected virtual void OnPreDraw() { }
|
||||
protected virtual void PreDraw(IBehaviourController _)
|
||||
{
|
||||
Debug.Assert.AssertInitialized(this);
|
||||
|
||||
OnPreDrawPreActiveCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreDraw();
|
||||
}
|
||||
|
||||
protected virtual void OnEnteredUniverse(IUniverse universe) { }
|
||||
protected virtual void EnteredUniverse(IUniverseObject sender, IUniverse universe) => OnEnteredUniverse(universe);
|
||||
|
||||
protected virtual void OnExitedUniverse(IUniverse universe) { }
|
||||
protected virtual void ExitedUniverse(IUniverseObject sender, IUniverse universe) => OnExitedUniverse(universe);
|
||||
}
|
||||
|
26
Engine.Core/Behaviour2D.cs
Normal file
26
Engine.Core/Behaviour2D.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
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.GetRequiredBehaviour<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 EnteredUniverse(IUniverseObject sender, IUniverse universe) => base.EnteredUniverse(sender, universe);
|
||||
protected sealed override void ExitedUniverse(IUniverseObject sender, IUniverse universe) => base.ExitedUniverse(sender, universe);
|
||||
}
|
84
Engine.Core/BehaviourBase.cs
Normal file
84
Engine.Core/BehaviourBase.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
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?.InvokeSafe(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.OnUniverseObjectAssigned += OnUniverseObjectAssigned;
|
||||
if (behaviourController.UniverseObject is not null)
|
||||
OnUniverseObjectAssigned(behaviourController);
|
||||
OnBehaviourControllerAssigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnUniverseObjectAssigned(IHasUniverseObject sender)
|
||||
{
|
||||
sender.UniverseObject.OnActiveChanged += OnUniverseObjectActiveChanged;
|
||||
UpdateActive();
|
||||
}
|
||||
|
||||
protected override void OnAssign(IStateEnable stateEnable)
|
||||
{
|
||||
base.OnAssign(stateEnable);
|
||||
|
||||
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
|
||||
}
|
||||
|
||||
protected override void UnassignInternal()
|
||||
{
|
||||
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
|
||||
BehaviourController.OnUniverseObjectAssigned -= OnUniverseObjectAssigned;
|
||||
base.UnassignInternal();
|
||||
_behaviourController = null!;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
||||
Debug.Assert.AssertStateEnableAssigned(this);
|
||||
}
|
||||
|
||||
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
|
||||
private void OnUniverseObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
|
||||
|
||||
private void UpdateActive()
|
||||
{
|
||||
bool previousActive = IsActive;
|
||||
_isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
||||
|
||||
if (previousActive != IsActive)
|
||||
OnActiveChanged?.InvokeSafe(this, previousActive);
|
||||
}
|
||||
}
|
@@ -2,105 +2,103 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCollector<T> : IAssignableGameManager, IEnumerable<T>
|
||||
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
||||
{
|
||||
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
|
||||
public event IAssignableGameManager.OnGameManagerAssignedDelegate? OnGameManagerAssigned = null;
|
||||
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
|
||||
public event IHasUniverse.UniverseAssignedEventHandler? OnUniverseAssigned = null;
|
||||
|
||||
public event OnCollectedDelegate? OnCollected = null;
|
||||
public event OnRemovedDelegate? OnRemoved = null;
|
||||
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
|
||||
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
|
||||
|
||||
private readonly List<T> _behaviours = new(32);
|
||||
protected readonly List<T> behaviours = new(32);
|
||||
|
||||
public IReadOnlyList<T> Behaviours => _behaviours;
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
public IReadOnlyList<T> Behaviours => behaviours;
|
||||
public IUniverse Universe { get; private set; } = null!;
|
||||
|
||||
public T this[Index index] => _behaviours[index];
|
||||
public T this[Index index] => behaviours[index];
|
||||
|
||||
public BehaviourCollector() { }
|
||||
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
|
||||
public BehaviourCollector(IUniverse universe) => Assign(universe);
|
||||
|
||||
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
|
||||
private void OnUniverseObjectRegistered(IUniverse manager, IUniverseObject universeObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
universeObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
universeObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
|
||||
foreach (IBehaviour item in universeObject.BehaviourController)
|
||||
OnBehaviourAdded(universeObject.BehaviourController, item);
|
||||
}
|
||||
|
||||
private void OnGameObjectUnregistered(IGameManager manager, IGameObject gameObject)
|
||||
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverseObject universeObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
||||
universeObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
||||
universeObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
||||
|
||||
foreach (IBehaviour item in universeObject.BehaviourController)
|
||||
OnBehaviourRemoved(universeObject.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);
|
||||
OnCollected?.Invoke(this, tBehaviour);
|
||||
behaviours.Add(tBehaviour);
|
||||
OnBehaviourAdd(behaviour);
|
||||
OnCollected?.InvokeSafe(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))
|
||||
if (!behaviours.Remove(tBehaviour))
|
||||
return;
|
||||
|
||||
OnRemoved?.Invoke(this, tBehaviour);
|
||||
OnBehaviourRemove(behaviour);
|
||||
OnRemoved?.InvokeSafe(this, tBehaviour);
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
protected virtual void OnAssign(IUniverse universe) { }
|
||||
public bool Assign(IUniverse universe)
|
||||
{
|
||||
if (GameManager is not null)
|
||||
if (Universe is not null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in gameManager)
|
||||
{
|
||||
OnGameObjectRegistered(gameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourAdded(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
foreach (IUniverseObject universeObject in universe.UniverseObjects)
|
||||
OnUniverseObjectRegistered(universe, universeObject);
|
||||
|
||||
gameManager.OnGameObjectRegistered += OnGameObjectRegistered;
|
||||
gameManager.OnGameObjectUnRegistered += OnGameObjectUnregistered;
|
||||
universe.OnUniverseObjectRegistered += OnUniverseObjectRegistered;
|
||||
universe.OnUniverseObjectUnRegistered += OnUniverseObjectUnregistered;
|
||||
|
||||
GameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
Universe = universe;
|
||||
OnAssign(universe);
|
||||
OnUniverseAssigned?.InvokeSafe(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (GameManager is null)
|
||||
if (Universe is null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in GameManager)
|
||||
{
|
||||
OnGameObjectUnregistered(GameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourRemoved(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
foreach (IUniverseObject universeObject in Universe.UniverseObjects)
|
||||
OnUniverseObjectUnregistered(Universe, universeObject);
|
||||
|
||||
GameManager.OnGameObjectRegistered -= OnGameObjectRegistered;
|
||||
GameManager.OnGameObjectUnRegistered -= OnGameObjectUnregistered;
|
||||
Universe.OnUniverseObjectRegistered -= OnUniverseObjectRegistered;
|
||||
Universe.OnUniverseObjectUnRegistered -= OnUniverseObjectUnregistered;
|
||||
|
||||
GameManager = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
Universe = null!;
|
||||
OnUnassigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
|
||||
|
||||
|
||||
public delegate void OnCollectedDelegate(BehaviourCollector<T> sender, T behaviourCollected);
|
||||
public delegate void OnRemovedDelegate(BehaviourCollector<T> sender, T behaviourRemoved);
|
||||
public IEnumerator<T> GetEnumerator() => behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
||||
}
|
||||
|
28
Engine.Core/BehaviourCollectorSorted.cs
Normal file
28
Engine.Core/BehaviourCollectorSorted.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
||||
{
|
||||
private Comparison<T>? _sortBy = null;
|
||||
public Comparison<T>? SortBy
|
||||
{
|
||||
get => _sortBy;
|
||||
set
|
||||
{
|
||||
_sortBy = value;
|
||||
|
||||
if (value is not null)
|
||||
behaviours.Sort(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
||||
{
|
||||
if (SortBy is not null)
|
||||
behaviours.Sort(SortBy);
|
||||
}
|
||||
|
||||
public BehaviourCollectorSorted() { }
|
||||
public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe) => SortBy = sortBy;
|
||||
}
|
@@ -1,65 +1,56 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||
public class BehaviourController : IBehaviourController
|
||||
public class BehaviourController : BaseEntity, IBehaviourController
|
||||
{
|
||||
public event IBehaviourController.OnPreUpdateDelegate? OnPreUpdate = null;
|
||||
public event IBehaviourController.OnUpdateDelegate? OnUpdate = null;
|
||||
public event IBehaviourController.OnPreDrawDelegate? OnPreDraw = null;
|
||||
|
||||
public event IBehaviourController.OnBehaviourAddedDelegate? OnBehaviourAdded = null;
|
||||
public event IBehaviourController.OnBehaviourRemovedDelegate? OnBehaviourRemoved = null;
|
||||
public event IAssignable.OnUnassignedDelegate? OnUnassigned;
|
||||
public event IAssignableGameObject.OnGameObjectAssignedDelegate? OnGameObjectAssigned = null;
|
||||
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
|
||||
public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
|
||||
public event IBehaviourController.PreDrawEventHandler? OnPreDraw = null;
|
||||
|
||||
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
|
||||
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
|
||||
public event IHasUniverseObject.UniverseObjectAssignedEventHandler? OnUniverseObjectAssigned = null;
|
||||
|
||||
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
|
||||
|
||||
private IGameObject _gameObject = null!;
|
||||
private IUniverseObject _universeObject = null!;
|
||||
|
||||
|
||||
public IGameObject GameObject => _gameObject;
|
||||
public IUniverseObject UniverseObject => _universeObject;
|
||||
|
||||
public T AddBehaviour<T>(T behaviour) where T : class, IBehaviour
|
||||
{
|
||||
InsertBehaviourByPriority(behaviour);
|
||||
|
||||
behaviour.Initialize();
|
||||
behaviour.Assign(this);
|
||||
|
||||
if (IsInitialized)
|
||||
behaviour.Initialize();
|
||||
behaviour.OnPriorityChanged += OnPriorityChange;
|
||||
OnBehaviourAdded?.Invoke(this, behaviour);
|
||||
OnBehaviourAdded?.InvokeSafe(this, behaviour);
|
||||
return behaviour;
|
||||
}
|
||||
|
||||
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
||||
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_gameObject, args));
|
||||
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_universeObject, args));
|
||||
|
||||
public T? GetBehaviour<T>()
|
||||
{
|
||||
foreach (var behaviourItem in behaviours)
|
||||
foreach (IBehaviour behaviourItem in behaviours)
|
||||
if (behaviourItem is T result)
|
||||
return result;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
behaviour = GetBehaviour<T>();
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public IList<T> GetBehaviours<T>()
|
||||
public IReadOnlyList<T> GetBehaviours<T>()
|
||||
{
|
||||
List<T>? behaviours = null;
|
||||
foreach (var behaviourItem in this.behaviours)
|
||||
foreach (IBehaviour behaviourItem in this.behaviours)
|
||||
{
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
@@ -71,15 +62,15 @@ public class BehaviourController : IBehaviourController
|
||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||
}
|
||||
|
||||
public void GetBehaviours<T>(List<T> behaviors)
|
||||
public void GetBehaviours<T>(IList<T> results)
|
||||
{
|
||||
behaviors.Clear();
|
||||
foreach (var behaviourItem in behaviours)
|
||||
results.Clear();
|
||||
foreach (IBehaviour behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T _)
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
|
||||
behaviours.Add(behaviourItem);
|
||||
results.Add(behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,53 +91,63 @@ public class BehaviourController : IBehaviourController
|
||||
public void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour
|
||||
{
|
||||
if (!behaviours.Contains(behaviour))
|
||||
throw new Exception($"{behaviour.GetType().Name} does not exist in {GameObject.Name}'s {nameof(IBehaviourController)}.");
|
||||
throw new Exception($"{behaviour.GetType().Name} does not exist in {UniverseObject.Name}'s {nameof(IBehaviourController)}.");
|
||||
|
||||
behaviour.OnPriorityChanged -= OnPriorityChange;
|
||||
behaviour.Finalize();
|
||||
behaviours.Remove(behaviour);
|
||||
OnBehaviourRemoved?.Invoke(this, behaviour);
|
||||
OnBehaviourRemoved?.InvokeSafe(this, behaviour);
|
||||
}
|
||||
|
||||
public bool Assign(IGameObject gameObject)
|
||||
protected virtual void OnAssign(IUniverseObject universeObject) { }
|
||||
public bool Assign(IUniverseObject universeObject)
|
||||
{
|
||||
if (GameObject is not null && GameObject.Initialized)
|
||||
if (UniverseObject is not null && UniverseObject.IsInitialized)
|
||||
return false;
|
||||
|
||||
_gameObject = gameObject;
|
||||
OnGameObjectAssigned?.Invoke(this);
|
||||
_universeObject = universeObject;
|
||||
OnAssign(universeObject);
|
||||
OnUniverseObjectAssigned?.InvokeSafe(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
if (GameObject is not null && GameObject.Initialized)
|
||||
return false;
|
||||
Debug.Assert.AssertUniverseObjectAssigned(this);
|
||||
|
||||
_gameObject = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
foreach (IBehaviour behaviour in behaviours)
|
||||
behaviour.Initialize();
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
{
|
||||
foreach (IBehaviour behaviour in behaviours)
|
||||
behaviour.Finalize();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!GameObject.StateEnable.Enabled)
|
||||
Debug.Assert.AssertInitialized(this);
|
||||
|
||||
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreUpdate?.Invoke(this);
|
||||
OnUpdate?.Invoke(this);
|
||||
OnPreUpdate?.InvokeSafe(this);
|
||||
OnUpdate?.InvokeSafe(this);
|
||||
}
|
||||
|
||||
public void UpdatePreDraw()
|
||||
{
|
||||
if (!GameObject.StateEnable.Enabled)
|
||||
Debug.Assert.AssertInitialized(this);
|
||||
|
||||
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnPreDraw?.Invoke(this);
|
||||
OnPreDraw?.InvokeSafe(this);
|
||||
}
|
||||
|
||||
public BehaviourController() { }
|
||||
public BehaviourController(IGameObject gameObject) => Assign(gameObject);
|
||||
public BehaviourController(IUniverseObject universeObject) => Assign(universeObject);
|
||||
|
||||
private void InsertBehaviourByPriority<T>(T behaviour) where T : class, IBehaviour
|
||||
{
|
||||
@@ -165,7 +166,6 @@ public class BehaviourController : IBehaviourController
|
||||
behaviours.Add(behaviour);
|
||||
}
|
||||
|
||||
|
||||
private void OnPriorityChange(IBehaviour sender, int previousPriority)
|
||||
{
|
||||
behaviours.Remove(sender);
|
||||
|
@@ -1,87 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
42
Engine.Core/CoroutineManager.cs
Normal file
42
Engine.Core/CoroutineManager.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class CoroutineManager : UniverseObject
|
||||
{
|
||||
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 OnEnteringUniverse(IUniverse universe)
|
||||
{
|
||||
universe.OnUpdate += OnUpdate;
|
||||
}
|
||||
|
||||
protected override void OnExitingUniverse(IUniverse universe)
|
||||
{
|
||||
universe.OnUpdate -= OnUpdate;
|
||||
}
|
||||
|
||||
private void OnUpdate(IUniverse sender, UniverseTime 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);
|
||||
}
|
||||
}
|
||||
}
|
10
Engine.Core/CoroutineYield.cs
Normal file
10
Engine.Core/CoroutineYield.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class CoroutineYield(Func<bool> condition) : ICoroutineYield
|
||||
{
|
||||
private readonly Func<bool> condition = condition;
|
||||
|
||||
public bool Yield() => condition.Invoke();
|
||||
}
|
30
Engine.Core/Debug/AssertHelpers.cs
Normal file
30
Engine.Core/Debug/AssertHelpers.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public static class Assert
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertInitialized(IInitializable initializable)
|
||||
=> System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable)
|
||||
=> System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertEntityAssigned(IHasEntity assignable)
|
||||
=> System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertUniverseAssigned(IHasUniverse assignable)
|
||||
=> System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be initialized");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable)
|
||||
=> System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be initialized");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertStateEnableAssigned(IHasStateEnable assignable)
|
||||
=> System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized");
|
||||
}
|
8
Engine.Core/Debug/ConsoleLogger.cs
Normal file
8
Engine.Core/Debug/ConsoleLogger.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public class ConsoleLogger : LoggerBase
|
||||
{
|
||||
protected override void Write(string message) => Console.WriteLine(message);
|
||||
}
|
20
Engine.Core/Debug/FileLogger.cs
Normal file
20
Engine.Core/Debug/FileLogger.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public class FileLogger : LoggerBase
|
||||
{
|
||||
public readonly string FilePath;
|
||||
|
||||
public FileLogger(string filePath)
|
||||
{
|
||||
FilePath = filePath;
|
||||
File.Open(filePath, FileMode.Create).Close();
|
||||
}
|
||||
|
||||
protected override void Write(string message)
|
||||
{
|
||||
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
|
||||
}
|
||||
}
|
15
Engine.Core/Debug/ILogger.cs
Normal file
15
Engine.Core/Debug/ILogger.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
Level FilterLevel { get; set; }
|
||||
|
||||
void Log(string message, Level level = Level.Info, bool force = false);
|
||||
|
||||
enum Level
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
}
|
20
Engine.Core/Debug/LoggerBase.cs
Normal file
20
Engine.Core/Debug/LoggerBase.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public abstract class LoggerBase : ILogger
|
||||
{
|
||||
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Info;
|
||||
|
||||
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||
{
|
||||
if (!force && level < FilterLevel)
|
||||
return;
|
||||
|
||||
string timestamp = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss tt");
|
||||
|
||||
Write($"[{timestamp}] [{level}] \t{message}");
|
||||
}
|
||||
|
||||
protected abstract void Write(string message);
|
||||
}
|
28
Engine.Core/Debug/LoggerExtensions.cs
Normal file
28
Engine.Core/Debug/LoggerExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Syntriax.Engine.Core.Debug;
|
||||
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
public static void Log<T>(this ILogger logger, T caller, string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
|
||||
{
|
||||
string body = $"{caller?.GetType().Name ?? typeof(T).Name}: {message}";
|
||||
logger.Log(body, level, force);
|
||||
}
|
||||
|
||||
public static void LogWarning<T>(this ILogger logger, T caller, string message, bool force = false) => Log(logger, caller, message, ILogger.Level.Info, force);
|
||||
|
||||
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
|
||||
{
|
||||
Log(logger, caller, message, ILogger.Level.Error, force);
|
||||
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}");
|
||||
}
|
||||
|
||||
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
|
||||
{
|
||||
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
|
||||
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
|
||||
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}");
|
||||
}
|
||||
}
|
@@ -4,5 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>false</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
46
Engine.Core/Engine.Core.puml
Normal file
46
Engine.Core/Engine.Core.puml
Normal file
@@ -0,0 +1,46 @@
|
||||
@startuml "Core Engine Relations"
|
||||
|
||||
top to bottom direction
|
||||
skinparam linetype ortho
|
||||
skinparam nodesep 100
|
||||
|
||||
title Core Engine Relations
|
||||
|
||||
interface Engine.Core.IEntity extends Engine.Core.IInitializable {}
|
||||
interface Engine.Core.IUniverseObject extends Engine.Core.IEntity, Engine.Core.INameable {}
|
||||
|
||||
interface Engine.Core.INameable {}
|
||||
|
||||
Engine.Core.IUniverseObject --> Engine.Core.IBehaviourController: has
|
||||
Engine.Core.IBehaviourController "1" --> "0..*" Engine.Core.IBehaviour: has
|
||||
|
||||
interface Engine.Core.IBehaviourController {}
|
||||
interface Engine.Core.IBehaviour {}
|
||||
interface Engine.Core.IBehaviour2D extends Engine.Core.IBehaviour {}
|
||||
interface Engine.Core.IBehaviour3D extends Engine.Core.IBehaviour {}
|
||||
|
||||
interface Engine.Core.IUniverse {}
|
||||
Engine.Core.IUniverse "1" -r-> "0..*" Engine.Core.IUniverseObject: has
|
||||
|
||||
' together {
|
||||
' interface Engine.Core.IAssignable {}
|
||||
' interface Engine.Core.IHasStateEnable extends Engine.Core.IAssignable {}
|
||||
' interface Engine.Core.IHasUniverse extends Engine.Core.IAssignable {}
|
||||
' interface Engine.Core.IHasUniverseObject extends Engine.Core.IAssignable {}
|
||||
' interface Engine.Core.IHasBehaviourController extends Engine.Core.IAssignable {}
|
||||
' ' Engine.Core.IHasStateEnable --> Engine.Core.IStateEnable: has
|
||||
' ' Engine.Core.IHasUniverse --> Engine.Core.IUniverse: has
|
||||
' ' Engine.Core.IHasUniverseObject --> Engine.Core.IUniverseObject: has
|
||||
' ' Engine.Core.IHasBehaviourController --> Engine.Core.IBehaviourController: has
|
||||
' }
|
||||
|
||||
together {
|
||||
interface Engine.Core.ITransform2D {}
|
||||
interface Engine.Core.ICamera2D {}
|
||||
interface Engine.Core.ICoroutineYield {}
|
||||
interface Engine.Core.IStateEnable {}
|
||||
interface Engine.Core.IInitializable {}
|
||||
interface Engine.Core.IBehaviourCollector {}
|
||||
}
|
||||
|
||||
@enduml
|
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
|
||||
{
|
||||
public readonly TimeSpan Total = Total;
|
||||
public readonly TimeSpan Elapsed = Elapsed;
|
||||
|
||||
public readonly float DeltaTimeFrame = (float)Elapsed.TotalMilliseconds;
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
public class AssignException : Exception
|
||||
{
|
||||
public AssignException() : base("Assign operation has failed.") { }
|
||||
public AssignException(string? message) : base(message) { }
|
||||
|
||||
// public static AssignException FromStateEnable(IStateEnable? stateEnable)
|
||||
// => new AssignException($"{nameof(IGameObject.AssignStateEnable)} failed on type {stateEnable?.GetType().ToString() ?? "\"null\""}");
|
||||
public static AssignException From<T, T2>(T to, T2? value)
|
||||
=> new AssignException($"Assign operation has failed on T: {typeof(T).FullName}, value: {value?.GetType().ToString() ?? "\"null\""}");
|
||||
// public static AssignException FromBehaviourController(IBehaviourController? behaviourController)
|
||||
// => new AssignException($"{nameof(IGameObject.AssignBehaviourController)} failed on type {behaviourController?.GetType().ToString() ?? "\"null\""}");
|
||||
}
|
||||
// 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}");
|
9
Engine.Core/Exceptions/AssignFailedException.cs
Normal file
9
Engine.Core/Exceptions/AssignFailedException.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
public class AssignFailedException(string? message) : Exception(message)
|
||||
{
|
||||
public static AssignFailedException From<T, T2>(T to, T2? value)
|
||||
=> new($"Assign operation has failed on T: {to?.GetType().FullName ?? "\"null\""}, value: {value?.GetType().ToString() ?? "\"null\""}");
|
||||
}
|
9
Engine.Core/Exceptions/BehaviourNotFoundException.cs
Normal file
9
Engine.Core/Exceptions/BehaviourNotFoundException.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
public class BehaviourNotFoundException(string? message) : Exception(message)
|
||||
{
|
||||
public static NotAssignedException FromType<TBehaviour>()
|
||||
=> new($"{typeof(TBehaviour).FullName} was not found");
|
||||
}
|
@@ -1,21 +1,9 @@
|
||||
using System;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
public class NotAssignedException : Exception
|
||||
public class NotAssignedException(string? message) : Exception(message)
|
||||
{
|
||||
public NotAssignedException() : base("The object has not been assigned.") { }
|
||||
public NotAssignedException(string? message) : base(message) { }
|
||||
|
||||
public static NotAssignedException From<T1, T2>(T1 to, T2? value) where T1 : IAssignable
|
||||
=> new NotAssignedException($"{typeof(T2).Name} has not been assigned to {typeof(T1).Name}");
|
||||
|
||||
public static void Check<T1, T2>(T1 to, T2? value) where T1 : IAssignable
|
||||
{
|
||||
if (value is not null)
|
||||
return;
|
||||
|
||||
throw From(to, value);
|
||||
}
|
||||
public static NotAssignedException From<T1, T2>(T1 to, T2? value)
|
||||
=> new($"{value?.GetType().FullName ?? "\"null\""} has not been assigned to {to?.GetType().FullName ?? "\"null\""}");
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
public class UniverseObjectNotFoundException(string? message) : Exception(message)
|
||||
{
|
||||
public static NotAssignedException FromType<TUniverseObject>()
|
||||
=> new($"{typeof(TUniverseObject).FullName} was not found");
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static Vector2D TransformVector2D(this ITransform transform, Vector2D vector)
|
||||
=> vector.Scale(transform.Scale)
|
||||
.Rotate(transform.Rotation * Math.DegreeToRadian)
|
||||
.Add(transform.Position);
|
||||
}
|
@@ -1,47 +1,123 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
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 a <see cref="IBehaviour"/> of the specified type in the provided <see cref="IBehaviourController"/>. Throws an error if not found.
|
||||
/// </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, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||
public static T GetRequiredBehaviour<T>(this IBehaviourController behaviourController) where T : class
|
||||
=> behaviourController.GetBehaviour<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}");
|
||||
|
||||
/// <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 it's <see cref="IUniverseObject"/>'s parents recursively.
|
||||
/// </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 universe; 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 it's <see cref="IUniverseObject"/>'s parents recursively.
|
||||
/// </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)
|
||||
if (controller.GetBehaviour<T>() is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
controller = controller.GameObject.Transform.Parent?.GameObject.BehaviourController;
|
||||
controller = controller.UniverseObject.Parent?.BehaviourController;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively. Throws an error if not found.
|
||||
/// </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, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
|
||||
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
|
||||
/// </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 universe; 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 it's <see cref="IUniverseObject"/>'s children recursively.
|
||||
/// </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 (ITransform transform in behaviourController.GameObject.Transform)
|
||||
if (GetBehaviourInChildren<T>(transform.GameObject.BehaviourController) is T behaviour)
|
||||
foreach (IUniverseObject child in behaviourController.UniverseObject)
|
||||
if (GetBehaviourInChildren<T>(child.BehaviourController) is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type in the children recursively. Throws an error if not found.
|
||||
/// </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, throws <see cref="BehaviourNotFoundException"/>.</returns>
|
||||
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
|
||||
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
|
||||
}
|
||||
|
@@ -1,35 +1,33 @@
|
||||
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<IGameObject> gameObjects) where T : class
|
||||
public static T? FindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
|
||||
{
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
if (gameObject.BehaviourController.TryGetBehaviour(out T? behaviour))
|
||||
foreach (IUniverseObject universeObject in universeObjects)
|
||||
if (universeObject.BehaviourController.GetBehaviour<T>() is T behaviour)
|
||||
return behaviour;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
public static bool TryFindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = FindBehaviour<T>(gameObjects);
|
||||
behaviour = FindBehaviour<T>(universeObjects);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours) where T : class
|
||||
public static void FindBehaviours<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class
|
||||
{
|
||||
behaviours.Clear();
|
||||
List<T> cache = [];
|
||||
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
foreach (IUniverseObject universeObject in universeObjects)
|
||||
{
|
||||
gameObject.BehaviourController.GetBehaviours(cache);
|
||||
universeObject.BehaviourController.GetBehaviours(cache);
|
||||
behaviours.AddRange(cache);
|
||||
}
|
||||
}
|
||||
|
9
Engine.Core/Extensions/EnumExtensions.cs
Normal file
9
Engine.Core/Extensions/EnumExtensions.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class EnumExtensions
|
||||
{
|
||||
public static bool CheckFlag(this Enum left, Enum right)
|
||||
=> ((int)(object)left & (int)(object)right) != 0;
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class GameManagerExtensions
|
||||
{
|
||||
public static IGameObject InstantiateGameObject(this IGameManager gameManager, params object?[]? args)
|
||||
=> gameManager.InstantiateGameObject<GameObject>(args);
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
public static IGameObject SetGameObject(this IGameObject gameObject, string name)
|
||||
{
|
||||
gameObject.Name = name;
|
||||
return gameObject;
|
||||
}
|
||||
}
|
@@ -1,10 +1,8 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static ITransform SetTransform(this ITransform transform,
|
||||
public static ITransform2D SetTransform(this ITransform2D transform,
|
||||
Vector2D? position = null, float? rotation = null, Vector2D? scale = null,
|
||||
Vector2D? localPosition = null, float? localRotation = null, Vector2D? localScale = null)
|
||||
{
|
||||
|
15
Engine.Core/Extensions/UniverseExtensions.cs
Normal file
15
Engine.Core/Extensions/UniverseExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class UniverseExtensions
|
||||
{
|
||||
public static IUniverseObject InstantiateUniverseObject(this IUniverse universe, params object?[]? args)
|
||||
=> universe.InstantiateUniverseObject<UniverseObject>(args);
|
||||
|
||||
public static T GetRequiredUniverseObject<T>(this IUniverse universe) where T : class
|
||||
=> universe.GetUniverseObject<T>() ?? throw new UniverseObjectNotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} object of type {typeof(T).FullName}");
|
||||
|
||||
public static T FindRequiredBehaviour<T>(this IUniverse universe) where T : class
|
||||
=> universe.FindBehaviour<T>() ?? throw new BehaviourNotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} with {nameof(IBehaviour)} of type {typeof(T).FullName}");
|
||||
}
|
39
Engine.Core/Extensions/UniverseObjectExtensions.cs
Normal file
39
Engine.Core/Extensions/UniverseObjectExtensions.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class UniverseObjectExtensions
|
||||
{
|
||||
public static T SetUniverseObject<T>(this T universeObject, string? name = "", IUniverseObject? parent = null) where T : IUniverseObject
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
universeObject.Name = name;
|
||||
if (parent is not null)
|
||||
universeObject.SetParent(parent);
|
||||
return universeObject;
|
||||
}
|
||||
|
||||
public static T? GetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
|
||||
{
|
||||
foreach (IUniverseObject universeObject in universeObjects)
|
||||
if (universeObject is T @object)
|
||||
return @object;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool TryGetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
|
||||
{
|
||||
behaviour = GetUniverseObject<T>(universeObjects);
|
||||
return behaviour is not null;
|
||||
}
|
||||
|
||||
public static void GetUniverseObjects<T>(this IEnumerable<IUniverseObject> universeObjects, List<T> behaviours) where T : class
|
||||
{
|
||||
behaviours.Clear();
|
||||
foreach (IUniverseObject universeObject in universeObjects)
|
||||
if (universeObject is T @object)
|
||||
behaviours.Add(@object);
|
||||
}
|
||||
}
|
@@ -1,188 +0,0 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Vector2D"/> type.
|
||||
/// </summary>
|
||||
public static class Vector2DExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The ending <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Vector2D"/> with its components inverted.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="Vector2D"/>s component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="vectorToAdd">The vector <see cref="Vector2D"/> to be added.</param>
|
||||
/// <returns>The result of the addition.</returns>
|
||||
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector2D"/> from another component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="vectorToSubtract">The <see cref="Vector2D"/> to be subtracted.</param>
|
||||
/// <returns>The result of the subtraction.</returns>
|
||||
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to multiply.</param>
|
||||
/// <param name="value">The scalar value to multiply with.</param>
|
||||
/// <returns>The result of the multiplication.</returns>
|
||||
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to divide.</param>
|
||||
/// <param name="value">The scalar value to divide with.</param>
|
||||
/// <returns>The result of the division.</returns>
|
||||
public static Vector2D Divide(this Vector2D vector, float value) => Vector2D.Divide(vector, value);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector2D"/> with the absolute values of each component.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> with absolute values.</returns>
|
||||
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to reflect.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector2D"/> of the reflecting surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector2D"/> (creates a <see cref="Vector2D"/> with the same direction but with a length of 1).
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Vector2D"/> pointing from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> pointing from <paramref name="from"/> to <paramref name="to"/>.</returns>
|
||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
|
||||
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
|
||||
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
|
||||
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
|
||||
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
|
||||
/// <returns>The clamped <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The start <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The end <see cref="Vector2D"/>.</param>
|
||||
/// <param name="t">The interpolation parameter (between 0 and 1).</param>
|
||||
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Lerp(this Vector2D from, Vector2D to, float t) => Vector2D.Lerp(from, to, t);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle in radians between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two <see cref="Vector2D"/>s are approximately equal within a certain epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="epsilon">The maximum difference allowed between components.</param>
|
||||
/// <returns>True if the <see cref="Vector2D"/>s are approximately equal, false otherwise.</returns>
|
||||
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@@ -1,20 +1,22 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class BehaviourControllerFactory
|
||||
{
|
||||
public IBehaviourController Instantiate(IGameObject gameObject)
|
||||
=> Instantiate<BehaviourController>(gameObject);
|
||||
public static IBehaviourController Instantiate(IUniverseObject universeObject)
|
||||
=> Instantiate<BehaviourController>(universeObject);
|
||||
|
||||
public T Instantiate<T>(IGameObject gameObject, params object?[]? args)
|
||||
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args)
|
||||
where T : class, IBehaviourController
|
||||
{
|
||||
T behaviourController = TypeFactory.Get<T>(args);
|
||||
|
||||
if (!behaviourController.Assign(gameObject))
|
||||
throw AssignException.From(behaviourController, gameObject);
|
||||
if (!universeObject.Assign(behaviourController))
|
||||
throw AssignFailedException.From(universeObject, behaviourController);
|
||||
|
||||
if (!behaviourController.Assign(universeObject))
|
||||
throw AssignFailedException.From(behaviourController, universeObject);
|
||||
|
||||
return behaviourController;
|
||||
}
|
||||
|
@@ -1,26 +1,25 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class BehaviourFactory
|
||||
{
|
||||
public T Instantiate<T>(IGameObject gameObject, params object?[]? args) where T : class, IBehaviour
|
||||
=> Instantiate<T>(gameObject, stateEnable: null, args);
|
||||
public static T Instantiate<T>(IUniverseObject universeObject, params object?[]? args) where T : class, IBehaviour
|
||||
=> Instantiate<T>(universeObject, stateEnable: null, args);
|
||||
|
||||
public T Instantiate<T>(IGameObject gameObject, IStateEnable? stateEnable, params object?[]? args)
|
||||
public static T Instantiate<T>(IUniverseObject universeObject, IStateEnable? stateEnable, params object?[]? args)
|
||||
where T : class, IBehaviour
|
||||
{
|
||||
T behaviour = TypeFactory.Get<T>(args);
|
||||
|
||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
||||
if (!stateEnable.Assign(behaviour))
|
||||
throw AssignException.From(stateEnable, behaviour);
|
||||
throw AssignFailedException.From(stateEnable, behaviour);
|
||||
|
||||
if (!behaviour.Assign(gameObject.BehaviourController))
|
||||
throw AssignException.From(behaviour, gameObject.BehaviourController);
|
||||
if (!behaviour.Assign(stateEnable))
|
||||
throw AssignException.From(behaviour, stateEnable);
|
||||
throw AssignFailedException.From(behaviour, stateEnable);
|
||||
if (!behaviour.Assign(universeObject.BehaviourController))
|
||||
throw AssignFailedException.From(behaviour, universeObject.BehaviourController);
|
||||
|
||||
return behaviour;
|
||||
}
|
||||
|
@@ -1,42 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class GameObjectFactory
|
||||
{
|
||||
public T Instantiate<T>(params object?[]? args) where T : class, IGameObject
|
||||
=> Instantiate<T>(transform: null, behaviourController: null, stateEnable: null, args);
|
||||
|
||||
public T Instantiate<T>(
|
||||
ITransform? transform = null,
|
||||
IBehaviourController? behaviourController = null,
|
||||
IStateEnable? stateEnable = null,
|
||||
params object?[]? args
|
||||
)
|
||||
where T : class, IGameObject
|
||||
{
|
||||
T gameObject = TypeFactory.Get<T>(args);
|
||||
|
||||
transform ??= TypeFactory.Get<Transform>();
|
||||
behaviourController ??= TypeFactory.Get<BehaviourController>();
|
||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
||||
|
||||
if (!transform.Assign(gameObject))
|
||||
throw AssignException.From(transform, gameObject);
|
||||
|
||||
if (!behaviourController.Assign(gameObject))
|
||||
throw AssignException.From(behaviourController, gameObject);
|
||||
if (!stateEnable.Assign(gameObject))
|
||||
throw AssignException.From(stateEnable, gameObject);
|
||||
|
||||
if (!gameObject.Assign(transform))
|
||||
throw AssignException.From(gameObject, transform);
|
||||
if (!gameObject.Assign(behaviourController))
|
||||
throw AssignException.From(gameObject, behaviourController);
|
||||
if (!gameObject.Assign(stateEnable))
|
||||
throw AssignException.From(gameObject, stateEnable);
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
}
|
@@ -1,18 +1,20 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class StateEnableFactory
|
||||
{
|
||||
public IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
|
||||
public static IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
|
||||
|
||||
public T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
|
||||
public static T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
|
||||
{
|
||||
T stateEnable = TypeFactory.Get<T>(args);
|
||||
|
||||
if (!entity.Assign(stateEnable))
|
||||
throw AssignFailedException.From(entity, stateEnable);
|
||||
|
||||
if (!stateEnable.Assign(entity))
|
||||
throw AssignException.From(stateEnable, entity);
|
||||
throw AssignFailedException.From(stateEnable, entity);
|
||||
|
||||
return stateEnable;
|
||||
}
|
||||
|
@@ -1,10 +1,8 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class TransformFactory
|
||||
{
|
||||
public ITransform Instantiate() => TypeFactory.Get<Transform>();
|
||||
public T Instantiate<T>(params object?[]? args) where T : class, ITransform
|
||||
public static ITransform2D Instantiate() => TypeFactory.Get<Transform2D>();
|
||||
public static T Instantiate<T>(params object?[]? args) where T : class, ITransform2D
|
||||
=> TypeFactory.Get<T>(args);
|
||||
}
|
||||
|
@@ -1,21 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public static class TypeFactory
|
||||
{
|
||||
public static T Get<T>(params object?[]? args) where T : class
|
||||
private static readonly ConcurrentDictionary<string, Type> registeredTypes = [];
|
||||
|
||||
public static string GetTypeName(Type type) => type.FullName ?? throw new ArgumentException($"{type.Name} must be a resolvable type");
|
||||
|
||||
public static T Get<T>(params object?[]? args) where T : class => (T)Get(typeof(T), args);
|
||||
public static object Get(string fullName, params object?[]? args) => Get(GetType(fullName), args);
|
||||
public static object Get(Type type, params object?[]? args)
|
||||
{
|
||||
T? result;
|
||||
object? result;
|
||||
|
||||
if (args is not null && args.Length != 0)
|
||||
result = Activator.CreateInstance(typeof(T), args) as T;
|
||||
result = Activator.CreateInstance(type, args);
|
||||
else
|
||||
result = Activator.CreateInstance(typeof(T)) as T;
|
||||
result = Activator.CreateInstance(type);
|
||||
|
||||
if (result is null)
|
||||
throw new Exception($"{typeof(T).Name} of type {typeof(T).Name} could not be created.");
|
||||
throw new Exception($"Type {type.Name} could not be created.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Type GetType(string fullName)
|
||||
{
|
||||
if (registeredTypes.TryGetValue(fullName, out Type? result))
|
||||
return result;
|
||||
|
||||
ReloadTypes();
|
||||
|
||||
if (registeredTypes.TryGetValue(fullName, out Type? reloadedType))
|
||||
return reloadedType;
|
||||
|
||||
throw new Exception($"Type {fullName} could not be found in the current domain.");
|
||||
}
|
||||
|
||||
public static void ReloadTypes()
|
||||
{
|
||||
registeredTypes.Clear();
|
||||
|
||||
IEnumerable<Type> domainTypes = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes());
|
||||
|
||||
// TODO: Replace this
|
||||
// There are some system & compiler generated types with duplicated names,
|
||||
// it is ugly it will cause headaches in the future because it will not
|
||||
// throw an error if there's a type with an unintended duplicate name
|
||||
foreach (Type type in domainTypes)
|
||||
registeredTypes.TryAdd(GetTypeName(type), type);
|
||||
}
|
||||
}
|
||||
|
36
Engine.Core/Factory/UniverseObjectFactory.cs
Normal file
36
Engine.Core/Factory/UniverseObjectFactory.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core.Factory;
|
||||
|
||||
public class UniverseObjectFactory
|
||||
{
|
||||
public static IUniverseObject Instantiate() => Instantiate<UniverseObject>();
|
||||
public static T Instantiate<T>(params object?[]? args) where T : class, IUniverseObject
|
||||
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
|
||||
|
||||
public static IUniverseObject Instantiate(IBehaviourController? behaviourController = null, IStateEnable? stateEnable = null) => Instantiate<UniverseObject>(behaviourController, stateEnable);
|
||||
public static T Instantiate<T>(
|
||||
IBehaviourController? behaviourController = null,
|
||||
IStateEnable? stateEnable = null,
|
||||
params object?[]? args
|
||||
)
|
||||
where T : class, IUniverseObject
|
||||
{
|
||||
T universeObject = TypeFactory.Get<T>(args);
|
||||
|
||||
behaviourController ??= TypeFactory.Get<BehaviourController>();
|
||||
stateEnable ??= TypeFactory.Get<StateEnable>();
|
||||
|
||||
if (!behaviourController.Assign(universeObject))
|
||||
throw AssignFailedException.From(behaviourController, universeObject);
|
||||
if (!stateEnable.Assign(universeObject))
|
||||
throw AssignFailedException.From(stateEnable, universeObject);
|
||||
|
||||
if (!universeObject.Assign(behaviourController))
|
||||
throw AssignFailedException.From(universeObject, behaviourController);
|
||||
if (!universeObject.Assign(stateEnable))
|
||||
throw AssignFailedException.From(universeObject, stateEnable);
|
||||
|
||||
return universeObject;
|
||||
}
|
||||
}
|
@@ -1,132 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
using Syntriax.Engine.Core.Factory;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
|
||||
public class GameManager : BaseEntity, IGameManager
|
||||
{
|
||||
public event IGameManager.OnGameObjectRegisteredDelegate? OnGameObjectRegistered = null;
|
||||
public event IGameManager.OnGameObjectUnRegisteredDelegate? OnGameObjectUnRegistered = null;
|
||||
|
||||
|
||||
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
|
||||
private GameObjectFactory _gameObjectFactory = null!;
|
||||
|
||||
private GameObjectFactory GameObjectFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_gameObjectFactory is null)
|
||||
_gameObjectFactory = new GameObjectFactory();
|
||||
return _gameObjectFactory;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||
|
||||
public override IStateEnable StateEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.StateEnable is null)
|
||||
{
|
||||
Assign(new StateEnableFactory().Instantiate(this));
|
||||
if (base.StateEnable is null)
|
||||
throw NotAssignedException.From(this, base.StateEnable);
|
||||
}
|
||||
|
||||
return base.StateEnable;
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterGameObject(IGameObject gameObject)
|
||||
{
|
||||
if (_gameObjects.Contains(gameObject))
|
||||
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is already registered to the {nameof(GameManager)}.");
|
||||
|
||||
Register(gameObject);
|
||||
}
|
||||
|
||||
public T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject
|
||||
{
|
||||
T gameObject = GameObjectFactory.Instantiate<T>(args);
|
||||
Register(gameObject);
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public IGameObject RemoveGameObject(IGameObject gameObject)
|
||||
{
|
||||
if (!_gameObjects.Contains(gameObject))
|
||||
throw new Exception($"{nameof(IGameObject)} named {gameObject.Name} is not registered to the {nameof(GameManager)}.");
|
||||
|
||||
Unregister(gameObject);
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.Initialize();
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
{
|
||||
base.FinalizeInternal();
|
||||
for (int i = GameObjects.Count; i >= 0; i--)
|
||||
GameObjects[i].Finalize();
|
||||
}
|
||||
|
||||
public void Update(EngineTime time)
|
||||
{
|
||||
Time.SetTime(time);
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.BehaviourController.Update();
|
||||
}
|
||||
|
||||
public void PreDraw()
|
||||
{
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.BehaviourController.UpdatePreDraw();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
private void Register(IGameObject gameObject)
|
||||
{
|
||||
gameObject.Assign(this);
|
||||
|
||||
gameObject.OnFinalized += OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Add(gameObject);
|
||||
OnGameObjectRegistered?.Invoke(this, gameObject);
|
||||
}
|
||||
|
||||
private void Unregister(IGameObject gameObject)
|
||||
{
|
||||
gameObject.OnFinalized -= OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Remove(gameObject);
|
||||
OnGameObjectUnRegistered?.Invoke(this, gameObject);
|
||||
}
|
||||
|
||||
private void OnGameObjectFinalize(IInitialize initialize)
|
||||
{
|
||||
if (initialize is IGameObject gameObject)
|
||||
Unregister(gameObject);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
public IEnumerator<IGameObject> GetEnumerator() => _gameObjects.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _gameObjects.GetEnumerator();
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Core.Exceptions;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||
public class GameObject : BaseEntity, IGameObject
|
||||
{
|
||||
public event IAssignableTransform.OnTransformAssignedDelegate? OnTransformAssigned = null;
|
||||
public event IAssignableGameManager.OnGameManagerAssignedDelegate? OnGameManagerAssigned = null;
|
||||
public event IAssignableBehaviourController.OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned = null;
|
||||
|
||||
public event INameable.OnNameChangedDelegate? OnNameChanged = null;
|
||||
|
||||
public event IGameObject.OnUpdatedDelegate? OnUpdated = null;
|
||||
|
||||
|
||||
private ITransform _transform = null!;
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private IGameManager _gameManager = null!;
|
||||
|
||||
private string _name = nameof(GameObject);
|
||||
|
||||
public ITransform Transform => _transform;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
public IGameManager GameManager => _gameManager;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
if (value == _name) return;
|
||||
|
||||
string previousName = _name;
|
||||
_name = value;
|
||||
OnNameChanged?.Invoke(this, previousName);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
|
||||
NotAssignedException.Check(this, _transform);
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
NotAssignedException.Check(this, _gameManager);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_stateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnUpdated?.Invoke(this);
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
{
|
||||
base.FinalizeInternal();
|
||||
|
||||
foreach (IBehaviour behaviour in _behaviourController.GetBehaviours<IBehaviour>())
|
||||
behaviour.Finalize();
|
||||
}
|
||||
|
||||
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 Assign(IGameManager gameManager)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_gameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void UnassignInternal()
|
||||
{
|
||||
base.UnassignInternal();
|
||||
|
||||
_stateEnable = null!;
|
||||
_transform = null!;
|
||||
_behaviourController = null!;
|
||||
_gameManager = null!;
|
||||
}
|
||||
|
||||
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
|
||||
|
||||
private void ConnectBehaviourController(IAssignableBehaviourController controller)
|
||||
{
|
||||
controller.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
controller.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
private void OnBehaviourRemoved(IBehaviourController _, IBehaviour behaviour) { if (Initialized) behaviour.Initialize(); }
|
||||
private void OnBehaviourAdded(IBehaviourController _, IBehaviour behaviour) { if (Initialized) behaviour.Finalize(); }
|
||||
}
|
17
Engine.Core/Helpers/DelegateExtensions.cs
Normal file
17
Engine.Core/Helpers/DelegateExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class DelegateExtensions
|
||||
{
|
||||
public static void InvokeSafe(this Delegate @delegate, params object?[] args)
|
||||
{
|
||||
foreach (Delegate invocation in @delegate.GetInvocationList())
|
||||
try { invocation.DynamicInvoke(args); }
|
||||
catch (Exception exception)
|
||||
{
|
||||
string methodCallRepresentation = $"{invocation.Method.DeclaringType?.FullName}.{invocation.Method.Name}({string.Join(", ", args)})";
|
||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
||||
}
|
||||
}
|
||||
}
|
7
Engine.Core/Helpers/Progression/IProgressionTracker.cs
Normal file
7
Engine.Core/Helpers/Progression/IProgressionTracker.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IProgressionTracker : IReadOnlyProgressionTracker
|
||||
{
|
||||
void Set(float progression, string status);
|
||||
void Reset();
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IReadOnlyProgressionTracker
|
||||
{
|
||||
event ProgressionUpdatedEventHandler? OnUpdated;
|
||||
event ProgressionEndedEventHandler? OnEnded;
|
||||
|
||||
float Progression { get; }
|
||||
string Status { get; }
|
||||
|
||||
delegate void ProgressionUpdatedEventHandler(IReadOnlyProgressionTracker sender, float previousProgression, string previousStatus);
|
||||
delegate void ProgressionEndedEventHandler(IReadOnlyProgressionTracker sender);
|
||||
}
|
36
Engine.Core/Helpers/Progression/ProgressionTracker.cs
Normal file
36
Engine.Core/Helpers/Progression/ProgressionTracker.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class ProgressionTracker : IProgressionTracker
|
||||
{
|
||||
public event IReadOnlyProgressionTracker.ProgressionUpdatedEventHandler? OnUpdated = null;
|
||||
public event IReadOnlyProgressionTracker.ProgressionEndedEventHandler? OnEnded = null;
|
||||
|
||||
public float Progression { get; private set; } = 0f;
|
||||
public string Status { get; private set; } = "Default";
|
||||
|
||||
void IProgressionTracker.Set(float progression, string status)
|
||||
{
|
||||
if (Progression >= 1f)
|
||||
return;
|
||||
|
||||
float previousProgression = Progression;
|
||||
string previousStatus = Status;
|
||||
|
||||
Progression = progression.Clamp(Progression, 1f);
|
||||
Status = status;
|
||||
|
||||
OnUpdated?.InvokeSafe(this, previousProgression, previousStatus);
|
||||
|
||||
if (progression >= 1f)
|
||||
OnEnded?.InvokeSafe(this);
|
||||
}
|
||||
|
||||
void IProgressionTracker.Reset()
|
||||
{
|
||||
Progression = 0f;
|
||||
Status = "Default";
|
||||
|
||||
OnUpdated = null;
|
||||
OnEnded = null;
|
||||
}
|
||||
}
|
9
Engine.Core/Helpers/Progression/ProgressiveTask.cs
Normal file
9
Engine.Core/Helpers/Progression/ProgressiveTask.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
|
||||
{
|
||||
public static implicit operator (IReadOnlyProgressionTracker progressionTracker, Task<T> task)(ProgressiveTask<T> value) => (value.ProgressionTracker, value.Task);
|
||||
public static implicit operator ProgressiveTask<T>((IReadOnlyProgressionTracker progressionTracker, Task<T> task) value) => new(value.progressionTracker, value.task);
|
||||
}
|
@@ -4,9 +4,10 @@ using System.Numerics;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class Math
|
||||
{/// <summary>
|
||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
||||
/// </summary>
|
||||
{
|
||||
/// <summary>
|
||||
/// The value of Pi (π), a mathematical constant approximately equal to 3.14159.
|
||||
/// </summary>
|
||||
public const float PI = 3.1415926535897932f;
|
||||
|
||||
/// <summary>
|
||||
@@ -29,6 +30,64 @@ public static class Math
|
||||
/// </summary>
|
||||
public const float DegreeToRadian = PI / 180f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets one minus of given <see cref="T"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The value <see cref="T"/>.</param>
|
||||
/// <returns>One minus of given <see cref="T"/>.</returns>
|
||||
public static T OneMinus<T>(T value) where T : INumber<T> => T.One - value;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="T"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="T"/>.</param>
|
||||
/// <param name="value">The second <see cref="T"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="T"/>s.</returns>
|
||||
public static T Add<T>(T left, T value) where T : INumber<T> => left + value;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="T"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="T"/> to subtract from.</param>
|
||||
/// <param name="value">The <see cref="T"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="T"/> from the first.</returns>
|
||||
public static T Subtract<T>(T left, T value) where T : INumber<T> => left - value;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="T"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="T"/>.</param>
|
||||
/// <param name="multiplier">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="T"/> by the scalar value.</returns>
|
||||
public static T Multiply<T>(T left, T multiplier) where T : INumber<T> => left * multiplier;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="T"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="T"/>.</param>
|
||||
/// <param name="divider">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="T"/> by the scalar value.</returns>
|
||||
public static T Divide<T>(T left, T divider) where T : INumber<T> => left / divider;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the true mathematical modulus of a <see cref="T"/> value.
|
||||
/// Unlike the remainder operator (%), this result is always non-negative,
|
||||
/// even when the <paramref name="value"/> operand is negative.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A numeric type that implements <see cref="INumber{T}"/>.</typeparam>
|
||||
/// <param name="value">The dividend <see cref="T"/> value.</param>
|
||||
/// <param name="modulus">The modulus <see cref="T"/> value (must be non-zero).</param>
|
||||
/// <returns>
|
||||
/// The non-negative remainder of <paramref name="value"/> divided by <paramref name="modulus"/>.
|
||||
/// </returns>
|
||||
public static T Mod<T>(T value, T modulus) where T : INumber<T>
|
||||
{
|
||||
T result = value % modulus;
|
||||
if (result < T.Zero)
|
||||
result += modulus;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute value of a number.
|
||||
/// </summary>
|
||||
@@ -37,6 +96,20 @@ public static class Math
|
||||
/// <returns>The absolute value of <paramref name="x"/>.</returns>
|
||||
public static T Abs<T>(T x) where T : INumber<T> => x > T.Zero ? x : -x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the 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>
|
||||
@@ -74,7 +147,7 @@ public static class Math
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||
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.
|
||||
@@ -156,6 +229,16 @@ public static class Math
|
||||
/// <returns>The number <paramref name="x"/> raised to the power <paramref name="y"/>.</returns>
|
||||
public static float Pow(float x, float y) => MathF.Pow(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
@@ -165,6 +248,15 @@ public static class Math
|
||||
/// <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>
|
||||
/// Rounds a number to an integer.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to round.</param>
|
||||
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
|
||||
/// <returns></returns>
|
||||
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
|
||||
public enum RoundMode { Ceil, Floor };
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square of a number.
|
||||
/// </summary>
|
||||
@@ -186,5 +278,4 @@ public static class Math
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The integral part of <paramref name="x"/>.</returns>
|
||||
public static float Truncate(float x) => MathF.Truncate(x);
|
||||
|
||||
}
|
||||
|
97
Engine.Core/MathExtensions.cs
Normal file
97
Engine.Core/MathExtensions.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class MathExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Math.OneMinus{T}(T)" />
|
||||
public static T OneMinus<T>(this T value) where T : INumber<T> => Math.OneMinus(value);
|
||||
|
||||
/// <inheritdoc cref="Math.Add{T}(T, T)" />
|
||||
public static T Add<T>(this T left, T value) where T : INumber<T> => Math.Add(left, value);
|
||||
|
||||
/// <inheritdoc cref="Math.Subtract{T}(T, T)" />
|
||||
public static T Subtract<T>(this T left, T value) where T : INumber<T> => Math.Subtract(left, value);
|
||||
|
||||
/// <inheritdoc cref="Math.Multiply{T}(T, T)" />
|
||||
public static T Multiply<T>(this T left, T multiplier) where T : INumber<T> => Math.Multiply(left, multiplier);
|
||||
|
||||
/// <inheritdoc cref="Math.Divide{T}(T, T)" />
|
||||
public static T Divide<T>(this T left, T divider) where T : INumber<T> => Math.Divide(left, divider);
|
||||
|
||||
/// <inheritdoc cref="Math.Mod{T}(T, T)" />
|
||||
public static T Mod<T>(this T value, T modulus) where T : INumber<T> => Math.Mod(value, modulus);
|
||||
|
||||
/// <inheritdoc cref="Math.Abs{T}(T)" />
|
||||
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.RoundToInt(float, Math.RoundMode)" />
|
||||
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
|
||||
|
||||
/// <inheritdoc cref="Math.Sqr{T}(T)" />
|
||||
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);
|
||||
}
|
10
Engine.Core/Preserver.cs
Normal file
10
Engine.Core/Preserver.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Syntriax.Engine.Core
|
||||
{
|
||||
// This is pretty much so the assembly gets loaded automatically because
|
||||
// the builds include the assembly but sometimes doesn't link load it at startup.
|
||||
// I will hopefully one day fix it and remove this.
|
||||
public static class Preserver
|
||||
{
|
||||
public static void Preserve() { }
|
||||
}
|
||||
}
|
@@ -1,8 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
||||
@@ -65,14 +63,21 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
return new(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="AABB"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
||||
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
|
||||
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,18 +85,9 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
/// </summary>
|
||||
public static class AABBExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a collection of <see cref="Vector2D"/>s to an <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||
/// <inheritdoc cref="AABB.ToAABB" />
|
||||
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="AABB"/>.</param>
|
||||
/// <param name="right">The second <see cref="AABB"/>.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right);
|
||||
/// <inheritdoc cref="AABB.ApproximatelyEquals" />
|
||||
public static bool ApproximatelyEquals(this AABB left, AABB right, float epsilon = float.Epsilon) => AABB.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@@ -1,9 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D circle.
|
||||
@@ -11,7 +8,7 @@ namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
/// <param name="center">The center of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the Circle struct with the specified center and radius.
|
||||
/// Initializes a new instance of the <see cref="Circle"/> struct with the specified center and radius.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
||||
public readonly struct Circle(Vector2D center, float radius)
|
||||
@@ -59,23 +56,27 @@ public readonly struct Circle(Vector2D center, float radius)
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Projection Project(Circle circle, Vector2D projectionVector)
|
||||
public static Projection1D Project(Circle circle, Vector2D projectionVector)
|
||||
{
|
||||
float projectedCenter = circle.Center.Dot(projectionVector);
|
||||
return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
|
||||
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform2D"/>.
|
||||
/// </summary>
|
||||
public static Circle TransformCircle(ITransform transform, Circle circle)
|
||||
=> new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude);
|
||||
public static Circle Transform(ITransform2D transform, Circle circle)
|
||||
=> new(transform.Transform(circle.Center), circle.Radius * (transform.Scale.Magnitude / Vector2D.One.Magnitude));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Circle"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(Circle left, Circle right)
|
||||
=> left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius);
|
||||
/// <param name="left">The first <see cref="Circle"/>.</param>
|
||||
/// <param name="right">The second <see cref="Circle"/>.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="Circle"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon)
|
||||
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,33 +84,24 @@ public readonly struct Circle(Vector2D center, float radius)
|
||||
/// </summary>
|
||||
public static class CircleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Circle.SetCenter(Circle, Vector2D)" />
|
||||
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Circle.SetRadius(Circle, float)" />
|
||||
public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Circle.Displace(Circle, Vector2D)" />
|
||||
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||
/// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
|
||||
public static Projection1D ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Circle"/> by the specified <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
|
||||
/// <inheritdoc cref="Circle.Transform(ITransform2D, Circle)" />
|
||||
public static Circle Transform(this ITransform2D transform, Circle circle) => Circle.Transform(transform, circle);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Circle"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
|
||||
/// <inheritdoc cref="Circle.Transform(ITransform2D, Circle)" />
|
||||
public static Circle Transform(this Circle circle, ITransform2D transform) => Circle.Transform(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);
|
||||
}
|
185
Engine.Core/Primitives/ColorHSV.cs
Normal file
185
Engine.Core/Primitives/ColorHSV.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an HSV color.
|
||||
/// </summary>
|
||||
/// <param name="hue">Hue of the <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="saturation">Saturation of the <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="value">Value of the <see cref="ColorHSV"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||
public readonly struct ColorHSV(float hue, float saturation, float value)
|
||||
{
|
||||
/// <summary>
|
||||
/// The Hue value of the <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
public readonly float Hue = hue.Clamp(0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// The Saturation value of the <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
public readonly float Saturation = saturation.Clamp(0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// The Value value of the <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
public readonly float Value = value.Clamp(0f, 1f);
|
||||
|
||||
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f));
|
||||
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f));
|
||||
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f));
|
||||
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f));
|
||||
public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
|
||||
public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
|
||||
public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f));
|
||||
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value);
|
||||
public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value);
|
||||
|
||||
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba;
|
||||
public static implicit operator ColorHSV(ColorRGB rgb)
|
||||
{
|
||||
float hue;
|
||||
float saturation;
|
||||
float value;
|
||||
|
||||
float rd = rgb.R / 255f;
|
||||
float gd = rgb.G / 255f;
|
||||
float bd = rgb.B / 255f;
|
||||
|
||||
float max = Math.Max(rd, Math.Max(gd, bd));
|
||||
float min = Math.Min(rd, Math.Min(gd, bd));
|
||||
float delta = max - min;
|
||||
|
||||
if (delta.ApproximatelyEquals(0))
|
||||
hue = 0f;
|
||||
else if (max.ApproximatelyEquals(rd))
|
||||
hue = 60f * ((gd - bd) / delta % 6f);
|
||||
else if (max.ApproximatelyEquals(gd))
|
||||
hue = 60f * (((bd - rd) / delta) + 2f);
|
||||
else
|
||||
hue = 60f * (((rd - gd) / delta) + 4f);
|
||||
|
||||
if (hue < 0f)
|
||||
hue += 360f;
|
||||
|
||||
hue /= 360f;
|
||||
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
|
||||
value = max;
|
||||
|
||||
return new(hue, saturation, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the given <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||
/// <returns>The inverted <see cref="ColorHSV"/>.</returns>
|
||||
public static ColorHSV Invert(ColorHSV color) => -color;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="ColorHSV"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="ColorHSV"/>s.</returns>
|
||||
public static ColorHSV Add(ColorHSV left, ColorHSV right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="ColorHSV"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="ColorHSV"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="ColorHSV"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="ColorHSV"/> from the first.</returns>
|
||||
public static ColorHSV Subtract(ColorHSV left, ColorHSV right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="ColorHSV"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="ColorHSV"/> by the scalar value.</returns>
|
||||
public static ColorHSV Multiply(ColorHSV color, float value) => color * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="ColorHSV"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
|
||||
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="ColorHSV"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="ColorHSV"/> from the starting point to the ending point.</returns>
|
||||
public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="ColorHSV"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
|
||||
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ColorHSV"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
|
||||
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="ColorHSV"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(ColorHSV left, ColorHSV right, float epsilon = float.Epsilon)
|
||||
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is ColorHSV objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
|
||||
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="ColorHSV"/> type.
|
||||
/// </summary>
|
||||
public static class ColorHSVExtensions
|
||||
{
|
||||
/// <inheritdoc cref="ColorHSV.Add(ColorHSV, ColorHSV)" />
|
||||
public static ColorHSV Add(this ColorHSV color, ColorHSV value) => ColorHSV.Add(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.Subtract(ColorHSV, ColorHSV)" />
|
||||
public static ColorHSV Subtract(this ColorHSV color, ColorHSV value) => ColorHSV.Subtract(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.Multiply(ColorHSV, ColorHSV)" />
|
||||
public static ColorHSV Multiply(this ColorHSV color, float value) => ColorHSV.Multiply(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
|
||||
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.FromTo(ColorHSV, ColorHSV)" />
|
||||
public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
|
||||
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
|
||||
|
||||
/// <inheritdoc cref="ColorHSV.ApproximatelyEquals(ColorHSV, ColorHSV, float) " />
|
||||
public static bool ApproximatelyEquals(this ColorHSV left, ColorHSV right, float epsilon = float.Epsilon) => ColorHSV.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
164
Engine.Core/Primitives/ColorRGB.cs
Normal file
164
Engine.Core/Primitives/ColorRGB.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an RGB color.
|
||||
/// </summary>
|
||||
/// <param name="r">Red value of the <see cref="ColorRGB"/>.</param>
|
||||
/// <param name="g">Green value of the <see cref="ColorRGB"/>.</param>
|
||||
/// <param name="b">Blue value of the <see cref="ColorRGB"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||
public readonly struct ColorRGB(byte r, byte g, byte b)
|
||||
{
|
||||
/// <summary>
|
||||
/// The Red value of the <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
public readonly byte R = r;
|
||||
|
||||
/// <summary>
|
||||
/// The Green value of the <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
public readonly byte G = g;
|
||||
|
||||
/// <summary>
|
||||
/// The Blue value of the <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
public readonly byte B = b;
|
||||
|
||||
public static ColorRGB operator -(ColorRGB color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B));
|
||||
public static ColorRGB operator +(ColorRGB left, ColorRGB right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255));
|
||||
public static ColorRGB operator -(ColorRGB left, ColorRGB right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255));
|
||||
public static ColorRGB operator *(ColorRGB left, ColorRGB right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255));
|
||||
public static ColorRGB operator *(ColorRGB color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
|
||||
public static ColorRGB operator *(float value, ColorRGB color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
|
||||
public static ColorRGB operator /(ColorRGB color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255));
|
||||
public static bool operator ==(ColorRGB left, ColorRGB right) => left.R == right.R && left.G == right.G && left.B == right.B;
|
||||
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
|
||||
|
||||
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
|
||||
public static implicit operator ColorRGB(ColorHSV hsv)
|
||||
{
|
||||
float hue = hsv.Hue * 360f;
|
||||
float chroma = hsv.Value * hsv.Saturation;
|
||||
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
|
||||
float m = hsv.Value - chroma;
|
||||
|
||||
float r1 = 0f;
|
||||
float g1 = 0f;
|
||||
float b1 = 0f;
|
||||
|
||||
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
|
||||
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
|
||||
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
|
||||
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
|
||||
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
|
||||
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
|
||||
|
||||
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
|
||||
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
|
||||
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
|
||||
|
||||
return new(r, g, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the given <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||
/// <returns>The inverted <see cref="ColorRGB"/>.</returns>
|
||||
public static ColorRGB Invert(ColorRGB color) => -color;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="ColorRGB"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="ColorRGB"/>.</param>
|
||||
/// <param name="right">The second <see cref="ColorRGB"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="ColorRGB"/>s.</returns>
|
||||
public static ColorRGB Add(ColorRGB left, ColorRGB right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="ColorRGB"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="ColorRGB"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="ColorRGB"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="ColorRGB"/> from the first.</returns>
|
||||
public static ColorRGB Subtract(ColorRGB left, ColorRGB right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="ColorRGB"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="ColorRGB"/> by the scalar value.</returns>
|
||||
public static ColorRGB Multiply(ColorRGB color, float value) => color * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="ColorRGB"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGB"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
|
||||
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="ColorRGB"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="ColorRGB"/> from the starting point to the ending point.</returns>
|
||||
public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="ColorRGB"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
|
||||
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ColorRGB"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
|
||||
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is ColorRGB objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
|
||||
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="ColorRGB"/> type.
|
||||
/// </summary>
|
||||
public static class ColorRGBExtensions
|
||||
{
|
||||
/// <inheritdoc cref="ColorRGB.Add(ColorRGB, ColorRGB)" />
|
||||
public static ColorRGB Add(this ColorRGB color, ColorRGB value) => ColorRGB.Add(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGB.Subtract(ColorRGB, ColorRGB)" />
|
||||
public static ColorRGB Subtract(this ColorRGB color, ColorRGB value) => ColorRGB.Subtract(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGB.Multiply(ColorRGB, ColorRGB)" />
|
||||
public static ColorRGB Multiply(this ColorRGB color, float value) => ColorRGB.Multiply(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
|
||||
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGB.FromTo(ColorRGB, ColorRGB)" />
|
||||
public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to);
|
||||
|
||||
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
|
||||
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
|
||||
}
|
147
Engine.Core/Primitives/ColorRGBA.cs
Normal file
147
Engine.Core/Primitives/ColorRGBA.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an RGBA color.
|
||||
/// </summary>
|
||||
/// <param name="r">Red value of the <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="g">Green value of the <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="b">Blue value of the <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="a">Alpha value of the <see cref="ColorRGBA"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
||||
{
|
||||
/// <summary>
|
||||
/// The Red value of the <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
public readonly byte R = r;
|
||||
|
||||
/// <summary>
|
||||
/// The Green value of the <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
public readonly byte G = g;
|
||||
|
||||
/// <summary>
|
||||
/// The Blue value of the <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
public readonly byte B = b;
|
||||
|
||||
/// <summary>
|
||||
/// The Alpha value of the <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
public readonly byte A = a;
|
||||
|
||||
public static ColorRGBA operator -(ColorRGBA color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B), color.A);
|
||||
public static ColorRGBA operator +(ColorRGBA left, ColorRGBA right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255), (byte)(left.A + right.A).Clamp(0, 255));
|
||||
public static ColorRGBA operator -(ColorRGBA left, ColorRGBA right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255), (byte)(left.A - right.A).Clamp(0, 255));
|
||||
public static ColorRGBA operator *(ColorRGBA left, ColorRGBA right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255), (byte)(left.A * right.A).Clamp(0, 255));
|
||||
public static ColorRGBA operator *(ColorRGBA color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
|
||||
public static ColorRGBA operator *(float value, ColorRGBA color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
|
||||
public static ColorRGBA operator /(ColorRGBA color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255), (byte)(color.A / value).Clamp(0, 255));
|
||||
public static bool operator ==(ColorRGBA left, ColorRGBA right) => left.R == right.R && left.G == right.G && left.B == right.B && left.A == right.A;
|
||||
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
|
||||
|
||||
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
|
||||
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorRGB)hsv;
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the given <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||
/// <returns>The inverted <see cref="ColorRGBA"/>.</returns>
|
||||
public static ColorRGBA Invert(ColorRGBA color) => -color;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="ColorRGBA"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="right">The second <see cref="ColorRGBA"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="ColorRGBA"/>s.</returns>
|
||||
public static ColorRGBA Add(ColorRGBA left, ColorRGBA right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="ColorRGBA"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="ColorRGBA"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="ColorRGBA"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="ColorRGBA"/> from the first.</returns>
|
||||
public static ColorRGBA Subtract(ColorRGBA left, ColorRGBA right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="ColorRGBA"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
||||
public static ColorRGBA Multiply(ColorRGBA color, float value) => color * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="ColorRGBA"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
|
||||
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="ColorRGBA"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="ColorRGBA"/> from the starting point to the ending point.</returns>
|
||||
public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="ColorRGBA"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
|
||||
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="ColorRGBA"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
|
||||
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is ColorRGBA objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
|
||||
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="ColorRGBA"/> type.
|
||||
/// </summary>
|
||||
public static class ColorRGBAExtensions
|
||||
{
|
||||
/// <inheritdoc cref="ColorRGBA.Add(ColorRGBA, ColorRGBA)" />
|
||||
public static ColorRGBA Add(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Add(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGBA.Subtract(ColorRGBA, ColorRGBA)" />
|
||||
public static ColorRGBA Subtract(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Subtract(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGBA.Multiply(ColorRGBA, ColorRGBA)" />
|
||||
public static ColorRGBA Multiply(this ColorRGBA color, float value) => ColorRGBA.Multiply(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
|
||||
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
|
||||
|
||||
/// <inheritdoc cref="ColorRGBA.FromTo(ColorRGBA, ColorRGBA)" />
|
||||
public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to);
|
||||
|
||||
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
|
||||
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
|
||||
}
|
219
Engine.Core/Primitives/Line2D.cs
Normal file
219
Engine.Core/Primitives/Line2D.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
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 = Math.Abs(line.From.X);
|
||||
float toX = Math.Abs(line.To.X);
|
||||
float pointX = Math.Abs(point.X);
|
||||
|
||||
float min = Math.Min(fromX, toX);
|
||||
float max = Math.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 <= Math.Max(line.From.X, line.To.X) && point.X >= Math.Min(line.From.X, line.To.X) &&
|
||||
point.Y <= Math.Max(line.From.Y, line.To.Y) && point.Y >= Math.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 = Math.Max(0, Math.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);
|
||||
}
|
53
Engine.Core/Primitives/Line2DEquation.cs
Normal file
53
Engine.Core/Primitives/Line2DEquation.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line equation in the form y = mx + b.
|
||||
/// </summary>
|
||||
/// <param name="slope">The slope of the line.</param>
|
||||
/// <param name="offsetY">The y-intercept of the line.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and y-intercept.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||
public readonly struct Line2DEquation(float slope, float offsetY)
|
||||
{
|
||||
/// <summary>
|
||||
/// The slope of the line equation.
|
||||
/// </summary>
|
||||
public readonly float Slope = slope;
|
||||
|
||||
/// <summary>
|
||||
/// The y-intercept of the line equation.
|
||||
/// </summary>
|
||||
public readonly float OffsetY = offsetY;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
||||
/// </summary>
|
||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
||||
public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two line equations are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first line equation to compare.</param>
|
||||
/// <param name="right">The second line equation to compare.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
||||
public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon)
|
||||
=> left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the LineEquation struct.
|
||||
/// </summary>
|
||||
public static class Line2DEquationExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Line2DEquation.Resolve(Line2DEquation, float)" />
|
||||
public static float Resolve(this Line2DEquation lineEquation, float x) => Line2DEquation.Resolve(lineEquation, x);
|
||||
|
||||
/// <inheritdoc cref="Line2DEquation.ApproximatelyEquals(Line2DEquation, Line2DEquation, float)" />
|
||||
public static bool ApproximatelyEquals(this Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon) => Line2DEquation.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range of values along a single axis.
|
||||
@@ -6,10 +6,10 @@ namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
/// <param name="min">The minimum value of the projection.</param>
|
||||
/// <param name="max">The maximum value of the projection.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Projection"/> struct with the specified minimum and maximum values.
|
||||
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||
public readonly struct Projection(float min, float max)
|
||||
public readonly struct Projection1D(float min, float max)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the minimum value of the projection.
|
||||
@@ -27,7 +27,7 @@ public readonly struct Projection(float min, float max)
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
|
||||
public static bool Overlaps(Projection1D left, Projection1D right) => Overlaps(left, right, out float _);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap and calculates the depth of the overlap.
|
||||
@@ -36,7 +36,7 @@ public readonly struct Projection(float min, float max)
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <param name="depth">The depth of the overlap, if any.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection left, Projection right, out float depth)
|
||||
public static bool Overlaps(Projection1D left, Projection1D right, out float depth)
|
||||
{
|
||||
// TODO Try to improve this
|
||||
bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
|
||||
@@ -73,24 +73,13 @@ public readonly struct Projection(float min, float max)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Projection"/> struct.
|
||||
/// Provides extension methods for the <see cref="Projection1D"/> struct.
|
||||
/// </summary>
|
||||
public static class ProjectionExtensions
|
||||
public static class Projection1DExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
|
||||
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D)" />
|
||||
public static bool Overlaps(this Projection1D left, Projection1D right) => Projection1D.Overlaps(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap and calculates the depth of the overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <param name="depth">The depth of the overlap, if any.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
|
||||
/// <inheritdoc cref="Projection1D.Overlaps(Projection1D, Projection1D, out float)" />
|
||||
public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth);
|
||||
}
|
357
Engine.Core/Primitives/Quaternion.cs
Normal file
357
Engine.Core/Primitives/Quaternion.cs
Normal file
@@ -0,0 +1,357 @@
|
||||
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 = Math.Acos(dot);
|
||||
float sinAngle = Math.Sin(angle);
|
||||
|
||||
float fromWeight = Math.Sin((1f - t) * angle) / sinAngle;
|
||||
float toWeight = Math.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 = Math.Sin(halfAngle);
|
||||
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.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() => System.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);
|
||||
}
|
306
Engine.Core/Primitives/Shape2D.cs
Normal file
306
Engine.Core/Primitives/Shape2D.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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 a <see cref="Shape2D"/> struct with the specified vertices.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||
public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
||||
{
|
||||
public static Shape2D Triangle => CreateNgon(3, Vector2D.Up);
|
||||
public static Shape2D Square => CreateNgon(4, Vector2D.One);
|
||||
public static Shape2D Pentagon => CreateNgon(5, Vector2D.Up);
|
||||
public static Shape2D Hexagon => CreateNgon(6, Vector2D.Right);
|
||||
|
||||
public event ShapeUpdatedEventHandler? OnShapeUpdated = null;
|
||||
|
||||
private List<Vector2D> _vertices = vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertices of the <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Vector2D> Vertices
|
||||
{
|
||||
get => _vertices;
|
||||
set
|
||||
{
|
||||
_vertices.Clear();
|
||||
|
||||
foreach (Vector2D vertex in value)
|
||||
_vertices.Add(vertex);
|
||||
|
||||
OnShapeUpdated?.InvokeSafe(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to copy.</param>
|
||||
/// <returns>A copy of the input <see cref="Shape2D"/>.</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 <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the given <see cref="Shape2D"/>.</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>
|
||||
/// Triangulates the given convex <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to triangulate.</param>
|
||||
/// <param name="triangles">The list to populate with triangles.</param>
|
||||
public static void TriangulateConvex(Shape2D shape, IList<Triangle> triangles)
|
||||
{
|
||||
triangles.Clear();
|
||||
|
||||
for (int i = 2; i < shape.Vertices.Count; i++)
|
||||
triangles.Add(new Triangle(shape[0], shape[i - 1], shape[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triangulates the given convex <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to triangulate.</param>
|
||||
/// <returns>A list of <see cref="Triangle"/>s that makes up the given convex <see cref="Shape2D"/>.</returns>
|
||||
public static List<Triangle> TriangulateConvex(Shape2D shape)
|
||||
{
|
||||
List<Triangle> triangles = new(shape.Vertices.Count - 2);
|
||||
TriangulateConvex(shape, triangles);
|
||||
return triangles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Line2D"/>s that form the edges of the <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to get <see cref="Line2D"/>s from.</param>
|
||||
/// <param name="lines">The list to populate with <see cref="Line2D"/>.</sparam>
|
||||
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 <see cref="Line2D"/>s that form the edges of the <see cref="Shape2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get <see cref="Line2D"/>s from.</param>
|
||||
/// <returns>A list of <see cref="Line2D"/>s that form the edges of the <see cref="Shape2D"/>.</returns>
|
||||
public static List<Line2D> GetLines(Shape2D shape)
|
||||
{
|
||||
List<Line2D> lines = new(shape.Vertices.Count - 1);
|
||||
GetLines(shape, lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Shape2D"/> onto a 1D plane.
|
||||
/// </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 <see cref="Shape2D"/> onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the <see cref="Shape2D"/> 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 <see cref="Shape2D"/> using the specified <see cref="ITransform2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="shape">The <see cref="Shape2D"/> to transform.</param>
|
||||
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
|
||||
/// <returns>The transformed <see cref="Shape2D"/>.</returns>
|
||||
public static Shape2D Transform(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.Transform(shape[i]));
|
||||
|
||||
return new Shape2D(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Shape2D"/> using the specified <see cref="ITransform2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="from">The <see cref="Shape2D"/> to transform.</param>
|
||||
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
|
||||
/// <param name="to">The transformed <see cref="Shape2D"/>.</param>
|
||||
public static void Transform(Shape2D from, ITransform2D transform, Shape2D to)
|
||||
{
|
||||
to._vertices.Clear();
|
||||
|
||||
int count = from._vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
to._vertices.Add(transform.Transform(from[i]));
|
||||
|
||||
to.OnShapeUpdated?.InvokeSafe(to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="Shape2D"/>s are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Shape2D"/> to compare.</param>
|
||||
/// <param name="right">The second <see cref="Shape2D"/> to compare.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><c>true</c> if the <see cref="Shape2D"/>s 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();
|
||||
|
||||
public delegate void ShapeUpdatedEventHandler(Shape2D shape2D);
|
||||
}
|
||||
|
||||
/// <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.TriangulateConvex(Shape2D, IList{Triangle})" />
|
||||
public static void ToTrianglesConvex(this Shape2D shape, IList<Triangle> lines) => Shape2D.TriangulateConvex(shape, lines);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.TriangulateConvex(Shape2D)" />
|
||||
public static List<Triangle> ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(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.Transform(Shape2D, ITransform2D)" />
|
||||
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" />
|
||||
public static void Transform(this ITransform2D transform, Shape2D from, Shape2D to) => Shape2D.Transform(from, transform, to);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" />
|
||||
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
|
||||
|
||||
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D,Shape2D)" />
|
||||
public static void Transform(this Shape2D from, ITransform2D transform, Shape2D to) => Shape2D.Transform(from, transform, 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,8 +1,4 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
|
||||
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
||||
@@ -12,7 +8,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
||||
public readonly Vector2D C { get; init; } = C;
|
||||
|
||||
public readonly float Area
|
||||
=> .5f * MathF.Abs(
|
||||
=> .5f * Math.Abs(
|
||||
A.X * (B.Y - C.Y) +
|
||||
B.X * (C.Y - A.Y) +
|
||||
C.X * (A.Y - B.Y)
|
||||
@@ -27,7 +23,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
||||
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
|
||||
|
||||
Vector2D center;
|
||||
if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon)
|
||||
if (Math.Abs(slopeAB - slopeBC) > float.Epsilon)
|
||||
{
|
||||
float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB));
|
||||
float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f;
|
||||
@@ -39,11 +35,19 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
||||
return new(center, Vector2D.Distance(center, triangle.A));
|
||||
}
|
||||
|
||||
public static bool ApproximatelyEquals(Triangle left, Triangle right)
|
||||
=> left.A.ApproximatelyEquals(right.A) && left.B.ApproximatelyEquals(right.B) && left.C.ApproximatelyEquals(right.C);
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="Triangle"/>s are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Triangle"/> to compare.</param>
|
||||
/// <param name="right">The second <see cref="Triangle"/> to compare.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><c>true</c> if the <see cref="Triangle"/>s are approximately equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon)
|
||||
=> left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon);
|
||||
}
|
||||
|
||||
public static class TriangleExtensions
|
||||
{
|
||||
public static bool ApproximatelyEquals(this Triangle left, Triangle right) => Triangle.ApproximatelyEquals(left, right);
|
||||
/// <inheritdoc cref="Triangle.ApproximatelyEquals(Triangle, Triangle, float)" />
|
||||
public static bool ApproximatelyEquals(this Triangle left, Triangle right, float epsilon = float.Epsilon) => Triangle.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
@@ -1,10 +1,13 @@
|
||||
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)
|
||||
{
|
||||
@@ -72,12 +75,17 @@ public readonly struct Vector2D(float x, float y)
|
||||
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
|
||||
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
|
||||
|
||||
public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
|
||||
public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
|
||||
public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
|
||||
public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
||||
public static float Length(Vector2D vector) => Math.Sqrt(LengthSquared(vector));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
@@ -184,7 +192,7 @@ public readonly struct Vector2D(float x, float y)
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
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.
|
||||
@@ -234,7 +242,7 @@ public readonly struct Vector2D(float x, float y)
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||
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.
|
||||
@@ -268,6 +276,17 @@ public readonly struct Vector2D(float x, float y)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the <see cref="Vector2D"/> using the specified <see cref="ITransform2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to transform.</param>
|
||||
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
|
||||
/// <returns>The transformed <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Transform(Vector2D vector, ITransform2D transform)
|
||||
=> vector.Scale(transform.Scale)
|
||||
.Rotate(transform.Rotation * Math.DegreeToRadian)
|
||||
.Add(transform.Position);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Vector2D"/>s are approximately equal within a specified epsilon range.
|
||||
/// </summary>
|
||||
@@ -295,5 +314,86 @@ public readonly struct Vector2D(float x, float y)
|
||||
/// 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);
|
||||
public override int GetHashCode() => System.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.Transform(Vector2D, ITransform2D)" />
|
||||
public static Vector2D Transform(this Vector2D vector, ITransform2D transform) => Vector2D.Transform(vector, transform);
|
||||
|
||||
/// <inheritdoc cref="Vector2D.Transform(Vector2D, ITransform2D)" />
|
||||
public static Vector2D Transform(this ITransform2D transform, Vector2D vector) => Vector2D.Transform(vector, transform);
|
||||
|
||||
/// <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);
|
||||
}
|
364
Engine.Core/Primitives/Vector3D.cs
Normal file
364
Engine.Core/Primitives/Vector3D.cs
Normal file
@@ -0,0 +1,364 @@
|
||||
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() => System.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);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Serialization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)]
|
||||
public class IgnoreSerializationAttribute : Attribute;
|
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Serialization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public class SerializeAllAttribute : Attribute;
|
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Serialization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class SerializeAttribute : Attribute;
|
3
Engine.Core/Serialization/EntityReference.cs
Normal file
3
Engine.Core/Serialization/EntityReference.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Syntriax.Engine.Core.Serialization;
|
||||
|
||||
public record class EntityReference(string? Id = null);
|
39
Engine.Core/Serialization/EntityRegistry.cs
Normal file
39
Engine.Core/Serialization/EntityRegistry.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core.Serialization;
|
||||
|
||||
public class EntityRegistry
|
||||
{
|
||||
public event EntityRegisteredEventHandler? OnEntityRegistered = null!;
|
||||
|
||||
private readonly Dictionary<string, Action<IEntity>?> assignCallbacks = [];
|
||||
private readonly Dictionary<string, IEntity> registeredEntities = [];
|
||||
public IReadOnlyDictionary<string, IEntity> RegisteredEntities => registeredEntities;
|
||||
|
||||
public void Add(IEntity entity)
|
||||
{
|
||||
if (registeredEntities.TryAdd(entity.Id, entity))
|
||||
OnEntityRegistered?.InvokeSafe(this, entity);
|
||||
}
|
||||
|
||||
public void QueueAssign(string id, Action<IEntity> setMethod)
|
||||
{
|
||||
assignCallbacks.TryAdd(id, null);
|
||||
assignCallbacks[id] = assignCallbacks[id] + setMethod;
|
||||
}
|
||||
|
||||
public void AssignAll()
|
||||
{
|
||||
foreach ((string id, Action<IEntity>? action) in assignCallbacks)
|
||||
action?.InvokeSafe(registeredEntities[id]);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
assignCallbacks.Clear();
|
||||
registeredEntities.Clear();
|
||||
}
|
||||
|
||||
public delegate void EntityRegisteredEventHandler(EntityRegistry sender, IEntity entity);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user