Recursive AI

Simulate buoyancy in C#

OnePhysics is a free, open-source physics library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

Float a box in a depth-based water grid, not an arbitrary 3D fluid simulation.

Terminal
dotnet add package RecursiveAI.OnePhysics

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using RecursiveAI.OneMath;
using RecursiveAI.OnePhysics.Physics3D;
 
var world = new PhysicsWorld3D();
world.AddShallowWater(0, 0, 10, 10,
    level: 2, cellSize: 0.5f, floor: 0);
var crate = world.AddBox(new Vector3F(1, 1, 1),
    at: new Vector3D(5, 3, 5), density: 500f);
for (int i = 0; i < 600; i++)
    world.Step(1f / 60f);
Console.WriteLine($"Y: {crate.Position.Y:F3}");
Text
Y: 2.024

Unzip the example, then dotnet run.

The full program

The full program adds a static bed before creating the water, drops a crate, counts water-entry/exit changes and samples the surface. Lengths are meters, density is kg/m³ and the step is in seconds. The floor parameter is a fallback bed elevation, not a rigid ground collider.

C#
using RecursiveAI.OneMath;
using RecursiveAI.OnePhysics;
using RecursiveAI.OnePhysics.Physics3D;
 
var world = new PhysicsWorld3D();
world.AddBox(new Vector3F(12, 1, 12),
    at: new Vector3D(5, -0.5, 5), type: BodyType.Static);
var water = world.AddShallowWater(0, 0, 10, 10,
    level: 2, cellSize: 0.5f, floor: -1);
var crate = world.AddBox(new Vector3F(1, 1, 1),
    at: new Vector3D(5, 3, 5), density: 500f);
int events = 0;
world.BodyWaterChanged += _ => events++;
Console.WriteLine($"Initial Y: {crate.Position.Y:F3}");
for (int step = 0; step < 600; step++)
    world.Step(1f / 60f);
Console.WriteLine($"Final Y: {crate.Position.Y:F3}");
Console.WriteLine($"Water changes: {events}");
if (world.QueryWater(new Vector3D(5, 0, 5), out var sample))
{
    Console.WriteLine($"Surface: {sample.Surface:F3}");
    Console.WriteLine($"Depth: {sample.Depth:F3}");
}

dotnet run prints:

Text
Initial Y: 3.000
Final Y: 2.024
Water changes: 1
Surface: 2.005
Depth: 2.005

Variations

Add a wave-making volume after setting up the full program’s world and water. Insert this before its step loop:

C#
water.AddVolume(0, 0, 2, 10, 5f);

This adds 5 m³ in the west-side rectangle; the world steps propagate the disturbance. With this line inserted, the program prints:

Text
Initial Y: 3.000
Final Y: 2.097
Water changes: 1
Surface: 2.077
Depth: 2.077

The short answer

Create a PhysicsWorld3D, add shallow water, add a dynamic body with a density, then step the world. Awake dynamic bodies receive buoyancy and drag from the local water. The sample uses a one-meter cube at a density of 500 kg/m³ and lets it fall into the water; it prints its final vertical position rather than rendering a scene.

Use this model for a lake, harbor, river or wave tank that reacts to bodies. The water guide describes waves, currents and wet/dry shores on a grid. It is not a general volumetric-fluid solver. AddWater is the documented alternative for an infinite flat pool that does not move.

How exactly does buoyancy work?

Buoyancy is the upward push of water on an immersed body. In OnePhysics, dynamic bodies float and displace water. Their density is part of what determines how they sit in it, while the local water supplies buoyancy and drag. Sleeping bodies can wake when moving water affects them. The guide’s one-meter cube at density 500 is described as floating at half depth; the output here is the actual finite-step result of this particular scene, not an exact equilibrium assertion.

Create the terrain before the shallow-water grid. The bed is sampled once from static shapes under the center of each cell when water is created. If no shape is hit, the configured floor is used instead. When terrain changes, call ResampleBed() to update those samples. A water floor sets the water model’s lower bound; add a static shape separately when bodies need collision with the ground.

The full program follows that order, then subscribes to BodyWaterChanged before stepping. Its counter records body-water changes, so it is not a count of simulation steps or a continuous immersion measurement. QueryWater returns a local surface and depth when the requested point is covered by water.

Can you provide a simulation of Archimedes’ principle?

Yes. The full program drops a cube from Y = 3.000 m; after stepping, its center is at Y = 2.024 m and the sampled water surface is at 2.005 m. The cube remains partly submerged, demonstrating buoyancy rather than sinking to the bed. The box size is in meters, the position is in world coordinates with Y vertical, density is in kg/m³, and each step advances by 1f / 60f seconds. The units guide documents the library’s unit conventions.

A shallow-water simulation has spatial resolution. Waves shorter than a few cells are lost, and the step can be split into substeps according to the fastest wave. Smaller cells do not turn this into arbitrary ocean or fluid simulation. The water guide describes the substep cap and boundary choices; sides are walls unless configured otherwise.

The variation adds volume to a rectangle rather than directly moving the crate. That lets you test a disturbance traveling through the water. Displacement controls how much body volume is added to the water; its default is 0.5, while 1 is physically complete but can make a long hull in a narrow channel ring. Damping and bottom friction also affect the response.

All onephysics recipes