Skip to content

About

collision with SAT to use with XNA / Monogame

Resources

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

XNA MonoGame SAT System

A small 2D collision demo built with MonoGame, .NET 9, and the Separating Axis Theorem (SAT). The project includes rotatable rectangles, circles, collision states, and an obstacle that prevents the player-controlled rectangle from passing through it.

Use the arrow keys to move the player rectangle. Yellow indicates Touching, and red indicates Overlapping.

Project demo

What is SAT?

The Separating Axis Theorem is a technique for detecting collisions between convex polygons. For every relevant axis, usually an edge normal, the algorithm projects the vertices of both polygons onto that axis and compares the resulting intervals.

  • If at least one axis has a gap between the intervals, the objects are Separated.
  • If the intervals only meet at their boundaries, the state is Touching.
  • If the intervals overlap on every tested axis, the state is Overlapping.

Two rotated rectangles must test the axes from both rectangles. For circle–rectangle collision, the implementation checks whether the circle center is inside the rectangle and, if it is not, measures the distance to the nearest edge. Although the geometry calculations differ, both approaches return the same CollisionKind.

Collision states

State Meaning
Separated There is a gap between the colliders.
Touching The colliders meet without penetration.
Overlapping The colliders penetrate each other.

SatCollider.Intersects is available when a boolean result is enough and any result other than Separated counts as an intersection.

Run locally

Requirements

  • .NET SDK 9 (9.0.200 or later is required to build the .slnx file from the command line)
  • An environment supported by MonoGame DesktopGL: Windows, macOS, or Linux

From the repository root, run:

cd XNA-Monogame-Sat-System
dotnet tool restore --tool-manifest Desktop/.config/dotnet-tools.json
dotnet run --project Desktop/Desktop.csproj

The first command restores MGCB, which builds the MonoGame content assets. It is local to the project, so no global MonoGame or MGCB installation is required.

The DesktopGL host is Desktop/Desktop.csproj and targets net9.0. The modern solution file is XNA Monogame Sat System/XNA Monogame Sat System.slnx; open it with a recent version of Visual Studio, Rider, or VS Code. Building the .slnx file from the command line requires .NET SDK 9.0.200 or later. With an earlier .NET 9 SDK, build or run the .csproj directly.

Project structure

Path Responsibility
Desktop/ DesktopGL host, manifest, icons, and local tools.
XNA Monogame Sat System/GameTester.cs Creates, updates, and draws the demo scene.
XNA Monogame Sat System/PlayerControlledRectangle.cs Controls the player rectangle and resolves movement blocking.
XNA Monogame Sat System/VerticallyOscillatingCircle.cs Updates the vertically moving circle.
XNA Monogame Sat System/Sat/ Colliders, SAT logic, collision states, and the collision world.

Demo flow

The following sequence describes one frame of the demo and points to the source files that perform each part of the work:

  1. GameTester creates the player rectangle, the rotating rectangle, the static rectangle, and the circles.
  2. The colliders are registered in CollisionWorld.Instance. The static rectangle receives BlocksMovement = true; the other objects still detect collisions but do not block the player.
  3. Every frame, PlayerControlledRectangle.Update() reads the arrow keys, tries to move the player along the X axis and then the Y axis, and applies its rotation.
  4. Before accepting a movement or rotation, PlayerControlledRectangle queries CollisionWorld. The world checks only colliders marked with BlocksMovement and calls GetCollisionKind.
  5. If an attempted change would result in Overlapping, the component performs a binary search between the previous and attempted values. It stops at the last valid value—usually Touching—which lets the player slide along an obstacle edge.
  6. In Sat/CollisionWorld.cs, HasBlockingOverlap calls collider.GetCollisionKind(otherCollider). The concrete collider selects the appropriate collision calculation; this is the entry point into the SAT implementation.
  7. For two rectangles, Sat/SatRectangle.cs calls GetRectangleCollisionKind. It first calls RecalculateGeometry for both rectangles, producing their four world-space vertices and two edge-normal axes.
  8. GetPolygonCollisionKind then tests both sets of axes. This is the core SAT step: an axis from the first rectangle is tested, then an axis from the other rectangle is tested. Testing both is necessary because a separating gap may exist only on an axis from the other rotated rectangle.
  9. For each axis, GetProjectionCollisionKind calls Project. Project calculates the dot product of every vertex with that axis and returns the smallest and largest values: the interval occupied by that polygon on the axis. ClassifySignedDistance in Sat/SatCollider.cs converts the interval comparison into Separated, Touching, or Overlapping. A single Separated axis ends the SAT test immediately.
  10. Rectangle–circle collision uses GetCircleCollisionKind in Sat/SatRectangle.cs instead. It checks whether the circle center is inside the rectangle; otherwise, it finds the nearest rectangle edge and compares that distance with the circle radius. Circle–circle collision is handled by GetCircleCollisionKind in Sat/SatCircle.cs, which compares the distance between centers with the combined radii.
  11. Back in GameTester, UpdateCollisionColors evaluates the scene collisions and chooses a color for each object. Finally, Draw renders the instructions, FPS counter, and colliders.

Steps 7 through 9 are where the Separating Axis Theorem is applied. Reading SatRectangle.GetRectangleCollisionKind, GetPolygonCollisionKind, GetProjectionCollisionKind, and Project in that order follows the complete rectangle–rectangle SAT path in the code.

Player rectangle control

PlayerControlledRectangle is a gameplay helper. It encapsulates a SatRectangle in its Collider property and keeps player-specific behavior out of GameTester:

  • keyboard input;
  • axis-by-axis movement;
  • rotation;
  • CollisionWorld queries;
  • prevention of Overlapping against obstacles;
  • drawing and color changes for its collider.

Because of this, scene code does not have to pass an obstacle on every update:

_playerRectangle.Update();

To make a collider block movement, mark it and register it in the world:

wall.BlocksMovement = true;
CollisionWorld.Instance.Register(wall);

Basic collider usage

var firstRectangle = new SatRectangle(position, size, GraphicsDevice);
var secondRectangle = new SatRectangle(otherPosition, otherSize, GraphicsDevice);

var collisionKind = firstRectangle.GetCollisionKind(secondRectangle);

if (collisionKind == CollisionKind.Overlapping)
{
    // React to the objects penetrating each other.
}

SatCircle exposes the same public collision API. The concrete shape decides internally which geometry calculation to use.

Demo-only visual elements

Colors, on-screen text, and the FPS counter exist to make the collision logic easy to observe:

  • yellow for Touching;
  • red for Overlapping;
  • default colors for Separated objects.

These colors are not a required part of a game. In a real project, the same collision state could trigger damage, scoring, physics, sound, events, or removal of an element from the screen. This solution separates detection (SatCollider and CollisionWorld) from the visual response used by the demo.

License

MIT License.

Authors

About

collision with SAT to use with XNA / Monogame

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages