Meatcorps.Engine.Collision
0.1.4
See the version list below for details.
dotnet add package Meatcorps.Engine.Collision --version 0.1.4
NuGet\Install-Package Meatcorps.Engine.Collision -Version 0.1.4
<PackageReference Include="Meatcorps.Engine.Collision" Version="0.1.4" />
<PackageVersion Include="Meatcorps.Engine.Collision" Version="0.1.4" />
<PackageReference Include="Meatcorps.Engine.Collision" />
paket add Meatcorps.Engine.Collision --version 0.1.4
#r "nuget: Meatcorps.Engine.Collision, 0.1.4"
#:package Meatcorps.Engine.Collision@0.1.4
#addin nuget:?package=Meatcorps.Engine.Collision&version=0.1.4
#tool nuget:?package=Meatcorps.Engine.Collision&version=0.1.4
Meatcorps.Engine.Collision — Module Readme
What this module is
A small, fast collision framework for arcade games.
World orchestrates; providers do the geometry + resolution; the spatial resource (your external grid/quadtree) owns broad‑phase. Sensors, layers, kinematic bodies, and deterministic ordering are built in.
Responsibilities (SRP)
WorldService (orchestration only)
- Integrates bodies (velocity, damping, gravity, movement‑angle snapping).
- Asks
IWorldEntityResourcefor broad‑phase candidates. - Filters by layer/mask.
- Runs providers for narrow‑phase + resolution.
- Emits
ICollisionEvents(Enter/Stay/Exit) to 1..N sinks. - Tracks contact pairs per frame for phase diffs.
Providers (geometry + resolution)
- Narrow‑phase test and manifold creation.
- Apply MTV based on an
IResolutionPolicy(pluggable). - May veto resolution (e.g., one‑way gate) even if overlapping.
IWorldEntityResource (broad‑phase only)
Add/Remove/Movebodies.Query(RectF)/Query(Vector2)returns candidate bodies.
Bodies & Colliders
IBody= kinematic data (position, velocity, mass…) + list of colliders.ICollider= shape + flags (IsSensor,Layer,CollisionMask,Enabled).- Rect‑only phase:
RectColliderwithLocalRectand computedWorldRect.
Directory layout (quick map)
Meatcorps.Engine.Collision
├─ Abstractions
│ ├─ BaseCollideProvider.cs
│ └─ BaseRectRectProvider.cs // default Rect×Rect narrow + resolve, veto hook
├─ Colliders
│ └─ RectCollider.cs
├─ Data
│ ├─ ColliderSet.cs // unordered type-pair key
│ ├─ ContactManifold.cs // Normal, Penetration, OverlapArea
│ ├─ ContactPair.cs
│ └─ ICollisionEvents.cs
├─ Enums
│ ├─ BodyType.cs
│ └─ ContactPhase.cs
├─ Extensions
│ └─ IColliderExtensions.cs // optional helpers
├─ Interfaces
│ ├─ IBody.cs, ICollider.cs
│ ├─ IColliderProvider.cs
│ ├─ IInternalBody.cs // internal setter for StableIndex
│ ├─ IResolutionPolicy.cs
│ ├─ IWorldEntityResource.cs
│ └─ IWorldService.cs
├─ Providers
│ ├─ Bodies/Body.cs // basic IBody impl
│ ├─ Colliders
│ │ ├─ OneWayGateProvider.cs // veto example
│ │ └─ RectRectProvider.cs // concrete Rect×Rect using base logic
│ └─ DefaultResolutionPolicy.cs
├─ Services
│ ├─ ColliderProviderRegistry.cs // type-pair -> provider
│ └─ WorldService.cs
└─ Utilities
└─ LayerBits.cs // enum -> mask sugar
Lifecycle (per frame)
Integrate dynamic/kinematic bodies
- apply gravity (
GravityScale), limit speed (MaxSpeed), damping (LinearDamping), snap velocity direction (MovementConstraintAngle= 0 free, 45 for 8‑way, 90 for 4‑way). IWorldEntityResource.Move(body, body.Position)
- apply gravity (
Candidates
candidates = worldResource.Query(body.BoundingBox)(AABB union of colliders).
Filter
- Skip self, disabled, Static–Static.
- Layer & mask:
(a.Layer & b.CollisionMask) != 0and vice versa.
Sensors vs Contacts
- If
a.IsSensor || b.IsSensor→ mark as trigger pair (no resolution). - Else → provider dispatch via
ColliderSet(unordered type pair).provider.CollideWith(a, b, out manifold)- If hit →
provider.Solve(a, b, manifold, policy).- Provider may veto (returns false) for one‑way gates, etc.
- If
Events
- World diffs current pairs vs previous:
- Enter = new this frame
- Stay = in both
- Exit = missing this frame
- Triggers call
ICollisionEvents.OnTrigger(phase, pair) - Contacts call
ICollisionEvents.OnContact(phase, pair, manifold?)
- World diffs current pairs vs previous:
Determinism: bodies processed by StableIndex (assigned on registration). ContactPair stores ordered (A,B) by stable ordering.
How to register (fluent, factory‑style)
// Build providers
var policy = new DefaultResolutionPolicy();
var providers = new ColliderProviderRegistry()
.Register(new RectRectProvider(policy))
.Register(new OneWayGateProvider(policy, allowedNormal: new Vector2(1,0), degreesTolerance: 60));
// Build world
var world = new WorldService(spatial, providers)
.SetGravity(Vector2.Zero)
.SetResolutionPolicy(policy)
.AddCollisionEvents(globalSink1)
.AddCollisionEvents(globalSink2);
// Create a body + collider
var body = new Body(world)
.SetType(BodyType.Dynamic)
.SetMass(1)
.SetMaxSpeed(180)
.SetMovementConstraintAngle(90); // 4-way
var col = new RectCollider(body, new RectF(-8,-8,16,16))
.SetLayer(LayerBits.Bit(CollisionLayer.Player))
.SetMask(LayerBits.MaskOf(CollisionLayer.Walls, CollisionLayer.Pellets, CollisionLayer.Ghost));
body.AddCollider(col);
world.RegisterBody(body);
Providers (default + extensibility)
BaseRectRectProvider (default) centralizes Rect×Rect logic:
- Uses
RectF.Intersects(ref,ref)andRectF.Intersection(ref,ref). - Builds MTV on shallow axis.
- Applies MTV according to
IResolutionPolicy(DefaultResolutionPolicyrules: push dynamic vs static; split by mass for dynamic vs dynamic). - Hook
OnAdditionalResolve(a,b,manifold)→ returnfalseto veto (one‑way gate, portals, etc.). - Hook
OnResolved(...)for side effects.
OneWayGateProvider example:
- Overrides
OnAdditionalResolveto allow contact only if the mover’s velocity direction matches an allowed normal within a tolerance.
Layers & masks (enum‑friendly)
- Each collider has
Layer : uint,CollisionMask : uint. - Use
LayerBitsto set them from enums:
public enum CollisionLayer { Walls=0, Pellets=1, Player=2, Ghost=3, Gate=4, Sensor=5 }
var playerLayer = LayerBits.Bit(CollisionLayer.Player);
var playerMask = LayerBits.MaskOf(CollisionLayer.Walls, CollisionLayer.Pellets, CollisionLayer.Ghost, CollisionLayer.Gate);
collider.SetLayer(playerLayer).SetMask(playerMask);
Sensors
ICollider.IsSensor == true→ no resolution, only triggers (Enter/Stay/Exit).- Pellets, energizers, tunnel triggers, doors as sensors, etc.
Performance & determinism
- Broad‑phase is external (
IWorldEntityResource) → use a uniform grid for tile games; swap later if needed without touching World. - No allocations in the hot path: reuse lists/sets where possible.
- StableIndex + ordered
ContactPairensures deterministic iteration and hashing.
Extension points
- New shapes: add new collider type + provider (world does not change).
- New policies: implement
IResolutionPolicy(e.g., different mass rules). - Custom events: add another
ICollisionEventssink. - Conditional behaviors: override provider’s
OnAdditionalResolve.
Testing tips
- Unit test providers in isolation: feed rectangles, assert MTV and veto rules.
- Unit test world with a simple fake
IWorldEntityResourcethat returns controlled candidates. - Snapshot tests for Enter/Stay/Exit sequences on scripted scenarios.
Future roadmap (when needed)
- Per‑collider local rotation (OBB) → SAT in providers; world unchanged.
- Raycasts/segment casts as optional queries in
IWorldService. - Sleep heuristics (optional) for large scenes.
Gotchas to avoid
- Don’t put resolution back into
WorldService— keep SRP. - Don’t include
IsSensorinColliderSetkeys — provider math doesn’t change; only behavior does. - Ensure type‑ids in
ColliderProviderRegistryare stable during the run (use a registry counter, notGetHashCode()).
That’s the snapshot. If you want, I can generate a tiny usage sample folder (Program + FakeSpatial + ConsoleEvents) that runs two bodies hitting a wall, a pellet trigger, and a one‑way gate, so you’ve got a runnable smoke test for the module.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Meatcorps.Engine.Core (>= 0.1.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.17-preview.24 | 30 | 3/11/2026 |
| 0.1.17-preview.23 | 32 | 3/11/2026 |
| 0.1.17-preview.22 | 33 | 3/9/2026 |
| 0.1.17-preview.21 | 35 | 3/9/2026 |
| 0.1.17-preview.20 | 36 | 3/5/2026 |
| 0.1.17-preview.19 | 38 | 2/25/2026 |
| 0.1.17-preview.18 | 42 | 2/25/2026 |
| 0.1.17-preview.17 | 42 | 2/24/2026 |
| 0.1.16 | 91 | 2/20/2026 |
| 0.1.15 | 95 | 2/14/2026 |
| 0.1.14 | 92 | 2/14/2026 |
| 0.1.13 | 93 | 2/14/2026 |
| 0.1.11 | 95 | 2/14/2026 |
| 0.1.10 | 93 | 2/14/2026 |
| 0.1.9 | 101 | 1/12/2026 |
| 0.1.8 | 97 | 1/11/2026 |
| 0.1.7 | 95 | 1/10/2026 |
| 0.1.6 | 99 | 1/10/2026 |
| 0.1.5 | 100 | 1/10/2026 |
| 0.1.4 | 103 | 1/4/2026 |