mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-15 15:09:29 +00:00
Merge branch 'develop' into pipeline-update-class-properties
This commit is contained in:
@@ -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 }}
|
||||
|
||||
@@ -9,3 +9,6 @@
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.user
|
||||
|
||||
# cake build output
|
||||
artifacts
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.9.0</Version>
|
||||
<Authors>craftworkgames</Authors>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryBranch>develop</RepositoryBranch>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
<PackageIcon>logo-nuget-128.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- path must be relative to the individual csproj's not this .targets file -->
|
||||
<None Include="../../../Logos/logo-nuget-128.png" Pack="true" PackagePath="" />
|
||||
<None Include="../../../README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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<Collider> _colliders = new();
|
||||
private List<Layer> _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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\cs\MonoGame.Extended.Collisions\MonoGame.Extended.Collisions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
using MonoGame.Extended.Benchmarks.Collisions;
|
||||
|
||||
//var summary = BenchmarkRunner.Run<DifferentPoolSizeCollision>();
|
||||
var summary = BenchmarkRunner.Run<SpaceAlgorithms>();
|
||||
@@ -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<Collider> _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<ICollisionActor> Query()
|
||||
{
|
||||
return _space.Query(_bound).ToList();
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Tweening.Tests;
|
||||
|
||||
public class ColorHandler
|
||||
{
|
||||
public Color Color { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\cs\MonoGame.Extended.Tweening\MonoGame.Extended.Tweening.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cake.Frosting" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>()
|
||||
.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 {}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
public override void Run(BuildContext context)
|
||||
{
|
||||
DotNetRestoreSettings restoreSettings = new DotNetRestoreSettings()
|
||||
{
|
||||
Verbosity = DotNetVerbosity.Quiet
|
||||
};
|
||||
|
||||
context.DotNetRestore(context.SolutionPath, restoreSettings);
|
||||
}
|
||||
}
|
||||
@@ -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<BuildContext>
|
||||
{
|
||||
public override void Run(BuildContext context)
|
||||
{
|
||||
DotNetTestSettings testSettings = new DotNetTestSettings()
|
||||
{
|
||||
Configuration = "Release",
|
||||
Verbosity = DotNetVerbosity.Normal,
|
||||
NoLogo = true,
|
||||
NoBuild = true
|
||||
};
|
||||
|
||||
context.DotNetTest(context.SolutionPath, testSettings);
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>Animations to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame animations spritesheet sprite</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -24,4 +21,4 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="SpriteSheets\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
-40
@@ -1,7 +1,11 @@
|
||||
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
|
||||
{
|
||||
@@ -11,17 +15,49 @@ namespace MonoGame.Extended.Collisions
|
||||
/// </summary>
|
||||
public class CollisionComponent : SimpleGameComponent
|
||||
{
|
||||
private readonly Dictionary<ICollisionActor, QuadtreeData> _targetDataDictionary = new();
|
||||
public const string DEFAULT_LAYER_NAME = "default";
|
||||
|
||||
private readonly Quadtree _collisionTree;
|
||||
private Dictionary<string, Layer> _layers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a collision tree covering the specified area.
|
||||
/// List of collision's layers
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Layer> Layers => _layers;
|
||||
|
||||
private HashSet<(Layer, Layer)> _layerCollision = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates component with default layer, which is a collision tree covering the specified area (using <see cref="QuadTree"/>.
|
||||
/// </summary>
|
||||
/// <param name="boundary">Boundary of the collision tree.</param>
|
||||
public CollisionComponent(RectangleF boundary)
|
||||
{
|
||||
_collisionTree = new Quadtree(boundary);
|
||||
SetDefaultLayer(new Layer(new QuadTreeSpace(boundary)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates component with specifies default layer.
|
||||
/// If layer is null, method creates component without default layer.
|
||||
/// </summary>
|
||||
/// <param name="layer">Default layer</param>
|
||||
public CollisionComponent(Layer layer = null)
|
||||
{
|
||||
if (layer is not null)
|
||||
SetDefaultLayer(layer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main layer has the name from <see cref="DEFAULT_LAYER_NAME"/>.
|
||||
/// The main layer collision with itself and all other layers.
|
||||
/// </summary>
|
||||
/// <param name="layer">Layer to set default</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,28 +70,31 @@ namespace MonoGame.Extended.Collisions
|
||||
/// <param name="gameTime"></param>
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
// Detect collisions
|
||||
foreach (var value in _targetDataDictionary.Values)
|
||||
foreach (var layer in _layers.Values)
|
||||
layer.Reset();
|
||||
|
||||
foreach (var (firstLayer, secondLayer) in _layerCollision)
|
||||
foreach (var actor in firstLayer.Space)
|
||||
{
|
||||
value.RemoveFromAllParents();
|
||||
|
||||
var target = value.Target;
|
||||
var collisions =_collisionTree.Query(target.Bounds);
|
||||
|
||||
// Generate list of collision Infos
|
||||
var collisions = secondLayer.Space.Query(actor.Bounds.BoundingRectangle);
|
||||
foreach (var other in collisions)
|
||||
{
|
||||
var collisionInfo = new CollisionEventArgs
|
||||
{
|
||||
Other = other.Target,
|
||||
PenetrationVector = CalculatePenetrationVector(value.Bounds, other.Bounds)
|
||||
};
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
target.OnCollision(collisionInfo);
|
||||
}
|
||||
_collisionTree.Insert(value);
|
||||
}
|
||||
_collisionTree.Shake();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,12 +104,7 @@ namespace MonoGame.Extended.Collisions
|
||||
/// <param name="target">Target to insert.</param>
|
||||
public void Insert(ICollisionActor target)
|
||||
{
|
||||
if (!_targetDataDictionary.ContainsKey(target))
|
||||
{
|
||||
var data = new QuadtreeData(target);
|
||||
_targetDataDictionary.Add(target, data);
|
||||
_collisionTree.Insert(data);
|
||||
}
|
||||
_layers[target.LayerName ?? DEFAULT_LAYER_NAME].Space.Insert(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,25 +113,58 @@ namespace MonoGame.Extended.Collisions
|
||||
/// <param name="target">Target to remove.</param>
|
||||
public void Remove(ICollisionActor target)
|
||||
{
|
||||
if (_targetDataDictionary.ContainsKey(target))
|
||||
{
|
||||
var data = _targetDataDictionary[target];
|
||||
data.RemoveFromAllParents();
|
||||
_targetDataDictionary.Remove(target);
|
||||
_collisionTree.Shake();
|
||||
}
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Add the new layer. The name of layer must be unique.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of layer</param>
|
||||
/// <param name="layer">The new layer</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="name"/> is null</exception>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the target is inserted in the collision tree.
|
||||
/// Remove the layer and all layer's collisions.
|
||||
/// </summary>
|
||||
/// <param name="target">Actor to check if contained</param>
|
||||
/// <returns>True if the target is contained in the collision tree.</returns>
|
||||
public bool Contains(ICollisionActor target)
|
||||
/// <param name="name">The name of the layer to delete</param>
|
||||
/// <param name="layer">The layer to delete</param>
|
||||
public void Remove(string name = null, Layer layer = null)
|
||||
{
|
||||
return _targetDataDictionary.ContainsKey(target);
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
@@ -1,101 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collision grid. This is used to break the game world into
|
||||
/// chunks to detect collisions efficiently.
|
||||
/// </summary>
|
||||
public class CollisionGrid
|
||||
{
|
||||
private readonly CollisionGridCell[] _data;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collision grid of specified size.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="columns">Number of columns in the grid.</param>
|
||||
/// <param name="rows">Number of rows in the grid.</param>
|
||||
/// <param name="cellWidth">The width of each individual cell.</param>
|
||||
/// <param name="cellHeight">The height of each individual cell.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of columns in this grid.
|
||||
/// </summary>
|
||||
public int Columns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of rows in this grid.
|
||||
/// </summary>
|
||||
public int Rows { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of each cell.
|
||||
/// </summary>
|
||||
public int CellWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of each cell.
|
||||
/// </summary>
|
||||
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<CollisionGridCell> 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<ICollidable> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single cell in a collision grid.
|
||||
/// </summary>
|
||||
public class CollisionGridCell : ICollidable
|
||||
{
|
||||
private readonly CollisionGrid _parentGrid;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a collision grid cell at a location in the parent grid.
|
||||
/// </summary>
|
||||
/// <param name="parentGrid">The collision grid which this cell is a part of.</param>
|
||||
/// <param name="column">The column position of this cell.</param>
|
||||
/// <param name="row">The row position of this cell.</param>
|
||||
/// <param name="data"></param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Column in the parent grid that this cell represents.
|
||||
/// </summary>
|
||||
public int Column { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Row in the parent grid that this cell represents.
|
||||
/// </summary>
|
||||
public int Row { get; }
|
||||
public int Data { get; private set; }
|
||||
public object Tag { get; set; }
|
||||
public CollisionGridCellFlag Flag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box of the cell.
|
||||
/// </summary>
|
||||
public RectangleF BoundingBox => _parentGrid.GetCellRectangle(Column, Row).ToRectangleF();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
public enum CollisionGridCellFlag
|
||||
{
|
||||
Empty,
|
||||
Solid,
|
||||
Interesting
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// This class holds data on a collision. It is passed as a parameter to
|
||||
/// OnCollision methods.
|
||||
/// </summary>
|
||||
public class CollisionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the object being collided with.
|
||||
/// </summary>
|
||||
public ICollidable Other { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector representing the overlap between the two objects.
|
||||
/// </summary>
|
||||
public Vector2 PenetrationVector { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -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<CollisionActor> _actors;
|
||||
|
||||
private readonly Vector2 _gravity;
|
||||
private CollisionGrid _grid;
|
||||
|
||||
public CollisionWorld(Vector2 gravity)
|
||||
{
|
||||
_gravity = gravity;
|
||||
_actors = new List<CollisionActor>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// An actor that can be collided with.
|
||||
/// </summary>
|
||||
public interface ICollisionActor
|
||||
{
|
||||
/// <summary>
|
||||
/// A name of layer, which will contains this actor.
|
||||
/// If it equals null, an actor will insert into a default layer
|
||||
/// </summary>
|
||||
string LayerName { get => null; }
|
||||
|
||||
/// <summary>
|
||||
/// A bounds of an actor. It is using for collision calculating
|
||||
/// </summary>
|
||||
IShapeF Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// It will called, when collision with an another actor fires
|
||||
/// </summary>
|
||||
/// <param name="collisionInfo">Data about collision</param>
|
||||
void OnCollision(CollisionEventArgs collisionInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MonoGame.Extended.Collisions;
|
||||
|
||||
/// <summary>
|
||||
/// Interface, which split space for optimization of collisions.
|
||||
/// </summary>
|
||||
public interface ISpaceAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts the actor into the space.
|
||||
/// The actor will have its OnCollision called when collisions occur.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor to insert.</param>
|
||||
void Insert(ICollisionActor actor);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the actor into the space.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor to remove.</param>
|
||||
bool Remove(ICollisionActor actor);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the actor into the space.
|
||||
/// The actor will have its OnCollision called when collisions occur.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor to remove.</param>
|
||||
IEnumerable<ICollisionActor> Query(RectangleF boundsBoundingRectangle);
|
||||
|
||||
/// <summary>
|
||||
/// for foreach
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<ICollisionActor>.Enumerator GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Restructure the space with new positions.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Collisions.Layers;
|
||||
|
||||
/// <summary>
|
||||
/// Layer is a group of collision's actors.
|
||||
/// </summary>
|
||||
public class Layer
|
||||
{
|
||||
/// <summary>
|
||||
/// If this property equals true, layer always will reset collision space.
|
||||
/// </summary>
|
||||
public bool IsDynamic { get; set; } = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The space, which contain actors.
|
||||
/// </summary>
|
||||
public readonly ISpaceAlgorithm Space;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for layer
|
||||
/// </summary>
|
||||
/// <param name="spaceAlgorithm">A space algorithm for actors</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="spaceAlgorithm"/> is null</exception>
|
||||
public Layer(ISpaceAlgorithm spaceAlgorithm)
|
||||
{
|
||||
Space = spaceAlgorithm ?? throw new ArgumentNullException(nameof(spaceAlgorithm));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restructure a inner collection, if layer is dynamic, because actors can change own position
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
if (IsDynamic)
|
||||
Space.Reset();
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>Collisions to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame collisions</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -26,4 +23,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace MonoGame.Extended.Collisions
|
||||
{
|
||||
/// <summary>
|
||||
/// An actor that can be collided with.
|
||||
/// </summary>
|
||||
public interface ICollisionActor
|
||||
{
|
||||
IShapeF Bounds { get; }
|
||||
|
||||
void OnCollision(CollisionEventArgs collisionInfo);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MonoGame.Extended.Collisions
|
||||
namespace MonoGame.Extended.Collisions.QuadTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for doing collision handling with a quad tree.
|
||||
/// </summary>
|
||||
public class Quadtree
|
||||
public class QuadTree
|
||||
{
|
||||
/// <summary>
|
||||
/// The default maximum depth.
|
||||
@@ -21,7 +21,7 @@ namespace MonoGame.Extended.Collisions
|
||||
/// <summary>
|
||||
/// Contains the children of this node.
|
||||
/// </summary>
|
||||
protected List<Quadtree> Children = new List<Quadtree>();
|
||||
protected List<QuadTree> Children = new List<QuadTree>();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The bounds of the new quad tree.</param>
|
||||
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<Quadtree>();
|
||||
var process = new Queue<QuadTree>();
|
||||
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<Quadtree>();
|
||||
var process = new Queue<QuadTree>();
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear current node and all children
|
||||
/// </summary>
|
||||
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
|
||||
/// </summary>
|
||||
/// <param name="area">The area to query for overlapping targets</param>
|
||||
/// <returns>A unique list of targets intersected by area.</returns>
|
||||
public List<QuadtreeData> Query(IShapeF area)
|
||||
public List<QuadtreeData> Query(ref RectangleF area)
|
||||
{
|
||||
var recursiveResult = new List<QuadtreeData>();
|
||||
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<QuadtreeData> recursiveResult)
|
||||
private void QueryWithoutReset(ref RectangleF area, List<QuadtreeData> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MonoGame.Extended.Collisions;
|
||||
namespace MonoGame.Extended.Collisions.QuadTree;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for the quad tree.
|
||||
@@ -10,7 +10,7 @@ namespace MonoGame.Extended.Collisions;
|
||||
public class QuadtreeData
|
||||
{
|
||||
private readonly ICollisionActor _target;
|
||||
private readonly HashSet<Quadtree> _parents = new();
|
||||
private readonly HashSet<QuadTree> _parents = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of QuadTreeData.
|
||||
@@ -19,13 +19,14 @@ public class QuadtreeData
|
||||
public QuadtreeData(ICollisionActor target)
|
||||
{
|
||||
_target = target;
|
||||
Bounds = _target.Bounds.BoundingRectangle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a parent node.
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public void RemoveParent(Quadtree parent)
|
||||
public void RemoveParent(QuadTree parent)
|
||||
{
|
||||
_parents.Remove(parent);
|
||||
}
|
||||
@@ -34,9 +35,10 @@ public class QuadtreeData
|
||||
/// Add a parent node.
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public void AddParent(Quadtree parent)
|
||||
public void AddParent(QuadTree parent)
|
||||
{
|
||||
_parents.Add(parent);
|
||||
Bounds = _target.Bounds.BoundingRectangle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,7 +57,7 @@ public class QuadtreeData
|
||||
/// <summary>
|
||||
/// Gets the bounding box for collision detection.
|
||||
/// </summary>
|
||||
public IShapeF Bounds => _target.Bounds;
|
||||
public RectangleF Bounds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collision actor target.
|
||||
|
||||
@@ -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<ICollisionActor> _actors = new();
|
||||
private readonly Dictionary<ICollisionActor, QuadtreeData> _targetDataDictionary = new();
|
||||
|
||||
public QuadTreeSpace(RectangleF boundary)
|
||||
{
|
||||
_collisionTree = new QuadTree(boundary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the target into the collision tree.
|
||||
/// The target will have its OnCollision called when collisions occur.
|
||||
/// </summary>
|
||||
/// <param name="target">Target to insert.</param>
|
||||
public void Insert(ICollisionActor target)
|
||||
{
|
||||
if (!_targetDataDictionary.ContainsKey(target))
|
||||
{
|
||||
var data = new QuadtreeData(target);
|
||||
_targetDataDictionary.Add(target, data);
|
||||
_collisionTree.Insert(data);
|
||||
_actors.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the target from the collision tree.
|
||||
/// </summary>
|
||||
/// <param name="target">Target to remove.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restructure a inner collection, if layer is dynamic, because actors can change own position
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_collisionTree.ClearAll();
|
||||
foreach (var value in _targetDataDictionary.Values)
|
||||
{
|
||||
_collisionTree.Insert(value);
|
||||
}
|
||||
_collisionTree.Shake();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// foreach support
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<ICollisionActor>.Enumerator GetEnumerator() => _actors.GetEnumerator();
|
||||
|
||||
/// <inheritdoc cref="QuadTree.Query"/>
|
||||
public IEnumerable<ICollisionActor> Query(RectangleF boundsBoundingRectangle)
|
||||
{
|
||||
return _collisionTree.Query(ref boundsBoundingRectangle).Select(x => x.Target);
|
||||
}
|
||||
}
|
||||
@@ -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<int, List<ICollisionActor>> _dictionary = new();
|
||||
private readonly List<ICollisionActor> _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<ICollisionActor> Query(RectangleF boundsBoundingRectangle)
|
||||
{
|
||||
var results = new HashSet<ICollisionActor>();
|
||||
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<ICollisionActor>.Enumerator GetEnumerator() => _actors.GetEnumerator();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_dictionary.Clear();
|
||||
foreach (var actor in _actors)
|
||||
InsertToHash(actor);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@ namespace MonoGame.Extended.Content.Pipeline
|
||||
|
||||
public ExternalReference<TInput> GetExternalReference<TInput>(string source)
|
||||
{
|
||||
if (_externalReferences.TryGetValue(source, out var contentItem))
|
||||
if (source is not null && _externalReferences.TryGetValue(source, out var contentItem))
|
||||
return contentItem as ExternalReference<TInput>;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>Content Pipeline importers and processors to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame content importer processor reader tiled texturepacker bmfont animations</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -45,4 +42,4 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -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<T>: ContentItem<T>
|
||||
{
|
||||
public TiledContentItem(T data) : base(data)
|
||||
{
|
||||
}
|
||||
|
||||
public void BuildExternalReference<T>(ContentProcessorContext context, TiledMapImageContent image)
|
||||
{
|
||||
var parameters = new OpaqueDataDictionary
|
||||
{
|
||||
{ "ColorKeyColor", image.TransparentColor },
|
||||
{ "ColorKeyEnabled", true }
|
||||
};
|
||||
BuildExternalReference<Texture2DContent>(context, image.Source, parameters);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace MonoGame.Extended.Content.Pipeline.Tiled
|
||||
{
|
||||
public class TiledMapContentItem : ContentItem<TiledMapContent>
|
||||
public class TiledMapContentItem : TiledContentItem<TiledMapContent>
|
||||
{
|
||||
public TiledMapContentItem(TiledMapContent data)
|
||||
public TiledMapContentItem(TiledMapContent data)
|
||||
: base(data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[ContentProcessor(DisplayName = "Tiled Map Processor - MonoGame.Extended")]
|
||||
public class TiledMapProcessor : ContentProcessor<TiledMapContentItem, TiledMapContentItem>
|
||||
@@ -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<Texture2DContent>(tileset.Image.Source);
|
||||
var parameters = new OpaqueDataDictionary
|
||||
{
|
||||
{ "ColorKeyColor", tileset.Image.TransparentColor },
|
||||
{ "ColorKeyEnabled", true }
|
||||
};
|
||||
//tileset.Image.ContentRef = context.BuildAsset<Texture2DContent, Texture2DContent>(externalReference, "", parameters, "", "");
|
||||
contentItem.BuildExternalReference<Texture2DContent>(context, tileset.Image.Source, parameters);
|
||||
contentItem.BuildExternalReference<Texture2DContent>(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<Texture2DContent>(imageLayer.Image.Source);
|
||||
var parameters = new OpaqueDataDictionary
|
||||
{
|
||||
{ "ColorKeyColor", imageLayer.Image.TransparentColor },
|
||||
{ "ColorKeyEnabled", true }
|
||||
};
|
||||
//imageLayer.Image.ContentRef = context.BuildAsset<Texture2DContent, Texture2DContent>(externalReference, "", parameters, "", "");
|
||||
contentItem.BuildExternalReference<Texture2DContent>(context, imageLayer.Image.Source, parameters);
|
||||
contentItem.BuildExternalReference<Texture2DContent>(context, imageLayer.Image);
|
||||
ContentLogger.Log($"Processed image layer '{imageLayer.Name}'");
|
||||
break;
|
||||
|
||||
@@ -335,4 +321,4 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TiledMapTilesetContent>
|
||||
public class TiledMapTilesetContentItem : TiledContentItem<TiledMapTilesetContent>
|
||||
{
|
||||
public TiledMapTilesetContentItem(TiledMapTilesetContent data)
|
||||
public TiledMapTilesetContentItem(TiledMapTilesetContent data)
|
||||
: base(data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Texture2DContent>(tileset.Image.Source);
|
||||
var parameters = new OpaqueDataDictionary
|
||||
{
|
||||
{ "ColorKeyColor", tileset.Image.TransparentColor },
|
||||
{ "ColorKeyEnabled", true }
|
||||
};
|
||||
//tileset.Image.ContentRef = context.BuildAsset<Texture2DContent, Texture2DContent>(externalReference, "", parameters, "", "");
|
||||
contentItem.BuildExternalReference<Texture2DContent>(context, tileset.Image.Source, parameters);
|
||||
if (tileset.Image is not null)
|
||||
contentItem.BuildExternalReference<Texture2DContent>(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<Texture2DContent>(context, tile.Image);
|
||||
}
|
||||
|
||||
ContentLogger.Log($"Processed tileset '{tileset.Name}'");
|
||||
|
||||
@@ -30,9 +30,9 @@ namespace MonoGame.Extended.Content.Pipeline.Tiled
|
||||
|
||||
public static void WriteTileset(ContentWriter writer, TiledMapTilesetContent tileset, IExternalReferenceRepository externalReferenceRepository)
|
||||
{
|
||||
var externalReference = externalReferenceRepository.GetExternalReference<Texture2DContent>(tileset.Image.Source);
|
||||
|
||||
var externalReference = externalReferenceRepository.GetExternalReference<Texture2DContent>(tileset.Image?.Source);
|
||||
writer.WriteExternalReference(externalReference);
|
||||
|
||||
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<Texture2DContent>(tilesetTile.Image?.Source);
|
||||
writer.WriteExternalReference(externalReference);
|
||||
|
||||
writer.Write(tilesetTile.LocalIdentifier);
|
||||
writer.Write(tilesetTile.Type);
|
||||
writer.Write(tilesetTile.Frames.Count);
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>An Entity Component System to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame ecs entity component system</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -25,4 +22,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>Graphics makes MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame graphics batcher effects</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -46,4 +43,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>A GUI system for MonoGame written from the ground up to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame gui</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -27,4 +24,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>An event based input system to MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame input event based listeners</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -26,4 +23,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>A high performance particle system to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame particle system</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -30,4 +27,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>Support for Tiled maps to make MonoGame more awesome. See http://www.mapeditor.org</Description>
|
||||
<PackageTags>monogame tiled maps orthographic isometric</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -30,4 +27,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -58,7 +59,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TiledMapTilesetTileAnimationFrame> 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,25 +29,35 @@ namespace MonoGame.Extended.Tiled
|
||||
var tileset = new TiledMapTileset(texture, 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));
|
||||
|
||||
reader.ReadTiledMapProperties(tilesetTile.Properties);
|
||||
tileset.Tiles.Add(tilesetTile);
|
||||
}
|
||||
|
||||
ReadTile(reader, tileset);
|
||||
|
||||
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<Texture2D>();
|
||||
|
||||
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 +74,7 @@ namespace MonoGame.Extended.Tiled
|
||||
|
||||
private static TiledMapTilesetTile ReadTiledMapTilesetTile(ContentReader reader, TiledMapTileset tileset, Func<TiledMapObject[], TiledMapTilesetTile> createTile)
|
||||
{
|
||||
var texture = reader.ReadExternalReference<Texture2D>();
|
||||
var objectCount = reader.ReadInt32();
|
||||
var objects = new TiledMapObject[objectCount];
|
||||
|
||||
|
||||
@@ -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<TiledMapObject>(objects) : new List<TiledMapObject>();
|
||||
Properties = new TiledMapProperties();
|
||||
}
|
||||
|
||||
public int LocalTileIdentifier { get; }
|
||||
public string Type { get; }
|
||||
public TiledMapProperties Properties { get; }
|
||||
public List<TiledMapObject> Objects { get; }
|
||||
public Texture2D Texture { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Tweening;
|
||||
|
||||
public class ColorTween: Tween<Color>
|
||||
{
|
||||
internal ColorTween(object target, float duration, float delay, TweenMember<Color> member, Color endValue) : base(target, duration, delay, member, endValue)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Interpolate(float n)
|
||||
{
|
||||
Member.Value = Color.Lerp(_startValue, _endValue, n);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace MonoGame.Extended.Tweening;
|
||||
|
||||
public class LinearOperations<T>
|
||||
{
|
||||
static LinearOperations()
|
||||
{
|
||||
var a = Expression.Parameter(typeof(T));
|
||||
var b = Expression.Parameter(typeof(T));
|
||||
var c = Expression.Parameter(typeof(float));
|
||||
Add = Expression.Lambda<Func<T, T, T>>(Expression.Add(a, b), a, b).Compile();
|
||||
Subtract = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(a, b), a, b).Compile();
|
||||
Multiply = Expression.Lambda<Func<T, float, T>>(Expression.Multiply(a, c), a, c).Compile();
|
||||
}
|
||||
|
||||
public static Func<T, T, T> Add { get; }
|
||||
public static Func<T, T, T> Subtract { get; }
|
||||
public static Func<T, float, T> Multiply { get; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace MonoGame.Extended.Tweening;
|
||||
|
||||
public class LinearTween<T>: Tween<T>
|
||||
where T: struct
|
||||
{
|
||||
private T _range;
|
||||
|
||||
internal LinearTween(object target, float duration, float delay, TweenMember<T> member, T endValue) : base(target, duration, delay, member, endValue)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_range = LinearOperations<T>.Subtract(_endValue, _startValue);
|
||||
}
|
||||
|
||||
protected override void Interpolate(float n)
|
||||
{
|
||||
var value = LinearOperations<T>.Add(_startValue, LinearOperations<T>.Multiply(_range, n));
|
||||
Member.Value = value;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>A tweening system to make MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame animations tweening</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -25,4 +22,4 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -3,10 +3,10 @@ using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Tweening
|
||||
{
|
||||
public class Tween<T> : Tween
|
||||
where T : struct
|
||||
public abstract class Tween<T> : Tween
|
||||
where T : struct
|
||||
{
|
||||
internal Tween(object target, float duration, float delay, TweenMember<T> member, T endValue)
|
||||
internal Tween(object target, float duration, float delay, TweenMember<T> member, T endValue)
|
||||
: base(target, duration, delay)
|
||||
{
|
||||
Member = member;
|
||||
@@ -16,20 +16,12 @@ namespace MonoGame.Extended.Tweening
|
||||
public TweenMember<T> 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<T>.Subtract(_endValue, _startValue);
|
||||
}
|
||||
|
||||
protected override void Interpolate(float n)
|
||||
{
|
||||
var value = TweenMember<T>.Add(_startValue, TweenMember<T>.Multiply(_range, n));
|
||||
Member.Value = value;
|
||||
}
|
||||
|
||||
protected override void Swap()
|
||||
@@ -189,4 +181,4 @@ namespace MonoGame.Extended.Tweening
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,15 @@ namespace MonoGame.Extended.Tweening
|
||||
}
|
||||
|
||||
public abstract class TweenMember<T> : 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<Func<T, T, T>>(Expression.Add(a, b), a, b).Compile();
|
||||
Subtract = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(a, b), a, b).Compile();
|
||||
Multiply = Expression.Lambda<Func<T, float, T>>(Expression.Multiply(a, c), a, c).Compile();
|
||||
}
|
||||
|
||||
public static Func<T, T, T> Add { get; }
|
||||
public static Func<T, T, T> Subtract { get; }
|
||||
public static Func<T, float, T> Multiply { get; }
|
||||
|
||||
protected TweenMember(object target, Func<object, object> getMethod, Action<object, object> setMethod)
|
||||
: base(target)
|
||||
{
|
||||
_getMethod = getMethod;
|
||||
_setMethod = setMethod;
|
||||
_setMethod = setMethod;
|
||||
}
|
||||
|
||||
|
||||
private readonly Func<object, object> _getMethod;
|
||||
private readonly Action<object, object> _setMethod;
|
||||
|
||||
@@ -48,4 +34,4 @@ namespace MonoGame.Extended.Tweening
|
||||
set { _setMethod(Target, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Tweening
|
||||
{
|
||||
@@ -26,6 +27,21 @@ namespace MonoGame.Extended.Tweening
|
||||
public Tween<TMember> TweenTo<TTarget, TMember>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
|
||||
where TTarget : class
|
||||
where TMember : struct
|
||||
{
|
||||
switch (toValue)
|
||||
{
|
||||
case Color toValueColor:
|
||||
return (Tween<TMember>)(object)TweenTo<TTarget, Color, ColorTween>(target, expression as Expression<Func<TTarget, Color>>, toValueColor, duration, delay);
|
||||
default:
|
||||
return TweenTo<TTarget, TMember, LinearTween<TMember>>(target, expression, toValue, duration, delay);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Tween<TMember> TweenTo<TTarget, TMember, TTween>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
|
||||
where TTarget : class
|
||||
where TMember : struct
|
||||
where TTween : Tween<TMember>
|
||||
{
|
||||
var memberExpression = (MemberExpression)expression.Body;
|
||||
var memberInfo = memberExpression.Member;
|
||||
@@ -35,7 +51,9 @@ namespace MonoGame.Extended.Tweening
|
||||
activeTween?.Cancel();
|
||||
|
||||
AllocationCount++;
|
||||
var tween = new Tween<TMember>(target, duration, delay, member, toValue);
|
||||
var tween = (TTween)Activator.CreateInstance(typeof(TTween),
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
|
||||
new object?[]{target, duration, delay, member, toValue}, null);
|
||||
_activeTweens.Add(tween);
|
||||
return tween;
|
||||
}
|
||||
@@ -112,4 +130,4 @@ namespace MonoGame.Extended.Tweening
|
||||
throw new InvalidOperationException($"'{memberName}' is not a property or field of the target");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace MonoGame.Extended
|
||||
return new HslColor(
|
||||
c1.H + t*(h2 - c1.H),
|
||||
c1.S + t*(c2.S - c1.S),
|
||||
c1.L + t*(c2.L - c2.L));
|
||||
c1.L + t*(c2.L - c1.L));
|
||||
}
|
||||
|
||||
public static HslColor FromRgb(Color color)
|
||||
@@ -267,4 +267,4 @@ namespace MonoGame.Extended
|
||||
return new HslColor(h, s, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,16 @@ namespace MonoGame.Extended
|
||||
set => Center = value;
|
||||
}
|
||||
|
||||
public RectangleF BoundingRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
var minX = Center.X - Radius;
|
||||
var minY = Center.Y - Radius;
|
||||
return new RectangleF(minX, minY, Diameter, Diameter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance from a point to the opposite point, both on the boundary of this <see cref="CircleF" />.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77
|
||||
|
||||
/// <summary>
|
||||
/// An axis-aligned, four sided, two dimensional box defined by a top-left position (<see cref="X" /> and
|
||||
@@ -97,6 +97,8 @@ namespace MonoGame.Extended
|
||||
}
|
||||
}
|
||||
|
||||
public RectangleF BoundingRectangle => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Size2" /> representing the extents of this <see cref="RectangleF" />.
|
||||
/// </summary>
|
||||
@@ -227,13 +229,13 @@ namespace MonoGame.Extended
|
||||
/// <param name="transformMatrix">The transform matrix.</param>
|
||||
/// <param name="result">The resulting transformed rectangle.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="BoundingRectangle" /> from the <paramref name="rectangle" /> transformed by the
|
||||
/// The <see cref="Extended.BoundingRectangle" /> from the <paramref name="rectangle" /> transformed by the
|
||||
/// <paramref name="transformMatrix" />.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a transformed <see cref="BoundingRectangle" /> is used for <paramref name="rectangle" /> then the
|
||||
/// resulting <see cref="BoundingRectangle" /> will have the compounded transformation, which most likely is
|
||||
/// If a transformed <see cref="Extended.BoundingRectangle" /> is used for <paramref name="rectangle" /> then the
|
||||
/// resulting <see cref="Extended.BoundingRectangle" /> will have the compounded transformation, which most likely is
|
||||
/// not desired.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
@@ -252,20 +254,20 @@ namespace MonoGame.Extended
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="RectangleF" /> from the specified <see cref="BoundingRectangle" /> transformed by
|
||||
/// Computes the <see cref="RectangleF" /> from the specified <see cref="Extended.BoundingRectangle" /> transformed by
|
||||
/// the
|
||||
/// specified <see cref="Matrix2" />.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The bounding rectangle.</param>
|
||||
/// <param name="transformMatrix">The transform matrix.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="BoundingRectangle" /> from the <paramref name="rectangle" /> transformed by the
|
||||
/// The <see cref="Extended.BoundingRectangle" /> from the <paramref name="rectangle" /> transformed by the
|
||||
/// <paramref name="transformMatrix" />.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a transformed <see cref="BoundingRectangle" /> is used for <paramref name="rectangle" /> then the
|
||||
/// resulting <see cref="BoundingRectangle" /> will have the compounded transformation, which most likely is
|
||||
/// If a transformed <see cref="Extended.BoundingRectangle" /> is used for <paramref name="rectangle" /> then the
|
||||
/// resulting <see cref="Extended.BoundingRectangle" /> will have the compounded transformation, which most likely is
|
||||
/// not desired.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
/// Gets or sets the position of the shape.
|
||||
/// </summary>
|
||||
Point2 Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets escribed rectangle, which lying outside the shape
|
||||
/// </summary>
|
||||
RectangleF BoundingRectangle { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,4 +66,4 @@
|
||||
return circle.Contains(closestPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Authors>craftworkgames</Authors>
|
||||
<Description>It makes MonoGame more awesome.</Description>
|
||||
<PackageTags>monogame extended pipeline bmfont tiled texture atlas input viewport fps shapes sprite</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/craftworkgames/MonoGame.Extended</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/craftworkgames/MonoGame.Extended</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/craftworkgames/MonoGame.Extended/master/Logos/logo-nuget-128.png</PackageIconUrl>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -26,5 +23,5 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace MonoGame.Extended.Serialization
|
||||
Converters.Add(new RangeJsonConverter<float>());
|
||||
Converters.Add(new RangeJsonConverter<HslColor>());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<float>();
|
||||
return new RectangleF(values[0], values[1], values[2], values[3]);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(RectangleF);
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,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 +331,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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TestContent>(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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user