Table of Contents

Box2D.NET Physics

A 2D simulation run by Box2D.NET rather than by Stride's own physics, with Stride reduced to drawing the result. That split is the whole lesson: the physics world steps on a fixed timestep of its own, and entity transforms are copied from body poses afterwards, so nothing in the scene graph is allowed to move a body directly. The same pattern applies to any external simulation you want to drive a Stride scene with.

The Program.cs file shows how to:

  • Hosting an external physics world alongside Stride
  • Stepping a simulation on a fixed timestep, independent of frame rate
  • Creating dynamic and static bodies in Box2D
  • Copying body poses onto entity transforms each frame
  • Why the scene graph must not write back to the simulation
  • Requires the Box2D.NET NuGet package
Note

This example references Stride.CommunityToolkit.Box2D, which is not on NuGet yet. Run it from a clone of the repository, where the package is a project reference; a copy of the project on its own will not build.

Box2D.NET Physics

View on GitHub.

using E06_Box2D.Helpers;
using Stride.CommunityToolkit.Box2D;
using Stride.CommunityToolkit.Engine;
using Stride.CommunityToolkit.Shapes;
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Games;

// Box2D.NET in Stride: a 2D simulation with shapes, collisions and interactive controls

// Per-monitor DPI awareness, so a 150% display gets a sharp window rather than a stretched one;
// it has to happen before the window exists
WindowsDpiManager.EnablePerMonitorV2();

Box2DSimulation? simulation = null;
SceneManager? sceneManager = null;

using var game = new Game();

var sceneSetup = new SceneSetup2D
{
    ClearColor = new Color(0.2f),
    Ground = null,
    Profiler = true
};

game.Run(sceneSetup, start: Start, update: Update);

void Start(Scene rootScene)
{
    game.Window.AllowUserResizing = true;
    game.Window.Title = "Box2D Physics Example - Stride Community Toolkit";

    // Shapes render through the toolkit's Box2D debug draw - testbed-style fill and pixel-constant
    // borders from an SDF shader, replacing the old per-mesh outline render features
    var shapeBatch = game.AddShapeBatch();
    shapeBatch.BorderWidth = 1.5f;
    shapeBatch.Fill.Alpha = 0.4f;
    // Neon tubes: a bloom outside every border in the border's own colour, adding light rather than
    // covering. A third of the strength at the edge is what reads as light around the stroke; full
    // strength would just look like a fatter border
    shapeBatch.Glow.Set(10f);
    shapeBatch.Glow.Strength = 0.35f;
    shapeBatch.Glow.Additive = true;

    simulation = new Box2DSimulation();
    ConfigurePhysicsWorld();

    // Left mouse picks any shape up and throws it; a click on empty space still spawns one
    game.GetCameraEntity().Add(new Grabber2DScript { Simulation = simulation });

    sceneManager = new SceneManager(game, rootScene, simulation);
    sceneManager.Initialize();
}

void Update(Scene rootScene, GameTime time)
{
    simulation?.Update(time.Elapsed);

    sceneManager?.Update(time);
}

void ConfigurePhysicsWorld()
{
    // Negative Y is down
    simulation.Gravity = new Vector2(0f, GameConfig.Gravity);

    simulation.EnableContactEvents = true;
    simulation.EnableSensorEvents = true;

    simulation.TimeScale = 1.0f;
    simulation.MaxStepsPerFrame = 3;
}