Install the OnePhysics preview for .NET 10, drop a ball on the ground, walk a character, cast a ray, and take a snapshot and a world hash, with the same world state, bit for bit, on every platform.
Install
OnePhysics targets net10.0, so you need the .NET 10 SDK. It is a preview: this guide uses 0.4.0-preview.1, and the API and results may change between previews.
dotnet new console -o OnePhysicsStart cd OnePhysicsStart dotnet add package RecursiveAI.OnePhysics --version 0.4.0-preview.1
Its only dependency is OneMath, installed with it, for vectors and deterministic math. It works under NativeAOT, trimming and browser WebAssembly.
The 2D engine is the supported surface. The 3D engine is in the same package as a preview of its own, marked [Experimental("OPHYS0002")]: see the 3D preview.
A first world
using RecursiveAI.OneMath; // Vector2F, Vector2D using RecursiveAI.OnePhysics; // BodyType, events, exceptions using RecursiveAI.OnePhysics.Physics2D; // PhysicsWorld2D, Body, Character var world = new PhysicsWorld2D(); world.AddBox(new Vector2F(20, 1), at: new Vector2D(0, -1), type: BodyType.Static); var ball = world.AddCircle(0.5f, at: new Vector2D(0, 5)); ball.OnCollisionEnter += c => Console.WriteLine($"hit {c.Other} along {c.Normal}"); var player = world.AddCharacter(at: new Vector2D(3, 1)); for (int i = 0; i < 600; i++) { player.Move(new Vector2F(0.01f, -0.1f), 1f / 60); world.Step(1f / 60); // fixed dt; events fire after the step, in a deterministic order } Console.WriteLine($"ball at {ball.Position}, player at {player.Position}"); if (world.Raycast(new Vector2D(0, 10), new Vector2F(0, -1), out var hit)) Console.WriteLine($"ray hit {hit.Body} at {hit.Point}"); byte[] saved = world.Snapshot(); // byte-exact; Restore(saved) continues bit for bit ulong hash = world.ComputeHash(); // the same on every platform Console.WriteLine($"snapshot {saved.Length} bytes, hash {hash:X16}");
hit Body(world 1, #0, gen 1) along (-0, -1) ball at (0, -5.037127994000912E-05), player at (8.99999986588955, 0.3999999836087227) ray hit Body(world 1, #1, gen 1) at (0, 0.49994962872006) snapshot 1804 bytes, hash C1C403756B09A31E
What each part does
AddBox,AddCircleand the other one-liners create a body with one shape. Sizes are full extents in meters, positions aredoubleand everything else isfloat. Bodies are dynamic unless you passBodyType.StaticorKinematic.OnCollisionEnterfires after the step, in a fixed order, so handlers run the same way on every machine. TheBodyin the message is a small struct handle: world, slot and generation.AddCharacteradds a character mover:Move(displacement, dt)moves it and resolves its contacts. Here it walks 6 m to the right and stands on the ground.Step(dt)advances the world by a fixed time step. Use onedtfor the whole session.Snapshot()writes the whole world to bytes;Restore(bytes)continues from there bit for bit.ComputeHash()is a 64-bit hash of the state, the same on every platform.
Determinism
Only IEEE + - * / and square root decide a result, with OneMath’s own transcendental functions, fixed-order reductions and constraint coloring. The world hash after N steps is the same on x64, arm64 and WebAssembly, scalar or SIMD, one thread or all cores. Previews are not bit-compatible with each other: a snapshot or replay from one preview doesn’t carry to the next. See determinism.