Merge branch 'feat/physics2d' into development
This commit is contained in:
commit
4000e761a7
|
@ -0,0 +1,484 @@
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from `dotnet new gitignore`
|
||||||
|
|
||||||
|
# dotenv files
|
||||||
|
.env
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# Tye
|
||||||
|
.tye/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
##
|
||||||
|
## Visual studio for Mac
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# Mac bundle stuff
|
||||||
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Vim temporary swap files
|
||||||
|
*.swp
|
|
@ -16,4 +16,9 @@ public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignab
|
||||||
/// Call priority of the <see cref="IBehaviour"/>.
|
/// Call priority of the <see cref="IBehaviour"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Priority { get; set; }
|
int Priority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the <see cref="IBehaviour"/> is active.
|
||||||
|
/// </summary>
|
||||||
|
bool IsActive { get; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
|
||||||
|
|
||||||
public interface ICamera : IAssignableTransform
|
|
||||||
{
|
|
||||||
Action<ICamera>? OnZoomChanged { get; set; }
|
|
||||||
|
|
||||||
float Zoom { get; set; }
|
|
||||||
|
|
||||||
void Update();
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
public interface IGameManager : IEntity, IEnumerable<IGameObject>
|
||||||
|
{
|
||||||
|
Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; }
|
||||||
|
Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
IReadOnlyList<IGameObject> GameObjects { get; }
|
||||||
|
|
||||||
|
|
||||||
|
void RegisterGameObject(IGameObject gameObject);
|
||||||
|
T InstantiateGameObject<T>(params object?[]? args) where T : class, IGameObject;
|
||||||
|
IGameObject RemoveGameObject(IGameObject gameObject);
|
||||||
|
|
||||||
|
void Update(EngineTime time);
|
||||||
|
void PreDraw();
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||||
public abstract class Behaviour : IBehaviour
|
public abstract class Behaviour : IBehaviour
|
||||||
{
|
{
|
||||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||||
|
@ -25,6 +26,8 @@ public abstract class Behaviour : IBehaviour
|
||||||
public IStateEnable StateEnable => _stateEnable;
|
public IStateEnable StateEnable => _stateEnable;
|
||||||
public IBehaviourController BehaviourController => _behaviourController;
|
public IBehaviourController BehaviourController => _behaviourController;
|
||||||
|
|
||||||
|
public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled;
|
||||||
|
|
||||||
public bool Initialized
|
public bool Initialized
|
||||||
{
|
{
|
||||||
get => _initialized;
|
get => _initialized;
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||||
public class BehaviourController : IBehaviourController
|
public class BehaviourController : IBehaviourController
|
||||||
{
|
{
|
||||||
public Action<IBehaviourController>? OnPreUpdate { get; set; }
|
public Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||||
|
@ -55,17 +57,17 @@ public class BehaviourController : IBehaviourController
|
||||||
|
|
||||||
public IList<T> GetBehaviours<T>()
|
public IList<T> GetBehaviours<T>()
|
||||||
{
|
{
|
||||||
IList<T> behaviours = new List<T>();
|
List<T>? behaviours = null;
|
||||||
foreach (var behaviourItem in this.behaviours)
|
foreach (var behaviourItem in this.behaviours)
|
||||||
{
|
{
|
||||||
if (behaviourItem is not T behaviour)
|
if (behaviourItem is not T behaviour)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
behaviours ??= new List<T>();
|
behaviours ??= [];
|
||||||
behaviours.Add(behaviour);
|
behaviours.Add(behaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
return behaviours;
|
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||||
|
|
|
@ -39,13 +39,13 @@ public abstract class BehaviourOverride : Behaviour
|
||||||
OnFinalize();
|
OnFinalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnPreUpdatePreEnabledCheck() { }
|
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||||
protected virtual void OnPreUpdate() { }
|
protected virtual void OnPreUpdate() { }
|
||||||
private void PreUpdate(IBehaviourController _)
|
private void PreUpdate(IBehaviourController _)
|
||||||
{
|
{
|
||||||
OnPreUpdatePreEnabledCheck();
|
OnPreUpdatePreActiveCheck();
|
||||||
|
|
||||||
if (!StateEnable.Enabled)
|
if (!IsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isInitializedThisFrame)
|
if (isInitializedThisFrame)
|
||||||
|
@ -61,23 +61,23 @@ public abstract class BehaviourOverride : Behaviour
|
||||||
isInitializedThisFrame = false;
|
isInitializedThisFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnUpdatePreEnabledCheck() { }
|
protected virtual void OnUpdatePreActiveCheck() { }
|
||||||
protected virtual void OnUpdate() { }
|
protected virtual void OnUpdate() { }
|
||||||
private void Update(IBehaviourController _)
|
private void Update(IBehaviourController _)
|
||||||
{
|
{
|
||||||
OnUpdatePreEnabledCheck();
|
OnUpdatePreActiveCheck();
|
||||||
|
|
||||||
if (!StateEnable.Enabled)
|
if (!IsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnUpdate();
|
OnUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnPreDrawPreEnabledCheck() { }
|
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||||
protected virtual void OnPreDraw() { }
|
protected virtual void OnPreDraw() { }
|
||||||
private void PreDraw(IBehaviourController _)
|
private void PreDraw(IBehaviourController _)
|
||||||
{
|
{
|
||||||
OnPreDrawPreEnabledCheck();
|
OnPreDrawPreActiveCheck();
|
||||||
|
|
||||||
if (!StateEnable.Enabled)
|
if (!StateEnable.Enabled)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
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,8 +2,8 @@ using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public record EngineTime
|
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
|
||||||
(
|
{
|
||||||
TimeSpan Total,
|
public readonly TimeSpan Total { get; init; } = Total;
|
||||||
TimeSpan Elapsed
|
public readonly TimeSpan Elapsed { get; init; } = Elapsed;
|
||||||
);
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,13 +4,23 @@ public static class Vector2DExtensions
|
||||||
{
|
{
|
||||||
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
|
public static float Length(this Vector2D vector) => Vector2D.Length(vector);
|
||||||
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
||||||
|
|
||||||
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
||||||
|
|
||||||
|
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
|
||||||
|
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||||
|
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||||
|
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||||
|
public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value);
|
||||||
|
|
||||||
|
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
|
||||||
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
||||||
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
||||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||||
|
|
||||||
|
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||||
|
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||||
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
||||||
|
@ -19,4 +29,7 @@ public static class Vector2DExtensions
|
||||||
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
||||||
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
||||||
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
||||||
|
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ using Syntriax.Engine.Core.Factory;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public class GameManager : IEntity, IEnumerable<IGameObject>
|
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
|
||||||
|
public class GameManager : IGameManager
|
||||||
{
|
{
|
||||||
public Action<GameManager>? OnCameraChanged { get; set; } = null;
|
|
||||||
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
|
||||||
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
|
||||||
|
|
||||||
|
@ -20,12 +20,11 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||||
|
|
||||||
|
|
||||||
private IList<IGameObject> _gameObjects = new List<IGameObject>(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
|
||||||
|
|
||||||
private IStateEnable _stateEnable = null!;
|
private IStateEnable _stateEnable = null!;
|
||||||
private GameObjectFactory _gameObjectFactory = null!;
|
private GameObjectFactory _gameObjectFactory = null!;
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
private ICamera _camera = null!;
|
|
||||||
|
|
||||||
private GameObjectFactory GameObjectFactory
|
private GameObjectFactory GameObjectFactory
|
||||||
{
|
{
|
||||||
|
@ -38,7 +37,8 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Initialized => _initialized;
|
public bool Initialized => _initialized;
|
||||||
public IList<IGameObject> GameObjects => _gameObjects;
|
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||||
|
|
||||||
public IStateEnable StateEnable
|
public IStateEnable StateEnable
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -54,19 +54,6 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICamera Camera
|
|
||||||
{
|
|
||||||
get => _camera;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_camera == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_camera = value;
|
|
||||||
OnCameraChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterGameObject(IGameObject gameObject)
|
public void RegisterGameObject(IGameObject gameObject)
|
||||||
{
|
{
|
||||||
if (_gameObjects.Contains(gameObject))
|
if (_gameObjects.Contains(gameObject))
|
||||||
|
@ -101,6 +88,7 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||||
foreach (var gameObject in GameObjects)
|
foreach (var gameObject in GameObjects)
|
||||||
gameObject.Initialize();
|
gameObject.Initialize();
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
OnInitialized?.Invoke(this);
|
OnInitialized?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +102,7 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
||||||
GameObjects[i].Finalize();
|
GameObjects[i].Finalize();
|
||||||
|
|
||||||
OnFinalized?.Invoke(this);
|
OnFinalized?.Invoke(this);
|
||||||
|
_initialized = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||||
public class GameObject : IGameObject
|
public class GameObject : IGameObject
|
||||||
{
|
{
|
||||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
public static class Math
|
||||||
|
{
|
||||||
|
public const float RadianToDegree = 180f / PI;
|
||||||
|
public const float DegreeToRadian = PI / 180f;
|
||||||
|
|
||||||
|
public const float E = 2.718281828459045f;
|
||||||
|
public const float PI = 3.1415926535897932f;
|
||||||
|
public const float Tau = 2f * PI;
|
||||||
|
|
||||||
|
public static T Abs<T>(T x) where T : INumber<T> => x > T.Zero ? x : -x;
|
||||||
|
public static float Acos(float x) => MathF.Acos(x);
|
||||||
|
public static float Asin(float x) => MathF.Asin(x);
|
||||||
|
public static float Atan2(float y, float x) => MathF.Atan2(y, x);
|
||||||
|
public static float Atanh(float x) => MathF.Atanh(x);
|
||||||
|
public static T Clamp<T>(this T x, T min, T max) where T : INumber<T> => (x < min) ? min : (x > max) ? max : x;
|
||||||
|
public static float Ceiling(float x) => MathF.Ceiling(x);
|
||||||
|
public static float CopySign(float x, float y) => MathF.CopySign(x, y);
|
||||||
|
public static float Floor(float x) => MathF.Floor(x);
|
||||||
|
public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y);
|
||||||
|
public static float Log(float x, float y) => MathF.Log(x, y);
|
||||||
|
public static T Max<T>(T x, T y) where T : INumber<T> => (x > y) ? x : y;
|
||||||
|
public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y);
|
||||||
|
public static T Min<T>(T x, T y) where T : INumber<T> => (x < y) ? x : y;
|
||||||
|
public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y);
|
||||||
|
public static float Pow(float x, float y) => MathF.Pow(x, y);
|
||||||
|
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
||||||
|
public static T Sqr<T>(T x) where T : INumber<T> => x * x;
|
||||||
|
public static float Sqrt(float x) => MathF.Sqrt(x);
|
||||||
|
public static float Truncate(float x) => MathF.Truncate(x);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
||||||
public class Transform : ITransform
|
public class Transform : ITransform
|
||||||
{
|
{
|
||||||
public Action<ITransform>? OnPositionChanged { get; set; } = null;
|
public Action<ITransform>? OnPositionChanged { get; set; } = null;
|
||||||
|
|
|
@ -2,12 +2,15 @@ using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||||
public record Vector2D(float X, float Y)
|
public readonly struct Vector2D(float X, float Y)
|
||||||
{
|
{
|
||||||
public float Magnitude => Length(this);
|
public readonly float X { get; init; } = X;
|
||||||
public float MagnitudeSquared => LengthSquared(this);
|
public readonly float Y { get; init; } = Y;
|
||||||
public Vector2D Normalized => Normalize(this);
|
|
||||||
|
public readonly float Magnitude => Length(this);
|
||||||
|
public readonly float MagnitudeSquared => LengthSquared(this);
|
||||||
|
public readonly Vector2D Normalized => Normalize(this);
|
||||||
|
|
||||||
public readonly static Vector2D Up = new(0f, 1f);
|
public readonly static Vector2D Up = new(0f, 1f);
|
||||||
public readonly static Vector2D Down = new(0f, -1f);
|
public readonly static Vector2D Down = new(0f, -1f);
|
||||||
|
@ -22,17 +25,28 @@ public record Vector2D(float X, float Y)
|
||||||
public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
|
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 *(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 Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
|
||||||
|
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
|
||||||
|
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
|
||||||
|
|
||||||
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
||||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||||
|
|
||||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||||
|
|
||||||
|
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||||
|
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||||
|
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||||
|
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||||
|
public static Vector2D Subdivide(Vector2D vector, float value) => vector / value;
|
||||||
|
|
||||||
|
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||||
|
|
||||||
|
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||||
|
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||||
|
@ -42,5 +56,28 @@ public record Vector2D(float X, float Y)
|
||||||
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
|
||||||
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
|
||||||
|
|
||||||
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
/// <summary>
|
||||||
|
/// Finds the Orientation of 3 <see cref="Vector2D"/>s
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>0 -> Collinear, 1 -> Clockwise, 2 -> Counterclockwise</returns>
|
||||||
|
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
|
||||||
|
{
|
||||||
|
Vector2D leftToMiddle = left.FromTo(middle);
|
||||||
|
Vector2D middleToRight = middle.FromTo(right);
|
||||||
|
|
||||||
|
float value = leftToMiddle.Y * middleToRight.X -
|
||||||
|
leftToMiddle.X * middleToRight.Y;
|
||||||
|
|
||||||
|
if (value > 0) return 1;
|
||||||
|
if (value < 0) return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
|
||||||
|
|
||||||
|
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
|
||||||
|
public override int GetHashCode() => HashCode.Combine(X, Y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
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
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface ICircleCollider2D : ICollider2D
|
||||||
|
{
|
||||||
|
Circle CircleLocal { get; set; }
|
||||||
|
Circle CircleWorld { get; }
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface ICollider2D : IBehaviour, IAssignableTransform
|
||||||
|
{
|
||||||
|
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
|
||||||
|
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
|
||||||
|
|
||||||
|
Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
|
||||||
|
|
||||||
|
IRigidBody2D? RigidBody2D { get; }
|
||||||
|
bool IsTrigger { get; set; }
|
||||||
|
|
||||||
|
void Recalculate();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
|
public interface ICollisionDetector2D
|
||||||
|
{
|
||||||
|
bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface ICollisionResolver2D
|
||||||
|
{
|
||||||
|
void Resolve(CollisionDetectionInformation collisionInformation);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface IPhysicsEngine2D
|
||||||
|
{
|
||||||
|
int IterationCount { get; set; }
|
||||||
|
|
||||||
|
void AddRigidBody(IRigidBody2D rigidBody);
|
||||||
|
void RemoveRigidBody(IRigidBody2D rigidBody);
|
||||||
|
|
||||||
|
void Step(float deltaTime);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface IPhysicsMaterial2D
|
||||||
|
{
|
||||||
|
float Friction { get; }
|
||||||
|
float Restitution { get; }
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface IRigidBody2D : IBehaviour, IAssignableTransform
|
||||||
|
{
|
||||||
|
IPhysicsMaterial2D Material { get; set; }
|
||||||
|
|
||||||
|
Vector2D Velocity { get; set; }
|
||||||
|
float AngularVelocity { get; set; }
|
||||||
|
|
||||||
|
float Mass { get; set; }
|
||||||
|
bool IsStatic { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
public interface IShapeCollider2D : ICollider2D
|
||||||
|
{
|
||||||
|
Shape ShapeLocal { get; set; }
|
||||||
|
Shape ShapeWorld { get; }
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
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;
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,74 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
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 ICollisionDetector2D collisionDetector = new CollisionDetector2D();
|
||||||
|
private ICollisionResolver2D collisionResolver = new CollisionResolver2D();
|
||||||
|
|
||||||
|
public int IterationCount { 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 / IterationCount;
|
||||||
|
|
||||||
|
for (int iterationIndex = 0; iterationIndex < IterationCount; 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)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using Syntriax.Engine.Physics2D.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D;
|
||||||
|
|
||||||
|
public readonly struct PhysicsMaterial2DDefault : IPhysicsMaterial2D
|
||||||
|
{
|
||||||
|
public readonly float Friction => .1f;
|
||||||
|
|
||||||
|
public readonly float Restitution => .1f;
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||||
|
public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
|
||||||
|
{
|
||||||
|
public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary;
|
||||||
|
public readonly Vector2D UpperBoundary { get; init; } = UpperBoundary;
|
||||||
|
|
||||||
|
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||||
|
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||||
|
public readonly Vector2D SizeHalf => Size * .5f;
|
||||||
|
|
||||||
|
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
||||||
|
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (Vector2D vector in vectors)
|
||||||
|
{
|
||||||
|
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
||||||
|
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter < 2)
|
||||||
|
throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||||
|
|
||||||
|
return new(lowerBoundary, upperBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(AABB left, AABB right)
|
||||||
|
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AABBExtensions
|
||||||
|
{
|
||||||
|
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Center: {Center.ToString(), nq}, Radius: {Radius}")]
|
||||||
|
public readonly struct Circle(Vector2D Center, float Radius)
|
||||||
|
{
|
||||||
|
public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
|
||||||
|
|
||||||
|
public readonly Vector2D Center { get; init; } = Center;
|
||||||
|
public readonly float Radius { get; init; } = Radius;
|
||||||
|
|
||||||
|
public readonly float RadiusSquared => Radius * Radius;
|
||||||
|
public readonly float Diameter => 2f * Radius;
|
||||||
|
|
||||||
|
public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius);
|
||||||
|
public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius);
|
||||||
|
|
||||||
|
public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
|
||||||
|
|
||||||
|
public static Projection Project(Circle circle, Vector2D projectionVector)
|
||||||
|
{
|
||||||
|
float projectedCenter = circle.Center.Dot(projectionVector);
|
||||||
|
return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Circle TransformCircle(ITransform transform, Circle circle)
|
||||||
|
=> new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude);
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(Circle left, Circle right)
|
||||||
|
=> left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CircleExtensions
|
||||||
|
{
|
||||||
|
public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center);
|
||||||
|
public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius);
|
||||||
|
|
||||||
|
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||||
|
|
||||||
|
public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||||
|
|
||||||
|
public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(), nq}, To: {To.ToString(), nq}, Direction: {Direction.ToString(), nq}, Length: {Length}")]
|
||||||
|
public readonly struct Line(Vector2D From, Vector2D To)
|
||||||
|
{
|
||||||
|
public readonly Vector2D From { get; init; } = From;
|
||||||
|
public readonly Vector2D To { get; init; } = To;
|
||||||
|
|
||||||
|
public readonly Line Reversed => new(To, From);
|
||||||
|
public readonly Vector2D Direction => From.FromTo(To).Normalize();
|
||||||
|
public readonly float Length => From.FromTo(To).Length();
|
||||||
|
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Intersects(Line line, Vector2D point)
|
||||||
|
=> LineEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Exist(Line line, List<Vector2D> vertices)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vertices.Count - 1; i++)
|
||||||
|
{
|
||||||
|
Vector2D vertexCurrent = vertices[i];
|
||||||
|
Vector2D vertexNext = vertices[i];
|
||||||
|
if (line.From == vertexCurrent && line.To == vertexNext) return true;
|
||||||
|
if (line.From == vertexNext && line.To == vertexCurrent) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2D vertexFirst = vertices[0];
|
||||||
|
Vector2D vertexLast = vertices[^1];
|
||||||
|
if (line.From == vertexFirst && line.To == vertexLast) return true;
|
||||||
|
if (line.From == vertexLast && line.To == vertexFirst) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2D Lerp(Line line, float t)
|
||||||
|
=> new Vector2D(
|
||||||
|
line.From.X + (line.To.X - line.From.X) * t,
|
||||||
|
line.From.Y + (line.To.Y - line.From.Y) * t
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2D IntersectionPoint(Line left, Line right)
|
||||||
|
=> Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(Line left, Line right)
|
||||||
|
=> left.From.ApproximatelyEquals(right.From) && left.To.ApproximatelyEquals(right.To);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LineExtensions
|
||||||
|
{
|
||||||
|
public static bool ApproximatelyEquals(this Line left, Line right) => Line.ApproximatelyEquals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||||
|
public readonly struct LineEquation(float Slope, float OffsetY)
|
||||||
|
{
|
||||||
|
public readonly float Slope { get; init; } = Slope;
|
||||||
|
public readonly float OffsetY { get; init; } = OffsetY;
|
||||||
|
|
||||||
|
public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(LineEquation left, LineEquation right)
|
||||||
|
=> left.Slope.ApproximatelyEquals(right.Slope) && left.OffsetY.ApproximatelyEquals(right.OffsetY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LineEquationExtensions
|
||||||
|
{
|
||||||
|
public static float Resolve(this LineEquation lineEquation, float x) => LineEquation.Resolve(lineEquation, x);
|
||||||
|
public static bool ApproximatelyEquals(this LineEquation left, LineEquation right) => LineEquation.ApproximatelyEquals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||||
|
public readonly struct Projection(float Min, float Max)
|
||||||
|
{
|
||||||
|
public readonly float Min { get; init; } = Min;
|
||||||
|
public readonly float Max { get; init; } = Max;
|
||||||
|
|
||||||
|
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class ProjectionExtensions
|
||||||
|
{
|
||||||
|
public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
|
||||||
|
public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Syntriax.Engine.Core;
|
||||||
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||||
|
|
||||||
|
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
|
||||||
|
public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||||
|
{
|
||||||
|
public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
|
||||||
|
public static readonly Shape Box = CreateNgon(4, Vector2D.One);
|
||||||
|
public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
|
||||||
|
public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
|
||||||
|
|
||||||
|
public readonly IList<Vector2D> Vertices { get; init; } = Vertices;
|
||||||
|
|
||||||
|
|
||||||
|
public Vector2D this[System.Index index] => Vertices[index];
|
||||||
|
|
||||||
|
public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
|
||||||
|
|
||||||
|
public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Line> GetLines(Shape shape)
|
||||||
|
{
|
||||||
|
List<Line> lines = new(shape.Vertices.Count - 1);
|
||||||
|
GetLines(shape, lines);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TransformShape(Shape from, ITransform transform, ref Shape to)
|
||||||
|
{
|
||||||
|
to.Vertices.Clear();
|
||||||
|
|
||||||
|
int count = from.Vertices.Count;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
to.Vertices.Add(transform.TransformVector2D(from[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<Vector2D> GetEnumerator() => Vertices.GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ShapeExtensions
|
||||||
|
{
|
||||||
|
public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape);
|
||||||
|
public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
|
||||||
|
public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
|
||||||
|
public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
|
||||||
|
|
||||||
|
public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
|
||||||
|
public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
|
||||||
|
|
||||||
|
public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
|
||||||
|
public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
|
||||||
|
|
||||||
|
public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
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,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine.Core\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine.Physics2D\Engine.Physics2D.csproj", "{3B3C3332-07E3-4A00-9898-0A5410BCB08C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -24,5 +26,9 @@ Global
|
||||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
Loading…
Reference in New Issue