Engine-agnostic first-person walking and bodycam camera controller for Godot, Unity, and plain .NET projects.
WalkSimController keeps movement and camera feel in a small .NET library. It has no dependency on Godot or Unity runtime types, so engine integration stays as a thin adapter layer.
- Smooth first-person walking velocity with diagonal input clamping.
- Mouse look with yaw wrapping, pitch clamps, and optional look lag.
- Head bob, strafe tilt, turn sway, and breathing camera motion.
- Deterministic per-frame API that returns an immutable
WalkSimState. System.Numericsinputs and outputs for easy use from game engines or simulations.- NUnit test coverage for core movement, mouse, reset, and sync behavior.
src/WalkSimController/ Core .NET library
tests/WalkSimController.Tests/ NUnit tests
samples/Godot/ Minimal Godot C# adapter
samples/Unity/ Minimal Unity C# adapter
Reference src/WalkSimController/WalkSimController.csproj from your game project, or copy the src/WalkSimController source into your solution. The core library targets netstandard2.1 for broad compatibility with Unity, Godot C#, and modern .NET projects.
using System.Numerics;
using WalkSimController;
var controller = new WalkSimController.WalkSimController(Vector3.Zero);
var state = controller.Update(
1f / 60f,
new FrameInput
{
MoveAxis = new Vector2(0f, 1f),
MouseDelta = new Vector2(2f, -1f),
}
);
// Apply state.BodyVelocity to your character body.
// Apply state.CameraRotation and state.CameraLocalOffset to your camera rig.After your engine resolves collisions, sync the real body position back into the controller:
controller.SyncBodyPosition(realBodyPosition);For teleport, respawn, or loading a saved game:
controller.Reset(position, yaw: 90f, pitch: 0f);GitHub's default source zip contains the full repository, including tests, samples for every engine, funding metadata, and contributor files. For game projects, prefer the release archives:
WalkSimController-src-<version>.zip: core source only, plusREADME.mdandLICENSE.WalkSimController-godot-<version>.zip: core source plus the Godot sample adapter.WalkSimController-unity-<version>.zip: core source plus the Unity sample adapter.
Use the full repository only if you want to run tests, change the library, or contribute.
The controller does not move engine objects directly. Each frame:
- Convert engine input into
FrameInput. - Call
Update(dt, input). - Apply
BodyVelocitythrough your engine character movement. - Sync the post-collision body position with
SyncBodyPosition. - Apply
CameraRotationandCameraLocalOffsetto your camera rig.
See samples/Godot/PlayerGodotAdapter.cs and samples/Unity/PlayerUnityAdapter.cs for minimal adapters.
Use a small transform hierarchy where engine physics owns the body position, the root body owns yaw, the camera rig owns bodycam rotation, and the camera owns local bob/breathing offset.
PlayerBody engine character body
| moves with BodyVelocity through engine physics
| rotates only around Y with RawCameraYaw
|
+-- CameraRig / HeadPivot local child of PlayerBody
| rotates with CameraRotation pitch/yaw-offset/roll
| do not move this with physics
|
+-- Camera local child of CameraRig
moves locally with CameraLocalOffset
Frame ownership:
FrameInput
MoveAxis + MouseDelta
|
v
WalkSimController.Update(dt, input)
|
v
WalkSimState
BodyVelocity -> engine character movement
RawCameraYaw -> PlayerBody yaw
CameraRotation -> CameraRig local rotation
CameraLocalOffset -> Camera local position
After the engine moves the character and resolves collisions, call SyncBodyPosition with the real body position. The controller predicts desired motion, but the engine remains the source of truth for collision-corrected body position.
Unity mapping:
GameObject with CharacterController + PlayerUnityAdapter
|
+-- CameraRig Transform
|
+-- Camera
Godot mapping:
CharacterBody3D with PlayerGodotAdapter
|
+-- CameraRig Node3D
|
+-- Camera3D
WalkSimSettings groups the tuning surface:
Movement: walk speed and velocity smoothing.Mouse: sensitivity, pitch clamps, and look lag.HeadBob: positional and rotational step motion.StrafeTilt: roll while strafing.TurnSway: roll response while turning.Breathing: idle and walking breathing motion.Bodycam: global comfort/intensity limits.
You can pass settings into the constructor:
var settings = new WalkSimSettings();
settings.Movement.WalkSpeed = 4.5f;
settings.Mouse.Sensitivity = 0.08f;
settings.Bodycam.MotionIntensity = 0.7f;
var controller = new WalkSimController.WalkSimController(Vector3.Zero, settings);dotnet build
dotnet testRelease archives are generated by scripts/package-release.sh:
./scripts/package-release.sh v1.0.0The script writes zip files to dist/. GitHub Releases run the same script and attach the generated archives automatically.
The core library targets netstandard2.1 and intentionally uses C# 9-compatible syntax. This keeps the source usable in Unity and Godot projects that compile copied source files with an older engine-managed C# compiler.
Do not require newer language features such as file-scoped namespaces, primary constructors, required members, collection expressions, or global using directives in src/WalkSimController. Engine compiler support can lag behind the installed local .NET SDK, so a local build with LangVersion=latest is not enough.
Before raising the language version, verify the lowest supported Unity and Godot versions compile the source directly. The minimum compatibility check is:
dotnet build src/WalkSimController/WalkSimController.csproj /p:LangVersion=9.0
dotnet test WalkSimController.slnMIT. See LICENSE.