Compare commits
No commits in common. "main" and "breaking-change/demonogame" have entirely different histories.
main
...
breaking-c
|
@ -1,484 +0,0 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from `dotnet new gitignore`
|
||||
|
||||
# dotenv files
|
||||
.env
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
.idea
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Vim temporary swap files
|
||||
*.swp
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// Callback triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
|
||||
/// </summary>
|
||||
Action<IAssignable>? OnUnassigned { get; set; }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableBehaviourController : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableBehaviourController : IAssignable
|
|||
IBehaviourController BehaviourController { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object.
|
||||
/// Assign a value to the <see cref="IBehaviourController"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="behaviourController">New <see cref="IBehaviourController"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableEntity : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IEntity"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableEntity>? OnEntityAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableEntity : IAssignable
|
|||
IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object.
|
||||
/// Assign a value to the <see cref="IEntity"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="entity">New <see cref="IEntity"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ITransform"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableGameManager : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; }
|
||||
|
||||
/// <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);
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableGameObject : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableGameObject : IAssignable
|
|||
IGameObject GameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IGameObject"/> field of this object.
|
||||
/// Assign a value to the <see cref="IGameObject"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="gameObject">New <see cref="IGameObject"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableStateEnable : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableStateEnable : IAssignable
|
|||
IStateEnable StateEnable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object.
|
||||
/// Assign a value to the <see cref="IStateEnable"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="stateEnable">New <see cref="IStateEnable"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Syntriax.Engine.Core.Abstract;
|
|||
public interface IAssignableTransform : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// Callback triggered when the <see cref="ITransform"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableTransform>? OnTransformAssigned { get; set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ public interface IAssignableTransform : IAssignable
|
|||
ITransform Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="ITransform"/> field of this object.
|
||||
/// Assign a value to the <see cref="ITransform"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="transform">New <see cref="ITransform"/> to assign.</param>
|
||||
/// <returns>
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public abstract class BaseEntity : IEntity
|
||||
{
|
||||
public Action<IEntity, string>? OnIdChanged { get; set; } = null;
|
||||
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
|
||||
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _id = string.Empty;
|
||||
|
||||
public virtual IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public virtual bool IsActive => StateEnable.Enabled;
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
if (value == _id)
|
||||
return;
|
||||
|
||||
string previousId = _id;
|
||||
|
||||
_id = value;
|
||||
OnIdChanged?.Invoke(this, previousId);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void UnassignInternal() { }
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
UnassignInternal();
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal() { }
|
||||
public bool Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void FinalizeInternal() { }
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
FinalizeInternal();
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected BaseEntity() => _id = Guid.NewGuid().ToString("D");
|
||||
protected BaseEntity(string id) => _id = id;
|
||||
}
|
|
@ -3,22 +3,17 @@ using System;
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
|
||||
/// Responsible for every behaviour an object in the game might have, controlled by <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignableStateEnable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
|
||||
/// Callback triggered when the <see cref="Priority"/> has changed.
|
||||
/// </summary>
|
||||
Action<IBehaviour>? OnPriorityChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the <see cref="IBehaviour"/>.
|
||||
/// Call priority of the <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IBehaviour"/> is active.
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
}
|
||||
|
|
|
@ -5,101 +5,89 @@ using System.Diagnostics.CodeAnalysis;
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a controller for managing <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// Responsible for controlling <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
|
||||
/// </summary>
|
||||
public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBehaviour>
|
||||
public interface IBehaviourController : IAssignableGameObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered before the update of <see cref="IBehaviour"/>s.
|
||||
/// Callback triggered when the <see cref="Update()"/> is called but right before the <see cref="OnUpdate"/> action is triggered.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered during the update of <see cref="IBehaviour"/>s.
|
||||
/// Callback triggered when the <see cref="Update()"/> is called.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered before the drawing phase.
|
||||
/// Callback triggered when the <see cref="OnPreDraw()"/> is called.
|
||||
/// </summary>
|
||||
Action<IBehaviourController>? OnPreDraw { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been registered a new <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
|
||||
/// Callback triggered when the <see cref="IBehaviourController"/> has been removed an existing <see cref="IBehaviour"/>.
|
||||
/// </summary>
|
||||
Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
|
||||
/// Registers the provided <see cref="IBehaviour"/> to be controlled by the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to add.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
/// <param name="behaviour">Uninitialized <see cref="IBehaviour"/> to be registered.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The provided <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
T AddBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IBehaviour"/> of the specified type to the <see cref="IBehaviourController"/>.
|
||||
/// Instantiates the provided <see cref="IBehaviour"/> type and registers it to the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to add.</typeparam>
|
||||
/// <param name="args">Construction parameters for the <see cref="IBehaviour"/>.</param>
|
||||
/// <returns>The added <see cref="IBehaviour"/>.</returns>
|
||||
/// <param name="args">Constructor parameters for the given <see cref="IBehaviour"/> class.</param>
|
||||
/// <typeparam name="T">An implemented class of <see cref="IBehaviour"/></typeparam>
|
||||
/// <returns>The instantiated <see cref="IBehaviour"/> class after initialization.</returns>
|
||||
T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IBehaviour"/> of the specified type.
|
||||
/// Looks up and tries to get the <see cref="IBehaviour"/> that is controlled by the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
|
||||
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, <see cref="null"/>.</returns>
|
||||
T? GetBehaviour<T>();
|
||||
|
||||
/// <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>
|
||||
/// <param name="behaviour">If return value is <see cref="true"/> outputs the class found in the <see cref="IBehaviourController"/>. If the return value is falls, this parameter is <see cref="null"/></param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/></typeparam>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the type of <see cref="IBehaviour"/> is present in the <see cref="IBehaviourController"/>, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool TryGetBehaviour<T>([NotNullWhen(returnValue: true)] out T? behaviour);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <returns>A list of <see cref="IBehaviour"/>s of the specified type.</returns>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/>.</typeparam>
|
||||
/// <returns>Returns a list of all the matching <see cref="IBehaviour"/>s found in the <see cref="IBehaviourController"/>.</returns>
|
||||
IList<T> GetBehaviours<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IBehaviour"/>s of the specified type and stores them in the provided list.
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
|
||||
/// <param name="behaviours">The list to store the <see cref="IBehaviour"/>s.</param>
|
||||
void GetBehaviours<T>(List<T> behaviours);
|
||||
|
||||
/// <summary>
|
||||
/// Removes <see cref="IBehaviour"/>s of the specified type from the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to remove.</typeparam>
|
||||
/// <param name="removeAll">A flag indicating whether to remove all <see cref="IBehaviour"/>s of the specified type.</param>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified <see cref="IBehaviour"/> from the <see cref="IBehaviourController"/>.
|
||||
/// Removes the <see cref="IBehaviour"/> found in the <see cref="IBehaviourController"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of <see cref="IBehaviour"/> to remove.</typeparam>
|
||||
/// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
|
||||
/// <param name="removeAll">If all of the instances of the given Type is to be removed or not.</param>
|
||||
/// <typeparam name="T">An implemented class or <see cref="interface"/> of <see cref="IBehaviour"/></typeparam>
|
||||
void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// Updates all <see cref="IBehaviour"/>s in the <see cref="IBehaviourController"/>.
|
||||
/// To be called in every frame of the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that a new frame is happening.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void Update();
|
||||
|
||||
/// <summary>
|
||||
/// Performs pre-draw operations.
|
||||
/// To be called before every draw call from the engine. Responsible for notifying <see cref="IBehaviour"/>'s under the <see cref="IBehaviourController"/>'s control that the engine is about to start drawing into the screen.
|
||||
/// </summary>
|
||||
/// <param name=""><see cref=""/> information from the game.</param>
|
||||
void UpdatePreDraw();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface ICamera : IAssignableTransform
|
||||
{
|
||||
Action<ICamera>? OnZoomChanged { get; set; }
|
||||
|
||||
float Zoom { get; set; }
|
||||
|
||||
void Update();
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D camera in the engine.
|
||||
/// </summary>
|
||||
public interface ICamera2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// The zoom level of the camera.
|
||||
/// </summary>
|
||||
float Zoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from screen coordinates to world coordinates.
|
||||
/// </summary>
|
||||
/// <param name="screenPosition">The position in screen coordinates.</param>
|
||||
/// <returns>The position in world coordinates.</returns>
|
||||
Vector2D ScreenToWorldPosition(Vector2D screenPosition);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a position from world coordinates to screen coordinates.
|
||||
/// </summary>
|
||||
/// <param name="worldPosition">The position in world coordinates.</param>
|
||||
/// <returns>The position in screen coordinates.</returns>
|
||||
Vector2D WorldToScreenPosition(Vector2D worldPosition);
|
||||
}
|
|
@ -1,20 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic entity in the engine.
|
||||
/// </summary>
|
||||
public interface IEntity : IInitialize, IAssignableStateEnable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
|
||||
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
Action<IEntity, string>? OnIdChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="IEntity"/>.
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
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>
|
||||
Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a <see cref="IGameObject"/> is unregistered from the <see cref="IGameManager"/>.
|
||||
/// </summary>
|
||||
Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
|
||||
|
||||
/// <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();
|
||||
}
|
|
@ -2,18 +2,9 @@ using System;
|
|||
|
||||
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
|
||||
public interface IGameObject : IEntity, IAssignableTransform, IAssignableBehaviourController, INameable, IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Update"/> method is called.
|
||||
/// </summary>
|
||||
Action<IGameObject>? OnUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the game object.
|
||||
/// </summary>
|
||||
void Update();
|
||||
}
|
||||
|
|
|
@ -2,35 +2,12 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
||||
/// </summary>
|
||||
public interface IInitialize
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
|
||||
/// </summary>
|
||||
Action<IInitialize>? OnInitialized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Finalize"/> method is called successfully.
|
||||
/// </summary>
|
||||
Action<IInitialize>? OnFinalized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the entity has been initialized.
|
||||
/// </summary>
|
||||
bool Initialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the entity.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if initialization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the entity so it can either be recycled or garbage collected.
|
||||
/// </summary>
|
||||
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
|
||||
bool Finalize();
|
||||
}
|
||||
|
|
|
@ -2,18 +2,8 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with a name.
|
||||
/// </summary>
|
||||
public interface INameable
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the name of the entity changes.
|
||||
/// </summary>
|
||||
Action<IEntity>? OnNameChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the entity.
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,18 +2,8 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity with an enable state that can be toggled.
|
||||
/// </summary>
|
||||
public interface IStateEnable : IAssignableEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
|
||||
/// </summary>
|
||||
Action<IStateEnable>? OnEnabledChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
|
||||
/// </summary>
|
||||
bool Enabled { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,38 +2,14 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the transformation properties of an object such as position, scale, and rotation.
|
||||
/// </summary>
|
||||
public interface ITransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnPositionChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnScaleChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
||||
/// </summary>
|
||||
Action<ITransform>? OnRotationChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position of the <see cref="ITransform"/> in 2D space.
|
||||
/// </summary>
|
||||
Vector2D Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the <see cref="ITransform"/>.
|
||||
/// </summary>
|
||||
Vector2D Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the <see cref="ITransform"/> in degrees.
|
||||
/// </summary>
|
||||
float Rotation { get; set; }
|
||||
}
|
||||
|
|
|
@ -5,21 +5,41 @@ 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 : IBehaviour
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IBehaviour>? OnPriorityChanged { get; set; } = null;
|
||||
|
||||
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
|
||||
private bool _initialized = false;
|
||||
private int _priority = 0;
|
||||
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
public override bool IsActive => base.IsActive && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public int Priority
|
||||
{
|
||||
|
@ -34,6 +54,17 @@ public abstract class Behaviour : BaseEntity, IBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
_stateEnable.Assign(this);
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IBehaviourController behaviourController)
|
||||
{
|
||||
if (Initialized)
|
||||
|
@ -44,16 +75,36 @@ public abstract class Behaviour : BaseEntity, IBehaviour
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override void UnassignInternal()
|
||||
public bool Unassign()
|
||||
{
|
||||
base.UnassignInternal();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
_behaviourController = null!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
public bool Initialize()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Finalize()
|
||||
{
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
public Action<BehaviourCacher<T>, T>? OnCached { get; set; } = null;
|
||||
public Action<BehaviourCacher<T>, T>? OnUncached { get; set; } = null;
|
||||
|
||||
private readonly List<T> _behaviours = new(32);
|
||||
|
||||
public IReadOnlyList<T> Behaviours => _behaviours;
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
|
||||
public T this[Index index] => _behaviours[index];
|
||||
|
||||
public BehaviourCacher() { }
|
||||
public BehaviourCacher(IGameManager gameManager) => Assign(gameManager);
|
||||
|
||||
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
private void OnGameObjectUnregistered(IGameManager manager, IGameObject gameObject)
|
||||
{
|
||||
gameObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
|
||||
gameObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not T tBehaviour)
|
||||
return;
|
||||
|
||||
_behaviours.Add(tBehaviour);
|
||||
OnCached?.Invoke(this, tBehaviour);
|
||||
}
|
||||
|
||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not T tBehaviour)
|
||||
return;
|
||||
|
||||
if (!_behaviours.Remove(tBehaviour))
|
||||
return;
|
||||
|
||||
OnUncached?.Invoke(this, tBehaviour);
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
{
|
||||
if (GameManager is not null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in gameManager)
|
||||
{
|
||||
OnGameObjectRegistered(gameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourAdded(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
|
||||
gameManager.OnGameObjectRegistered += OnGameObjectRegistered;
|
||||
gameManager.OnGameObjectUnRegistered += OnGameObjectUnregistered;
|
||||
|
||||
GameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (GameManager is null)
|
||||
return false;
|
||||
|
||||
foreach (IGameObject gameObject in GameManager)
|
||||
{
|
||||
OnGameObjectUnregistered(GameManager, gameObject);
|
||||
foreach (IBehaviour behaviour in gameObject.BehaviourController)
|
||||
OnBehaviourRemoved(gameObject.BehaviourController, behaviour);
|
||||
}
|
||||
|
||||
GameManager.OnGameObjectRegistered -= OnGameObjectRegistered;
|
||||
GameManager.OnGameObjectUnRegistered -= OnGameObjectUnregistered;
|
||||
|
||||
GameManager = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
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 Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
|
@ -41,46 +38,34 @@ public class BehaviourController : IBehaviourController
|
|||
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
|
||||
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_gameObject, args));
|
||||
|
||||
public T? GetBehaviour<T>()
|
||||
{
|
||||
foreach (var 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;
|
||||
foreach (var behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T result)
|
||||
continue;
|
||||
|
||||
behaviour = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
behaviour = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IList<T> GetBehaviours<T>()
|
||||
{
|
||||
List<T>? behaviours = null;
|
||||
IList<T> behaviours = new List<T>();
|
||||
foreach (var behaviourItem in this.behaviours)
|
||||
{
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
|
||||
behaviours ??= [];
|
||||
behaviours ??= new List<T>();
|
||||
behaviours.Add(behaviour);
|
||||
}
|
||||
|
||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||
}
|
||||
|
||||
public void GetBehaviours<T>(List<T> behaviors)
|
||||
{
|
||||
behaviors.Clear();
|
||||
foreach (var behaviourItem in behaviours)
|
||||
{
|
||||
if (behaviourItem is not T _)
|
||||
continue;
|
||||
|
||||
behaviours.Add(behaviourItem);
|
||||
}
|
||||
return behaviours;
|
||||
}
|
||||
|
||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||
|
@ -170,7 +155,4 @@ public class BehaviourController : IBehaviourController
|
|||
behaviours.Remove(behaviour);
|
||||
InsertBehaviourByPriority(behaviour);
|
||||
}
|
||||
|
||||
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
|
||||
}
|
||||
|
|
|
@ -39,13 +39,13 @@ public abstract class BehaviourOverride : Behaviour
|
|||
OnFinalize();
|
||||
}
|
||||
|
||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||
protected virtual void OnPreUpdatePreEnabledCheck() { }
|
||||
protected virtual void OnPreUpdate() { }
|
||||
private void PreUpdate(IBehaviourController _)
|
||||
{
|
||||
OnPreUpdatePreActiveCheck();
|
||||
OnPreUpdatePreEnabledCheck();
|
||||
|
||||
if (!IsActive)
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
if (isInitializedThisFrame)
|
||||
|
@ -61,23 +61,23 @@ public abstract class BehaviourOverride : Behaviour
|
|||
isInitializedThisFrame = false;
|
||||
}
|
||||
|
||||
protected virtual void OnUpdatePreActiveCheck() { }
|
||||
protected virtual void OnUpdatePreEnabledCheck() { }
|
||||
protected virtual void OnUpdate() { }
|
||||
private void Update(IBehaviourController _)
|
||||
{
|
||||
OnUpdatePreActiveCheck();
|
||||
OnUpdatePreEnabledCheck();
|
||||
|
||||
if (!IsActive)
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
OnUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||
protected virtual void OnPreDrawPreEnabledCheck() { }
|
||||
protected virtual void OnPreDraw() { }
|
||||
private void PreDraw(IBehaviourController _)
|
||||
{
|
||||
OnPreDrawPreActiveCheck();
|
||||
OnPreDrawPreEnabledCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Core", "Engine.Core.csproj", "{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {CC449885-B99C-4C71-842D-3C19A35E786F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -2,10 +2,8 @@ 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;
|
||||
}
|
||||
public record EngineTime
|
||||
(
|
||||
TimeSpan Total,
|
||||
TimeSpan Elapsed
|
||||
);
|
||||
|
|
|
@ -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,32 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class BehaviourExtensions
|
||||
{
|
||||
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour)
|
||||
{
|
||||
behaviour = default;
|
||||
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
if (gameObject.BehaviourController.TryGetBehaviour(out behaviour))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours)
|
||||
{
|
||||
behaviours.Clear();
|
||||
List<T> cache = [];
|
||||
|
||||
foreach (IGameObject gameObject in gameObjects)
|
||||
{
|
||||
gameObject.BehaviourController.GetBehaviours(cache);
|
||||
behaviours.AddRange(cache);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class FloatExtensions
|
||||
{
|
||||
public static bool ApproximatelyEquals(this float a, float b)
|
||||
=> ApproximatelyEquals(a, b, float.Epsilon);
|
||||
public static bool ApproximatelyEquals(this float a, float b, float epsilon)
|
||||
{
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
const float floatNormal = (1 << 23) * float.Epsilon;
|
||||
float absA = Math.Abs(a);
|
||||
float absB = Math.Abs(b);
|
||||
float diff = Math.Abs(a - b);
|
||||
|
||||
if (a == 0.0f || b == 0.0f || diff < floatNormal)
|
||||
return diff < (epsilon * floatNormal);
|
||||
|
||||
return diff / Math.Min(absA + absB, float.MaxValue) < epsilon;
|
||||
}
|
||||
}
|
|
@ -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,14 +0,0 @@
|
|||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static ITransform SetTransform(this ITransform transform, Vector2D? position = null, float? rotation = null, Vector2D? scale = null)
|
||||
{
|
||||
if (position.HasValue) transform.Position = position.Value;
|
||||
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||
if (scale.HasValue) transform.Scale = scale.Value;
|
||||
return transform;
|
||||
}
|
||||
}
|
|
@ -1,188 +1,22 @@
|
|||
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);
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.FromTo(vector, scale);
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
|
|
@ -8,16 +8,24 @@ using Syntriax.Engine.Core.Factory;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
|
||||
public class GameManager : BaseEntity, IGameManager
|
||||
public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||
{
|
||||
public Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||
public Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
public Action<GameManager>? OnCameraChanged { get; set; } = null;
|
||||
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
|
||||
|
||||
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
private IList<IGameObject> _gameObjects = new List<IGameObject>(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private GameObjectFactory _gameObjectFactory = null!;
|
||||
private bool _initialized = false;
|
||||
private ICamera _camera = null!;
|
||||
|
||||
private GameObjectFactory GameObjectFactory
|
||||
{
|
||||
|
@ -29,20 +37,33 @@ public class GameManager : BaseEntity, IGameManager
|
|||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||
|
||||
public override IStateEnable StateEnable
|
||||
public bool Initialized => _initialized;
|
||||
public IList<IGameObject> GameObjects => _gameObjects;
|
||||
public IStateEnable StateEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (base.StateEnable is null)
|
||||
if (_stateEnable is null)
|
||||
{
|
||||
Assign(new StateEnableFactory().Instantiate(this));
|
||||
if (base.StateEnable is null)
|
||||
throw NotAssignedException.From(this, base.StateEnable);
|
||||
if (_stateEnable is null)
|
||||
throw NotAssignedException.From(this, _stateEnable);
|
||||
}
|
||||
|
||||
return base.StateEnable;
|
||||
return _stateEnable;
|
||||
}
|
||||
}
|
||||
|
||||
public ICamera Camera
|
||||
{
|
||||
get => _camera;
|
||||
set
|
||||
{
|
||||
if (_camera == value)
|
||||
return;
|
||||
|
||||
_camera = value;
|
||||
OnCameraChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,20 +91,50 @@ public class GameManager : BaseEntity, IGameManager
|
|||
return gameObject;
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
public bool Initialize()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, StateEnable);
|
||||
|
||||
foreach (var gameObject in GameObjects)
|
||||
gameObject.Initialize();
|
||||
|
||||
OnInitialized?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
public bool Finalize()
|
||||
{
|
||||
base.FinalizeInternal();
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
for (int i = GameObjects.Count; i >= 0; i--)
|
||||
GameObjects[i].Finalize();
|
||||
|
||||
OnFinalized?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(EngineTime time)
|
||||
|
@ -103,8 +154,6 @@ public class GameManager : BaseEntity, IGameManager
|
|||
|
||||
private void Register(IGameObject gameObject)
|
||||
{
|
||||
gameObject.Assign(this);
|
||||
|
||||
gameObject.OnFinalized += OnGameObjectFinalize;
|
||||
|
||||
_gameObjects.Add(gameObject);
|
||||
|
|
|
@ -5,28 +5,47 @@ using Syntriax.Engine.Core.Exceptions;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||
public class GameObject : BaseEntity, IGameObject
|
||||
public class GameObject : IGameObject
|
||||
{
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get; set; } = null;
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
public Action<IEntity>? OnNameChanged { get; set; } = null;
|
||||
|
||||
public Action<IInitialize>? OnInitialized { get; set; } = null;
|
||||
public Action<IInitialize>? OnFinalized { get; set; } = null;
|
||||
|
||||
public Action<IGameObject>? OnUpdated { get; set; } = null;
|
||||
|
||||
|
||||
private ITransform _transform = null!;
|
||||
private IBehaviourController _behaviourController = null!;
|
||||
private IStateEnable _stateEnable = null!;
|
||||
private IGameManager _gameManager = null!;
|
||||
|
||||
private string _name = nameof(GameObject);
|
||||
private bool _initialized = false;
|
||||
|
||||
public ITransform Transform => _transform;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
public IGameManager GameManager => _gameManager;
|
||||
public IStateEnable StateEnable => _stateEnable;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
private set
|
||||
{
|
||||
if (value == _initialized)
|
||||
return;
|
||||
|
||||
_initialized = value;
|
||||
if (value)
|
||||
OnInitialized?.Invoke(this);
|
||||
else
|
||||
OnFinalized?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
|
@ -40,14 +59,17 @@ public class GameObject : BaseEntity, IGameObject
|
|||
}
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
public bool Initialize()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
NotAssignedException.Check(this, _transform);
|
||||
NotAssignedException.Check(this, _behaviourController);
|
||||
NotAssignedException.Check(this, _stateEnable);
|
||||
NotAssignedException.Check(this, _gameManager);
|
||||
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
|
@ -58,12 +80,29 @@ public class GameObject : BaseEntity, IGameObject
|
|||
OnUpdated?.Invoke(this);
|
||||
}
|
||||
|
||||
protected override void FinalizeInternal()
|
||||
public bool Finalize()
|
||||
{
|
||||
base.FinalizeInternal();
|
||||
if (!Initialized)
|
||||
return false;
|
||||
|
||||
foreach (IBehaviour behaviour in _behaviourController.GetBehaviours<IBehaviour>())
|
||||
behaviour.Finalize();
|
||||
|
||||
System.Threading.Tasks.Parallel.ForEach(
|
||||
_behaviourController.GetBehaviours<IBehaviour>(),
|
||||
behaviour => behaviour.Finalize()
|
||||
);
|
||||
|
||||
Initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IStateEnable stateEnable)
|
||||
{
|
||||
if (Initialized)
|
||||
return false;
|
||||
|
||||
_stateEnable = stateEnable;
|
||||
OnStateEnableAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(ITransform transform)
|
||||
|
@ -86,24 +125,17 @@ public class GameObject : BaseEntity, IGameObject
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
public bool Unassign()
|
||||
{
|
||||
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!;
|
||||
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
using System;
|
||||
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>
|
||||
public const float PI = 3.1415926535897932f;
|
||||
|
||||
/// <summary>
|
||||
/// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319.
|
||||
/// </summary>
|
||||
public const float Tau = 2f * PI;
|
||||
|
||||
/// <summary>
|
||||
/// The base of the natural logarithm, approximately equal to 2.71828.
|
||||
/// </summary>
|
||||
public const float E = 2.718281828459045f;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from radians to degrees.
|
||||
/// </summary>
|
||||
public const float RadianToDegree = 180f / PI;
|
||||
|
||||
/// <summary>
|
||||
/// The conversion factor from degrees to radians.
|
||||
/// </summary>
|
||||
public const float DegreeToRadian = PI / 180f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute value of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The absolute value of <paramref name="x"/>.</returns>
|
||||
public static T Abs<T>(T x) where T : INumber<T> => x > T.Zero ? x : -x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arccosine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arccosine of <paramref name="x"/>.</returns>
|
||||
public static float Acos(float x) => MathF.Acos(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arcsine of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The arcsine of <paramref name="x"/>.</returns>
|
||||
public static float Asin(float x) => MathF.Asin(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle whose tangent is the quotient of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="y">The y-coordinate of a point.</param>
|
||||
/// <param name="x">The x-coordinate of a point.</param>
|
||||
/// <returns>The angle, measured in radians.</returns>
|
||||
public static float Atan2(float y, float x) => MathF.Atan2(y, x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hyperbolic arctangent of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The hyperbolic arctangent of <paramref name="x"/>.</returns>
|
||||
public static float Atanh(float x) => MathF.Atanh(x);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a number between a minimum and maximum value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smallest integral value that is greater than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The smallest integral value that is greater than or equal to <paramref name="x"/>.</returns>
|
||||
public static float Ceiling(float x) => MathF.Ceiling(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The magnitude value.</param>
|
||||
/// <param name="y">The sign value.</param>
|
||||
/// <returns>A value with the magnitude of <paramref name="x"/> and the sign of <paramref name="y"/>.</returns>
|
||||
public static float CopySign(float x, float y) => MathF.CopySign(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the largest integral value that is less than or equal to the specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The largest integral value that is less than or equal to <paramref name="x"/>.</returns>
|
||||
public static float Floor(float x) => MathF.Floor(x);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remainder of the division of two specified numbers.
|
||||
/// </summary>
|
||||
/// <param name="x">The dividend.</param>
|
||||
/// <param name="y">The divisor.</param>
|
||||
/// <returns>The remainder of the division of <paramref name="x"/> by <paramref name="y"/>.</returns>
|
||||
public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the natural (base e) logarithm of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <param name="y">The base.</param>
|
||||
/// <returns>The natural logarithm of <paramref name="x"/> with base <paramref name="y"/>.</returns>
|
||||
public static float Log(float x, float y) => MathF.Log(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the larger of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The larger of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Max<T>(T x, T y) where T : INumber<T> => (x > y) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is larger.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is larger.</returns>
|
||||
public static T AbsMax<T>(T x, T y) where T : INumber<T> => (Abs(x) > Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smaller of two numbers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numbers.</typeparam>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The smaller of <paramref name="x"/> and <paramref name="y"/>.</returns>
|
||||
public static T Min<T>(T x, T y) where T : INumber<T> => (x < y) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number whose absolute value is smaller.
|
||||
/// </summary>
|
||||
/// <param name="x">The first number.</param>
|
||||
/// <param name="y">The second number.</param>
|
||||
/// <returns>The number whose absolute value is smaller.</returns>
|
||||
public static T AbsMin<T>(T x, T y) where T : INumber<T> => (Abs(x) < Abs(y)) ? x : y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specified number raised to the specified power.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to raise to a power.</param>
|
||||
/// <param name="y">The power to raise <paramref name="x"/> to.</param>
|
||||
/// <returns>The number <paramref name="x"/> raised to the power <paramref name="y"/>.</returns>
|
||||
public static float Pow(float x, float y) => MathF.Pow(x, y);
|
||||
|
||||
/// <summary>
|
||||
/// Rounds a number to a specified number of fractional digits.
|
||||
/// </summary>
|
||||
/// <param name="x">The number to round.</param>
|
||||
/// <param name="digits">The number of fractional digits in the return value.</param>
|
||||
/// <param name="mode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
|
||||
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
|
||||
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square of a number.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the number.</typeparam>
|
||||
/// <param name="x">The number to square.</param>
|
||||
/// <returns>The square of <paramref name="x"/>.</returns>
|
||||
public static T Sqr<T>(T x) where T : INumber<T> => x * x;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square root of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The square root of <paramref name="x"/>.</returns>
|
||||
public static float Sqrt(float x) => MathF.Sqrt(x);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the integral part of a number.
|
||||
/// </summary>
|
||||
/// <param name="x">The number.</param>
|
||||
/// <returns>The integral part of <paramref name="x"/>.</returns>
|
||||
public static float Truncate(float x) => MathF.Truncate(x);
|
||||
|
||||
}
|
|
@ -9,7 +9,5 @@ public static class Time
|
|||
public static TimeSpan Total => _engineTime.Total;
|
||||
public static TimeSpan Elapsed => _engineTime.Elapsed;
|
||||
|
||||
public static float DeltaTimeFrame => _engineTime.DeltaTimeFrame;
|
||||
|
||||
public static void SetTime(EngineTime engineTime) => _engineTime = engineTime;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ using Syntriax.Engine.Core.Abstract;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
||||
public class Transform : ITransform
|
||||
{
|
||||
public Action<ITransform>? OnPositionChanged { get; set; } = null;
|
||||
|
|
|
@ -2,65 +2,18 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a two-dimensional vector.
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||
public readonly struct Vector2D(float x, float y)
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")]
|
||||
public record Vector2D(float X, float Y)
|
||||
{
|
||||
/// <summary>
|
||||
/// The X coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float X = x;
|
||||
|
||||
/// <summary>
|
||||
/// The Y coordinate of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly float Y = y;
|
||||
|
||||
/// <summary>
|
||||
/// The magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float Magnitude => Length(this);
|
||||
|
||||
/// <summary>
|
||||
/// The squared magnitude (length) of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
||||
/// </summary>
|
||||
public Vector2D Normalized => Normalize(this);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing upwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Up = new(0f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing downwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Down = new(0f, -1f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing leftwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Left = new(-1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the unit <see cref="Vector2D"/> pointing rightwards.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Right = new(1f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the zero <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public readonly static Vector2D Zero = new(0f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the <see cref="Vector2D"/> with both components equal to 1.
|
||||
/// </summary>
|
||||
public readonly static Vector2D One = new(1f, 1f);
|
||||
|
||||
public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
|
||||
|
@ -69,231 +22,25 @@ public readonly struct Vector2D(float x, float y)
|
|||
public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
|
||||
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
|
||||
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
|
||||
|
||||
/// <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));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The squared length of the <see cref="Vector2D"/>.</returns>
|
||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The start <see cref="Vector2D"/>.</param>
|
||||
/// <param name="to">The end <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the direction of the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The inverted <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The sum of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one <see cref="Vector2D"/> from another.
|
||||
/// </summary>
|
||||
/// <param name="left">The <see cref="Vector2D"/> to subtract from.</param>
|
||||
/// <param name="right">The <see cref="Vector2D"/> to subtract.</param>
|
||||
/// <returns>The result of subtracting the second <see cref="Vector2D"/> from the first.</returns>
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of multiplying the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
|
||||
/// <summary>
|
||||
/// Divides a <see cref="Vector2D"/> by a scalar value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <param name="value">The scalar value.</param>
|
||||
/// <returns>The result of dividing the <see cref="Vector2D"/> by the scalar value.</returns>
|
||||
public static Vector2D Divide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the absolute value of each component of the vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> with each component's absolute value.</returns>
|
||||
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the <see cref="Vector2D"/> (creates a unit <see cref="Vector2D"/> with the same direction).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to normalize.</param>
|
||||
/// <returns>The normalized <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||
|
||||
/// <summary>
|
||||
/// Reflects a <see cref="Vector2D"/> off a surface with the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The incident <see cref="Vector2D"/>.</param>
|
||||
/// <param name="normal">The normal <see cref="Vector2D"/> of the surface.</param>
|
||||
/// <returns>The reflected <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Vector2D"/> from one point to another.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point.</param>
|
||||
/// <param name="to">The ending point.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> from the starting point to the ending point.</returns>
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
|
||||
/// <summary>
|
||||
/// Scales a <see cref="Vector2D"/> by another <see cref="Vector2D"/> component-wise.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to scale.</param>
|
||||
/// <param name="scale">The <see cref="Vector2D"/> containing the scaling factors for each component.</param>
|
||||
/// <returns>The scaled <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a perpendicular <see cref="Vector2D"/> to the given <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="vector">The input <see cref="Vector2D"/>.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> perpendicular to the input <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a <see cref="Vector2D"/> by the specified angle (in radians).
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to rotate.</param>
|
||||
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||
/// <returns>The rotated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise minimum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the minimum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component-wise maximum of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The <see cref="Vector2D"/> containing the maximum components from both input <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Clamps each component of a <see cref="Vector2D"/> between the corresponding component of two other <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vector">The <see cref="Vector2D"/> to clamp.</param>
|
||||
/// <param name="min">The <see cref="Vector2D"/> representing the minimum values for each component.</param>
|
||||
/// <param name="max">The <see cref="Vector2D"/> representing the maximum values for each component.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector2D"/>s.</returns>
|
||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting <see cref="Vector2D"/> (t = 0).</param>
|
||||
/// <param name="to">The ending <see cref="Vector2D"/> (t = 1).</param>
|
||||
/// <param name="t">The interpolation parameter.</param>
|
||||
/// <returns>The interpolated <see cref="Vector2D"/>.</returns>
|
||||
public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The cross product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The angle between the two <see cref="Vector2D"/>s in radians.</returns>
|
||||
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <returns>The dot product of the two <see cref="Vector2D"/>s.</returns>
|
||||
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the orientation of three points represented by <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="middle">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The third <see cref="Vector2D"/>.</param>
|
||||
/// <returns>
|
||||
/// <para>0 - Collinear.</para>
|
||||
/// <para>1 - Clockwise.</para>
|
||||
/// <para>2 - Counterclockwise.</para>
|
||||
/// </returns>
|
||||
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
|
||||
{
|
||||
Vector2D leftToMiddle = left.FromTo(middle);
|
||||
Vector2D middleToRight = middle.FromTo(right);
|
||||
|
||||
float value = leftToMiddle.Y * middleToRight.X -
|
||||
leftToMiddle.X * middleToRight.Y;
|
||||
|
||||
if (value > 0) return 1;
|
||||
if (value < 0) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Vector2D"/>s are approximately equal within a specified epsilon range.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="Vector2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="Vector2D"/>.</param>
|
||||
/// <param name="epsilon">The epsilon range.</param>
|
||||
/// <returns><see cref="true"/> if the <see cref="Vector2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Vector2D"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the <see cref="Vector2D"/>.</returns>
|
||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash code for the <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
|
||||
public override int GetHashCode() => HashCode.Combine(X, Y);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Input", "Engine.Input.csproj", "{637B86E7-3699-4248-9A9F-C5CB09779B53}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{637B86E7-3699-4248-9A9F-C5CB09779B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{637B86E7-3699-4248-9A9F-C5CB09779B53}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{637B86E7-3699-4248-9A9F-C5CB09779B53}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{637B86E7-3699-4248-9A9F-C5CB09779B53}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FB9A6AA6-FAEF-4DE6-BDE0-C9CC4F02B29A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,19 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with the shape of a <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public interface ICircleCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The local <see cref="Circle"/> shape of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Circle CircleLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The world space representation of the <see cref="Circle"/> shape.
|
||||
/// </summary>
|
||||
Circle CircleWorld { get; }
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collider.
|
||||
/// </summary>
|
||||
public interface ICollider2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is detected.
|
||||
/// </summary>
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a collision is resolved.
|
||||
/// </summary>
|
||||
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRigidBody2D"/> associated with the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
IRigidBody2D? RigidBody2D { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="ICollider2D"/> is a trigger.
|
||||
/// </summary>
|
||||
bool IsTrigger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates <see cref="ICollider2D"/> properties.
|
||||
/// </summary>
|
||||
void Recalculate();
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision detector.
|
||||
/// </summary>
|
||||
public interface ICollisionDetector2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to detect a collision between two <see cref="ICollider2D"/>s.
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">Type of the first <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <typeparam name="T2">Type of the second <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <param name="left">The first <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="right">The second <see cref="ICollider2D"/>.</param>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
/// <returns><see cref="true"/> if a collision is detected, otherwise <see cref="false"/>.</returns>
|
||||
bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D collision resolver.
|
||||
/// </summary>
|
||||
public interface ICollisionResolver2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves collisions based on collision detection information provided.
|
||||
/// </summary>
|
||||
/// <param name="collisionInformation">Information about the collision.</param>
|
||||
void Resolve(CollisionDetectionInformation collisionInformation);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics engine.
|
||||
/// </summary>
|
||||
public interface IPhysicsEngine2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of iterations the <see cref="IPhysicsEngine2D"/> performs per step.
|
||||
/// </summary>
|
||||
int IterationPerStep { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Advances the physics simulation by the specified time.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The time step.</param>
|
||||
void Step(float deltaTime);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D physics object's responsive attributes.
|
||||
/// </summary>
|
||||
public interface IPhysicsMaterial2D
|
||||
{
|
||||
/// <summary>
|
||||
/// The friction coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Friction { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The restitution (bounciness) coefficient of the physics object.
|
||||
/// </summary>
|
||||
float Restitution { get; }
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D rigid body in the engine.
|
||||
/// </summary>
|
||||
public interface IRigidBody2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// The physics material of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
IPhysicsMaterial2D Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The velocity of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
Vector2D Velocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The angular velocity (rotation rate) of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float AngularVelocity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the <see cref="IRigidBody2D"/>.
|
||||
/// </summary>
|
||||
float Mass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value indicating whether the <see cref="IRigidBody2D"/> is static/immovable.
|
||||
/// </summary>
|
||||
bool IsStatic { get; set; }
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="ICollider2D"/> with a custom <see cref="Shape"/>.
|
||||
/// </summary>
|
||||
public interface IShapeCollider2D : ICollider2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the local <see cref="Shape"/> of the <see cref="ICollider2D"/>.
|
||||
/// </summary>
|
||||
Shape ShapeLocal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world space representation of the <see cref="Shape"/>.
|
||||
/// </summary>
|
||||
Shape ShapeWorld { get; }
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
|
||||
{
|
||||
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; } = null;
|
||||
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; } = null;
|
||||
public Action<ICollider2D, ICollider2D>? OnTriggered { get; set; } = null;
|
||||
|
||||
|
||||
protected bool NeedsRecalculation { get; private set; } = true;
|
||||
protected IRigidBody2D? _rigidBody2D = null;
|
||||
|
||||
public IRigidBody2D? RigidBody2D => _rigidBody2D;
|
||||
public bool IsTrigger { get; set; } = false;
|
||||
|
||||
ITransform IAssignableTransform.Transform => Transform;
|
||||
Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
|
||||
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
|
||||
public void Recalculate()
|
||||
{
|
||||
if (!NeedsRecalculation)
|
||||
return;
|
||||
|
||||
CalculateCollider();
|
||||
NeedsRecalculation = false;
|
||||
}
|
||||
|
||||
public abstract void CalculateCollider();
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
BehaviourController.TryGetBehaviour(out _rigidBody2D);
|
||||
|
||||
BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
|
||||
|
||||
Transform.OnPositionChanged += SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged += SetNeedsRecalculation;
|
||||
Transform.OnScaleChanged += SetNeedsRecalculation;
|
||||
}
|
||||
|
||||
private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is IRigidBody2D rigidBody)
|
||||
_rigidBody2D = rigidBody;
|
||||
}
|
||||
|
||||
private void OnBehaviourRemovedFromController(IBehaviourController _, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is IRigidBody2D _)
|
||||
_rigidBody2D = null;
|
||||
}
|
||||
|
||||
private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true;
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController;
|
||||
Transform.OnScaleChanged -= SetNeedsRecalculation;
|
||||
|
||||
Transform.OnPositionChanged -= SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged -= SetNeedsRecalculation;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D
|
||||
{
|
||||
public Circle CircleWorld { get; protected set; } = Circle.UnitCircle;
|
||||
public Circle CircleLocal { get; set; } = Circle.UnitCircle;
|
||||
|
||||
|
||||
public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal);
|
||||
|
||||
|
||||
public Collider2DCircleBehaviour() { }
|
||||
public Collider2DCircleBehaviour(Circle circle) => CircleLocal = circle;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D
|
||||
{
|
||||
public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
|
||||
public Shape ShapeLocal { get; set; } = Shape.Box;
|
||||
|
||||
private Shape _shapeWorld = Shape.Box.CreateCopy();
|
||||
|
||||
public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld);
|
||||
|
||||
|
||||
public Collider2DShapeBehaviour() { }
|
||||
public Collider2DShapeBehaviour(Shape shape) => ShapeLocal = shape;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Normal: {Normal.ToString(), nq}, Penetration: {Penetration}")]
|
||||
public readonly struct CollisionDetectionInformation
|
||||
(
|
||||
ICollider2D Left,
|
||||
ICollider2D Right,
|
||||
Vector2D Normal,
|
||||
float Penetration
|
||||
)
|
||||
{
|
||||
public ICollider2D Left { get; init; } = Left;
|
||||
public ICollider2D Right { get; init; } = Right;
|
||||
public Vector2D Normal { get; init; } = Normal;
|
||||
public float Penetration { get; init; } = Penetration;
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class CollisionDetector2D : ICollisionDetector2D
|
||||
{
|
||||
public bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation)
|
||||
where T1 : ICollider2D
|
||||
where T2 : ICollider2D
|
||||
{
|
||||
collisionInformation = default;
|
||||
if (left is IShapeCollider2D shapeColliderLeft)
|
||||
{
|
||||
if (right is IShapeCollider2D shapeColliderRight)
|
||||
return DetectShapeShape(shapeColliderLeft, shapeColliderRight, out collisionInformation);
|
||||
else if (right is ICircleCollider2D circleColliderRight)
|
||||
return DetectShapeCircle(shapeColliderLeft, circleColliderRight, out collisionInformation);
|
||||
}
|
||||
else if (left is ICircleCollider2D circleColliderLeft)
|
||||
{
|
||||
if (right is IShapeCollider2D shapeColliderRight)
|
||||
return DetectCircleShape(circleColliderLeft, shapeColliderRight, out collisionInformation);
|
||||
else if (right is ICircleCollider2D circleColliderRight)
|
||||
return DetectCircleCircle(circleColliderLeft, circleColliderRight, out collisionInformation);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation);
|
||||
}
|
||||
|
||||
private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
collisionInformation = default;
|
||||
return DetectShapeShapeOneWay(left, right, ref collisionInformation) && DetectShapeShapeOneWay(right, left, ref collisionInformation);
|
||||
}
|
||||
|
||||
private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
var vertices = left.ShapeWorld.Vertices;
|
||||
int count = vertices.Count;
|
||||
|
||||
for (int indexProjection = 0; indexProjection < count; indexProjection++)
|
||||
{
|
||||
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
|
||||
|
||||
Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector);
|
||||
|
||||
if (!leftProjection.Overlaps(rightProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(left, right, projectionVector, depth);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
collisionInformation = default;
|
||||
|
||||
var vertices = shapeCollider.ShapeWorld.Vertices;
|
||||
int count = vertices.Count;
|
||||
|
||||
for (int indexProjection = 0; indexProjection < count; indexProjection++)
|
||||
{
|
||||
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
|
||||
|
||||
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
|
||||
Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
|
||||
|
||||
if (!shapeProjection.Overlaps(circleProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth);
|
||||
}
|
||||
|
||||
{
|
||||
Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized;
|
||||
|
||||
Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
|
||||
|
||||
if (!shapeProjection.Overlaps(circleProjection, out float depth))
|
||||
return false;
|
||||
|
||||
if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
|
||||
collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
collisionInformation = default;
|
||||
|
||||
Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
|
||||
|
||||
Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
|
||||
bool collision = leftProjection.Overlaps(rightProjection, out float depth);
|
||||
|
||||
if (collision)
|
||||
collisionInformation = new(left, right, leftToRightCenterProjectionVector, depth);
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
// private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation)
|
||||
// {
|
||||
// collisionInformation = default;
|
||||
|
||||
// Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center);
|
||||
// float distanceCircleCenter = leftToRightCenter.Magnitude;
|
||||
// float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius;
|
||||
|
||||
// float circleSurfaceDistance = distanceCircleCenter - radiusSum;
|
||||
|
||||
// bool collision = circleSurfaceDistance <= 0f;
|
||||
|
||||
// if (collision)
|
||||
// collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance);
|
||||
|
||||
// return collision;
|
||||
// }
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class CollisionResolver2D : ICollisionResolver2D
|
||||
{
|
||||
public void Resolve(CollisionDetectionInformation collisionInformation)
|
||||
{
|
||||
Vector2D displacementVector = collisionInformation.Normal * collisionInformation.Penetration;
|
||||
|
||||
ICollider2D left = collisionInformation.Left;
|
||||
ICollider2D right = collisionInformation.Right;
|
||||
|
||||
bool isLeftStatic = left.RigidBody2D?.IsStatic ?? true;
|
||||
bool isRightStatic = right.RigidBody2D?.IsStatic ?? true;
|
||||
|
||||
if (isLeftStatic && isRightStatic)
|
||||
return;
|
||||
|
||||
if (isLeftStatic)
|
||||
right.Transform.Position += displacementVector;
|
||||
else if (isRightStatic)
|
||||
left.Transform.Position -= displacementVector;
|
||||
else
|
||||
{
|
||||
float leftMass = left.RigidBody2D?.Mass ?? float.Epsilon;
|
||||
float rightMass = right.RigidBody2D?.Mass ?? float.Epsilon;
|
||||
float sumMass = leftMass + rightMass;
|
||||
|
||||
float leftMomentumPercentage = leftMass / sumMass;
|
||||
float rightMomentumPercentage = rightMass / sumMass;
|
||||
|
||||
right.Transform.Position += leftMomentumPercentage * displacementVector;
|
||||
left.Transform.Position -= rightMomentumPercentage * displacementVector;
|
||||
}
|
||||
|
||||
left.Recalculate();
|
||||
right.Recalculate();
|
||||
|
||||
left.OnCollisionResolved?.Invoke(collisionInformation.Left, collisionInformation);
|
||||
right.OnCollisionResolved?.Invoke(right, collisionInformation);
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,74 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Engine.Physics2D;
|
||||
|
||||
public static partial class Physics2D
|
||||
{
|
||||
public static bool Overlaps(this Circle left, Circle right)
|
||||
{
|
||||
float distanceSquared = left.Center.FromTo(right.Center).LengthSquared();
|
||||
float radiusSumSquared = left.RadiusSquared + right.RadiusSquared;
|
||||
|
||||
return distanceSquared < radiusSumSquared;
|
||||
}
|
||||
|
||||
public static bool Overlaps(this Circle left, Circle right, out Vector2D normal, out float depth)
|
||||
{
|
||||
Vector2D distanceVector = left.Center.FromTo(right.Center);
|
||||
float distanceSquared = distanceVector.LengthSquared();
|
||||
float radiusSumSquared = left.RadiusSquared + right.RadiusSquared;
|
||||
bool isOverlapping = distanceSquared < radiusSumSquared;
|
||||
|
||||
depth = 0f;
|
||||
normal = distanceVector.Normalized;
|
||||
|
||||
if (isOverlapping)
|
||||
depth = MathF.Sqrt(radiusSumSquared - distanceSquared);
|
||||
|
||||
return isOverlapping;
|
||||
}
|
||||
|
||||
public static bool Overlaps(this Circle circle, Vector2D point) => circle.Center.FromTo(point).LengthSquared() <= circle.RadiusSquared;
|
||||
public static bool Overlaps(this Circle circle, Vector2D point, out Vector2D normal, out float depth)
|
||||
{
|
||||
Vector2D distanceVector = circle.Center.FromTo(point);
|
||||
float distanceSquared = distanceVector.LengthSquared();
|
||||
float radiusSquared = circle.RadiusSquared;
|
||||
bool isOverlapping = distanceSquared < radiusSquared;
|
||||
|
||||
depth = 0f;
|
||||
normal = distanceVector.Normalized;
|
||||
|
||||
if (isOverlapping)
|
||||
depth = MathF.Sqrt(radiusSquared - distanceSquared);
|
||||
|
||||
return isOverlapping;
|
||||
}
|
||||
|
||||
public static bool Overlaps(this AABB aabb, Vector2D point)
|
||||
=> point.X >= aabb.LowerBoundary.X && point.X <= aabb.UpperBoundary.X &&
|
||||
point.Y >= aabb.LowerBoundary.Y && point.Y <= aabb.UpperBoundary.Y;
|
||||
|
||||
public static bool Overlaps(this AABB left, AABB right)
|
||||
=> left.LowerBoundary.X <= right.UpperBoundary.X && left.UpperBoundary.X >= right.LowerBoundary.X &&
|
||||
left.LowerBoundary.Y <= right.UpperBoundary.Y && left.UpperBoundary.Y >= right.LowerBoundary.Y;
|
||||
|
||||
public static bool Overlaps(Triangle triangle, Vector2D point)
|
||||
{
|
||||
float originalTriangleArea = triangle.Area;
|
||||
|
||||
float pointTriangleArea1 = new Triangle(point, triangle.B, triangle.C).Area;
|
||||
float pointTriangleArea2 = new Triangle(triangle.A, point, triangle.C).Area;
|
||||
float pointTriangleArea3 = new Triangle(triangle.A, triangle.B, point).Area;
|
||||
|
||||
float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3;
|
||||
|
||||
return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f);
|
||||
}
|
||||
|
||||
public static bool LaysOn(this Vector2D point, Line line) => Line.Intersects(line, point);
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
{
|
||||
private readonly List<IRigidBody2D> rigidBodies = new(32);
|
||||
private readonly List<ICollider2D> colliders = new(64);
|
||||
|
||||
private int _iterationCount = 1;
|
||||
|
||||
private readonly ICollisionDetector2D collisionDetector = null!;
|
||||
private readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
|
||||
public void AddRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
if (rigidBodies.Contains(rigidBody))
|
||||
return;
|
||||
|
||||
rigidBodies.Add(rigidBody);
|
||||
|
||||
foreach (var collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
|
||||
colliders.Add(collider2D);
|
||||
|
||||
rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
rigidBody.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
public void RemoveRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
rigidBodies.Remove(rigidBody);
|
||||
}
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
for (int i = 0; i < rigidBodies.Count; i++)
|
||||
StepRigidBody(rigidBodies[i], intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
foreach (var collider in colliders)
|
||||
collider.Recalculate();
|
||||
|
||||
// Can Parallel
|
||||
for (int x = 0; x < colliders.Count; x++)
|
||||
{
|
||||
ICollider2D? colliderX = colliders[x];
|
||||
if (!colliderX.IsActive)
|
||||
return;
|
||||
|
||||
for (int y = x + 1; y < colliders.Count; y++)
|
||||
{
|
||||
ICollider2D? colliderY = colliders[y];
|
||||
|
||||
if (!colliderY.IsActive)
|
||||
return;
|
||||
|
||||
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
|
||||
if (bothCollidersAreTriggers)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreStatic = colliderX.RigidBody2D?.IsStatic ?? true && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic;
|
||||
if (bothCollidersAreStatic)
|
||||
continue;
|
||||
|
||||
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
|
||||
{
|
||||
if (colliderX.IsTrigger)
|
||||
{
|
||||
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
|
||||
continue;
|
||||
}
|
||||
else if (colliderY.IsTrigger)
|
||||
{
|
||||
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
|
||||
continue;
|
||||
}
|
||||
|
||||
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
|
||||
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
|
||||
|
||||
collisionResolver?.Resolve(information);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
|
||||
}
|
||||
|
||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not ICollider2D collider2D)
|
||||
return;
|
||||
|
||||
colliders.Add(collider2D);
|
||||
}
|
||||
|
||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not ICollider2D collider2D)
|
||||
return;
|
||||
|
||||
colliders.Remove(collider2D);
|
||||
}
|
||||
|
||||
public PhysicsEngine2D()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
|
||||
|
||||
|
||||
private int _iterationPerStep = 1;
|
||||
|
||||
protected readonly ICollisionDetector2D collisionDetector = null!;
|
||||
protected readonly ICollisionResolver2D collisionResolver = null!;
|
||||
|
||||
protected BehaviourCacher<IRigidBody2D> rigidBodyCacher = new();
|
||||
protected BehaviourCacher<ICollider2D> colliderCacher = new();
|
||||
|
||||
|
||||
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
|
||||
public IGameManager GameManager { get; private set; } = null!;
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
// Can Parallel
|
||||
foreach (var rigidBody in rigidBodyCacher)
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
foreach (var collider in colliderCacher)
|
||||
collider.Recalculate();
|
||||
|
||||
// Can Parallel
|
||||
for (int x = 0; x < colliderCacher.Behaviours.Count; x++)
|
||||
{
|
||||
ICollider2D? colliderX = colliderCacher.Behaviours[x];
|
||||
if (!colliderX.IsActive)
|
||||
return;
|
||||
|
||||
for (int y = x + 1; y < colliderCacher.Behaviours.Count; y++)
|
||||
{
|
||||
ICollider2D? colliderY = colliderCacher.Behaviours[y];
|
||||
|
||||
if (!colliderY.IsActive)
|
||||
return;
|
||||
|
||||
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
|
||||
if (bothCollidersAreTriggers)
|
||||
continue;
|
||||
|
||||
bool bothCollidersAreStatic = colliderX.RigidBody2D?.IsStatic ?? true && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic;
|
||||
if (bothCollidersAreStatic)
|
||||
continue;
|
||||
|
||||
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
|
||||
{
|
||||
if (colliderX.IsTrigger)
|
||||
{
|
||||
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
|
||||
continue;
|
||||
}
|
||||
else if (colliderY.IsTrigger)
|
||||
{
|
||||
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
|
||||
continue;
|
||||
}
|
||||
|
||||
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
|
||||
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
|
||||
|
||||
collisionResolver?.Resolve(information);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
if (rigidBody.IsStatic || !rigidBody.IsActive)
|
||||
return;
|
||||
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
|
||||
}
|
||||
|
||||
public bool Assign(IGameManager gameManager)
|
||||
{
|
||||
if (GameManager is not null)
|
||||
return false;
|
||||
|
||||
colliderCacher.Assign(gameManager);
|
||||
rigidBodyCacher.Assign(gameManager);
|
||||
|
||||
GameManager = gameManager;
|
||||
OnGameManagerAssigned?.Invoke(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unassign()
|
||||
{
|
||||
if (GameManager is null)
|
||||
return false;
|
||||
|
||||
colliderCacher.Unassign();
|
||||
rigidBodyCacher.Unassign();
|
||||
|
||||
GameManager = null!;
|
||||
OnUnassigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher()
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(IGameManager gameManager)
|
||||
{
|
||||
Assign(gameManager);
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(IGameManager gameManager, ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
Assign(gameManager);
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
|
||||
public PhysicsEngine2DCacher(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public readonly struct PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D
|
||||
{
|
||||
public readonly float Friction { get; init; } = Friction;
|
||||
public readonly float Restitution { get; init; } = Restitution;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public readonly struct PhysicsMaterial2DDefault : IPhysicsMaterial2D
|
||||
{
|
||||
public readonly float Friction => .1f;
|
||||
|
||||
public readonly float Restitution => .1f;
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
||||
/// </summary>
|
||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB"/>.</param>
|
||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB"/>.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="AABB"/> struct with the specified lower and upper boundaries.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||
public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
{
|
||||
/// <summary>
|
||||
/// The lower boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D LowerBoundary = lowerBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// The upper boundary of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D UpperBoundary = upperBoundary;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the center point of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets half the size of the <see cref="AABB"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D SizeHalf => Size * .5f;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
||||
/// </summary>
|
||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
||||
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
||||
|
||||
foreach (Vector2D vector in vectors)
|
||||
{
|
||||
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
||||
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter < 2)
|
||||
throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||
|
||||
return new(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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(AABB left, AABB right)
|
||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="AABB"/> struct.
|
||||
/// </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>
|
||||
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);
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D circle.
|
||||
/// </summary>
|
||||
/// <param name="center">The center of the circle.</param>
|
||||
/// <param name="radius">The radius of the circle.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the Circle struct with the specified center and radius.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
||||
public readonly struct Circle(Vector2D center, float radius)
|
||||
{
|
||||
/// <summary>
|
||||
/// The center of the circle.
|
||||
/// </summary>
|
||||
public readonly Vector2D Center = center;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Radius = radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float RadiusSquared => Radius * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diameter of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public readonly float Diameter => 2f * Radius;
|
||||
|
||||
/// <summary>
|
||||
/// A predefined unit <see cref="Circle"/> with a center at the origin and a radius of 1.
|
||||
/// </summary>
|
||||
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius);
|
||||
|
||||
/// <summary>
|
||||
/// Displaces the <see cref="Circle"/> by the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the <see cref="Circle"/> onto the specified <see cref="Vector2D"/>.
|
||||
/// </summary>
|
||||
public static Projection 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"/>.
|
||||
/// </summary>
|
||||
public static Circle TransformCircle(ITransform transform, Circle circle)
|
||||
=> new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Circle"/> struct.
|
||||
/// </summary>
|
||||
public static class CircleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the center of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the radius of the <see cref="Circle"/>.
|
||||
/// </summary>
|
||||
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>
|
||||
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);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D line segment defined by two endpoints.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the Line struct with the specified endpoints.
|
||||
/// </remarks>
|
||||
/// <param name="from">The starting point of the <see cref="Line"/> segment.</param>
|
||||
/// <param name="to">The ending point of the <see cref="Line"/> segment.</param>
|
||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
||||
public readonly struct Line(Vector2D from, Vector2D to)
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting point of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D From = from;
|
||||
|
||||
/// <summary>
|
||||
/// The ending point of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D To = to;
|
||||
|
||||
/// <summary>
|
||||
/// The reversed <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Line Reversed => new(To, From);
|
||||
|
||||
/// <summary>
|
||||
/// The normalized direction <see cref="Vector2D"/> of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly Vector2D Direction => From.FromTo(To).Normalize();
|
||||
|
||||
/// <summary>
|
||||
/// The length of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly float Length => From.FromTo(To).Length();
|
||||
|
||||
/// <summary>
|
||||
/// The squared length of the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||
|
||||
/// <summary>
|
||||
/// The equation of the <see cref="Line"/> defined by this <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static LineEquation GetLineEquation(Line line)
|
||||
{
|
||||
Vector2D slopeVector = line.From.FromTo(line.To);
|
||||
float slope = slopeVector.Y / slopeVector.X;
|
||||
|
||||
float yOffset = line.From.Y - (slope * line.From.X);
|
||||
|
||||
return new LineEquation(slope, yOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line"/>.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line line, Vector2D point)
|
||||
=> LineEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the parameter 't' representing the point's position on the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static float GetT(Line line, Vector2D point)
|
||||
{
|
||||
float fromX = MathF.Abs(line.From.X);
|
||||
float toX = MathF.Abs(line.To.X);
|
||||
float pointX = MathF.Abs(point.X);
|
||||
|
||||
float min = MathF.Min(fromX, toX);
|
||||
float max = MathF.Max(fromX, toX) - min;
|
||||
|
||||
pointX -= min;
|
||||
|
||||
float t = pointX / max;
|
||||
|
||||
// FIXME
|
||||
// I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line
|
||||
// Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason
|
||||
// too unmotivated to find a solution. Future me, find a better way if possible, please.
|
||||
if (!Lerp(line, t).ApproximatelyEquals(point))
|
||||
return 1f - t;
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Line"/> segment intersects with another <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line left, Line 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="Line"/> segment.
|
||||
/// </summary>
|
||||
public static bool OnSegment(Line line, Vector2D point)
|
||||
{
|
||||
if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) &&
|
||||
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two <see cref="Line"/> segments intersect.
|
||||
/// </summary>
|
||||
public static bool Intersects(Line left, Line 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="Line"/> segments.
|
||||
/// </summary>
|
||||
public static Vector2D IntersectionPoint(Line left, Line right)
|
||||
=> Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the parameter 't' representing the intersection point's position on the <see cref="Line"/> segment.
|
||||
/// </summary>
|
||||
public static float IntersectionParameterT(Line left, Line 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="Line"/> segment using parameter 't'.
|
||||
/// </summary>
|
||||
public static Vector2D Lerp(Line line, float t)
|
||||
=> new(
|
||||
line.From.X + (line.To.X - line.From.X) * t,
|
||||
line.From.Y + (line.To.Y - line.From.Y) * t
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point on the <see cref="Line"/> segment to the specified point.
|
||||
/// </summary>
|
||||
public static Vector2D ClosestPointTo(Line line, Vector2D point)
|
||||
{
|
||||
// Convert edge points to vectors
|
||||
var edgeVector = new Vector2D(line.To.X - line.From.X, line.To.Y - line.From.Y);
|
||||
var pointVector = new Vector2D(point.X - line.From.X, point.Y - line.From.Y);
|
||||
|
||||
// Calculate the projection of pointVector onto edgeVector
|
||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
||||
|
||||
// Clamp t to the range [0, 1] to ensure the closest point is on the edge
|
||||
t = MathF.Max(0, MathF.Min(1, t));
|
||||
|
||||
// Calculate the closest point on the edge
|
||||
float closestX = line.From.X + t * edgeVector.X;
|
||||
float closestY = line.From.Y + t * edgeVector.Y;
|
||||
|
||||
return new Vector2D((float)closestX, (float)closestY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Line"/> segments are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(Line left, Line right)
|
||||
=> left.From.ApproximatelyEquals(right.From) && left.To.ApproximatelyEquals(right.To);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the Line struct.
|
||||
/// </summary>
|
||||
public static class LineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if two <see cref="Line"/>s are approximately equal.
|
||||
/// </summary>
|
||||
public static bool ApproximatelyEquals(this Line left, Line right) => Line.ApproximatelyEquals(left, right);
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line equation in the form y = mx + b.
|
||||
/// </summary>
|
||||
/// <param name="slope">The slope of the line.</param>
|
||||
/// <param name="offsetY">The y-intercept of the line.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="LineEquation"/> struct with the specified slope and y-intercept.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||
public readonly struct LineEquation(float slope, float offsetY)
|
||||
{
|
||||
/// <summary>
|
||||
/// The slope of the line equation.
|
||||
/// </summary>
|
||||
public readonly float Slope = slope;
|
||||
|
||||
/// <summary>
|
||||
/// The y-intercept of the line equation.
|
||||
/// </summary>
|
||||
public readonly float OffsetY = offsetY;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
||||
/// </summary>
|
||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
||||
public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two line equations are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first line equation to compare.</param>
|
||||
/// <param name="right">The second line equation to compare.</param>
|
||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
||||
public static bool ApproximatelyEquals(LineEquation left, LineEquation right)
|
||||
=> left.Slope.ApproximatelyEquals(right.Slope) && left.OffsetY.ApproximatelyEquals(right.OffsetY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the LineEquation struct.
|
||||
/// </summary>
|
||||
public static class LineEquationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the y-coordinate for a given x-coordinate using the line equation.
|
||||
/// </summary>
|
||||
/// <param name="lineEquation">The line equation to resolve.</param>
|
||||
/// <param name="x">The x-coordinate for which to resolve the y-coordinate.</param>
|
||||
/// <returns>The y-coordinate resolved using the line equation.</returns>
|
||||
public static float Resolve(this LineEquation lineEquation, float x) => LineEquation.Resolve(lineEquation, x);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two line equations are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first line equation to compare.</param>
|
||||
/// <param name="right">The second line equation to compare.</param>
|
||||
/// <returns>True if the line equations are approximately equal; otherwise, false.</returns>
|
||||
public static bool ApproximatelyEquals(this LineEquation left, LineEquation right) => LineEquation.ApproximatelyEquals(left, right);
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range of values along a single axis.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum value of the projection.</param>
|
||||
/// <param name="max">The maximum value of the projection.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Projection"/> struct with the specified minimum and maximum values.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||
public readonly struct Projection(float min, float max)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the minimum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Min = min;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value of the projection.
|
||||
/// </summary>
|
||||
public readonly float Max = max;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two projections overlap.
|
||||
/// </summary>
|
||||
/// <param name="left">The first projection to check.</param>
|
||||
/// <param name="right">The second projection to check.</param>
|
||||
/// <returns><see cref="true"/> if the projections overlap; otherwise, <see cref="false"/>.</returns>
|
||||
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
|
||||
|
||||
/// <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(Projection left, Projection right, out float depth)
|
||||
{
|
||||
// TODO Try to improve this
|
||||
bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
|
||||
if (rightMinInLeft)
|
||||
{
|
||||
depth = left.Max - right.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rightMaxInLeft = right.Max < left.Max && right.Max > left.Min;
|
||||
if (rightMaxInLeft)
|
||||
{
|
||||
depth = left.Min - right.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMinInRight = left.Min > right.Min && left.Min < right.Max;
|
||||
if (leftMinInRight)
|
||||
{
|
||||
depth = right.Max - left.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMaxInRight = left.Max < right.Max && left.Max > right.Min;
|
||||
if (leftMaxInRight)
|
||||
{
|
||||
depth = right.Min - left.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
depth = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Projection"/> struct.
|
||||
/// </summary>
|
||||
public static class ProjectionExtensions
|
||||
{
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
}
|
|
@ -1,294 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a shape defined by a collection of vertices.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices of the shape.</param>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="Shape"/> struct with the specified vertices.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||
public readonly struct Shape(List<Vector2D> vertices) : IEnumerable<Vector2D>
|
||||
{
|
||||
public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
|
||||
public static readonly Shape Box = CreateNgon(4, Vector2D.One);
|
||||
public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
|
||||
public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
|
||||
|
||||
private readonly List<Vector2D> _verticesList = vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertices of the shape.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Vector2D> Vertices => _verticesList;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the vertex to get or set.</param>
|
||||
/// <returns>The vertex at the specified index.</returns>
|
||||
public Vector2D this[System.Index index] => Vertices[index];
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the current shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to copy.</param>
|
||||
/// <returns>A copy of the input shape.</returns>
|
||||
public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regular polygon (ngon) with the specified number of vertices.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">The number of vertices in the polygon.</param>
|
||||
/// <returns>A regular polygon with the specified number of vertices.</returns>
|
||||
public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
|
||||
|
||||
/// <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 Shape CreateNgon(int vertexCount, Vector2D positionToRotate)
|
||||
{
|
||||
if (vertexCount < 3)
|
||||
throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2.");
|
||||
|
||||
List<Vector2D> vertices = new(vertexCount);
|
||||
|
||||
float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian;
|
||||
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices.Add(positionToRotate.Rotate(i * radiansPerVertex));
|
||||
|
||||
return new(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the super triangle that encloses the given shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the given shape.</returns>
|
||||
public static Triangle GetSuperTriangle(Shape shape)
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
float maxX = float.MinValue, maxY = float.MinValue;
|
||||
|
||||
foreach (Vector2D point in shape.Vertices)
|
||||
{
|
||||
minX = Math.Min(minX, point.X);
|
||||
minY = Math.Min(minY, point.Y);
|
||||
maxX = Math.Max(maxX, point.X);
|
||||
maxY = Math.Max(maxY, point.Y);
|
||||
}
|
||||
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
float deltaMax = Math.Max(dx, dy);
|
||||
float midX = (minX + maxX) / 2;
|
||||
float midY = (minY + maxY) / 2;
|
||||
|
||||
Vector2D p1 = new((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
Vector2D p2 = new((float)midX, (float)midY + 20 * (float)deltaMax);
|
||||
Vector2D p3 = new((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
|
||||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <param name="lines">The list to populate with lines.</param>
|
||||
public static void GetLines(Shape shape, IList<Line> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
for (int i = 0; i < shape.Vertices.Count - 1; i++)
|
||||
lines.Add(new(shape.Vertices[i], shape.Vertices[i + 1]));
|
||||
lines.Add(new(shape.Vertices[^1], shape.Vertices[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <returns>A list of lines that form the edges of the shape.</returns>
|
||||
public static List<Line> GetLines(Shape shape)
|
||||
{
|
||||
List<Line> lines = new(shape.Vertices.Count - 1);
|
||||
GetLines(shape, lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <param name="list">The list to populate with projected values.</param>
|
||||
public static void Project(Shape shape, Vector2D projectionVector, IList<float> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
int count = shape.Vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
list.Add(projectionVector.Dot(shape[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the shape onto the vector.</returns>
|
||||
public static Projection Project(Shape shape, Vector2D projectionVector)
|
||||
{
|
||||
float min = float.MaxValue;
|
||||
float max = float.MinValue;
|
||||
|
||||
for (int i = 0; i < shape.Vertices.Count; i++)
|
||||
{
|
||||
float projectedLength = projectionVector.Dot(shape.Vertices[i]);
|
||||
min = Math.Min(projectedLength, min);
|
||||
max = Math.Max(projectedLength, max);
|
||||
}
|
||||
|
||||
return new(min, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <returns>The transformed shape.</returns>
|
||||
public static Shape TransformShape(Shape shape, ITransform transform)
|
||||
{
|
||||
List<Vector2D> vertices = new(shape.Vertices.Count);
|
||||
|
||||
int count = shape.Vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
vertices.Add(transform.TransformVector2D(shape[i]));
|
||||
|
||||
return new Shape(vertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="from">The shape to transform.</param>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="to">The transformed shape.</param>
|
||||
public static void TransformShape(Shape from, ITransform transform, ref Shape to)
|
||||
{
|
||||
to._verticesList.Clear();
|
||||
|
||||
int count = from._verticesList.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
to._verticesList.Add(transform.TransformVector2D(from[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two shapes are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first shape to compare.</param>
|
||||
/// <param name="right">The second shape to compare.</param>
|
||||
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool ApproximatelyEquals(Shape left, Shape right)
|
||||
{
|
||||
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]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Shape"/> struct.
|
||||
/// </summary>
|
||||
public static class ShapeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a copy of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to copy.</param>
|
||||
/// <returns>A copy of the input shape.</returns>
|
||||
public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the super triangle that encloses the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to enclose.</param>
|
||||
/// <returns>The super triangle that encloses the shape.</returns>
|
||||
public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <param name="lines">The list to populate with lines.</param>
|
||||
public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of lines that form the edges of the shape.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to get lines from.</param>
|
||||
/// <returns>A list of lines that form the edges of the shape.</returns>
|
||||
public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <param name="list">The list to populate with projected values.</param>
|
||||
public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
|
||||
|
||||
/// <summary>
|
||||
/// Projects the shape onto a vector.
|
||||
/// </summary>
|
||||
/// <param name="shape">The shape to project.</param>
|
||||
/// <param name="projectionVector">The vector to project onto.</param>
|
||||
/// <returns>The projection of the shape onto the vector.</returns>
|
||||
public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="shape">The shape to transform.</param>
|
||||
/// <returns>The transformed shape.</returns>
|
||||
public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the shape using the specified transform.
|
||||
/// </summary>
|
||||
/// <param name="transform">The transform to apply.</param>
|
||||
/// <param name="from">The shape to transform.</param>
|
||||
/// <param name="to">The transformed shape.</param>
|
||||
public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two shapes are approximately equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first shape to compare.</param>
|
||||
/// <param name="right">The second shape to compare.</param>
|
||||
/// <returns><c>true</c> if the shapes are approximately equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
[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)
|
||||
{
|
||||
public readonly Vector2D A { get; init; } = A;
|
||||
public readonly Vector2D B { get; init; } = B;
|
||||
public readonly Vector2D C { get; init; } = C;
|
||||
|
||||
public readonly float Area
|
||||
=> .5f * MathF.Abs(
|
||||
A.X * (B.Y - C.Y) +
|
||||
B.X * (C.Y - A.Y) +
|
||||
C.X * (A.Y - B.Y)
|
||||
);
|
||||
|
||||
public static Circle GetCircumCircle(Triangle triangle)
|
||||
{
|
||||
Vector2D midAB = (triangle.A + triangle.B) / 2f;
|
||||
Vector2D midBC = (triangle.B + triangle.C) / 2f;
|
||||
|
||||
float slopeAB = (triangle.B.Y - triangle.A.Y) / (triangle.B.X - triangle.A.X);
|
||||
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
|
||||
|
||||
Vector2D center;
|
||||
if (MathF.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;
|
||||
center = new Vector2D(x, y);
|
||||
}
|
||||
else
|
||||
center = (midAB + midBC) * .5f;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static class TriangleExtensions
|
||||
{
|
||||
public static bool ApproximatelyEquals(this Triangle left, Triangle right) => Triangle.ApproximatelyEquals(left, right);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class RigidBody2D : BehaviourOverride, IRigidBody2D
|
||||
{
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
|
||||
private const float LOWEST_ALLOWED_MASS = 0.00001f;
|
||||
private float _mass = 1f;
|
||||
|
||||
|
||||
public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault();
|
||||
|
||||
public Vector2D Velocity { get; set; } = Vector2D.Zero;
|
||||
public float AngularVelocity { get; set; } = 0f;
|
||||
public bool IsStatic { get; set; } = false;
|
||||
|
||||
public float Mass { get => _mass; set => _mass = Core.Math.Max(value, LOWEST_ALLOWED_MASS); }
|
||||
|
||||
ITransform IAssignableTransform.Transform => Transform;
|
||||
|
||||
|
||||
public bool Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
}
|
|
@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine.Core\
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine.Physics2D\Engine.Physics2D.csproj", "{3B3C3332-07E3-4A00-9898-0A5410BCB08C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -26,9 +24,5 @@ Global
|
|||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
17
README.md
17
README.md
|
@ -1,17 +0,0 @@
|
|||
# Work In Progress
|
||||
|
||||
This engine is still in development but the implemented features include:
|
||||
- Modular Systems
|
||||
- Behaviour System
|
||||
- 2D Physics Engine(**Not Fully Completed, but usable**)
|
||||
- Rigid Body Simulations
|
||||
- Collision Detection (Convex Shape & Circle)
|
||||
- Collision Resolution (**Not Fully Completed**)
|
||||
- Vector2D, AABB, Circle, Line, LineEquation, Projection & Shape Data Types
|
||||
- General Math
|
||||
|
||||
---
|
||||
|
||||
**A detailed README file will be written in the future. If you want to check out how to use this, please checkout this example Pong game made using this engine on top of [MonoGame](https://monogame.net/) from the link bellow.**
|
||||
|
||||
[Pong Source Code](https://git.syntriax.com/Syntriax/Engine-Pong)
|
Loading…
Reference in New Issue