Compare commits
94 Commits
e7587a0827
...
4000e761a7
Author | SHA1 | Date |
---|---|---|
Syntriax | 4000e761a7 | |
Syntriax | 9768dbdded | |
Syntriax | 9853e0af36 | |
Syntriax | 0461454793 | |
Syntriax | d7d53e467a | |
Syntriax | 1c7d941bc1 | |
Syntriax | dc96b93024 | |
Syntriax | 1ffddab2c1 | |
Syntriax | c1c1676b9a | |
Syntriax | 11483231a5 | |
Syntriax | 2ca243d79c | |
Syntriax | 69eca44dd8 | |
Syntriax | 6a104d8abd | |
Syntriax | affd2bb8c4 | |
Syntriax | 15788f2eca | |
Syntriax | ab2489f6cf | |
Syntriax | 574104c224 | |
Syntriax | 0d4c96a2fc | |
Syntriax | 5620f2f1eb | |
Syntriax | a3c4afb223 | |
Syntriax | 4d9121118d | |
Syntriax | 05d88f7ca2 | |
Syntriax | 309c8db6e1 | |
Syntriax | 0ba6913a61 | |
Syntriax | 9556be6f17 | |
Syntriax | bd43d39367 | |
Syntriax | 32e2a6e7d3 | |
Syntriax | 7b47703ba0 | |
Syntriax | b14d10db0c | |
Syntriax | c32add40ff | |
Syntriax | 058c6dafe3 | |
Syntriax | 85bad951ff | |
Syntriax | 6a84c3ec1a | |
Syntriax | ceb29cc42f | |
Syntriax | c6d2bad23e | |
Syntriax | 2bfd391286 | |
Syntriax | ac09b78edd | |
Syntriax | 4607955d55 | |
Syntriax | 271a9a244b | |
Syntriax | f980546d37 | |
Syntriax | dfcc877e58 | |
Syntriax | 8ebde9dedf | |
Syntriax | 238bf2d574 | |
Syntriax | ceebe21041 | |
Syntriax | 0ba8927858 | |
Syntriax | ab9181fe3f | |
Syntriax | 266443504f | |
Syntriax | 3c39e6709d | |
Syntriax | e7ca96e2e2 | |
Syntriax | 3b83be695c | |
Syntriax | 601d15fa45 | |
Syntriax | 00b80f1a01 | |
Syntriax | 9e1f38897f | |
Syntriax | 0af1b11396 | |
Syntriax | f5be49609b | |
Syntriax | f7467a62ee | |
Syntriax | 816f09fffe | |
Syntriax | 24cbc8a267 | |
Syntriax | 385defd8e6 | |
Syntriax | ed15238dcd | |
Syntriax | 350ef030ac | |
Syntriax | 3428fcc6ca | |
Syntriax | 528649659d | |
Syntriax | da67f4559b | |
Syntriax | 08b31d9db1 | |
Syntriax | 326bcfca61 | |
Syntriax | bfab35c27e | |
Syntriax | 468615e4cb | |
Syntriax | 56b46b93eb | |
Syntriax | 0c3bf48d2c | |
Syntriax | 77e1949f59 | |
Syntriax | fbf9bd5832 | |
Syntriax | d40183db65 | |
Syntriax | a60f79f12b | |
Syntriax | 4bf618251f | |
Syntriax | b3d404bb6b | |
Syntriax | 87bf47eefd | |
Syntriax | e5732f0ac5 | |
Syntriax | c3bcaaee06 | |
Syntriax | 83d8a03be3 | |
Syntriax | 1acecdf3ce | |
Syntriax | 51b1f79a5d | |
Syntriax | 09c63b65df | |
Syntriax | 909b93088c | |
Syntriax | 5ed7ccdded | |
Syntriax | 0d29ab066f | |
Syntriax | bd03d036aa | |
Syntriax | 3b299c947c | |
Syntriax | 6a5d10980a | |
Syntriax | dcda78b010 | |
Syntriax | 5170dd0aea | |
Syntriax | 5c185a664c | |
Syntriax | 8ccebaa8fb | |
Syntriax | a9485475c7 |
|
@ -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"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||
public abstract class Behaviour : IBehaviour
|
||||
{
|
||||
public Action<IAssignable>? OnUnassigned { get; set; } = null;
|
||||
|
@ -25,6 +26,8 @@ public abstract class Behaviour : IBehaviour
|
|||
public IStateEnable StateEnable => _stateEnable;
|
||||
public IBehaviourController BehaviourController => _behaviourController;
|
||||
|
||||
public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => _initialized;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||
public class BehaviourController : IBehaviourController
|
||||
{
|
||||
public Action<IBehaviourController>? OnPreUpdate { get; set; }
|
||||
|
@ -55,17 +57,17 @@ public class BehaviourController : IBehaviourController
|
|||
|
||||
public IList<T> GetBehaviours<T>()
|
||||
{
|
||||
IList<T> behaviours = new List<T>();
|
||||
List<T>? behaviours = null;
|
||||
foreach (var behaviourItem in this.behaviours)
|
||||
{
|
||||
if (behaviourItem is not T behaviour)
|
||||
continue;
|
||||
|
||||
behaviours ??= new List<T>();
|
||||
behaviours ??= [];
|
||||
behaviours.Add(behaviour);
|
||||
}
|
||||
|
||||
return behaviours;
|
||||
return behaviours ?? Enumerable.Empty<T>().ToList();
|
||||
}
|
||||
|
||||
public void RemoveBehaviour<T>(bool removeAll = false) where T : class, IBehaviour
|
||||
|
|
|
@ -39,13 +39,13 @@ public abstract class BehaviourOverride : Behaviour
|
|||
OnFinalize();
|
||||
}
|
||||
|
||||
protected virtual void OnPreUpdatePreEnabledCheck() { }
|
||||
protected virtual void OnPreUpdatePreActiveCheck() { }
|
||||
protected virtual void OnPreUpdate() { }
|
||||
private void PreUpdate(IBehaviourController _)
|
||||
{
|
||||
OnPreUpdatePreEnabledCheck();
|
||||
OnPreUpdatePreActiveCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (isInitializedThisFrame)
|
||||
|
@ -61,23 +61,23 @@ public abstract class BehaviourOverride : Behaviour
|
|||
isInitializedThisFrame = false;
|
||||
}
|
||||
|
||||
protected virtual void OnUpdatePreEnabledCheck() { }
|
||||
protected virtual void OnUpdatePreActiveCheck() { }
|
||||
protected virtual void OnUpdate() { }
|
||||
private void Update(IBehaviourController _)
|
||||
{
|
||||
OnUpdatePreEnabledCheck();
|
||||
OnUpdatePreActiveCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
OnUpdate();
|
||||
}
|
||||
|
||||
protected virtual void OnPreDrawPreEnabledCheck() { }
|
||||
protected virtual void OnPreDrawPreActiveCheck() { }
|
||||
protected virtual void OnPreDraw() { }
|
||||
private void PreDraw(IBehaviourController _)
|
||||
{
|
||||
OnPreDrawPreEnabledCheck();
|
||||
OnPreDrawPreActiveCheck();
|
||||
|
||||
if (!StateEnable.Enabled)
|
||||
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;
|
||||
|
||||
public record EngineTime
|
||||
(
|
||||
TimeSpan Total,
|
||||
TimeSpan Elapsed
|
||||
);
|
||||
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
|
||||
{
|
||||
public readonly TimeSpan Total { get; init; } = Total;
|
||||
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 LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector);
|
||||
|
||||
public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to);
|
||||
|
||||
public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector);
|
||||
public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd);
|
||||
public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract);
|
||||
public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value);
|
||||
public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value);
|
||||
|
||||
public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector);
|
||||
public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal);
|
||||
public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector);
|
||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||
|
||||
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||
public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max);
|
||||
|
@ -19,4 +29,7 @@ public static class Vector2DExtensions
|
|||
public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right);
|
||||
public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
|
||||
public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right);
|
||||
|
||||
|
||||
public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ using Syntriax.Engine.Core.Factory;
|
|||
|
||||
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>? OnGameObjectUnRegistered { get; set; } = null;
|
||||
|
||||
|
@ -20,12 +20,11 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
|||
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 GameObjectFactory _gameObjectFactory = null!;
|
||||
private bool _initialized = false;
|
||||
private ICamera _camera = null!;
|
||||
|
||||
private GameObjectFactory GameObjectFactory
|
||||
{
|
||||
|
@ -38,7 +37,8 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
|||
}
|
||||
|
||||
public bool Initialized => _initialized;
|
||||
public IList<IGameObject> GameObjects => _gameObjects;
|
||||
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
|
||||
|
||||
public IStateEnable StateEnable
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (_gameObjects.Contains(gameObject))
|
||||
|
@ -101,6 +88,7 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
|||
foreach (var gameObject in GameObjects)
|
||||
gameObject.Initialize();
|
||||
|
||||
_initialized = true;
|
||||
OnInitialized?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
@ -114,6 +102,7 @@ public class GameManager : IEntity, IEnumerable<IGameObject>
|
|||
GameObjects[i].Finalize();
|
||||
|
||||
OnFinalized?.Invoke(this);
|
||||
_initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
|
||||
public class GameObject : IGameObject
|
||||
{
|
||||
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
|
||||
|
|
|
@ -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;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
||||
public class Transform : ITransform
|
||||
{
|
||||
public Action<ITransform>? OnPositionChanged { get; set; } = null;
|
||||
|
|
|
@ -2,12 +2,15 @@ using System;
|
|||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")]
|
||||
public record Vector2D(float X, float Y)
|
||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||
public readonly struct Vector2D(float X, float Y)
|
||||
{
|
||||
public float Magnitude => Length(this);
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
public Vector2D Normalized => Normalize(this);
|
||||
public readonly float X { get; init; } = X;
|
||||
public readonly float Y { get; init; } = Y;
|
||||
|
||||
public readonly float Magnitude => Length(this);
|
||||
public readonly float MagnitudeSquared => LengthSquared(this);
|
||||
public readonly Vector2D Normalized => Normalize(this);
|
||||
|
||||
public readonly static Vector2D Up = new(0f, 1f);
|
||||
public readonly static Vector2D Down = new(0f, -1f);
|
||||
|
@ -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 *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
|
||||
public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
|
||||
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
|
||||
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
|
||||
|
||||
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
|
||||
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
|
||||
|
||||
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
|
||||
|
||||
public static Vector2D Invert(Vector2D vector) => -vector;
|
||||
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
|
||||
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
|
||||
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
|
||||
public static Vector2D Subdivide(Vector2D vector, float value) => vector / value;
|
||||
|
||||
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
|
||||
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
|
||||
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
|
||||
|
@ -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 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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine.Physics2D\Engine.Physics2D.csproj", "{3B3C3332-07E3-4A00-9898-0A5410BCB08C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -24,5 +26,9 @@ Global
|
|||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
Loading…
Reference in New Issue