---
title: "Floating-origin physics in C#"
url: https://recursiveai.org/onegame/cookbook/shift-physics-origin/
description: "Floating-origin physics in C# with onegame. Includes a runnable .NET project, sample output and the game integration details."
version: "1.26.0"
---

# Floating-origin physics in C#

**onegame** is a free, open-source game engine for .NET from Recursive AI. MIT or Apache-2.0, no editor required.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneGame.Physics) · [GitHub](https://github.com/recursive-ai-oss/onegame) · [Docs](https://recursiveai.org/onegame/docs/) · [Download example (.zip)](https://recursiveai.org/onegame/cookbook/shift-physics-origin/shift-physics-origin.zip)

A floating origin keeps local 3D coordinates near zero to preserve precision far from the global origin, without changing global positions.

```shell
dotnet add package \
  RecursiveAI.OneGame.Physics
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using OneGame.Core;
using OneGame.Physics;

var space = new PhysicsSpace3D();
var body = space.AddSphere(
    Fixed.One, new FVec3(1000, 0, 0));
space.ShiftOrigin(new FVec3(1000, 0, 0));
Console.WriteLine(
    $"Local X: {space.Position(body).X}");
var global =
    space.Origin + space.Position(body);
Console.WriteLine($"Global X: {global.X}");
```

Prints:

```text
Local X: 0
Global X: 1000
```

The sphere starts at global X = 1000. Shifting by 1000 moves its local X to zero and stores that offset in `Origin`; adding the two still gives 1000.

Offsets must lie on the physics 2⁻³¹ grid (even `Fixed.Raw`), and the accumulated origin must fit signed 32-bit world units; `PhysicsSpace3D` rejects out-of-range coordinates.

Unzip [the example](https://recursiveai.org/onegame/cookbook/shift-physics-origin/shift-physics-origin.zip), then `dotnet run`.

`Fixed` is a deterministic fixed-point number type; `FVec3` is a three-dimensional vector of those numbers.

## Variations

### Accumulate a second shift

```csharp
// After the shift in the snippet at the top:
space.ShiftOrigin(new FVec3(100, 0, 0));
Console.WriteLine($"Local X: {space.Position(body).X}");
Console.WriteLine($"Global X: {(space.Origin + space.Position(body)).X}");
```

Prints these additional lines:

```text
Local X: -100
Global X: 1000
```

## Related recipes

[Draw a 3D physics overlay](https://recursiveai.org/onegame/cookbook/draw-3d-physics-debug-overlay/) · [Handle trigger events](https://recursiveai.org/onegame/cookbook/handle-physics-trigger-events/)

---

## The short answer

Call `PhysicsSpace3D.ShiftOrigin(offset)` from the authority’s deterministic simulation logic—the game instance that decides the shared state. Read a body’s global position as `space.Origin + space.Position(body)`. The shift changes local coordinates and the stored origin together; it does not move the body to another global location.

This API belongs to `PhysicsSpace3D`. `PhysicsSpace2D` does not expose world-origin shifting. Do not copy this call into a `PhysicsSpace2D` example or treat view transforms as an equivalent physics operation.

## How do I keep floating-origin physics deterministic?

Fixed-point inputs alone do not make a physics solver deterministic. `PhysicsSpace3D` converts positions through a checked numeric bridge and steps OnePhysics on one thread. OnePhysics uses IEEE `+`, `−`, `×`, `÷` and `sqrt` without fused operations, so its documented arithmetic produces the same bits on x64, arm64 and WebAssembly. The [range and determinism guide](https://recursiveai.org/onegame/docs/physics/#range-rounding-and-determinism) explains the checked conversions; the underlying solver is not fixed-point-only.

For origin shifting, the key decision is who chooses the shift and when it happens. In onegame the authority makes that decision in simulation code. Every replay must encounter the same operation with the same offset at the same point in the tick sequence. A camera is view state and can differ between players, so its current position must not decide when the physics world rebases.

Register the 3D space with `PhysicsSpace3D.AddTo(world)` in the game’s system registration path. It becomes a hidden table named `physics3d`, with a step after entities. That registration must also happen when constructing a world for loading a save. The console example uses a standalone space to isolate the coordinate change; it does not install a second step system or choose a rebasing policy for a game.

The documented local coordinate interval includes the exact endpoints ±2²¹. Shift before local positions approach that limit. One possible policy is to shift by +1000 on X when a tracked body’s local X reaches 1000, as in the example, with a matching rule for negative travel. This is an example threshold, not a library default. Apply the same rule at the same simulation tick on every replay, and account for all active bodies, not merely the camera position. The accumulated origin has a separate requirement: it must fit signed 32-bit world units.

Offsets must be representable on the physics 2⁻³¹ grid, which means an even `Fixed.Raw` value. Integer offsets such as 1000 in the sample meet that requirement. Do not construct arbitrary raw fixed-point offsets and assume every representable onegame number can cross the bridge to physics coordinates unchanged.

The sign matters. A positive X shift increases the accumulated X origin and subtracts that offset from local body X coordinates. The sample starts a sphere at X = 1000 and shifts by +1000. Local X becomes zero, while global X remains 1000. A second shift of +100 makes local X −100 and origin X 1100; the global sum is still 1000. These calculations are useful when checking world-space markers against physics-local positions.

A shift is part of physics state, not a display adjustment. The accumulated origin is saved and hashed. Two spaces with different origins deliberately have different hashes even if their global body positions appear identical. Do not normalize those hashes away in a drift check: the different local coordinate frames are a real state difference that can affect the continuation of a simulation.

Snapshots preserve the coordinate frame so restored physics can continue with the same origin. Views should draw from the state intended for them: the physics table is hidden, and clients normally receive mirrored synced properties rather than the entire authority physics world. Decide whether a mirrored value is local or global and use that convention consistently. The [3D physics documentation](https://recursiveai.org/onegame/docs/physics/#3d-spaces) describes the coordinate bridge and rebasing contract.
