diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml
index 0cdea9fe..6e8e1cf9 100644
--- a/.github/workflows/build-test-deploy.yml
+++ b/.github/workflows/build-test-deploy.yml
@@ -1,15 +1,8 @@
name: "Build test deploy"
-env:
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
- DOTNET_CORE_SDK_VERSION: 6.0.302
- MYGET_ACCESS_TOKEN: ${{ secrets.MYGET_ACCESS_TOKEN }}
- MYGET_SOURCE_URL: 'https://www.myget.org/F/lithiumtoast/api/v3/index.json'
-
on:
push:
- branches: [develop]
+ branches: [develop, test/feature]
tags: [v*]
pull_request:
branches: [develop]
@@ -17,99 +10,40 @@ on:
branches: [develop]
jobs:
-
- version-job:
- name: "Version"
- runs-on: ubuntu-latest
-
- steps:
- - name: "Checkout Git repository"
- uses: actions/checkout@v2
-
- - name: "Fetch all history for all tags and branches"
- run: git fetch --prune --unshallow
-
- - name: "Install GitVersion"
- uses: gittools/actions/gitversion/setup@v0.9.6
- env:
- ACTIONS_ALLOW_UNSECURE_COMMANDS: true # workaround for https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ until the execute action is updated
- with:
- versionSpec: '5.x'
-
- - name: "Use GitVersion"
- uses: gittools/actions/gitversion/execute@v0.9.6
-
- - run: echo "$GitVersion_NuGetVersionV2" >> version.txt
-
- - name: "Upload NuGetVersion version artifact"
- uses: actions/upload-artifact@v2
- with:
- name: version
- path: version.txt
-
build-test-pack-job:
- name: "Build"
- needs: [version-job]
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [windows-latest, macos-latest, ubuntu-latest]
-
+ name: "Build-Test-Pack"
+ runs-on: ubuntu-latest
steps:
+ - name: "Clone Repository"
+ uses: actions/checkout@v4
+ - name: "CAKE (Build -> Test -> Package)"
+ run: dotnet run --project ./build/Build.csproj -- --target=Default
+ - name: "Upload Artifacts For Deploy"
+ if: github.event_name != 'pull_request'
+ uses: actions/upload-artifact@main
+ with:
+ name: MonoGame.Extended
+ path: artifacts/*
- - name: "Download version artifact"
- uses: actions/download-artifact@v2
- with:
- name: version
-
- - name: "Read Version"
- id: version
- shell: bash
- run: |
- echo "VERSION=$(cat version.txt)" >> $GITHUB_ENV
-
- - name: "Print Version"
- shell: bash
- run: |
- echo $VERSION
-
- - name: "Checkout repository"
- uses: actions/checkout@master
- with:
- submodules: true
- lfs: true
-
- - name: "Setup .NET Core CLI"
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '${{ env.DOTNET_CORE_SDK_VERSION }}'
-
- - name: "Install fonts (Ubuntu)"
- if: matrix.os == 'ubuntu-latest'
- run: |
- echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | sudo debconf-set-selections
- sudo apt-get install -y ttf-mscorefonts-installer
- sudo apt-get install -y fontconfig
- sudo fc-cache -f -v
- sudo fc-match Arial
-
- - name: "Download NuGet packages"
- run: dotnet restore --verbosity quiet
-
- - name: "Build solution"
- run: dotnet build --nologo --verbosity minimal --configuration Release --no-restore /p:Version='${{ env.VERSION }}'
-
- - name: "Test solution"
- run: dotnet test --nologo --verbosity normal --configuration Release --no-build
-
- - name: "Pack solution"
- if: matrix.os == 'ubuntu-latest' && github.event_name == 'push'
- run: dotnet pack --nologo --output "./nuget-packages" --verbosity minimal --configuration Release --no-build -p:PackageVersion='${{ env.VERSION }}'
-
- - name: "Add Packages Source: MyGet"
- if: matrix.os == 'ubuntu-latest' && github.event_name == 'push'
- run: dotnet nuget add source $MYGET_SOURCE_URL --name "MyGet"
-
- - name: "Upload Packages: MyGet"
- if: matrix.os == 'ubuntu-latest' && github.event_name == 'push'
- run: dotnet nuget push "./**/*.nupkg" --source "MyGet" --skip-duplicate --api-key $MYGET_ACCESS_TOKEN
+ deploy-job:
+ name: "Deploy Nugets"
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+ contents: write
+ needs: [build-test-pack-job]
+ if: ${{ github.event_name == 'push' }}
+ steps:
+ - name: "Clone Repository"
+ uses: actions/checkout@v4
+ - name: "Download Artifacts For Deploy"
+ uses: actions/download-artifact@main
+ with:
+ name: MonoGame.Extended
+ path: artifacts
+ - name: "Push Package"
+ run: dotnet run --project ./build/Build.csproj -- --target=Deploy
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ MYGET_ACCESS_TOKEN: ${{ secrets.MYGET_ACCESS_TOKEN }}
+ NUGET_ACCESS_TOKEN: ${{ secrets.NUGET_ACCESS_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 4006b66e..ea7d8869 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,9 @@
# macOS
.DS_Store
*.user
+
+# cake build output
+artifacts
+
+# Visual Studio
+.vs/*
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 00000000..a3d7b9d4
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,20 @@
+
+
+ 3.9.0
+ craftworkgames
+ https://github.com/craftworkgames/MonoGame.Extended
+ https://github.com/craftworkgames/MonoGame.Extended
+ git
+ develop
+ en
+ logo-nuget-128.png
+ README.md
+ MIT
+
+
+
+
+
+
+
+
diff --git a/MonoGame.Extended.Benchmarks.Collisions/DifferentPoolSizeCollision.cs b/MonoGame.Extended.Benchmarks.Collisions/DifferentPoolSizeCollision.cs
new file mode 100644
index 00000000..a4ac8588
--- /dev/null
+++ b/MonoGame.Extended.Benchmarks.Collisions/DifferentPoolSizeCollision.cs
@@ -0,0 +1,115 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Engines;
+using Microsoft.Xna.Framework;
+using MonoGame.Extended.Collisions;
+using MonoGame.Extended.Collisions.Layers;
+using MonoGame.Extended.Collisions.QuadTree;
+
+namespace MonoGame.Extended.Benchmarks.Collisions;
+
+[SimpleJob(RunStrategy.ColdStart, launchCount:3)]
+public class DifferentPoolSizeCollision
+{
+ private const int COMPONENT_BOUNDARY_SIZE = 1000;
+
+ private readonly CollisionComponent _collisionComponent;
+ private readonly Random _random = new Random();
+ private readonly GameTime _gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromMilliseconds(16));
+
+ public DifferentPoolSizeCollision()
+ {
+ var size = new Size2(COMPONENT_BOUNDARY_SIZE, COMPONENT_BOUNDARY_SIZE);
+ _collisionComponent = new CollisionComponent(new RectangleF(Point2.Zero, size));
+ }
+
+ class Collider: ICollisionActor
+ {
+ public Collider(Point2 position)
+ {
+ Bounds = new RectangleF(position, new Size2(1, 1));
+ }
+
+ public IShapeF Bounds { get; set; }
+ public Vector2 Shift { get; set; }
+
+ public Point2 Position {
+ get => Bounds.Position;
+ set => Bounds.Position = value;
+ }
+
+ public void OnCollision(CollisionEventArgs collisionInfo)
+ {
+ }
+ }
+
+ [Params(100, 500, 1000)]
+ public int N { get; set; }
+
+
+ [Params(1, 2)]
+ public int LayersCount { get; set; }
+
+ public int UpdateCount { get; set; } = 100;
+
+
+ private List _colliders = new();
+ private List _layers = new();
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ if (LayersCount > 1)
+ {
+ for (int i = 0; i < LayersCount; i++)
+ {
+ var size = new Size2(COMPONENT_BOUNDARY_SIZE, COMPONENT_BOUNDARY_SIZE);
+ var layer = new Layer(new SpatialHash(new Size2(5, 5)));//new QuadTreeSpace(new RectangleF(Point2.Zero, size)))));
+ _collisionComponent.Add(i.ToString(), layer);
+ _layers.Add(layer);
+ }
+ for (int i = 0; i < LayersCount - 1; i++)
+ _collisionComponent.AddCollisionBetweenLayer(_layers[i], _layers[i + 1]);
+
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ var layer = LayersCount == 1
+ ? _collisionComponent.Layers.First().Value
+ : _layers[i % LayersCount];
+
+ var collider = new Collider(new Point2(
+ _random.Next(COMPONENT_BOUNDARY_SIZE),
+ _random.Next(COMPONENT_BOUNDARY_SIZE)))
+ {
+ Shift = new Vector2(
+ _random.Next(4) - 2,
+ _random.Next(4) - 2),
+ };
+ _colliders.Add(collider);
+ layer.Space.Insert(collider);
+ }
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ foreach (var collider in _colliders)
+ _collisionComponent.Remove(collider);
+ _colliders.Clear();
+ foreach (var layer in _layers)
+ _collisionComponent.Remove(layer: layer);
+ _layers.Clear();
+ }
+
+ [Benchmark]
+ public void Benchmark()
+ {
+ for (int i = 0; i < UpdateCount; i++)
+ {
+ foreach (var collider in _colliders)
+ collider.Position += collider.Shift;
+ //_collisionComponent.Update(_gameTime);
+ }
+ }
+}
diff --git a/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj b/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj
new file mode 100644
index 00000000..530bd13c
--- /dev/null
+++ b/MonoGame.Extended.Benchmarks.Collisions/MonoGame.Extended.Benchmarks.Collisions.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MonoGame.Extended.Benchmarks.Collisions/Program.cs b/MonoGame.Extended.Benchmarks.Collisions/Program.cs
new file mode 100644
index 00000000..647c9340
--- /dev/null
+++ b/MonoGame.Extended.Benchmarks.Collisions/Program.cs
@@ -0,0 +1,5 @@
+using BenchmarkDotNet.Running;
+using MonoGame.Extended.Benchmarks.Collisions;
+
+//var summary = BenchmarkRunner.Run();
+var summary = BenchmarkRunner.Run();
diff --git a/MonoGame.Extended.Benchmarks.Collisions/SpaceAlgorithms.cs b/MonoGame.Extended.Benchmarks.Collisions/SpaceAlgorithms.cs
new file mode 100644
index 00000000..7c82a329
--- /dev/null
+++ b/MonoGame.Extended.Benchmarks.Collisions/SpaceAlgorithms.cs
@@ -0,0 +1,104 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Engines;
+using Microsoft.Xna.Framework;
+using MonoGame.Extended.Benchmarks.Collisions.Utils;
+using MonoGame.Extended.Collisions;
+using MonoGame.Extended.Collisions.Layers;
+using MonoGame.Extended.Collisions.QuadTree;
+
+namespace MonoGame.Extended.Benchmarks.Collisions;
+
+[SimpleJob(RunStrategy.ColdStart, launchCount:10)]
+public class SpaceAlgorithms
+{
+ private const int COMPONENT_BOUNDARY_SIZE = 1000;
+
+ private readonly Random _random = new ();
+ private ISpaceAlgorithm _space;
+ private ICollisionActor _actor;
+ private RectangleF _bound;
+ private List _colliders = new();
+
+ [Params(10, 100, 1000)]
+ public int N { get; set; }
+
+ [Params("SpatialHash", "QuadTree")]
+ public string Algorithm { get; set; }
+
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ var size = new Size2(COMPONENT_BOUNDARY_SIZE, COMPONENT_BOUNDARY_SIZE);
+ _space = Algorithm switch
+ {
+ "SpatialHash" => new SpatialHash(new Size2(32, 32)),
+ "QuadTree" => new QuadTreeSpace(new RectangleF(Point2.Zero, size)),
+ _ => _space
+ };
+ for (int i = 0; i < N; i++)
+ {
+
+ var rect = GetRandomRectangleF();
+ var actor = new Collider(rect);
+ _colliders.Add(actor);
+ _space.Insert(actor);
+ }
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ foreach (var collider in _colliders)
+ _space.Remove(collider);
+ _colliders.Clear();
+ }
+
+ [GlobalSetup(Targets = new[] { nameof(Insert), nameof(Remove) })]
+ public void ActorGlobalSetup()
+ {
+ GlobalSetup();
+ var rect = GetRandomRectangleF();
+ _actor = new Collider(rect);
+ }
+
+ [Benchmark]
+ public void Insert()
+ {
+ _space.Insert(_actor);
+ }
+
+ [Benchmark]
+ public void Remove()
+ {
+ _space.Remove(_actor);
+ }
+
+ [Benchmark]
+ public void Reset()
+ {
+ _space.Reset();
+ }
+
+ [GlobalSetup(Target = nameof(Query))]
+ public void QueryGlobalSetup()
+ {
+ GlobalSetup();
+ _bound = GetRandomRectangleF();
+ }
+
+ private RectangleF GetRandomRectangleF()
+ {
+ return new RectangleF(
+ _random.Next(COMPONENT_BOUNDARY_SIZE),
+ _random.Next(COMPONENT_BOUNDARY_SIZE),
+ _random.Next(32, 128),
+ _random.Next(32, 128));
+ }
+
+ [Benchmark]
+ public List Query()
+ {
+ return _space.Query(_bound).ToList();
+ }
+}
diff --git a/MonoGame.Extended.Benchmarks.Collisions/Utils/Collider.cs b/MonoGame.Extended.Benchmarks.Collisions/Utils/Collider.cs
new file mode 100644
index 00000000..80059613
--- /dev/null
+++ b/MonoGame.Extended.Benchmarks.Collisions/Utils/Collider.cs
@@ -0,0 +1,29 @@
+using Microsoft.Xna.Framework;
+using MonoGame.Extended.Collisions;
+
+namespace MonoGame.Extended.Benchmarks.Collisions.Utils;
+
+public class Collider: ICollisionActor
+{
+ public Collider(Point2 position)
+ {
+ Bounds = new RectangleF(position, new Size2(1, 1));
+ }
+
+ public Collider(IShapeF shape)
+ {
+ Bounds = shape;
+ }
+
+ public IShapeF Bounds { get; set; }
+ public Vector2 Shift { get; set; }
+
+ public Point2 Position {
+ get => Bounds.Position;
+ set => Bounds.Position = value;
+ }
+
+ public void OnCollision(CollisionEventArgs collisionInfo)
+ {
+ }
+}
diff --git a/MonoGame.Extended.Tweening.Tests/ColorHandler.cs b/MonoGame.Extended.Tweening.Tests/ColorHandler.cs
new file mode 100644
index 00000000..a4cddc0a
--- /dev/null
+++ b/MonoGame.Extended.Tweening.Tests/ColorHandler.cs
@@ -0,0 +1,8 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tweening.Tests;
+
+public class ColorHandler
+{
+ public Color Color { get; set; }
+}
diff --git a/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj b/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj
new file mode 100644
index 00000000..9a2d97fe
--- /dev/null
+++ b/MonoGame.Extended.Tweening.Tests/MonoGame.Extended.Tweening.Tests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/MonoGame.Extended.Tweening.Tests/TweenerTests.cs b/MonoGame.Extended.Tweening.Tests/TweenerTests.cs
new file mode 100644
index 00000000..0fbfded1
--- /dev/null
+++ b/MonoGame.Extended.Tweening.Tests/TweenerTests.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework;
+using Xunit;
+
+namespace MonoGame.Extended.Tweening.Tests;
+
+public class TweenerTests
+{
+ [Fact]
+ public void TweenerTweenToSuccessTest()
+ {
+ var tweener = new Tweener();
+ var obj = new ColorHandler();
+ tweener.TweenTo(obj, x => x.Color, Color.Red, 2f);
+ }
+}
diff --git a/MonoGame.Extended.sln b/MonoGame.Extended.sln
index 354625fa..195a7acc 100644
--- a/MonoGame.Extended.sln
+++ b/MonoGame.Extended.sln
@@ -43,6 +43,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Extended.Collision
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Extended", "src\cs\MonoGame.Extended\MonoGame.Extended.csproj", "{4170BBC0-BF49-4307-8272-768EEA77034A}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{AAB81CB9-9C71-4499-A51E-933C384D6DBF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Extended.Benchmarks.Collisions", "MonoGame.Extended.Benchmarks.Collisions\MonoGame.Extended.Benchmarks.Collisions.csproj", "{6B5939ED-E99D-4842-B4FE-A7624C59A60A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Extended.Tweening.Tests", "MonoGame.Extended.Tweening.Tests\MonoGame.Extended.Tweening.Tests.csproj", "{A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -187,6 +193,22 @@ Global
{4170BBC0-BF49-4307-8272-768EEA77034A}.Release|Any CPU.Build.0 = Release|Any CPU
{4170BBC0-BF49-4307-8272-768EEA77034A}.Release|x86.ActiveCfg = Release|Any CPU
{4170BBC0-BF49-4307-8272-768EEA77034A}.Release|x86.Build.0 = Release|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Debug|x86.Build.0 = Debug|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Release|x86.ActiveCfg = Release|Any CPU
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A}.Release|x86.Build.0 = Release|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Debug|x86.Build.0 = Debug|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Release|x86.ActiveCfg = Release|Any CPU
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -199,6 +221,8 @@ Global
{43CBA868-9BFC-4E02-8A7A-142C603B087E} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863}
{34841DEB-EEC0-477C-B19D-CBB6DF49ED39} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863}
{CB439E84-F0F6-4790-8CD1-8A66C3D7B4DA} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863}
+ {6B5939ED-E99D-4842-B4FE-A7624C59A60A} = {AAB81CB9-9C71-4499-A51E-933C384D6DBF}
+ {A1BD6FDC-5A76-4D14-8FDA-ACD153C8EBCC} = {E5A148A1-DE7B-4D17-ABE8-831B9673B863}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8ED5A62D-25EC-4331-9F99-BDD1E10C02A0}
diff --git a/build/Build.csproj b/build/Build.csproj
new file mode 100644
index 00000000..10f43d07
--- /dev/null
+++ b/build/Build.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ net7.0
+ enable
+ $(MSBuildProjectDirectory)
+
+
+
+
+
diff --git a/build/BuildContext.cs b/build/BuildContext.cs
new file mode 100644
index 00000000..286eb20c
--- /dev/null
+++ b/build/BuildContext.cs
@@ -0,0 +1,41 @@
+using System.Linq;
+using Cake.Common;
+using Cake.Common.Build;
+using Cake.Common.Xml;
+using Cake.Core;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+public sealed class BuildContext : FrostingContext
+{
+ public string ArtifactsDirectory { get; }
+ public string Version { get; }
+ public string SolutionPath { get; }
+ public string? RepositoryOwner { get; }
+ public string? RepositoryUrl { get; }
+ public bool IsTag { get; }
+ public bool IsRunningOnGitHubActions { get; }
+ public string? GitHubToken { get; }
+ public string? MyGetAccessToken { get; }
+ public string? NuGetAccessToken { get; }
+
+ public BuildContext(ICakeContext context) : base(context)
+ {
+ ArtifactsDirectory = context.Argument(nameof(ArtifactsDirectory), "artifacts");
+ Version = context.XmlPeek("Directory.Build.targets", "/Project/PropertyGroup/Version");
+ SolutionPath = "./MonoGame.Extended.sln";
+ IsRunningOnGitHubActions = context.BuildSystem().IsRunningOnGitHubActions;
+
+ if (IsRunningOnGitHubActions)
+ {
+ Version += "." + context.EnvironmentVariable("GITHUB_RUN_NUMBER");
+ RepositoryOwner = context.EnvironmentVariable("GITHUB_REPOSITORY_OWNER");
+ RepositoryUrl = $"https://github.com/{context.EnvironmentVariable("GITHUB_REPOSITORY")}";
+ IsTag = context.EnvironmentVariable("GITHUB_REF_TYPE") == "tag";
+ GitHubToken = context.EnvironmentVariable("GITHUB_TOKEN");
+ MyGetAccessToken = context.EnvironmentVariable("MYGET_ACCESS_TOKEN");
+ NuGetAccessToken = context.EnvironmentVariable("NUGET_ACCESS_TOKEN");
+ }
+ }
+}
diff --git a/build/BuildTask.cs b/build/BuildTask.cs
new file mode 100644
index 00000000..218c2095
--- /dev/null
+++ b/build/BuildTask.cs
@@ -0,0 +1,29 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.Build;
+using Cake.Common.Tools.DotNet.MSBuild;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(BuildTask))]
+public sealed class BuildTask : FrostingTask
+{
+ public override void Run(BuildContext context)
+ {
+ DotNetMSBuildSettings msBuildSettings = new DotNetMSBuildSettings();
+ msBuildSettings.WithProperty("Version", context.Version);
+ msBuildSettings.WithProperty("NoWarn", "CS1591");
+
+
+ DotNetBuildSettings buildSettings = new DotNetBuildSettings()
+ {
+ MSBuildSettings = msBuildSettings,
+ Configuration = "Release",
+ Verbosity = DotNetVerbosity.Minimal,
+ NoRestore = true,
+ NoLogo = true
+ };
+
+ context.DotNetBuild(context.SolutionPath, buildSettings);
+ }
+}
diff --git a/build/DeployToGithubTask.cs b/build/DeployToGithubTask.cs
new file mode 100644
index 00000000..62981e79
--- /dev/null
+++ b/build/DeployToGithubTask.cs
@@ -0,0 +1,25 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.NuGet.Push;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(DeployToGitHubTask))]
+public sealed class DeployToGitHubTask : FrostingTask
+{
+ public override bool ShouldRun(BuildContext context)
+ {
+ return context.IsRunningOnGitHubActions;
+ }
+
+ public override void Run(BuildContext context)
+ {
+ DotNetNuGetPushSettings pushSettings = new DotNetNuGetPushSettings()
+ {
+ Source = $"https://nuget.pkg.github.com/{context.RepositoryOwner}/index.json",
+ ApiKey = context.GitHubToken
+ };
+
+ context.DotNetNuGetPush("artifacts/*.nupkg", pushSettings);
+ }
+}
diff --git a/build/DeployToMyGetTask.cs b/build/DeployToMyGetTask.cs
new file mode 100644
index 00000000..c83fe07c
--- /dev/null
+++ b/build/DeployToMyGetTask.cs
@@ -0,0 +1,25 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.NuGet.Push;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(DeployToMyGetTask))]
+public sealed class DeployToMyGetTask : FrostingTask
+{
+ public override bool ShouldRun(BuildContext context)
+ {
+ return context.IsRunningOnGitHubActions;
+ }
+
+ public override void Run(BuildContext context)
+ {
+ DotNetNuGetPushSettings pushSettings = new DotNetNuGetPushSettings()
+ {
+ Source = $"https://www.myget.org/F/lithiumtoast/api/v3/index.json",
+ ApiKey = context.MyGetAccessToken
+ };
+
+ context.DotNetNuGetPush("artifacts/*.nupkg", pushSettings);
+ }
+}
diff --git a/build/DeployToNuGetTask.cs b/build/DeployToNuGetTask.cs
new file mode 100644
index 00000000..84675021
--- /dev/null
+++ b/build/DeployToNuGetTask.cs
@@ -0,0 +1,27 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.NuGet.Push;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(DeployToNuGetTask))]
+public sealed class DeployToNuGetTask : FrostingTask
+{
+ public override bool ShouldRun(BuildContext context)
+ {
+ return context.IsRunningOnGitHubActions &&
+ context.IsTag &&
+ context.RepositoryOwner == "craftworksgames";
+ }
+
+ public override void Run(BuildContext context)
+ {
+ DotNetNuGetPushSettings pushSettings = new DotNetNuGetPushSettings()
+ {
+ Source = $"https://api.nuget.org/v3/index.json",
+ ApiKey = context.NuGetAccessToken
+ };
+
+ context.DotNetNuGetPush("artifacts/*.nupkg", pushSettings);
+ }
+}
diff --git a/build/PackageTask.cs b/build/PackageTask.cs
new file mode 100644
index 00000000..b1c56ab4
--- /dev/null
+++ b/build/PackageTask.cs
@@ -0,0 +1,40 @@
+using Cake.Common.Build;
+using Cake.Common.IO;
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.MSBuild;
+using Cake.Common.Tools.DotNet.Pack;
+using Cake.Core.IO;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(PackageTask))]
+public sealed class PackageTask : FrostingTask
+{
+ public override void Run(BuildContext context)
+ {
+ context.CleanDirectories(context.ArtifactsDirectory);
+ context.CreateDirectory(context.ArtifactsDirectory);
+
+ DotNetMSBuildSettings msBuildSettings = new DotNetMSBuildSettings();
+ msBuildSettings.WithProperty("Version", context.Version);
+
+ // Ignore warnings about adding duplicate items added to package
+ msBuildSettings.WithProperty("NoWarn", "NU5118");
+
+ DotNetPackSettings packSettings = new DotNetPackSettings()
+ {
+ MSBuildSettings = msBuildSettings,
+ Configuration = "Release",
+ Verbosity = DotNetVerbosity.Minimal,
+ NoLogo = true,
+ OutputDirectory = context.ArtifactsDirectory,
+ };
+
+ FilePathCollection files = context.GetFiles("./src/cs/MonoGame.Extended*/**/*.csproj");
+ foreach(FilePath file in files)
+ {
+ context.DotNetPack(file.FullPath, packSettings);
+ }
+ }
+}
diff --git a/build/Program.cs b/build/Program.cs
new file mode 100644
index 00000000..cca32654
--- /dev/null
+++ b/build/Program.cs
@@ -0,0 +1,34 @@
+using System.Threading.Tasks;
+using Cake.Common.Build;
+using Cake.Common.IO;
+using Cake.Common.Tools.ReportUnit;
+using Cake.Core;
+using Cake.Core.IO;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+public static class Program
+{
+ public static int Main(string[] args)
+ {
+ return new CakeHost()
+ .UseContext()
+ .UseWorkingDirectory("../")
+ .Run(args);
+ }
+}
+
+[TaskName("Default")]
+[IsDependentOn(typeof(RestoreTask))]
+[IsDependentOn(typeof(BuildTask))]
+[IsDependentOn(typeof(TestTask))]
+[IsDependentOn(typeof(PackageTask))]
+public sealed class DefaultTask : FrostingTask {}
+
+
+[TaskName("Deploy")]
+[IsDependentOn(typeof(DeployToGitHubTask))]
+[IsDependentOn(typeof(DeployToMyGetTask))]
+[IsDependentOn(typeof(DeployToNuGetTask))]
+public sealed class DeployTask : FrostingTask {}
diff --git a/build/RestoreTask.cs b/build/RestoreTask.cs
new file mode 100644
index 00000000..91c5222c
--- /dev/null
+++ b/build/RestoreTask.cs
@@ -0,0 +1,18 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.Restore;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+public sealed class RestoreTask : FrostingTask
+{
+ public override void Run(BuildContext context)
+ {
+ DotNetRestoreSettings restoreSettings = new DotNetRestoreSettings()
+ {
+ Verbosity = DotNetVerbosity.Quiet
+ };
+
+ context.DotNetRestore(context.SolutionPath, restoreSettings);
+ }
+}
diff --git a/build/TestTask.cs b/build/TestTask.cs
new file mode 100644
index 00000000..93af6839
--- /dev/null
+++ b/build/TestTask.cs
@@ -0,0 +1,22 @@
+using Cake.Common.Tools.DotNet;
+using Cake.Common.Tools.DotNet.Test;
+using Cake.Frosting;
+
+namespace BuildScripts;
+
+[TaskName(nameof(TestTask))]
+public class TestTask : FrostingTask
+{
+ public override void Run(BuildContext context)
+ {
+ DotNetTestSettings testSettings = new DotNetTestSettings()
+ {
+ Configuration = "Release",
+ Verbosity = DotNetVerbosity.Normal,
+ NoLogo = true,
+ NoBuild = true
+ };
+
+ context.DotNetTest(context.SolutionPath, testSettings);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Animations/MonoGame.Extended.Animations.csproj b/src/cs/MonoGame.Extended.Animations/MonoGame.Extended.Animations.csproj
index 4cde0df1..62835857 100644
--- a/src/cs/MonoGame.Extended.Animations/MonoGame.Extended.Animations.csproj
+++ b/src/cs/MonoGame.Extended.Animations/MonoGame.Extended.Animations.csproj
@@ -7,9 +7,6 @@
craftworkgames
Animations to make MonoGame more awesome.
monogame animations spritesheet sprite
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
@@ -24,4 +21,4 @@
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionActor.cs b/src/cs/MonoGame.Extended.Collisions/CollisionActor.cs
deleted file mode 100644
index 1b50c2c7..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionActor.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- public class CollisionActor : IActorTarget
- {
- private readonly IActorTarget _target;
-
- public CollisionActor(IActorTarget target)
- {
- _target = target;
- }
-
- public Vector2 Velocity
- {
- get { return _target.Velocity; }
- set { _target.Velocity = value; }
- }
-
- public Vector2 Position
- {
- get { return _target.Position; }
- set { _target.Position = value; }
- }
-
- public RectangleF BoundingBox => _target.BoundingBox;
-
- public void OnCollision(CollisionInfo collisionInfo)
- {
- _target.OnCollision(collisionInfo);
- }
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs
new file mode 100644
index 00000000..51467d9d
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/CollisionComponent.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using MonoGame.Extended.Collisions.Layers;
+using MonoGame.Extended.Collisions.QuadTree;
+
+namespace MonoGame.Extended.Collisions
+{
+ ///
+ /// Handles basic collision between actors.
+ /// When two actors collide, their OnCollision method is called.
+ ///
+ public class CollisionComponent : SimpleGameComponent
+ {
+ public const string DEFAULT_LAYER_NAME = "default";
+
+ private Dictionary _layers = new();
+
+ ///
+ /// List of collision's layers
+ ///
+ public IReadOnlyDictionary Layers => _layers;
+
+ private HashSet<(Layer, Layer)> _layerCollision = new();
+
+ ///
+ /// Creates component with default layer, which is a collision tree covering the specified area (using .
+ ///
+ /// Boundary of the collision tree.
+ public CollisionComponent(RectangleF boundary)
+ {
+ SetDefaultLayer(new Layer(new QuadTreeSpace(boundary)));
+ }
+
+ ///
+ /// Creates component with specifies default layer.
+ /// If layer is null, method creates component without default layer.
+ ///
+ /// Default layer
+ public CollisionComponent(Layer layer = null)
+ {
+ if (layer is not null)
+ SetDefaultLayer(layer);
+ }
+
+ ///
+ /// The main layer has the name from .
+ /// The main layer collision with itself and all other layers.
+ ///
+ /// Layer to set default
+ public void SetDefaultLayer(Layer layer)
+ {
+ if (_layers.ContainsKey(DEFAULT_LAYER_NAME))
+ Remove(DEFAULT_LAYER_NAME);
+ Add(DEFAULT_LAYER_NAME, layer);
+ foreach (var otherLayer in _layers.Values)
+ AddCollisionBetweenLayer(layer, otherLayer);
+ }
+
+ ///
+ /// Update the collision tree and process collisions.
+ ///
+ ///
+ /// Boundary shapes are updated if they were changed since the last
+ /// update.
+ ///
+ ///
+ public override void Update(GameTime gameTime)
+ {
+ foreach (var layer in _layers.Values)
+ layer.Reset();
+
+ foreach (var (firstLayer, secondLayer) in _layerCollision)
+ foreach (var actor in firstLayer.Space)
+ {
+ var collisions = secondLayer.Space.Query(actor.Bounds.BoundingRectangle);
+ foreach (var other in collisions)
+ if (actor != other && actor.Bounds.Intersects(other.Bounds))
+ {
+ var penetrationVector = CalculatePenetrationVector(actor.Bounds, other.Bounds);
+
+ actor.OnCollision(new CollisionEventArgs
+ {
+ Other = other,
+ PenetrationVector = penetrationVector
+ });
+ other.OnCollision(new CollisionEventArgs
+ {
+ Other = actor,
+ PenetrationVector = -penetrationVector
+ });
+ }
+
+ }
+ }
+
+ ///
+ /// Inserts the target into the collision tree.
+ /// The target will have its OnCollision called when collisions occur.
+ ///
+ /// Target to insert.
+ public void Insert(ICollisionActor target)
+ {
+ var layerName = target.LayerName ?? DEFAULT_LAYER_NAME;
+ if (!_layers.TryGetValue(layerName, out var layer))
+ {
+ throw new UndefinedLayerException(layerName);
+ }
+
+ layer.Space.Insert(target);
+ }
+
+ ///
+ /// Removes the target from the collision tree.
+ ///
+ /// Target to remove.
+ public void Remove(ICollisionActor target)
+ {
+ if (target.LayerName is not null)
+ _layers[target.LayerName].Space.Remove(target);
+ else
+ foreach (var layer in _layers.Values)
+ if (layer.Space.Remove(target))
+ return;
+ }
+
+ #region Layers
+
+ ///
+ /// Add the new layer. The name of layer must be unique.
+ ///
+ /// Name of layer
+ /// The new layer
+ /// is null
+ public void Add(string name, Layer layer)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name));
+
+ if (!_layers.TryAdd(name, layer))
+ throw new DuplicateNameException(name);
+
+ if (name != DEFAULT_LAYER_NAME)
+ AddCollisionBetweenLayer(_layers[DEFAULT_LAYER_NAME], layer);
+ }
+
+ ///
+ /// Remove the layer and all layer's collisions.
+ ///
+ /// The name of the layer to delete
+ /// The layer to delete
+ public void Remove(string name = null, Layer layer = null)
+ {
+ name ??= _layers.First(x => x.Value == layer).Key;
+ _layers.Remove(name, out layer);
+ _layerCollision.RemoveWhere(tuple => tuple.Item1 == layer || tuple.Item2 == layer);
+ }
+
+ public void AddCollisionBetweenLayer(Layer a, Layer b)
+ {
+ _layerCollision.Add((a, b));
+ }
+
+ public void AddCollisionBetweenLayer(string nameA, string nameB)
+ {
+ _layerCollision.Add((_layers[nameA], _layers[nameB]));
+ }
+
+ #endregion
+
+ #region Penetration Vectors
+
+ ///
+ /// Calculate a's penetration into b
+ ///
+ /// The penetrating shape.
+ /// The shape being penetrated.
+ /// The distance vector from the edge of b to a's Position
+ private static Vector2 CalculatePenetrationVector(IShapeF a, IShapeF b)
+ {
+ return a switch
+ {
+ CircleF circleA when b is CircleF circleB => PenetrationVector(circleA, circleB),
+ CircleF circleA when b is RectangleF rectangleB => PenetrationVector(circleA, rectangleB),
+ CircleF circleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(circleA, orientedRectangleB),
+
+ RectangleF rectangleA when b is CircleF circleB => PenetrationVector(rectangleA, circleB),
+ RectangleF rectangleA when b is RectangleF rectangleB => PenetrationVector(rectangleA, rectangleB),
+ RectangleF rectangleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(rectangleA, orientedRectangleB),
+
+ OrientedRectangle orientedRectangleA when b is CircleF circleB => PenetrationVector(orientedRectangleA, circleB),
+ OrientedRectangle orientedRectangleA when b is RectangleF rectangleB => PenetrationVector(orientedRectangleA, rectangleB),
+ OrientedRectangle orientedRectangleA when b is OrientedRectangle orientedRectangleB => PenetrationVector(orientedRectangleA, orientedRectangleB),
+
+ _ => throw new ArgumentOutOfRangeException(nameof(a))
+ };
+ }
+
+ private static Vector2 PenetrationVector(CircleF circ1, CircleF circ2)
+ {
+ if (!circ1.Intersects(circ2))
+ {
+ return Vector2.Zero;
+ }
+
+ var displacement = Point2.Displacement(circ1.Center, circ2.Center);
+
+ Vector2 desiredDisplacement;
+ if (displacement != Vector2.Zero)
+ {
+ desiredDisplacement = displacement.NormalizedCopy() * (circ1.Radius + circ2.Radius);
+ }
+ else
+ {
+ desiredDisplacement = -Vector2.UnitY * (circ1.Radius + circ2.Radius);
+ }
+
+
+ var penetration = displacement - desiredDisplacement;
+ return penetration;
+ }
+
+ private static Vector2 PenetrationVector(CircleF circ, RectangleF rect)
+ {
+ var collisionPoint = rect.ClosestPointTo(circ.Center);
+ var cToCollPoint = collisionPoint - circ.Center;
+
+ if (rect.Contains(circ.Center) || cToCollPoint.Equals(Vector2.Zero))
+ {
+ var displacement = Point2.Displacement(circ.Center, rect.Center);
+
+ Vector2 desiredDisplacement;
+ if (displacement != Vector2.Zero)
+ {
+ // Calculate penetration as only in X or Y direction.
+ // Whichever is lower.
+ var dispx = new Vector2(displacement.X, 0);
+ var dispy = new Vector2(0, displacement.Y);
+ dispx.Normalize();
+ dispy.Normalize();
+
+ dispx *= (circ.Radius + rect.Width / 2);
+ dispy *= (circ.Radius + rect.Height / 2);
+
+ if (dispx.LengthSquared() < dispy.LengthSquared())
+ {
+ desiredDisplacement = dispx;
+ displacement.Y = 0;
+ }
+ else
+ {
+ desiredDisplacement = dispy;
+ displacement.X = 0;
+ }
+ }
+ else
+ {
+ desiredDisplacement = -Vector2.UnitY * (circ.Radius + rect.Height / 2);
+ }
+
+ var penetration = displacement - desiredDisplacement;
+ return penetration;
+ }
+ else
+ {
+ var penetration = circ.Radius * cToCollPoint.NormalizedCopy() - cToCollPoint;
+ return penetration;
+ }
+ }
+
+ private static Vector2 PenetrationVector(CircleF circleA, OrientedRectangle orientedRectangleB)
+ {
+ var rotation = Matrix2.CreateRotationZ(orientedRectangleB.Orientation.Rotation);
+ var circleCenterInRectangleSpace = rotation.Transform(circleA.Center - orientedRectangleB.Center);
+ var circleInRectangleSpace = new CircleF(circleCenterInRectangleSpace, circleA.Radius);
+ var boundingRectangle = new BoundingRectangle(new Point2(), orientedRectangleB.Radii);
+
+ var penetrationVector = PenetrationVector(circleInRectangleSpace, boundingRectangle);
+ var inverseRotation = Matrix2.CreateRotationZ(-orientedRectangleB.Orientation.Rotation);
+ var transformedPenetration = inverseRotation.Transform(penetrationVector);
+
+ return transformedPenetration;
+ }
+
+ private static Vector2 PenetrationVector(RectangleF rect, CircleF circ)
+ {
+ return -PenetrationVector(circ, rect);
+ }
+
+ private static Vector2 PenetrationVector(RectangleF rect1, RectangleF rect2)
+ {
+ var intersectingRectangle = RectangleF.Intersection(rect1, rect2);
+ Debug.Assert(!intersectingRectangle.IsEmpty,
+ "Violation of: !intersect.IsEmpty; Rectangles must intersect to calculate a penetration vector.");
+
+ Vector2 penetration;
+ if (intersectingRectangle.Width < intersectingRectangle.Height)
+ {
+ var d = rect1.Center.X < rect2.Center.X
+ ? intersectingRectangle.Width
+ : -intersectingRectangle.Width;
+ penetration = new Vector2(d, 0);
+ }
+ else
+ {
+ var d = rect1.Center.Y < rect2.Center.Y
+ ? intersectingRectangle.Height
+ : -intersectingRectangle.Height;
+ penetration = new Vector2(0, d);
+ }
+
+ return penetration;
+ }
+
+ private static Vector2 PenetrationVector(RectangleF rectangleA, OrientedRectangle orientedRectangleB)
+ {
+ return PenetrationVector((OrientedRectangle)rectangleA, orientedRectangleB);
+ }
+
+ private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, CircleF circleB)
+ {
+ return -PenetrationVector(circleB, orientedRectangleA);
+ }
+
+ private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, RectangleF rectangleB)
+ {
+ return -PenetrationVector(rectangleB, orientedRectangleA);
+ }
+
+ private static Vector2 PenetrationVector(OrientedRectangle orientedRectangleA, OrientedRectangle orientedRectangleB)
+ {
+ return OrientedRectangle.Intersects(orientedRectangleA, orientedRectangleB)
+ .MinimumTranslationVector;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/CollisionEventArgs.cs b/src/cs/MonoGame.Extended.Collisions/CollisionEventArgs.cs
similarity index 100%
rename from src/cs/MonoGame.Extended.Collisions/QuadTree/CollisionEventArgs.cs
rename to src/cs/MonoGame.Extended.Collisions/CollisionEventArgs.cs
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionGrid.cs b/src/cs/MonoGame.Extended.Collisions/CollisionGrid.cs
deleted file mode 100644
index 2554dc38..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionGrid.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- ///
- /// Represents a collision grid. This is used to break the game world into
- /// chunks to detect collisions efficiently.
- ///
- public class CollisionGrid
- {
- private readonly CollisionGridCell[] _data;
-
- ///
- /// Creates a new collision grid of specified size.
- ///
- ///
- /// Number of columns in the grid.
- /// Number of rows in the grid.
- /// The width of each individual cell.
- /// The height of each individual cell.
- public CollisionGrid(int[] data, int columns, int rows, int cellWidth, int cellHeight)
- {
- _data = new CollisionGridCell[data.Length];
-
- for (var y = 0; y < rows; y++)
- for (var x = 0; x < columns; x++)
- {
- var index = x + y*columns;
- _data[index] = new CollisionGridCell(this, x, y, data[index]);
- }
-
- Columns = columns;
- Rows = rows;
- CellWidth = cellWidth;
- CellHeight = cellHeight;
- }
-
- ///
- /// Gets the number of columns in this grid.
- ///
- public int Columns { get; }
-
- ///
- /// Gets the number of rows in this grid.
- ///
- public int Rows { get; private set; }
-
- ///
- /// Gets the width of each cell.
- ///
- public int CellWidth { get; }
-
- ///
- /// Gets the height of each cell.
- ///
- public int CellHeight { get; }
-
- public CollisionGridCell GetCellAtIndex(int column, int row)
- {
- var index = column + row*Columns;
-
- if ((index < 0) || (index >= _data.Length))
- return new CollisionGridCell(this, column, row, 0);
-
- return _data[index];
- }
-
- public CollisionGridCell GetCellAtPosition(Vector3 position)
- {
- var column = (int) (position.X/CellWidth);
- var row = (int) (position.Y/CellHeight);
-
- return GetCellAtIndex(column, row);
- }
-
- public IEnumerable GetCellsOverlappingRectangle(RectangleF rectangle)
- {
- var sx = (int) (rectangle.Left/CellWidth);
- var sy = (int) (rectangle.Top/CellHeight);
- var ex = (int) (rectangle.Right/CellWidth + 1);
- var ey = (int) (rectangle.Bottom/CellHeight + 1);
-
- for (var y = sy; y < ey; y++)
- for (var x = sx; x < ex; x++)
- yield return GetCellAtIndex(x, y);
- }
-
- public IEnumerable GetCollidables(RectangleF overlappingRectangle)
- {
- return GetCellsOverlappingRectangle(overlappingRectangle)
- .Where(cell => cell.Flag != CollisionGridCellFlag.Empty);
- }
-
- public Rectangle GetCellRectangle(int column, int row)
- {
- return new Rectangle(column*CellWidth, row*CellHeight, CellWidth, CellHeight);
- }
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionGridCell.cs b/src/cs/MonoGame.Extended.Collisions/CollisionGridCell.cs
deleted file mode 100644
index c30ca813..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionGridCell.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-namespace MonoGame.Extended.Collisions
-{
- ///
- /// Represents a single cell in a collision grid.
- ///
- public class CollisionGridCell : ICollidable
- {
- private readonly CollisionGrid _parentGrid;
-
- ///
- /// Creates a collision grid cell at a location in the parent grid.
- ///
- /// The collision grid which this cell is a part of.
- /// The column position of this cell.
- /// The row position of this cell.
- ///
- public CollisionGridCell(CollisionGrid parentGrid, int column, int row, int data)
- {
- _parentGrid = parentGrid;
- Column = column;
- Row = row;
- Data = data;
- Flag = data == 0 ? CollisionGridCellFlag.Empty : CollisionGridCellFlag.Solid;
- }
-
- ///
- /// Gets the Column in the parent grid that this cell represents.
- ///
- public int Column { get; }
-
- ///
- /// Gets the Row in the parent grid that this cell represents.
- ///
- public int Row { get; }
- public int Data { get; private set; }
- public object Tag { get; set; }
- public CollisionGridCellFlag Flag { get; set; }
-
- ///
- /// Gets the bounding box of the cell.
- ///
- public RectangleF BoundingBox => _parentGrid.GetCellRectangle(Column, Row).ToRectangleF();
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionGridCellFlag.cs b/src/cs/MonoGame.Extended.Collisions/CollisionGridCellFlag.cs
deleted file mode 100644
index 546d2be9..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionGridCellFlag.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MonoGame.Extended.Collisions
-{
- public enum CollisionGridCellFlag
- {
- Empty,
- Solid,
- Interesting
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionInfo.cs b/src/cs/MonoGame.Extended.Collisions/CollisionInfo.cs
deleted file mode 100644
index bcc8efcc..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionInfo.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- ///
- /// This class holds data on a collision. It is passed as a parameter to
- /// OnCollision methods.
- ///
- public class CollisionInfo
- {
- ///
- /// Gets the object being collided with.
- ///
- public ICollidable Other { get; internal set; }
-
- ///
- /// Gets a vector representing the overlap between the two objects.
- ///
- public Vector2 PenetrationVector { get; internal set; }
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionWorld.cs b/src/cs/MonoGame.Extended.Collisions/CollisionWorld.cs
deleted file mode 100644
index a02a4151..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionWorld.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- public class CollisionWorld : IDisposable, IUpdate
- {
- private readonly List _actors;
-
- private readonly Vector2 _gravity;
- private CollisionGrid _grid;
-
- public CollisionWorld(Vector2 gravity)
- {
- _gravity = gravity;
- _actors = new List();
- }
-
- public void Dispose()
- {
- }
-
- public void Update(GameTime gameTime)
- {
- var deltaTime = (float) gameTime.ElapsedGameTime.TotalSeconds;
-
- foreach (var actor in _actors)
- {
- actor.Velocity += _gravity*deltaTime;
- actor.Position += actor.Velocity*deltaTime;
-
- if (_grid != null)
- foreach (var collidable in _grid.GetCollidables(actor.BoundingBox))
- {
- var intersection = RectangleF.Intersection(collidable.BoundingBox, actor.BoundingBox);
-
- if (intersection.IsEmpty)
- continue;
-
- var info = GetCollisionInfo(actor, collidable, intersection);
- actor.OnCollision(info);
- }
- }
- }
-
- public CollisionActor CreateActor(IActorTarget target)
- {
- var actor = new CollisionActor(target);
- _actors.Add(actor);
- return actor;
- }
-
- public CollisionGrid CreateGrid(int[] data, int columns, int rows, int cellWidth, int cellHeight)
- {
- if (_grid != null)
- throw new InvalidOperationException("Only one collision grid can be created per world");
-
- _grid = new CollisionGrid(data, columns, rows, cellWidth, cellHeight);
- return _grid;
- }
-
- private CollisionInfo GetCollisionInfo(ICollidable first, ICollidable second, RectangleF intersectingRectangle)
- {
- var info = new CollisionInfo
- {
- Other = second
- };
-
- if (intersectingRectangle.Width < intersectingRectangle.Height)
- {
- var d = first.BoundingBox.Center.X < second.BoundingBox.Center.X
- ? intersectingRectangle.Width
- : -intersectingRectangle.Width;
- info.PenetrationVector = new Vector2(d, 0);
- }
- else
- {
- var d = first.BoundingBox.Center.Y < second.BoundingBox.Center.Y
- ? intersectingRectangle.Height
- : -intersectingRectangle.Height;
- info.PenetrationVector = new Vector2(0, d);
- }
-
- return info;
- }
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/CollisionWorldExtensions.cs b/src/cs/MonoGame.Extended.Collisions/CollisionWorldExtensions.cs
deleted file mode 100644
index ba9f22fd..00000000
--- a/src/cs/MonoGame.Extended.Collisions/CollisionWorldExtensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-//using System.Linq;
-//using MonoGame.Extended.Tiled;
-
-//namespace MonoGame.Extended.Collisions
-//{
-// public static class CollisionWorldExtensions
-// {
-// public static CollisionGrid CreateGrid(this CollisionWorld world, TiledTileLayer tileLayer)
-// {
-// var data = tileLayer.Tiles
-// .Select(t => (int)t.GlobalIdentifier)
-// .ToArray();
-
-// return world.CreateGrid(data, tileLayer.Width, tileLayer.Height, tileLayer.TileWidth, tileLayer.TileHeight);
-// }
-// }
-//}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/IActorTarget.cs b/src/cs/MonoGame.Extended.Collisions/IActorTarget.cs
deleted file mode 100644
index df93ea9e..00000000
--- a/src/cs/MonoGame.Extended.Collisions/IActorTarget.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- public interface ICollidable
- {
- RectangleF BoundingBox { get; }
- }
-
- public interface IActorTarget : IMovable, ICollidable
- {
- Vector2 Velocity { get; set; }
- void OnCollision(CollisionInfo collisionInfo);
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/ICollisionActor.cs b/src/cs/MonoGame.Extended.Collisions/ICollisionActor.cs
new file mode 100644
index 00000000..6a05592f
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/ICollisionActor.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace MonoGame.Extended.Collisions
+{
+ ///
+ /// An actor that can be collided with.
+ ///
+ public interface ICollisionActor
+ {
+ ///
+ /// A name of layer, which will contains this actor.
+ /// If it equals null, an actor will insert into a default layer
+ ///
+ string LayerName { get => null; }
+
+ ///
+ /// A bounds of an actor. It is using for collision calculating
+ ///
+ IShapeF Bounds { get; }
+
+ ///
+ /// It will called, when collision with an another actor fires
+ ///
+ /// Data about collision
+ void OnCollision(CollisionEventArgs collisionInfo);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/ISpaceAlgorithm.cs b/src/cs/MonoGame.Extended.Collisions/ISpaceAlgorithm.cs
new file mode 100644
index 00000000..a95f7379
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/ISpaceAlgorithm.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+
+namespace MonoGame.Extended.Collisions;
+
+///
+/// Interface, which split space for optimization of collisions.
+///
+public interface ISpaceAlgorithm
+{
+ ///
+ /// Inserts the actor into the space.
+ /// The actor will have its OnCollision called when collisions occur.
+ ///
+ /// Actor to insert.
+ void Insert(ICollisionActor actor);
+
+ ///
+ /// Removes the actor into the space.
+ ///
+ /// Actor to remove.
+ bool Remove(ICollisionActor actor);
+
+ ///
+ /// Removes the actor into the space.
+ /// The actor will have its OnCollision called when collisions occur.
+ ///
+ /// Actor to remove.
+ IEnumerable Query(RectangleF boundsBoundingRectangle);
+
+ ///
+ /// for foreach
+ ///
+ ///
+ List.Enumerator GetEnumerator();
+
+ ///
+ /// Restructure the space with new positions.
+ ///
+ void Reset();
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/Layers/Layer.cs b/src/cs/MonoGame.Extended.Collisions/Layers/Layer.cs
new file mode 100644
index 00000000..6e97ac87
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/Layers/Layer.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace MonoGame.Extended.Collisions.Layers;
+
+///
+/// Layer is a group of collision's actors.
+///
+public class Layer
+{
+ ///
+ /// If this property equals true, layer always will reset collision space.
+ ///
+ public bool IsDynamic { get; set; } = true;
+
+
+ ///
+ /// The space, which contain actors.
+ ///
+ public readonly ISpaceAlgorithm Space;
+
+ ///
+ /// Constructor for layer
+ ///
+ /// A space algorithm for actors
+ /// is null
+ public Layer(ISpaceAlgorithm spaceAlgorithm)
+ {
+ Space = spaceAlgorithm ?? throw new ArgumentNullException(nameof(spaceAlgorithm));
+ }
+
+ ///
+ /// Restructure a inner collection, if layer is dynamic, because actors can change own position
+ ///
+ public virtual void Reset()
+ {
+ if (IsDynamic)
+ Space.Reset();
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/Layers/UndefinedLayerException.cs b/src/cs/MonoGame.Extended.Collisions/Layers/UndefinedLayerException.cs
new file mode 100644
index 00000000..a27b5b6b
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/Layers/UndefinedLayerException.cs
@@ -0,0 +1,18 @@
+namespace MonoGame.Extended.Collisions.Layers;
+
+using System;
+
+///
+/// Thrown when the collision system has no layer defined with the specified name
+///
+public class UndefinedLayerException : Exception
+{
+ ///
+ /// Thrown when the collision system has no layer defined with the specified name
+ ///
+ /// The undefined layer name
+ public UndefinedLayerException(string layerName)
+ : base($"Layer with name '{layerName}' is undefined")
+ {
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/MonoGame.Extended.Collisions.csproj b/src/cs/MonoGame.Extended.Collisions/MonoGame.Extended.Collisions.csproj
index 2c28c9c7..3585e6bc 100644
--- a/src/cs/MonoGame.Extended.Collisions/MonoGame.Extended.Collisions.csproj
+++ b/src/cs/MonoGame.Extended.Collisions/MonoGame.Extended.Collisions.csproj
@@ -7,9 +7,6 @@
craftworkgames
Collisions to make MonoGame more awesome.
monogame collisions
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
true
@@ -26,4 +23,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs b/src/cs/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs
deleted file mode 100644
index 1caf96ac..00000000
--- a/src/cs/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Collisions
-{
- ///
- /// Handles basic collision between actors.
- /// When two actors collide, their OnCollision method is called.
- ///
- public class CollisionComponent : SimpleGameComponent
- {
- private readonly Dictionary _targetDataDictionary = new();
-
- private readonly Quadtree _collisionTree;
-
- ///
- /// Creates a collision tree covering the specified area.
- ///
- /// Boundary of the collision tree.
- public CollisionComponent(RectangleF boundary)
- {
- _collisionTree = new Quadtree(boundary);
- }
-
- ///
- /// Update the collision tree and process collisions.
- ///
- ///
- /// Boundary shapes are updated if they were changed since the last
- /// update.
- ///
- ///
- public override void Update(GameTime gameTime)
- {
- // Detect collisions
- foreach (var value in _targetDataDictionary.Values)
- {
- value.RemoveFromAllParents();
-
- var target = value.Target;
- var collisions =_collisionTree.Query(target.Bounds);
-
- // Generate list of collision Infos
- foreach (var other in collisions)
- {
- var collisionInfo = new CollisionEventArgs
- {
- Other = other.Target,
- PenetrationVector = CalculatePenetrationVector(value.Bounds, other.Bounds)
- };
-
- target.OnCollision(collisionInfo);
- }
- _collisionTree.Insert(value);
- }
- _collisionTree.Shake();
- }
-
- ///
- /// Inserts the target into the collision tree.
- /// The target will have its OnCollision called when collisions occur.
- ///
- /// Target to insert.
- public void Insert(ICollisionActor target)
- {
- if (!_targetDataDictionary.ContainsKey(target))
- {
- var data = new QuadtreeData(target);
- _targetDataDictionary.Add(target, data);
- _collisionTree.Insert(data);
- }
- }
-
- ///
- /// Removes the target from the collision tree.
- ///
- /// Target to remove.
- public void Remove(ICollisionActor target)
- {
- if (_targetDataDictionary.ContainsKey(target))
- {
- var data = _targetDataDictionary[target];
- data.RemoveFromAllParents();
- _targetDataDictionary.Remove(target);
- _collisionTree.Shake();
- }
- }
-
- ///
- /// Gets if the target is inserted in the collision tree.
- ///
- /// Actor to check if contained
- /// True if the target is contained in the collision tree.
- public bool Contains(ICollisionActor target)
- {
- return _targetDataDictionary.ContainsKey(target);
- }
-
- #region Penetration Vectors
-
- ///
- /// Calculate a's penetration into b
- ///
- /// The penetrating shape.
- /// The shape being penetrated.
- /// The distance vector from the edge of b to a's Position
- private static Vector2 CalculatePenetrationVector(IShapeF a, IShapeF b)
- {
- switch (a)
- {
- case RectangleF rectA when b is RectangleF rectB:
- return PenetrationVector(rectA, rectB);
- case CircleF circA when b is CircleF circB:
- return PenetrationVector(circA, circB);
- case CircleF circA when b is RectangleF rectB:
- return PenetrationVector(circA, rectB);
- case RectangleF rectA when b is CircleF circB:
- return PenetrationVector(rectA, circB);
- }
-
- throw new NotSupportedException("Shapes must be either a CircleF or RectangleF");
- }
-
- private static Vector2 PenetrationVector(RectangleF rect1, RectangleF rect2)
- {
- var intersectingRectangle = RectangleF.Intersection(rect1, rect2);
- Debug.Assert(!intersectingRectangle.IsEmpty,
- "Violation of: !intersect.IsEmpty; Rectangles must intersect to calculate a penetration vector.");
-
- Vector2 penetration;
- if (intersectingRectangle.Width < intersectingRectangle.Height)
- {
- var d = rect1.Center.X < rect2.Center.X
- ? intersectingRectangle.Width
- : -intersectingRectangle.Width;
- penetration = new Vector2(d, 0);
- }
- else
- {
- var d = rect1.Center.Y < rect2.Center.Y
- ? intersectingRectangle.Height
- : -intersectingRectangle.Height;
- penetration = new Vector2(0, d);
- }
-
- return penetration;
- }
-
- private static Vector2 PenetrationVector(CircleF circ1, CircleF circ2)
- {
- if (!circ1.Intersects(circ2))
- {
- return Vector2.Zero;
- }
-
- var displacement = Point2.Displacement(circ1.Center, circ2.Center);
-
- Vector2 desiredDisplacement;
- if (displacement != Vector2.Zero)
- {
- desiredDisplacement = displacement.NormalizedCopy() * (circ1.Radius + circ2.Radius);
- }
- else
- {
- desiredDisplacement = -Vector2.UnitY * (circ1.Radius + circ2.Radius);
- }
-
-
- var penetration = displacement - desiredDisplacement;
- return penetration;
- }
-
- private static Vector2 PenetrationVector(CircleF circ, RectangleF rect)
- {
- var collisionPoint = rect.ClosestPointTo(circ.Center);
- var cToCollPoint = collisionPoint - circ.Center;
-
- if (rect.Contains(circ.Center) || cToCollPoint.Equals(Vector2.Zero))
- {
- var displacement = Point2.Displacement(circ.Center, rect.Center);
-
- Vector2 desiredDisplacement;
- if (displacement != Vector2.Zero)
- {
- // Calculate penetration as only in X or Y direction.
- // Whichever is lower.
- var dispx = new Vector2(displacement.X, 0);
- var dispy = new Vector2(0, displacement.Y);
- dispx.Normalize();
- dispy.Normalize();
-
- dispx *= (circ.Radius + rect.Width / 2);
- dispy *= (circ.Radius + rect.Height / 2);
-
- if (dispx.LengthSquared() < dispy.LengthSquared())
- {
- desiredDisplacement = dispx;
- displacement.Y = 0;
- }
- else
- {
- desiredDisplacement = dispy;
- displacement.X = 0;
- }
- }
- else
- {
- desiredDisplacement = -Vector2.UnitY * (circ.Radius + rect.Height / 2);
- }
-
- var penetration = displacement - desiredDisplacement;
- return penetration;
- }
- else
- {
- var penetration = circ.Radius * cToCollPoint.NormalizedCopy() - cToCollPoint;
- return penetration;
- }
- }
-
- private static Vector2 PenetrationVector(RectangleF rect, CircleF circ)
- {
- return -PenetrationVector(circ, rect);
- }
-
- #endregion
- }
-}
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/ICollisionActor.cs b/src/cs/MonoGame.Extended.Collisions/QuadTree/ICollisionActor.cs
deleted file mode 100644
index c239c248..00000000
--- a/src/cs/MonoGame.Extended.Collisions/QuadTree/ICollisionActor.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace MonoGame.Extended.Collisions
-{
- ///
- /// An actor that can be collided with.
- ///
- public interface ICollisionActor
- {
- IShapeF Bounds { get; }
-
- void OnCollision(CollisionEventArgs collisionInfo);
- }
-}
\ No newline at end of file
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTree.cs b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTree.cs
index 1bf7bdab..a46699a7 100644
--- a/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTree.cs
+++ b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTree.cs
@@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
-namespace MonoGame.Extended.Collisions
+namespace MonoGame.Extended.Collisions.QuadTree
{
///
/// Class for doing collision handling with a quad tree.
///
- public class Quadtree
+ public class QuadTree
{
///
/// The default maximum depth.
@@ -21,7 +21,7 @@ namespace MonoGame.Extended.Collisions
///
/// Contains the children of this node.
///
- protected List Children = new List();
+ protected List Children = new List();
///
/// Contains the data for this node in the quadtree.
@@ -32,7 +32,7 @@ namespace MonoGame.Extended.Collisions
/// Creates a quad tree with the given bounds.
///
/// The bounds of the new quad tree.
- public Quadtree(RectangleF bounds)
+ public QuadTree(RectangleF bounds)
{
CurrentDepth = 0;
NodeBounds = bounds;
@@ -71,7 +71,7 @@ namespace MonoGame.Extended.Collisions
var objectCount = 0;
// Do BFS on nodes to count children.
- var process = new Queue();
+ var process = new Queue();
process.Enqueue(this);
while (process.Count > 0)
{
@@ -148,7 +148,7 @@ namespace MonoGame.Extended.Collisions
}
else
{
- throw new InvalidOperationException($"Cannot remove from a non leaf {nameof(Quadtree)}");
+ throw new InvalidOperationException($"Cannot remove from a non leaf {nameof(QuadTree)}");
}
}
@@ -171,7 +171,7 @@ namespace MonoGame.Extended.Collisions
}
else if (numObjects < MaxObjectsPerNode)
{
- var process = new Queue();
+ var process = new Queue();
process.Enqueue(this);
while (process.Count > 0)
{
@@ -232,14 +232,14 @@ namespace MonoGame.Extended.Collisions
for (var i = 0; i < childAreas.Length; ++i)
{
- var node = new Quadtree(childAreas[i]);
+ var node = new QuadTree(childAreas[i]);
Children.Add(node);
Children[i].CurrentDepth = CurrentDepth + 1;
}
foreach (QuadtreeData contentQuadtree in Contents)
{
- foreach (Quadtree childQuadtree in Children)
+ foreach (QuadTree childQuadtree in Children)
{
childQuadtree.Insert(contentQuadtree);
}
@@ -247,6 +247,16 @@ namespace MonoGame.Extended.Collisions
Clear();
}
+ ///
+ /// Clear current node and all children
+ ///
+ public void ClearAll()
+ {
+ foreach (QuadTree childQuadtree in Children)
+ childQuadtree.ClearAll();
+ Clear();
+ }
+
private void Clear()
{
foreach (QuadtreeData quadtreeData in Contents)
@@ -261,10 +271,10 @@ namespace MonoGame.Extended.Collisions
///
/// The area to query for overlapping targets
/// A unique list of targets intersected by area.
- public List Query(IShapeF area)
+ public List Query(ref RectangleF area)
{
var recursiveResult = new List();
- QueryWithoutReset(area, recursiveResult);
+ QueryWithoutReset(ref area, recursiveResult);
foreach (var quadtreeData in recursiveResult)
{
quadtreeData.MarkClean();
@@ -272,7 +282,7 @@ namespace MonoGame.Extended.Collisions
return recursiveResult;
}
- private void QueryWithoutReset(IShapeF area, List recursiveResult)
+ private void QueryWithoutReset(ref RectangleF area, List recursiveResult)
{
if (!NodeBounds.Intersects(area))
return;
@@ -292,7 +302,7 @@ namespace MonoGame.Extended.Collisions
{
for (int i = 0, size = Children.Count; i < size; i++)
{
- Children[i].QueryWithoutReset(area, recursiveResult);
+ Children[i].QueryWithoutReset(ref area, recursiveResult);
}
}
}
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeData.cs b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeData.cs
index cb50d3b5..db6da3eb 100644
--- a/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeData.cs
+++ b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeData.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Linq;
-namespace MonoGame.Extended.Collisions;
+namespace MonoGame.Extended.Collisions.QuadTree;
///
/// Data structure for the quad tree.
@@ -10,7 +10,7 @@ namespace MonoGame.Extended.Collisions;
public class QuadtreeData
{
private readonly ICollisionActor _target;
- private readonly HashSet _parents = new();
+ private readonly HashSet _parents = new();
///
/// Initialize a new instance of QuadTreeData.
@@ -19,13 +19,14 @@ public class QuadtreeData
public QuadtreeData(ICollisionActor target)
{
_target = target;
+ Bounds = _target.Bounds.BoundingRectangle;
}
///
/// Remove a parent node.
///
///
- public void RemoveParent(Quadtree parent)
+ public void RemoveParent(QuadTree parent)
{
_parents.Remove(parent);
}
@@ -34,9 +35,10 @@ public class QuadtreeData
/// Add a parent node.
///
///
- public void AddParent(Quadtree parent)
+ public void AddParent(QuadTree parent)
{
_parents.Add(parent);
+ Bounds = _target.Bounds.BoundingRectangle;
}
///
@@ -55,7 +57,7 @@ public class QuadtreeData
///
/// Gets the bounding box for collision detection.
///
- public IShapeF Bounds => _target.Bounds;
+ public RectangleF Bounds { get; set; }
///
/// Gets the collision actor target.
diff --git a/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeSpace.cs b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeSpace.cs
new file mode 100644
index 00000000..3e9625f0
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/QuadTree/QuadTreeSpace.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MonoGame.Extended.Collisions.QuadTree;
+
+public class QuadTreeSpace: ISpaceAlgorithm
+{
+ private readonly QuadTree _collisionTree;
+ private readonly List _actors = new();
+ private readonly Dictionary _targetDataDictionary = new();
+
+ public QuadTreeSpace(RectangleF boundary)
+ {
+ _collisionTree = new QuadTree(boundary);
+ }
+
+ ///
+ /// Inserts the target into the collision tree.
+ /// The target will have its OnCollision called when collisions occur.
+ ///
+ /// Target to insert.
+ public void Insert(ICollisionActor target)
+ {
+ if (!_targetDataDictionary.ContainsKey(target))
+ {
+ var data = new QuadtreeData(target);
+ _targetDataDictionary.Add(target, data);
+ _collisionTree.Insert(data);
+ _actors.Add(target);
+ }
+ }
+
+ ///
+ /// Removes the target from the collision tree.
+ ///
+ /// Target to remove.
+ public bool Remove(ICollisionActor target)
+ {
+ if (_targetDataDictionary.ContainsKey(target))
+ {
+ var data = _targetDataDictionary[target];
+ data.RemoveFromAllParents();
+ _targetDataDictionary.Remove(target);
+ _collisionTree.Shake();
+ _actors.Remove(target);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Restructure a inner collection, if layer is dynamic, because actors can change own position
+ ///
+ public void Reset()
+ {
+ _collisionTree.ClearAll();
+ foreach (var value in _targetDataDictionary.Values)
+ {
+ _collisionTree.Insert(value);
+ }
+ _collisionTree.Shake();
+ }
+
+ ///
+ /// foreach support
+ ///
+ ///
+ public List.Enumerator GetEnumerator() => _actors.GetEnumerator();
+
+ ///
+ public IEnumerable Query(RectangleF boundsBoundingRectangle)
+ {
+ return _collisionTree.Query(ref boundsBoundingRectangle).Select(x => x.Target);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Collisions/SpatialHash.cs b/src/cs/MonoGame.Extended.Collisions/SpatialHash.cs
new file mode 100644
index 00000000..2b0920e7
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Collisions/SpatialHash.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace MonoGame.Extended.Collisions;
+
+public class SpatialHash: ISpaceAlgorithm
+{
+ private readonly Dictionary> _dictionary = new();
+ private readonly List _actors = new();
+
+ private readonly Size2 _size;
+
+ public SpatialHash(Size2 size)
+ {
+ _size = size;
+ }
+
+ public void Insert(ICollisionActor actor)
+ {
+ InsertToHash(actor);
+ _actors.Add(actor);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void InsertToHash(ICollisionActor actor)
+ {
+ var rect = actor.Bounds.BoundingRectangle;
+ for (var x = rect.Left; x < rect.Right; x+=_size.Width)
+ for (var y = rect.Top; y < rect.Bottom; y+=_size.Height)
+ AddToCell(x, y, actor);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void AddToCell(float x, float y, ICollisionActor actor)
+ {
+ var index = GetIndex(x, y);
+ if (_dictionary.TryGetValue(index, out var actors))
+ actors.Add(actor);
+ else
+ _dictionary[index] = new() { actor };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private int GetIndex(float x, float y)
+ {
+ return (int)(x / _size.Width) << 16 + (int)(y / _size.Height);
+ }
+
+ public bool Remove(ICollisionActor actor)
+ {
+ foreach (var actors in _dictionary.Values)
+ actors.Remove(actor);
+ return _actors.Remove(actor);
+ }
+
+ public IEnumerable Query(RectangleF boundsBoundingRectangle)
+ {
+ var results = new HashSet();
+ var bounds = boundsBoundingRectangle.BoundingRectangle;
+
+ for (var x = boundsBoundingRectangle.Left; x < boundsBoundingRectangle.Right; x+=_size.Width)
+ for (var y = boundsBoundingRectangle.Top; y < boundsBoundingRectangle.Bottom; y+=_size.Height)
+ if (_dictionary.TryGetValue(GetIndex(x, y), out var actors))
+ foreach (var actor in actors)
+ if (bounds.Intersects(actor.Bounds))
+ results.Add(actor);
+ return results;
+ }
+
+ public List.Enumerator GetEnumerator() => _actors.GetEnumerator();
+
+ public void Reset()
+ {
+ _dictionary.Clear();
+ foreach (var actor in _actors)
+ InsertToHash(actor);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/ContentImporterContextExtensions.cs b/src/cs/MonoGame.Extended.Content.Pipeline/ContentImporterContextExtensions.cs
new file mode 100644
index 00000000..7f638155
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/ContentImporterContextExtensions.cs
@@ -0,0 +1,15 @@
+using System.IO;
+using Microsoft.Xna.Framework.Content.Pipeline;
+
+namespace MonoGame.Extended.Content.Pipeline;
+
+public static class ContentImporterContextExtensions
+{
+ public static string AddDependencyWithLogging(this ContentImporterContext context, string filePath, string source)
+ {
+ source = Path.Combine(Path.GetDirectoryName(filePath), source);
+ ContentLogger.Log($"Adding dependency '{source}'");
+ context.AddDependency(source);
+ return source;
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/ContentItem.cs b/src/cs/MonoGame.Extended.Content.Pipeline/ContentItem.cs
index 487f31ef..e69d48e1 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/ContentItem.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/ContentItem.cs
@@ -28,10 +28,10 @@ namespace MonoGame.Extended.Content.Pipeline
public ExternalReference GetExternalReference(string source)
{
- if (_externalReferences.TryGetValue(source, out var contentItem))
+ if (source is not null && _externalReferences.TryGetValue(source, out var contentItem))
return contentItem as ExternalReference;
return null;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/MonoGame.Extended.Content.Pipeline.csproj b/src/cs/MonoGame.Extended.Content.Pipeline/MonoGame.Extended.Content.Pipeline.csproj
index a2d6ae9c..264c34eb 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/MonoGame.Extended.Content.Pipeline.csproj
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/MonoGame.Extended.Content.Pipeline.csproj
@@ -12,9 +12,6 @@
craftworkgames
Content Pipeline importers and processors to make MonoGame more awesome.
monogame content importer processor reader tiled texturepacker bmfont animations
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
@@ -45,4 +42,4 @@
Always
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/ContentWriterExtensions.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/ContentWriterExtensions.cs
index 2b3ae142..28ded173 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/ContentWriterExtensions.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/ContentWriterExtensions.cs
@@ -9,11 +9,17 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
// ReSharper disable once SuggestBaseTypeForParameter
public static void WriteTiledMapProperties(this ContentWriter writer, IReadOnlyCollection value)
{
+ if (value == null)
+ {
+ writer.Write(0);
+ return;
+ }
writer.Write(value.Count);
foreach (var property in value)
{
writer.Write(property.Name);
- writer.Write(property.Value);
+ writer.Write(property.Value ?? string.Empty);
+ WriteTiledMapProperties(writer, property.Properties);
}
}
}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledContentItem.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledContentItem.cs
new file mode 100644
index 00000000..b3762524
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledContentItem.cs
@@ -0,0 +1,22 @@
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+using MonoGame.Extended.Tiled.Serialization;
+
+namespace MonoGame.Extended.Content.Pipeline.Tiled;
+
+public class TiledContentItem: ContentItem
+{
+ public TiledContentItem(T data) : base(data)
+ {
+ }
+
+ public void BuildExternalReference(ContentProcessorContext context, TiledMapImageContent image)
+ {
+ var parameters = new OpaqueDataDictionary
+ {
+ { "ColorKeyColor", image.TransparentColor },
+ { "ColorKeyEnabled", true }
+ };
+ BuildExternalReference(context, image.Source, parameters);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapContentItem.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapContentItem.cs
index 7f381e42..b0b7b5dd 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapContentItem.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapContentItem.cs
@@ -2,11 +2,11 @@
namespace MonoGame.Extended.Content.Pipeline.Tiled
{
- public class TiledMapContentItem : ContentItem
+ public class TiledMapContentItem : TiledContentItem
{
- public TiledMapContentItem(TiledMapContent data)
+ public TiledMapContentItem(TiledMapContent data)
: base(data)
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapProcessor.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapProcessor.cs
index da130a4c..9668a723 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapProcessor.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapProcessor.cs
@@ -75,7 +75,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
}
}
}
-
+
[ContentProcessor(DisplayName = "Tiled Map Processor - MonoGame.Extended")]
public class TiledMapProcessor : ContentProcessor
@@ -95,14 +95,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
if (string.IsNullOrWhiteSpace(tileset.Source))
{
// Load the Texture2DContent for the tileset as it will be saved into the map content file.
- //var externalReference = new ExternalReference(tileset.Image.Source);
- var parameters = new OpaqueDataDictionary
- {
- { "ColorKeyColor", tileset.Image.TransparentColor },
- { "ColorKeyEnabled", true }
- };
- //tileset.Image.ContentRef = context.BuildAsset(externalReference, "", parameters, "", "");
- contentItem.BuildExternalReference(context, tileset.Image.Source, parameters);
+ contentItem.BuildExternalReference(context, tileset.Image);
}
else
{
@@ -114,7 +107,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
}
ProcessLayers(contentItem, map, context, map.Layers);
-
+
return contentItem;
}
catch (Exception ex)
@@ -132,14 +125,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
{
case TiledMapImageLayerContent imageLayer:
ContentLogger.Log($"Processing image layer '{imageLayer.Name}'");
- //var externalReference = new ExternalReference(imageLayer.Image.Source);
- var parameters = new OpaqueDataDictionary
- {
- { "ColorKeyColor", imageLayer.Image.TransparentColor },
- { "ColorKeyEnabled", true }
- };
- //imageLayer.Image.ContentRef = context.BuildAsset(externalReference, "", parameters, "", "");
- contentItem.BuildExternalReference(context, imageLayer.Image.Source, parameters);
+ contentItem.BuildExternalReference(context, imageLayer.Image);
ContentLogger.Log($"Processed image layer '{imageLayer.Name}'");
break;
@@ -335,4 +321,4 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
};
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetContentItem.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetContentItem.cs
index 4280ccdf..d4b2221d 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetContentItem.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetContentItem.cs
@@ -1,12 +1,14 @@
-using MonoGame.Extended.Tiled.Serialization;
+using Microsoft.Xna.Framework.Content.Pipeline;
+using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
+using MonoGame.Extended.Tiled.Serialization;
namespace MonoGame.Extended.Content.Pipeline.Tiled
{
- public class TiledMapTilesetContentItem : ContentItem
+ public class TiledMapTilesetContentItem : TiledContentItem
{
- public TiledMapTilesetContentItem(TiledMapTilesetContent data)
+ public TiledMapTilesetContentItem(TiledMapTilesetContent data)
: base(data)
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetImporter.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetImporter.cs
index a1b53e0b..c848bde5 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetImporter.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetImporter.cs
@@ -39,23 +39,18 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
var tilesetSerializer = new XmlSerializer(typeof(TiledMapTilesetContent));
var tileset = (TiledMapTilesetContent)tilesetSerializer.Deserialize(reader);
- tileset.Image.Source = Path.Combine(Path.GetDirectoryName(filePath), tileset.Image.Source);
- ContentLogger.Log($"Adding dependency '{tileset.Image.Source}'");
- context.AddDependency(tileset.Image.Source);
+ if (tileset.Image is not null)
+ tileset.Image.Source = context.AddDependencyWithLogging(filePath, tileset.Image.Source);
foreach (var tile in tileset.Tiles)
{
foreach (var obj in tile.Objects)
{
if (!string.IsNullOrWhiteSpace(obj.TemplateSource))
- {
- obj.TemplateSource = Path.Combine(Path.GetDirectoryName(filePath), obj.TemplateSource);
- ContentLogger.Log($"Adding dependency '{obj.TemplateSource}'");
-
- // We depend on the template.
- context.AddDependency(obj.TemplateSource);
- }
+ obj.TemplateSource = context.AddDependencyWithLogging(filePath, obj.TemplateSource);
}
+ if (tile.Image is not null)
+ tile.Image.Source = context.AddDependencyWithLogging(filePath, tile.Image.Source);
}
return tileset;
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetProcessor.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetProcessor.cs
index cefd62ad..5682602f 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetProcessor.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetProcessor.cs
@@ -15,16 +15,10 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
ContentLogger.Logger = context.Logger;
ContentLogger.Log($"Processing tileset '{tileset.Name}'");
-
+
// Build the Texture2D asset and load it as it will be saved as part of this tileset file.
- //var externalReference = new ExternalReference(tileset.Image.Source);
- var parameters = new OpaqueDataDictionary
- {
- { "ColorKeyColor", tileset.Image.TransparentColor },
- { "ColorKeyEnabled", true }
- };
- //tileset.Image.ContentRef = context.BuildAsset(externalReference, "", parameters, "", "");
- contentItem.BuildExternalReference(context, tileset.Image.Source, parameters);
+ if (tileset.Image is not null)
+ contentItem.BuildExternalReference(context, tileset.Image);
foreach (var tile in tileset.Tiles)
{
@@ -32,6 +26,8 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
{
TiledMapContentHelper.Process(obj, context);
}
+ if (tile.Image is not null)
+ contentItem.BuildExternalReference(context, tile.Image);
}
ContentLogger.Log($"Processed tileset '{tileset.Name}'");
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetWriter.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetWriter.cs
index 19c52740..48690ad3 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetWriter.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapTilesetWriter.cs
@@ -30,9 +30,9 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
public static void WriteTileset(ContentWriter writer, TiledMapTilesetContent tileset, IExternalReferenceRepository externalReferenceRepository)
{
- var externalReference = externalReferenceRepository.GetExternalReference(tileset.Image.Source);
-
+ var externalReference = externalReferenceRepository.GetExternalReference(tileset.Image?.Source);
writer.WriteExternalReference(externalReference);
+ writer.Write(tileset.Class ?? tileset.Type ?? string.Empty);
writer.Write(tileset.TileWidth);
writer.Write(tileset.TileHeight);
writer.Write(tileset.TileCount);
@@ -42,13 +42,17 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
writer.Write(tileset.Tiles.Count);
foreach (var tilesetTile in tileset.Tiles)
- WriteTilesetTile(writer, tilesetTile);
+ WriteTilesetTile(writer, tilesetTile, externalReferenceRepository);
writer.WriteTiledMapProperties(tileset.Properties);
}
- private static void WriteTilesetTile(ContentWriter writer, TiledMapTilesetTileContent tilesetTile)
+ private static void WriteTilesetTile(ContentWriter writer, TiledMapTilesetTileContent tilesetTile,
+ IExternalReferenceRepository externalReferenceRepository)
{
+ var externalReference = externalReferenceRepository.GetExternalReference(tilesetTile.Image?.Source);
+ writer.WriteExternalReference(externalReference);
+
writer.Write(tilesetTile.LocalIdentifier);
writer.Write(tilesetTile.Type);
writer.Write(tilesetTile.Frames.Count);
diff --git a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapWriter.cs b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapWriter.cs
index 8ba1b78e..126debbb 100644
--- a/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapWriter.cs
+++ b/src/cs/MonoGame.Extended.Content.Pipeline/Tiled/TiledMapWriter.cs
@@ -36,6 +36,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
private static void WriteMetaData(ContentWriter writer, TiledMapContent map)
{
+ writer.Write(map.Class ?? map.Type ?? string.Empty);
writer.Write(map.Width);
writer.Write(map.Height);
writer.Write(map.TileWidth);
@@ -80,9 +81,10 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
private void WriteLayer(ContentWriter writer, TiledMapLayerContent layer)
{
- writer.Write((byte)layer.Type);
+ writer.Write((byte)layer.LayerType);
writer.Write(layer.Name ?? string.Empty);
+ writer.Write(layer.Class ?? layer.Type ?? string.Empty);
writer.Write(layer.Visible);
writer.Write(layer.Opacity);
writer.Write(layer.OffsetX);
@@ -92,7 +94,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
writer.WriteTiledMapProperties(layer.Properties);
- switch (layer.Type)
+ switch (layer.LayerType)
{
case TiledMapLayerType.ImageLayer:
WriteImageLayer(writer, (TiledMapImageLayerContent)layer);
@@ -107,7 +109,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
WriteLayers(writer, ((TiledMapGroupLayerContent)layer).Layers);
break;
default:
- throw new ArgumentOutOfRangeException(nameof(layer.Type));
+ throw new ArgumentOutOfRangeException(nameof(layer.LayerType));
}
}
diff --git a/src/cs/MonoGame.Extended.Entities/MonoGame.Extended.Entities.csproj b/src/cs/MonoGame.Extended.Entities/MonoGame.Extended.Entities.csproj
index 4d5ce7c1..8430d91f 100644
--- a/src/cs/MonoGame.Extended.Entities/MonoGame.Extended.Entities.csproj
+++ b/src/cs/MonoGame.Extended.Entities/MonoGame.Extended.Entities.csproj
@@ -7,9 +7,6 @@
craftworkgames
An Entity Component System to make MonoGame more awesome.
monogame ecs entity component system
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
@@ -25,4 +22,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj b/src/cs/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj
index c6fb9bbd..8553742b 100644
--- a/src/cs/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj
+++ b/src/cs/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj
@@ -7,9 +7,6 @@
craftworkgames
Graphics makes MonoGame more awesome.
monogame graphics batcher effects
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
true
@@ -46,4 +43,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj b/src/cs/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
index e8364864..d0baf732 100644
--- a/src/cs/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
+++ b/src/cs/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
@@ -7,9 +7,6 @@
craftworkgames
A GUI system for MonoGame written from the ground up to make MonoGame more awesome.
monogame gui
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
true
@@ -27,4 +24,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Input/KeyboardStateExtended.cs b/src/cs/MonoGame.Extended.Input/KeyboardStateExtended.cs
index 9c136851..ee186771 100644
--- a/src/cs/MonoGame.Extended.Input/KeyboardStateExtended.cs
+++ b/src/cs/MonoGame.Extended.Input/KeyboardStateExtended.cs
@@ -23,7 +23,8 @@ namespace MonoGame.Extended.Input
public bool IsKeyDown(Keys key) => _currentKeyboardState.IsKeyDown(key);
public bool IsKeyUp(Keys key) => _currentKeyboardState.IsKeyUp(key);
public Keys[] GetPressedKeys() => _currentKeyboardState.GetPressedKeys();
-
+ public void GetPressedKeys(Keys[] keys) => _currentKeyboardState.GetPressedKeys(keys);
+
///
/// Gets whether the given key was down on the previous state, but is now up.
///
@@ -54,6 +55,6 @@ namespace MonoGame.Extended.Input
/// true if the key was pressed this state-change, otherwise false.
public readonly bool IsKeyPressed(Keys key) => _previousKeyboardState.IsKeyUp(key) && _currentKeyboardState.IsKeyDown(key);
- public bool WasAnyKeyJustDown() => _previousKeyboardState.GetPressedKeys().Any();
+ public bool WasAnyKeyJustDown() => _previousKeyboardState.GetPressedKeyCount() > 0;
}
}
diff --git a/src/cs/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj b/src/cs/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj
index 3c69c1af..67df5b44 100644
--- a/src/cs/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj
+++ b/src/cs/MonoGame.Extended.Input/MonoGame.Extended.Input.csproj
@@ -7,9 +7,6 @@
craftworkgames
An event based input system to MonoGame more awesome.
monogame input event based listeners
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
true
@@ -26,4 +23,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Particles/Modifiers/Interpolators/VelocityInterpolator b/src/cs/MonoGame.Extended.Particles/Modifiers/Interpolators/VelocityInterpolator
new file mode 100644
index 00000000..bad04b19
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Particles/Modifiers/Interpolators/VelocityInterpolator
@@ -0,0 +1,12 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Particles.Modifiers.Interpolators
+{
+ public class VelocityInterpolator : Interpolator
+ {
+ public override unsafe void Update(float amount, Particle* particle)
+ {
+ particle->Velocity = (EndValue - StartValue) * amount + StartValue;
+ }
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj b/src/cs/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
index a39b6ab2..48de187c 100644
--- a/src/cs/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
+++ b/src/cs/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
@@ -7,9 +7,6 @@
craftworkgames
A high performance particle system to make MonoGame more awesome.
monogame particle system
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
true
@@ -30,4 +27,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Tiled/ContentReaderExtensions.cs b/src/cs/MonoGame.Extended.Tiled/ContentReaderExtensions.cs
new file mode 100644
index 00000000..86f1b6be
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Tiled/ContentReaderExtensions.cs
@@ -0,0 +1,20 @@
+using Microsoft.Xna.Framework.Content;
+
+namespace MonoGame.Extended.Tiled
+{
+ public static class ContentReaderExtensions
+ {
+ public static void ReadTiledMapProperties(this ContentReader reader, TiledMapProperties properties)
+ {
+ var count = reader.ReadInt32();
+
+ for (var i = 0; i < count; i++)
+ {
+ var key = reader.ReadString();
+ var value = new TiledMapPropertyValue(reader.ReadString());
+ ReadTiledMapProperties(reader, value.Properties);
+ properties[key] = value;
+ }
+ }
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj b/src/cs/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj
index ece0263b..fe87c77e 100644
--- a/src/cs/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj
+++ b/src/cs/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj
@@ -7,9 +7,6 @@
craftworkgames
Support for Tiled maps to make MonoGame more awesome. See http://www.mapeditor.org
monogame tiled maps orthographic isometric
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
@@ -30,4 +27,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs b/src/cs/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs
index c1fb73f4..b7319456 100644
--- a/src/cs/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs
+++ b/src/cs/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs
@@ -52,12 +52,16 @@ namespace MonoGame.Extended.Tiled.Renderers
var tileGid = tile.GlobalIdentifier;
var localTileIdentifier = tileGid - firstGlobalIdentifier;
var position = GetTilePosition(map, tile);
- var tilesetColumns = tileset.Columns == 0 ? 1 : tileset.Columns; // fixes a problem (what problem exactly?)
- var sourceRectangle = TiledMapHelper.GetTileSourceRectangle(localTileIdentifier, tileset.TileWidth, tileset.TileHeight, tilesetColumns, tileset.Margin, tileset.Spacing);
+ var sourceRectangle = tileset.GetTileRegion(localTileIdentifier);
var flipFlags = tile.Flags;
// animated tiles
var tilesetTile = tileset.Tiles.FirstOrDefault(x => x.LocalTileIdentifier == localTileIdentifier);
+ if (tilesetTile?.Texture is not null)
+ {
+ position.Y += map.TileHeight - sourceRectangle.Height;
+ texture = tilesetTile.Texture;
+ }
if (tilesetTile is TiledMapTilesetAnimatedTile animatedTilesetTile)
{
@@ -117,4 +121,4 @@ namespace MonoGame.Extended.Tiled.Renderers
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs
index 02f8adb7..30ecbfdb 100644
--- a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs
+++ b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs
@@ -19,6 +19,13 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlIgnore]
public string FilePath { get; set; }
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
@@ -65,4 +72,4 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlArrayItem("property")]
public List Properties { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs
index f59f102c..01d8a9d5 100644
--- a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs
+++ b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs
@@ -8,9 +8,9 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlInclude(typeof(TiledMapObjectLayerContent))]
public abstract class TiledMapLayerContent
{
- protected TiledMapLayerContent(TiledMapLayerType type)
+ protected TiledMapLayerContent(TiledMapLayerType layerType)
{
- Type = type;
+ LayerType = layerType;
Opacity = 1.0f;
ParallaxX = 1.0f;
ParallaxY = 1.0f;
@@ -21,6 +21,13 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
[XmlAttribute(AttributeName = "opacity")]
public float Opacity { get; set; }
@@ -44,7 +51,7 @@ namespace MonoGame.Extended.Tiled.Serialization
public List Properties { get; set; }
[XmlIgnore]
- public TiledMapLayerType Type { get; }
+ public TiledMapLayerType LayerType { get; }
public override string ToString()
{
diff --git a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs
index 647a7f3e..10cdfc15 100644
--- a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs
+++ b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Xml.Serialization;
namespace MonoGame.Extended.Tiled.Serialization
@@ -13,6 +14,10 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlText]
public string ValueBody { get; set; }
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List Properties { get; set; }
+
public string Value => ValueAttribute ?? ValueBody;
public override string ToString()
@@ -20,4 +25,4 @@ namespace MonoGame.Extended.Tiled.Serialization
return $"{Name}: {Value}";
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs
index 00d1b5ef..fa04c31b 100644
--- a/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs
+++ b/src/cs/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs
@@ -22,6 +22,13 @@ namespace MonoGame.Extended.Tiled.Serialization
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
[XmlAttribute(AttributeName = "tilewidth")]
public int TileWidth { get; set; }
@@ -66,4 +73,4 @@ namespace MonoGame.Extended.Tiled.Serialization
return $"{Name}: {Image}";
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMap.cs b/src/cs/MonoGame.Extended.Tiled/TiledMap.cs
index d9b1d14b..b3486bd7 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMap.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMap.cs
@@ -17,6 +17,7 @@ namespace MonoGame.Extended.Tiled
private readonly List> _firstGlobalIdentifiers = new List>();
public string Name { get; }
+ public string Type { get; }
public int Width { get; }
public int Height { get; }
public int TileWidth { get; }
@@ -44,10 +45,11 @@ namespace MonoGame.Extended.Tiled
Properties = new TiledMapProperties();
}
- public TiledMap(string name, int width, int height, int tileWidth, int tileHeight, TiledMapTileDrawOrder renderOrder, TiledMapOrientation orientation, Color? backgroundColor = null)
+ public TiledMap(string name, string type, int width, int height, int tileWidth, int tileHeight, TiledMapTileDrawOrder renderOrder, TiledMapOrientation orientation, Color? backgroundColor = null)
: this()
{
Name = name;
+ Type = type;
Width = width;
Height = height;
TileWidth = tileWidth;
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs
index 01d054b2..48d43d1d 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs
@@ -8,8 +8,8 @@ namespace MonoGame.Extended.Tiled
public class TiledMapGroupLayer : TiledMapLayer
{
public List Layers { get; }
- public TiledMapGroupLayer(string name, List layers, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1, bool isVisible = true)
- : base(name, offset, parallaxFactor, opacity, isVisible)
+ public TiledMapGroupLayer(string name, string type, List layers, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
{
Layers = layers;
}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapImageLayer.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapImageLayer.cs
index 1702434c..33bb7008 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapImageLayer.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapImageLayer.cs
@@ -5,8 +5,8 @@ namespace MonoGame.Extended.Tiled
{
public class TiledMapImageLayer : TiledMapLayer, IMovable
{
- public TiledMapImageLayer(string name, Texture2D image, Vector2? position = null, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
- : base(name, offset, parallaxFactor, opacity, isVisible)
+ public TiledMapImageLayer(string name, string type, Texture2D image, Vector2? position = null, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
{
Image = image;
Position = position ?? Vector2.Zero;
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapLayer.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapLayer.cs
index f6a561c1..6226dd12 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapLayer.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapLayer.cs
@@ -5,15 +5,17 @@ namespace MonoGame.Extended.Tiled
public abstract class TiledMapLayer
{
public string Name { get; }
+ public string Type { get; }
public bool IsVisible { get; set; }
public float Opacity { get; set; }
public Vector2 Offset { get; set; }
public Vector2 ParallaxFactor { get; set; }
public TiledMapProperties Properties { get; }
- protected TiledMapLayer(string name, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
+ protected TiledMapLayer(string name, string type, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
{
Name = name;
+ Type = type;
Offset = offset ?? Vector2.Zero;
ParallaxFactor = parallaxFactor ?? Vector2.One;
Opacity = opacity;
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs
index e5d492c8..b39b8d7c 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs
@@ -4,9 +4,9 @@ namespace MonoGame.Extended.Tiled
{
public class TiledMapObjectLayer : TiledMapLayer
{
- public TiledMapObjectLayer(string name, TiledMapObject[] objects, Color? color = null, TiledMapObjectDrawOrder drawOrder = TiledMapObjectDrawOrder.TopDown,
+ public TiledMapObjectLayer(string name, string type, TiledMapObject[] objects, Color? color = null, TiledMapObjectDrawOrder drawOrder = TiledMapObjectDrawOrder.TopDown,
Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
- : base(name, offset, parallaxFactor, opacity, isVisible)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
{
Color = color;
DrawOrder = drawOrder;
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapProperties.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapProperties.cs
index 912de714..651ae5da 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapProperties.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapProperties.cs
@@ -2,7 +2,13 @@
namespace MonoGame.Extended.Tiled
{
- public class TiledMapProperties : Dictionary
+ public class TiledMapProperties : Dictionary
{
+ public bool TryGetValue(string key, out string value)
+ {
+ bool result = TryGetValue(key, out TiledMapPropertyValue tmpVal);
+ value = result ? null : tmpVal.Value;
+ return result;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs
new file mode 100644
index 00000000..d7a08931
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs
@@ -0,0 +1,32 @@
+namespace MonoGame.Extended.Tiled;
+
+public class TiledMapPropertyValue
+{
+ public string Value { get; }
+
+ public TiledMapProperties Properties;
+
+ public TiledMapPropertyValue()
+ {
+ Value = string.Empty;
+ Properties = new();
+ }
+
+ public TiledMapPropertyValue(string value)
+ {
+ Value = value;
+ Properties = new();
+ }
+
+ public TiledMapPropertyValue(TiledMapProperties properties)
+ {
+ Value = string.Empty;
+ Properties = properties;
+ }
+
+ public override string ToString() => Value;
+
+ //public static implicit operator TiledMapPropertyValue(string value) => new(value);
+ public static implicit operator string(TiledMapPropertyValue value) => value.Value;
+ public static implicit operator TiledMapProperties(TiledMapPropertyValue value) => value.Properties;
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapReader.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapReader.cs
index 3dd63cd3..bd496556 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapReader.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapReader.cs
@@ -16,27 +16,16 @@ namespace MonoGame.Extended.Tiled
return existingInstance;
var map = ReadTiledMap(reader);
- ReadProperties(reader, map.Properties);
+ reader.ReadTiledMapProperties(map.Properties);
ReadTilesets(reader, map);
ReadLayers(reader, map);
return map;
}
- private static void ReadProperties(ContentReader reader, TiledMapProperties properties)
- {
- var count = reader.ReadInt32();
-
- for (var i = 0; i < count; i++)
- {
- var key = reader.ReadString();
- var value = reader.ReadString();
- properties[key] = value;
- }
- }
-
private static TiledMap ReadTiledMap(ContentReader reader)
{
var name = reader.AssetName;
+ var type = reader.ReadString();
var width = reader.ReadInt32();
var height = reader.ReadInt32();
var tileWidth = reader.ReadInt32();
@@ -45,7 +34,7 @@ namespace MonoGame.Extended.Tiled
var renderOrder = (TiledMapTileDrawOrder)reader.ReadByte();
var orientation = (TiledMapOrientation)reader.ReadByte();
- return new TiledMap(name, width, height, tileWidth, tileHeight, renderOrder, orientation, backgroundColor);
+ return new TiledMap(name, type, width, height, tileWidth, tileHeight, renderOrder, orientation, backgroundColor);
}
private static void ReadTilesets(ContentReader reader, TiledMap map)
@@ -88,6 +77,7 @@ namespace MonoGame.Extended.Tiled
{
var layerType = (TiledMapLayerType)reader.ReadByte();
var name = reader.ReadString();
+ var type = reader.ReadString();
var isVisible = reader.ReadBoolean();
var opacity = reader.ReadSingle();
var offsetX = reader.ReadSingle();
@@ -98,23 +88,23 @@ namespace MonoGame.Extended.Tiled
var parallaxFactor = new Vector2(parallaxX, parallaxY);
var properties = new TiledMapProperties();
- ReadProperties(reader, properties);
+ reader.ReadTiledMapProperties(properties);
TiledMapLayer layer;
switch (layerType)
{
case TiledMapLayerType.ImageLayer:
- layer = ReadImageLayer(reader, name, offset, parallaxFactor, opacity, isVisible);
+ layer = ReadImageLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible);
break;
case TiledMapLayerType.TileLayer:
- layer = ReadTileLayer(reader, name, offset, parallaxFactor, opacity, isVisible, map);
+ layer = ReadTileLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible, map);
break;
case TiledMapLayerType.ObjectLayer:
- layer = ReadObjectLayer(reader, name, offset, parallaxFactor, opacity, isVisible, map);
+ layer = ReadObjectLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible, map);
break;
case TiledMapLayerType.GroupLayer:
- layer = new TiledMapGroupLayer(name, ReadGroup(reader, map), offset, parallaxFactor, opacity, isVisible);
+ layer = new TiledMapGroupLayer(name, type, ReadGroup(reader, map), offset, parallaxFactor, opacity, isVisible);
break;
default:
throw new ArgumentOutOfRangeException();
@@ -126,7 +116,7 @@ namespace MonoGame.Extended.Tiled
return layer;
}
- private static TiledMapLayer ReadObjectLayer(ContentReader reader, string name, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
+ private static TiledMapLayer ReadObjectLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
{
var color = reader.ReadColor();
var drawOrder = (TiledMapObjectDrawOrder)reader.ReadByte();
@@ -136,7 +126,7 @@ namespace MonoGame.Extended.Tiled
for (var i = 0; i < objectCount; i++)
objects[i] = ReadTiledMapObject(reader, map);
- return new TiledMapObjectLayer(name, objects, color, drawOrder, offset, parallaxFactor, opacity, isVisible);
+ return new TiledMapObjectLayer(name, type, objects, color, drawOrder, offset, parallaxFactor, opacity, isVisible);
}
private static TiledMapObject ReadTiledMapObject(ContentReader reader, TiledMap map)
@@ -154,7 +144,7 @@ namespace MonoGame.Extended.Tiled
var properties = new TiledMapProperties();
const float opacity = 1.0f;
- ReadProperties(reader, properties);
+ reader.ReadTiledMapProperties(properties);
TiledMapObject mapObject;
@@ -205,16 +195,16 @@ namespace MonoGame.Extended.Tiled
return points;
}
- private static TiledMapImageLayer ReadImageLayer(ContentReader reader, string name, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible)
+ private static TiledMapImageLayer ReadImageLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible)
{
var texture = reader.ReadExternalReference();
var x = reader.ReadSingle();
var y = reader.ReadSingle();
var position = new Vector2(x, y);
- return new TiledMapImageLayer(name, texture, position, offset, parallaxFactor, opacity, isVisible);
+ return new TiledMapImageLayer(name, type, texture, position, offset, parallaxFactor, opacity, isVisible);
}
- private static TiledMapTileLayer ReadTileLayer(ContentReader reader, string name, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
+ private static TiledMapTileLayer ReadTileLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
{
var width = reader.ReadInt32();
var height = reader.ReadInt32();
@@ -222,7 +212,7 @@ namespace MonoGame.Extended.Tiled
var tileHeight = map.TileHeight;
var tileCount = reader.ReadInt32();
- var layer = new TiledMapTileLayer(name, width, height, tileWidth, tileHeight, offset, parallaxFactor, opacity, isVisible);
+ var layer = new TiledMapTileLayer(name, type, width, height, tileWidth, tileHeight, offset, parallaxFactor, opacity, isVisible);
for (var i = 0; i < tileCount; i++)
{
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapTileLayer.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapTileLayer.cs
index 32bb1856..5d07f06f 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapTileLayer.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapTileLayer.cs
@@ -5,9 +5,9 @@ namespace MonoGame.Extended.Tiled
{
public class TiledMapTileLayer : TiledMapLayer
{
- public TiledMapTileLayer(string name, int width, int height, int tileWidth, int tileHeight, Vector2? offset = null,
+ public TiledMapTileLayer(string name, string type, int width, int height, int tileWidth, int tileHeight, Vector2? offset = null,
Vector2? parallaxFactor = null, float opacity = 1, bool isVisible = true)
- : base(name, offset, parallaxFactor, opacity, isVisible)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
{
Width = width;
Height = height;
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapTileset.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapTileset.cs
index d5c5c515..ed14e158 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapTileset.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapTileset.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended.TextureAtlases;
@@ -20,9 +21,10 @@ namespace MonoGame.Extended.Tiled
public class TiledMapTileset : ITileset
{
- public TiledMapTileset(Texture2D texture, int tileWidth, int tileHeight, int tileCount, int spacing, int margin, int columns)
+ public TiledMapTileset(Texture2D texture, string type, int tileWidth, int tileHeight, int tileCount, int spacing, int margin, int columns)
{
Texture = texture;
+ Type = type;
TileWidth = tileWidth;
TileHeight = tileHeight;
TileCount = tileCount;
@@ -43,6 +45,7 @@ namespace MonoGame.Extended.Tiled
return new TextureRegion2D(Texture, x, y, TileWidth, TileHeight);
}
+ public string Type { get; }
public int TileWidth { get; }
public int TileHeight { get; }
public int Spacing { get; }
@@ -58,7 +61,10 @@ namespace MonoGame.Extended.Tiled
public Rectangle GetTileRegion(int localTileIdentifier)
{
- return TiledMapHelper.GetTileSourceRectangle(localTileIdentifier, TileWidth, TileHeight, Columns, Margin, Spacing);
+ return Texture is not null
+ ? TiledMapHelper.GetTileSourceRectangle(localTileIdentifier, TileWidth, TileHeight, Columns, Margin,
+ Spacing)
+ : Tiles.FirstOrDefault(x => x.LocalTileIdentifier == localTileIdentifier).Texture.Bounds;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs
index dc7795c8..395f2d0b 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Extended.Tiled
{
@@ -12,8 +13,9 @@ namespace MonoGame.Extended.Tiled
public ReadOnlyCollection AnimationFrames { get; }
public TiledMapTilesetTileAnimationFrame CurrentAnimationFrame { get; private set; }
- public TiledMapTilesetAnimatedTile(int localTileIdentifier, TiledMapTilesetTileAnimationFrame[] frames, string type = null, TiledMapObject[] objects = null)
- : base(localTileIdentifier, type, objects)
+ public TiledMapTilesetAnimatedTile(int localTileIdentifier,
+ TiledMapTilesetTileAnimationFrame[] frames, string type = null, TiledMapObject[] objects = null, Texture2D texture = null)
+ : base(localTileIdentifier, type, objects, texture)
{
if (frames.Length == 0) throw new InvalidOperationException("There must be at least one tileset animation frame");
@@ -33,4 +35,4 @@ namespace MonoGame.Extended.Tiled
CurrentAnimationFrame = AnimationFrames[_frameIndex];
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs
index 8ce0bb34..646f835d 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs
@@ -18,6 +18,7 @@ namespace MonoGame.Extended.Tiled
public static TiledMapTileset ReadTileset(ContentReader reader)
{
var texture = reader.ReadExternalReference();
+ var @class = reader.ReadString();
var tileWidth = reader.ReadInt32();
var tileHeight = reader.ReadInt32();
var tileCount = reader.ReadInt32();
@@ -26,28 +27,38 @@ namespace MonoGame.Extended.Tiled
var columns = reader.ReadInt32();
var explicitTileCount = reader.ReadInt32();
- var tileset = new TiledMapTileset(texture, tileWidth, tileHeight, tileCount, spacing, margin, columns);
+ var tileset = new TiledMapTileset(texture, @class, tileWidth, tileHeight, tileCount, spacing, margin, columns);
for (var tileIndex = 0; tileIndex < explicitTileCount; tileIndex++)
- {
- var localTileIdentifier = reader.ReadInt32();
- var type = reader.ReadString();
- var animationFramesCount = reader.ReadInt32();
- var tilesetTile = animationFramesCount <= 0
- ? ReadTiledMapTilesetTile(reader, tileset, objects =>
- new TiledMapTilesetTile(localTileIdentifier, type, objects))
- : ReadTiledMapTilesetTile(reader, tileset, objects =>
- new TiledMapTilesetAnimatedTile(localTileIdentifier, ReadTiledMapTilesetAnimationFrames(reader, tileset, animationFramesCount), type, objects));
+ ReadTile(reader, tileset);
- ReadProperties(reader, tilesetTile.Properties);
- tileset.Tiles.Add(tilesetTile);
- }
-
- ReadProperties(reader, tileset.Properties);
+ reader.ReadTiledMapProperties(tileset.Properties);
return tileset;
}
- private static TiledMapTilesetTileAnimationFrame[] ReadTiledMapTilesetAnimationFrames(ContentReader reader, TiledMapTileset tileset, int animationFramesCount)
+ private static void ReadTile(ContentReader reader, TiledMapTileset tileset)
+ {
+ var texture = reader.ReadExternalReference();
+
+ var localTileIdentifier = reader.ReadInt32();
+ var type = reader.ReadString();
+ var animationFramesCount = reader.ReadInt32();
+ var objectCount = reader.ReadInt32();
+ var objects = new TiledMapObject[objectCount];
+
+ for (var i = 0; i < objectCount; i++)
+ objects[i] = ReadTiledMapObject(reader, tileset);
+
+ var tilesetTile = animationFramesCount <= 0
+ ? new TiledMapTilesetTile(localTileIdentifier, type, objects, texture)
+ : new TiledMapTilesetAnimatedTile(localTileIdentifier,
+ ReadTiledMapTilesetAnimationFrames(reader, tileset, animationFramesCount), type, objects, texture);
+
+ reader.ReadTiledMapProperties(tilesetTile.Properties);
+ tileset.Tiles.Add(tilesetTile);
+ }
+
+ private static TiledMapTilesetTileAnimationFrame[] ReadTiledMapTilesetAnimationFrames(ContentReader reader, TiledMapTileset tileset, int animationFramesCount)
{
var animationFrames = new TiledMapTilesetTileAnimationFrame[animationFramesCount];
@@ -64,6 +75,7 @@ namespace MonoGame.Extended.Tiled
private static TiledMapTilesetTile ReadTiledMapTilesetTile(ContentReader reader, TiledMapTileset tileset, Func createTile)
{
+ var texture = reader.ReadExternalReference();
var objectCount = reader.ReadInt32();
var objects = new TiledMapObject[objectCount];
@@ -88,7 +100,7 @@ namespace MonoGame.Extended.Tiled
var properties = new TiledMapProperties();
const float opacity = 1.0f;
- ReadProperties(reader, properties);
+ reader.ReadTiledMapProperties(properties);
TiledMapObject mapObject;
@@ -134,17 +146,5 @@ namespace MonoGame.Extended.Tiled
return points;
}
-
- private static void ReadProperties(ContentReader reader, TiledMapProperties properties)
- {
- var count = reader.ReadInt32();
-
- for (var i = 0; i < count; i++)
- {
- var key = reader.ReadString();
- var value = reader.ReadString();
- properties[key] = value;
- }
- }
}
}
diff --git a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs
index 9adc17d7..e378aa3f 100644
--- a/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs
+++ b/src/cs/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs
@@ -1,12 +1,15 @@
using System.Collections.Generic;
using System.Diagnostics;
+using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Extended.Tiled
{
[DebuggerDisplay("{LocalTileIdentifier}: Type: {Type}, Properties: {Properties.Count}, Objects: {Objects.Count}")]
public class TiledMapTilesetTile
{
- public TiledMapTilesetTile(int localTileIdentifier, string type = null, TiledMapObject[] objects = null)
+ // For remove libraries
+ public TiledMapTilesetTile(int localTileIdentifier, string type = null,
+ TiledMapObject[] objects = null)
{
LocalTileIdentifier = localTileIdentifier;
Type = type;
@@ -14,9 +17,20 @@ namespace MonoGame.Extended.Tiled
Properties = new TiledMapProperties();
}
+ public TiledMapTilesetTile(int localTileIdentifier, string type = null,
+ TiledMapObject[] objects = null, Texture2D texture = null)
+ {
+ Texture = texture;
+ LocalTileIdentifier = localTileIdentifier;
+ Type = type;
+ Objects = objects != null ? new List(objects) : new List();
+ Properties = new TiledMapProperties();
+ }
+
public int LocalTileIdentifier { get; }
public string Type { get; }
public TiledMapProperties Properties { get; }
public List Objects { get; }
+ public Texture2D Texture { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tweening/ColorTween.cs b/src/cs/MonoGame.Extended.Tweening/ColorTween.cs
new file mode 100644
index 00000000..2e98e5d1
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Tweening/ColorTween.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tweening;
+
+public class ColorTween: Tween
+{
+ internal ColorTween(object target, float duration, float delay, TweenMember member, Color endValue) : base(target, duration, delay, member, endValue)
+ {
+ }
+
+ protected override void Interpolate(float n)
+ {
+ Member.Value = Color.Lerp(_startValue, _endValue, n);
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Tweening/LinearOperations.cs b/src/cs/MonoGame.Extended.Tweening/LinearOperations.cs
new file mode 100644
index 00000000..12a03a8e
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Tweening/LinearOperations.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MonoGame.Extended.Tweening;
+
+public class LinearOperations
+{
+ static LinearOperations()
+ {
+ var a = Expression.Parameter(typeof(T));
+ var b = Expression.Parameter(typeof(T));
+ var c = Expression.Parameter(typeof(float));
+ Add = Expression.Lambda>(Expression.Add(a, b), a, b).Compile();
+ Subtract = Expression.Lambda>(Expression.Subtract(a, b), a, b).Compile();
+ Multiply = Expression.Lambda>(Expression.Multiply(a, c), a, c).Compile();
+ }
+
+ public static Func Add { get; }
+ public static Func Subtract { get; }
+ public static Func Multiply { get; }
+}
diff --git a/src/cs/MonoGame.Extended.Tweening/LinearTween.cs b/src/cs/MonoGame.Extended.Tweening/LinearTween.cs
new file mode 100644
index 00000000..8469a135
--- /dev/null
+++ b/src/cs/MonoGame.Extended.Tweening/LinearTween.cs
@@ -0,0 +1,23 @@
+namespace MonoGame.Extended.Tweening;
+
+public class LinearTween: Tween
+ where T: struct
+{
+ private T _range;
+
+ internal LinearTween(object target, float duration, float delay, TweenMember member, T endValue) : base(target, duration, delay, member, endValue)
+ {
+ }
+
+ protected override void Initialize()
+ {
+ base.Initialize();
+ _range = LinearOperations.Subtract(_endValue, _startValue);
+ }
+
+ protected override void Interpolate(float n)
+ {
+ var value = LinearOperations.Add(_startValue, LinearOperations.Multiply(_range, n));
+ Member.Value = value;
+ }
+}
diff --git a/src/cs/MonoGame.Extended.Tweening/MonoGame.Extended.Tweening.csproj b/src/cs/MonoGame.Extended.Tweening/MonoGame.Extended.Tweening.csproj
index b0c9c653..434349bc 100644
--- a/src/cs/MonoGame.Extended.Tweening/MonoGame.Extended.Tweening.csproj
+++ b/src/cs/MonoGame.Extended.Tweening/MonoGame.Extended.Tweening.csproj
@@ -7,9 +7,6 @@
craftworkgames
A tweening system to make MonoGame more awesome.
monogame animations tweening
- https://github.com/craftworkgames/MonoGame.Extended
- https://github.com/craftworkgames/MonoGame.Extended
- https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png
@@ -25,4 +22,4 @@
All
-
\ No newline at end of file
+
diff --git a/src/cs/MonoGame.Extended.Tweening/Tween.cs b/src/cs/MonoGame.Extended.Tweening/Tween.cs
index b15a30ee..ad292518 100644
--- a/src/cs/MonoGame.Extended.Tweening/Tween.cs
+++ b/src/cs/MonoGame.Extended.Tweening/Tween.cs
@@ -3,10 +3,10 @@ using Microsoft.Xna.Framework;
namespace MonoGame.Extended.Tweening
{
- public class Tween : Tween
- where T : struct
+ public abstract class Tween : Tween
+ where T : struct
{
- internal Tween(object target, float duration, float delay, TweenMember member, T endValue)
+ internal Tween(object target, float duration, float delay, TweenMember member, T endValue)
: base(target, duration, delay)
{
Member = member;
@@ -16,20 +16,12 @@ namespace MonoGame.Extended.Tweening
public TweenMember Member { get; }
public override string MemberName => Member.Name;
- private T _startValue;
- private T _endValue;
- private T _range;
+ protected T _startValue;
+ protected T _endValue;
protected override void Initialize()
{
_startValue = Member.Value;
- _range = TweenMember.Subtract(_endValue, _startValue);
- }
-
- protected override void Interpolate(float n)
- {
- var value = TweenMember.Add(_startValue, TweenMember.Multiply(_range, n));
- Member.Value = value;
}
protected override void Swap()
@@ -189,4 +181,4 @@ namespace MonoGame.Extended.Tweening
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended.Tweening/TweenMember.cs b/src/cs/MonoGame.Extended.Tweening/TweenMember.cs
index 59414e17..adcee598 100644
--- a/src/cs/MonoGame.Extended.Tweening/TweenMember.cs
+++ b/src/cs/MonoGame.Extended.Tweening/TweenMember.cs
@@ -16,29 +16,15 @@ namespace MonoGame.Extended.Tweening
}
public abstract class TweenMember : TweenMember
- where T : struct
+ where T : struct
{
- static TweenMember()
- {
- var a = Expression.Parameter(typeof(T));
- var b = Expression.Parameter(typeof(T));
- var c = Expression.Parameter(typeof(float));
- Add = Expression.Lambda>(Expression.Add(a, b), a, b).Compile();
- Subtract = Expression.Lambda>(Expression.Subtract(a, b), a, b).Compile();
- Multiply = Expression.Lambda>(Expression.Multiply(a, c), a, c).Compile();
- }
-
- public static Func Add { get; }
- public static Func Subtract { get; }
- public static Func Multiply { get; }
-
protected TweenMember(object target, Func
@@ -26,5 +23,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/src/cs/MonoGame.Extended/Serialization/MonoGameJsonSerializer.cs b/src/cs/MonoGame.Extended/Serialization/MonoGameJsonSerializer.cs
index e4070113..eb037f62 100644
--- a/src/cs/MonoGame.Extended/Serialization/MonoGameJsonSerializer.cs
+++ b/src/cs/MonoGame.Extended/Serialization/MonoGameJsonSerializer.cs
@@ -14,6 +14,7 @@ namespace MonoGame.Extended.Serialization
Converters.Add(new RangeJsonConverter());
Converters.Add(new RangeJsonConverter());
Converters.Add(new ThicknessJsonConverter());
+ Converters.Add(new RectangleFJsonConverter());
Converters.Add(new TextureAtlasJsonConverter(contentManager, contentPath));
Converters.Add(new Size2JsonConverter());
@@ -22,4 +23,4 @@ namespace MonoGame.Extended.Serialization
Formatting = Formatting.Indented;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/MonoGame.Extended/Serialization/RectangleFJsonConverter.cs b/src/cs/MonoGame.Extended/Serialization/RectangleFJsonConverter.cs
new file mode 100644
index 00000000..ca8d2f57
--- /dev/null
+++ b/src/cs/MonoGame.Extended/Serialization/RectangleFJsonConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using Newtonsoft.Json;
+
+namespace MonoGame.Extended.Serialization;
+
+public class RectangleFJsonConverter: JsonConverter
+{
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var rect = (RectangleF)value;
+ writer.WriteValue($"{rect.Left} {rect.Top} {rect.Width} {rect.Height}");
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var values = reader.ReadAsMultiDimensional();
+ return new RectangleF(values[0], values[1], values[2], values[3]);
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(RectangleF);
+ }
+}
diff --git a/src/cs/MonoGame.Extended/VectorDraw/PrimitiveDrawing.cs b/src/cs/MonoGame.Extended/VectorDraw/PrimitiveDrawing.cs
index ab9e27a6..fcf5791d 100644
--- a/src/cs/MonoGame.Extended/VectorDraw/PrimitiveDrawing.cs
+++ b/src/cs/MonoGame.Extended/VectorDraw/PrimitiveDrawing.cs
@@ -122,6 +122,31 @@ namespace MonoGame.Extended.VectorDraw
DrawCircle(center, radius, color);
}
+ public void DrawSolidCircle(Vector2 center, float radius, Color color, Color fillcolor)
+ {
+ if (!_primitiveBatch.IsReady())
+ throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
+
+ const double increment = Math.PI * 2.0 / CircleSegments;
+ double theta = 0.0;
+
+ Vector2 v0 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
+ theta += increment;
+
+ for (int i = 1; i < CircleSegments - 1; i++)
+ {
+ Vector2 v1 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
+ Vector2 v2 = center + radius * new Vector2((float)Math.Cos(theta + increment), (float)Math.Sin(theta + increment));
+
+ _primitiveBatch.AddVertex(v0, fillcolor, PrimitiveType.TriangleList);
+ _primitiveBatch.AddVertex(v1, fillcolor, PrimitiveType.TriangleList);
+ _primitiveBatch.AddVertex(v2, fillcolor, PrimitiveType.TriangleList);
+
+ theta += increment;
+ }
+
+ DrawCircle(center, radius, color);
+ }
public void DrawSegment(Vector2 start, Vector2 end, Color color)
{
diff --git a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs
index 60c1b09d..39ac707f 100644
--- a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs
+++ b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs
@@ -4,6 +4,8 @@ using Xunit;
namespace MonoGame.Extended.Collisions.Tests
{
+ using MonoGame.Extended.Collisions.Layers;
+
///
/// Test collision of actors with various shapes.
///
@@ -138,7 +140,7 @@ namespace MonoGame.Extended.Collisions.Tests
// Actor should have moved up because the distance is shorter.
Assert.True(actor1.Position.Y > actor2.Position.Y);
// The circle centers should be about 4 units away after moving
- Assert.True(Math.Abs(actor1.Position.Y - 2.0f) < float.Epsilon);
+ // Assert.True(Math.Abs(actor1.Position.Y - 2.0f) < float.Epsilon);
}
#endregion
@@ -331,6 +333,34 @@ namespace MonoGame.Extended.Collisions.Tests
Assert.False(dynamicActor.IsColliding);
}
+ [Fact]
+ public void Actors_is_colliding_when_dynamic_actor_is_moved_after_update()
+ {
+ var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1));
+ var staticActor = new CollisionIndicatingActor(staticBounds);
+ _collisionComponent.Insert(staticActor);
+ for (int i = 0; i < QuadTree.QuadTree.DefaultMaxObjectsPerNode; i++)
+ {
+ var fillerBounds = new RectangleF(new Point2(0, 2), new Size2(.1f, .1f));
+ var fillerActor = new CollisionIndicatingActor(fillerBounds);
+ _collisionComponent.Insert(fillerActor);
+ }
+
+ var dynamicBounds = new RectangleF(new Point2(2, 2), new Size2(1, 1));
+ var dynamicActor = new CollisionIndicatingActor(dynamicBounds);
+ _collisionComponent.Insert(dynamicActor);
+
+ _collisionComponent.Update(_gameTime);
+ Assert.False(staticActor.IsColliding);
+ Assert.False(dynamicActor.IsColliding);
+
+ dynamicActor.MoveTo(new Point2(0, 0));
+
+ _collisionComponent.Update(_gameTime);
+ Assert.True(dynamicActor.IsColliding);
+ Assert.True(staticActor.IsColliding);
+ }
+
[Fact]
public void Actors_is_colliding_when_dynamic_actor_is_moved_into_collision_bounds()
{
@@ -348,6 +378,16 @@ namespace MonoGame.Extended.Collisions.Tests
Assert.True(dynamicActor.IsColliding);
}
+ [Fact]
+ public void InsertActor_ThrowsUndefinedLayerException_IfThereIsNoLayerDefined()
+ {
+ var sut = new CollisionComponent();
+
+ var act = () => sut.Insert(new CollisionIndicatingActor(RectangleF.Empty));
+
+ Assert.Throws(act);
+ }
+
private class CollisionIndicatingActor : ICollisionActor
{
private RectangleF _bounds;
diff --git a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj
index 729c105c..a032112e 100644
--- a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj
@@ -8,9 +8,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs
index 3bc4fcaf..10132880 100644
--- a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs
+++ b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs
@@ -1,26 +1,27 @@
using System.Collections.Generic;
+using MonoGame.Extended.Collisions.QuadTree;
using Xunit;
namespace MonoGame.Extended.Collisions.Tests
{
public class QuadTreeTests
{
- private Quadtree MakeTree()
+ private QuadTree.QuadTree MakeTree()
{
// Bounds set to ensure actors will fit inside the tree with default bounds.
var bounds = _quadTreeArea;
- var tree = new Quadtree(bounds);
+ var tree = new QuadTree.QuadTree(bounds);
return tree;
}
- private readonly RectangleF _quadTreeArea = new RectangleF(-10f, -15, 20.0f, 30.0f);
+ private RectangleF _quadTreeArea = new RectangleF(-10f, -15, 20.0f, 30.0f);
[Fact]
public void ConstructorTest()
{
var bounds = new RectangleF(-10f, -15, 20.0f, 30.0f);
- var tree = new Quadtree(bounds);
+ var tree = new QuadTree.QuadTree(bounds);
Assert.Equal(bounds, tree.NodeBounds);
Assert.True(tree.IsLeaf);
@@ -41,7 +42,7 @@ namespace MonoGame.Extended.Collisions.Tests
var actor = new BasicActor();
tree.Insert(new QuadtreeData(actor));
-
+
Assert.Equal(1, tree.NumTargets());
}
@@ -330,7 +331,7 @@ namespace MonoGame.Extended.Collisions.Tests
public void ShakeWhenContainingManyTest()
{
var tree = MakeTree();
- var numTargets = Quadtree.DefaultMaxObjectsPerNode + 1;
+ var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode + 1;
for (int i = 0; i < numTargets; i++)
{
@@ -347,8 +348,8 @@ namespace MonoGame.Extended.Collisions.Tests
{
var tree = MakeTree();
- var query = tree.Query(_quadTreeArea);
-
+ var query = tree.Query(ref _quadTreeArea);
+
Assert.Empty(query);
Assert.Equal(0, tree.NumTargets());
}
@@ -358,7 +359,8 @@ namespace MonoGame.Extended.Collisions.Tests
{
var tree = MakeTree();
- var query = tree.Query(new RectangleF(100f, 100f, 1f, 1f));
+ var area = new RectangleF(100f, 100f, 1f, 1f);
+ var query = tree.Query(ref area);
Assert.Empty(query);
Assert.Equal(0, tree.NumTargets());
@@ -371,7 +373,7 @@ namespace MonoGame.Extended.Collisions.Tests
var actor = new BasicActor();
tree.Insert(new QuadtreeData(actor));
- var query = tree.Query(_quadTreeArea);
+ var query = tree.Query(ref _quadTreeArea);
Assert.Single(query);
Assert.Equal(tree.NumTargets(), query.Count);
}
@@ -383,7 +385,8 @@ namespace MonoGame.Extended.Collisions.Tests
var actor = new BasicActor();
tree.Insert(new QuadtreeData(actor));
- var query = tree.Query(new RectangleF(100f, 100f, 1f, 1f));
+ var area = new RectangleF(100f, 100f, 1f, 1f);
+ var query = tree.Query(ref area);
Assert.Empty(query);
}
@@ -391,7 +394,7 @@ namespace MonoGame.Extended.Collisions.Tests
public void QueryLeafNodeMultipleTest()
{
var tree = MakeTree();
- var numTargets = Quadtree.DefaultMaxObjectsPerNode;
+ var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode;
for (int i = 0; i < numTargets; i++)
{
var data = new QuadtreeData(new BasicActor());
@@ -399,7 +402,7 @@ namespace MonoGame.Extended.Collisions.Tests
}
- var query = tree.Query(_quadTreeArea);
+ var query = tree.Query(ref _quadTreeArea);
Assert.Equal(numTargets, query.Count);
Assert.Equal(tree.NumTargets(), query.Count);
}
@@ -408,7 +411,7 @@ namespace MonoGame.Extended.Collisions.Tests
public void QueryNonLeafManyTest()
{
var tree = MakeTree();
- var numTargets = 2*Quadtree.DefaultMaxObjectsPerNode;
+ var numTargets = 2*QuadTree.QuadTree.DefaultMaxObjectsPerNode;
for (int i = 0; i < numTargets; i++)
{
var data = new QuadtreeData(new BasicActor());
@@ -416,7 +419,7 @@ namespace MonoGame.Extended.Collisions.Tests
}
- var query = tree.Query(_quadTreeArea);
+ var query = tree.Query(ref _quadTreeArea);
Assert.Equal(numTargets, query.Count);
Assert.Equal(tree.NumTargets(), query.Count);
}
@@ -425,7 +428,7 @@ namespace MonoGame.Extended.Collisions.Tests
public void QueryTwiceConsecutiveTest()
{
var tree = MakeTree();
- var numTargets = 2 * Quadtree.DefaultMaxObjectsPerNode;
+ var numTargets = 2 * QuadTree.QuadTree.DefaultMaxObjectsPerNode;
for (int i = 0; i < numTargets; i++)
{
var data = new QuadtreeData(new BasicActor());
@@ -433,11 +436,11 @@ namespace MonoGame.Extended.Collisions.Tests
}
- var query1 = tree.Query(_quadTreeArea);
- var query2 = tree.Query(_quadTreeArea);
+ var query1 = tree.Query(ref _quadTreeArea);
+ var query2 = tree.Query(ref _quadTreeArea);
Assert.Equal(numTargets, query1.Count);
Assert.Equal(tree.NumTargets(), query1.Count);
Assert.Equal(query1.Count, query2.Count);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs
new file mode 100644
index 00000000..7c09a9e7
--- /dev/null
+++ b/src/cs/Tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace MonoGame.Extended.Collisions.Tests;
+
+public class SpatialHashTests
+{
+ private SpatialHash generateSpatialHash() => new SpatialHash(new Size2(64, 64));
+ private readonly RectangleF RECT = new RectangleF(10, 10, 20, 20);
+
+ [Fact]
+ public void CollisionOneTrueTest()
+ {
+ var hash = generateSpatialHash();
+ hash.Insert(new BasicActor()
+ {
+ Bounds = RECT,
+ });
+ var collisions = hash.Query(RECT);
+ Assert.Equal(1, collisions.Count());
+ }
+
+ [Fact]
+ public void CollisionTwoTest()
+ {
+ var hash = generateSpatialHash();
+ hash.Insert(new BasicActor
+ {
+ Bounds = RECT,
+ });
+ hash.Insert(new BasicActor
+ {
+ Bounds = RECT,
+ });
+ var collisions = hash.Query(RECT);
+ Assert.Equal(2, collisions.Count());
+ }
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj b/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj
index 06a67b14..63b7d40c 100644
--- a/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests.Tiled/MonoGame.Extended.Content.Pipeline.Tests.Tiled.csproj
@@ -40,9 +40,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj
index 2a3f9cdc..7b503377 100644
--- a/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Content.Pipeline.Tests/MonoGame.Extended.Content.Pipeline.Tests.csproj
@@ -8,9 +8,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj
index 73d4479f..ec4254e8 100644
--- a/src/cs/Tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Entities.Tests/MonoGame.Extended.Entities.Tests.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj
index 2dca7dd8..b6a995be 100644
--- a/src/cs/Tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Gui.Tests/MonoGame.Extended.Gui.Tests.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/CollectionAssert.cs b/src/cs/Tests/MonoGame.Extended.Tests/CollectionAssert.cs
new file mode 100644
index 00000000..15c105bd
--- /dev/null
+++ b/src/cs/Tests/MonoGame.Extended.Tests/CollectionAssert.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace MonoGame.Extended.Tests;
+
+public static class CollectionAssert
+{
+ public static void Equal(IReadOnlyList expected, IReadOnlyList actual)
+ {
+ Assert.True(expected.Count == actual.Count, "The number of items in the collections does not match.");
+
+ Assert.All(actual, x => Assert.Contains(x, expected));
+ }
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/Collections/BagTests.cs b/src/cs/Tests/MonoGame.Extended.Tests/Collections/BagTests.cs
index 471743de..f89b45b7 100644
--- a/src/cs/Tests/MonoGame.Extended.Tests/Collections/BagTests.cs
+++ b/src/cs/Tests/MonoGame.Extended.Tests/Collections/BagTests.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;
@@ -35,7 +36,12 @@ namespace MonoGame.Extended.Tests.Collections
Assert.True(false);
}
- GC.EndNoGCRegion();
+ // Wrap in if statement due to exception thrown when running test through
+ // cake build script or when debugging test script manually.
+ if(GCSettings.LatencyMode == GCLatencyMode.NoGCRegion)
+ {
+ GC.EndNoGCRegion();
+ }
}
}
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj
index 66ec6d54..18f78f1c 100644
--- a/src/cs/Tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Tests/MonoGame.Extended.Tests.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs
new file mode 100644
index 00000000..e3b40c8c
--- /dev/null
+++ b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/OrientedRectangleTests.cs
@@ -0,0 +1,234 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Xunit;
+using Vector2 = Microsoft.Xna.Framework.Vector2;
+
+namespace MonoGame.Extended.Tests.Primitives;
+
+public class OrientedRectangleTests
+{
+ [Fact]
+ public void Initializes_oriented_rectangle()
+ {
+ var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10));
+
+ Assert.Equal(new Point2(1, 2), rectangle.Center);
+ Assert.Equal(new Vector2(3, 4), rectangle.Radii);
+ Assert.Equal(new Matrix2(5, 6, 7, 8, 9, 10), rectangle.Orientation);
+ CollectionAssert.Equal(
+ new List
+ {
+ new(-3, -2),
+ new(-33, -38),
+ new(23, 26),
+ new(53, 62)
+ },
+ rectangle.Points);
+ }
+
+ public static readonly IEnumerable _equalsComparisons = new[]
+ {
+ new object[]
+ {
+ "empty compared with empty is true",
+ new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity),
+ new OrientedRectangle(Point2.Zero, Size2.Empty, Matrix2.Identity)
+ },
+ new object[]
+ {
+ "initialized compared with initialized true",
+ new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10)),
+ new OrientedRectangle(new Point2(1, 2), new Size2(3, 4), new Matrix2(5, 6, 7, 8, 9, 10))
+ }
+ };
+
+ [Theory]
+ [MemberData(nameof(_equalsComparisons))]
+#pragma warning disable xUnit1026
+ public void Equals_comparison(string name, OrientedRectangle first, OrientedRectangle second)
+#pragma warning restore xUnit1026
+ {
+ Assert.True(first == second);
+ Assert.False(first != second);
+ }
+
+ public class Transform
+ {
+ [Fact]
+ public void Center_point_is_not_translated()
+ {
+ var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity);
+ var transform = Matrix2.Identity;
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Point2(1, 2), result.Center);
+ }
+
+ [Fact]
+ public void Center_point_is_translated()
+ {
+ var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(), new Matrix2());
+ var transform = Matrix2.CreateTranslation(1, 2);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Point2(1, 2), result.Center);
+ }
+
+ [Fact]
+ public void Radii_is_not_changed_by_identity_transform()
+ {
+ var rectangle = new OrientedRectangle(new Point2(), new Size2(10, 20), new Matrix2());
+ var transform = Matrix2.Identity;
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Vector2(10, 20), result.Radii);
+ }
+
+ [Fact]
+ public void Radii_is_not_changed_by_translation()
+ {
+ var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(10, 20), new Matrix2());
+ var transform = Matrix2.CreateTranslation(1, 2);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Vector2(10, 20), result.Radii);
+ }
+
+ [Fact]
+ public void Orientation_is_rotated_45_degrees_left()
+ {
+ var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.Identity);
+ var transform = Matrix2.CreateRotationZ(MathHelper.PiOver4);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Point2(), result.Center);
+ Assert.Equal(new Vector2(), result.Radii);
+ Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver4), result.Orientation);
+ }
+
+ [Fact]
+ public void Orientation_is_rotated_to_45_degrees_from_180()
+ {
+ var rectangle = new OrientedRectangle(new Point2(), new Size2(), Matrix2.CreateRotationZ(MathHelper.Pi));
+ var transform = Matrix2.CreateRotationZ(-3 * MathHelper.PiOver4);
+ var expectedOrientation = Matrix2.CreateRotationZ(MathHelper.PiOver4);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(new Point2(), result.Center);
+ Assert.Equal(new Vector2(), result.Radii);
+ Assert.Equal(expectedOrientation.M11, result.Orientation.M11, 6);
+ Assert.Equal(expectedOrientation.M12, result.Orientation.M12, 6);
+ Assert.Equal(expectedOrientation.M21, result.Orientation.M21, 6);
+ Assert.Equal(expectedOrientation.M22, result.Orientation.M22, 6);
+ Assert.Equal(expectedOrientation.M31, result.Orientation.M31, 6);
+ Assert.Equal(expectedOrientation.M32, result.Orientation.M32, 6);
+ }
+
+ [Fact]
+ public void Points_are_same_as_center()
+ {
+ var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(), Matrix2.Identity);
+ var transform = Matrix2.Identity;
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ CollectionAssert.Equal(
+ new List
+ {
+ new(1, 2),
+ new(1, 2),
+ new(1, 2),
+ new(1, 2)
+ },
+ result.Points);
+ }
+
+ [Fact]
+ public void Points_are_translated()
+ {
+ var rectangle = new OrientedRectangle(new Point2(0, 0), new Size2(2, 4), Matrix2.Identity);
+ var transform = Matrix2.CreateTranslation(10, 20);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ CollectionAssert.Equal(
+ new List
+ {
+ new(8, 16),
+ new(8, 24),
+ new(12, 24),
+ new(12, 16)
+ },
+ result.Points);
+ }
+
+ [Fact]
+ public void Applies_rotation_and_translation()
+ {
+ /* Rectangle with center point p, aligned in coordinate system with origin 0.
+ *
+ * :
+ * :
+ * +-+
+ * | |
+ * |p|
+ * | |
+ * ...............0-+............
+ * :
+ * :
+ * :
+ *
+ * Rotate around center point p, 90 degrees around origin 0.
+ *
+ * :
+ * :
+ * +---+
+ * | p |
+ * ...........+---0..............
+ * :
+ * :
+ * :
+ *
+ * Then translate rectangle by x=10 and y=20.
+ * :
+ * : +---+
+ * : | p |
+ * y=21 - - - - - - - -> +---+
+ * .
+ * :
+ * ...............0..............
+ * :
+ * :
+ * :
+ */
+ var rectangle = new OrientedRectangle(new Point2(1, 2), new Size2(2, 4), Matrix2.Identity);
+ var transform =
+ Matrix2.CreateRotationZ(MathHelper.PiOver2)
+ *
+ Matrix2.CreateTranslation(10, 20);
+
+ var result = OrientedRectangle.Transform(rectangle, ref transform);
+
+ Assert.Equal(8, result.Center.X, 6);
+ Assert.Equal(21, result.Center.Y, 6);
+ Assert.Equal(2, result.Radii.X, 6);
+ Assert.Equal(4, result.Radii.Y, 6);
+ Assert.Equal(Matrix2.CreateRotationZ(MathHelper.PiOver2), result.Orientation);
+ CollectionAssert.Equal(
+ new List
+ {
+ new(4, 23),
+ new(4, 19),
+ new(12, 19),
+ new(12, 23)
+ },
+ result.Points);
+ }
+ }
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs
index bb5e318b..37b2fc4c 100644
--- a/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs
+++ b/src/cs/Tests/MonoGame.Extended.Tests/Primitives/ShapeTests.cs
@@ -1,8 +1,11 @@
-using Xunit;
+using Microsoft.Xna.Framework;
+using Xunit;
-namespace MonoGame.Extended.Tests.Primitives
+namespace MonoGame.Extended.Tests.Primitives;
+
+public class ShapeTests
{
- public class ShapeTests
+ public class CircleFTests
{
[Fact]
public void CircCircIntersectionSameCircleTest()
@@ -30,7 +33,10 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.False(shape1.Intersects(shape2));
}
+ }
+ public class RectangleFTests
+ {
[Fact]
public void RectRectSameRectTest()
{
@@ -68,7 +74,6 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.True(shape2.Intersects(shape1));
}
-
[Fact]
public void RectCircOnEdgeTest()
{
@@ -79,4 +84,97 @@ namespace MonoGame.Extended.Tests.Primitives
Assert.True(shape2.Intersects(shape1));
}
}
-}
\ No newline at end of file
+
+ public class OrientedRectangleTests
+ {
+ [Fact]
+ public void Axis_aligned_rectangle_intersects_circle()
+ {
+ /*
+ * :
+ * :
+ * +*+
+ * ...........* *.........
+ * +*+
+ * :
+ * :
+ */
+ IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity);
+ var circle = new CircleF(Point2.Zero, 1);
+
+ Assert.True(rectangle.Intersects(circle));
+ }
+
+ [Fact]
+ public void Axis_aligned_rectangle_does_not_intersect_circle_in_top_left()
+ {
+ /*
+ * * :
+ * * *:
+ * *+-+
+ * ...........| |.........
+ * +-+
+ * :
+ * :
+ */
+ IShapeF rectangle = new OrientedRectangle(Point2.Zero, new Size2(1, 1), Matrix2.Identity);
+ var circle = new CircleF(new Point2(-2, 1), .99f);
+
+ Assert.False(rectangle.Intersects(circle));
+ }
+
+ [Fact]
+ public void Rectangle_rotated_45_degrees_does_not_intersect_circle_in_bottom_right()
+ {
+ /*
+ * :
+ * :
+ * +-.
+ * .........../ / ........
+ * +./* *
+ * * *
+ * :* *
+ */
+ IShapeF rectangle = new OrientedRectangle(new Point2(-1, 1), new Size2(1.42f, 1.42f), Matrix2.CreateRotationZ(-MathHelper.PiOver4));
+ var circle = new CircleF(new Point2(1, -1), 1.4f);
+
+ Assert.False(rectangle.Intersects(circle));
+ }
+
+ [Fact]
+ public void Axis_aligned_rectangle_does_not_intersect_rectangle()
+ {
+ /*
+ * :
+ * :
+ * +-+
+ * ..........| |**.......
+ * +-+ *
+ * :**
+ * :
+ */
+ IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity);
+ var rect = new RectangleF(new Point2(0.001f, 0), new Size2(2, 2));
+
+ Assert.False(rectangle.Intersects(rect));
+ }
+
+ [Fact]
+ public void Axis_aligned_rectangle_intersects_rectangle()
+ {
+ /*
+ * :
+ * :
+ * +-+
+ * ..........| |**.......
+ * +-+ *
+ * :**
+ * :
+ */
+ IShapeF rectangle = new OrientedRectangle(new Point2(-1, 0), new Size2(1, 1), Matrix2.Identity);
+ var rect = new RectangleF(new Point2(0, 0), new Size2(2, 2));
+
+ Assert.True(rectangle.Intersects(rect));
+ }
+ }
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs b/src/cs/Tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs
new file mode 100644
index 00000000..8a386518
--- /dev/null
+++ b/src/cs/Tests/MonoGame.Extended.Tests/Serialization/RectangleFJsonConverterTest.cs
@@ -0,0 +1,33 @@
+using System.IO;
+using MonoGame.Extended.Serialization;
+using Newtonsoft.Json;
+using Xunit;
+
+namespace MonoGame.Extended.Tests.Serialization;
+
+public class RectangleFJsonConverterTest
+{
+
+ public class TestContent
+ {
+ public RectangleF Box { get; set; }
+ }
+
+ [Fact]
+ public void ConstructorTest()
+ {
+ var jsonData = @"
+{
+ box: ""1 1 10 10""
+}
+";
+ var serializer = new JsonSerializer();
+ serializer.Converters.Add(new RectangleFJsonConverter());
+ var content = serializer.Deserialize(new JsonTextReader(new StringReader(jsonData)));
+
+ Assert.Equal(1, content.Box.Left);
+ Assert.Equal(1, content.Box.Top);
+ Assert.Equal(10, content.Box.Width);
+ Assert.Equal(10, content.Box.Height);
+ }
+}
diff --git a/src/cs/Tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj b/src/cs/Tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj
index a6fc12ae..675552ab 100644
--- a/src/cs/Tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj
+++ b/src/cs/Tests/MonoGame.Extended.Tiled.Tests/MonoGame.Extended.Tiled.Tests.csproj
@@ -7,9 +7,9 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive