Collect, train, and infer all happen in your Godot/.NET process - no Python install, no ONNX export, no native blob. The whole loop is managed C#, GPU-accelerated through ILGPU when you have a CUDA card and a hand-written SIMD CPU backend when you don’t.
That makes the workflow I actually wanted possible: edit a reward, set the agent to Train, press Play, and watch it learn in the same editor session. Click Save Model to write a .tres, flip the agent to Inference, press Play again, and ship the .tres with your game.
The clip above is the drift racer: about 20 cars sharing one policy. Nothing in the reward mentions “corners” or “racing line” - only “make progress along the track centerline.” The power-slide is emergent, because the traction model loosens at speed.
A few things it can learn (each is just a few components on a node plus a reward function):
- Pole-cart - the 60-second proof; converges while you watch.
- Turret - learns to lead a strafing target and squeeze the shot at the intercept. Shows off a mixed continuous + discrete control surface (yaw/pitch deltas plus a discrete “fire”).
- Physics arm - a jointed RigidBody driven by ApplyTorque, i.e. trained through the physics solver, not by writing transforms.
- PuckWorld - the reinforcejs keep-away classic: hug a moving target, flee a homing enemy.
The one idea: a node advertises three interfaces - an observation source, a control surface, and a reward source - and the trainer composes everything from them, never needing to know what the thing is. A gun, a car, a leg joint, and a shader param are all “just controls.” If you can write a Reward() for it, you can train it.
Why it’s fast in Godot: it’s batched, not threaded. “128 arenas in parallel” means 128 agents stepped in one _physics_process tick, then one policy forward pass (a single GEMM), then scatter the actions back. No per-agent threads.
Being upfront: v1 is PPO only, with direct- and physics-control modes, an editor dock with live loss/reward/episode plots, and the example scenes above. PPO can degrade on very long runs, so there are two on-by-default stabilizers (KL early-stop and reactive LR backoff) and the demos checkpoint the best policy, not the last. Out of scope for v1: distributed training, a Python bridge, a visual graph editor, an algorithm zoo. It is meant to stay one small composable tool.
Requirements: the .NET/mono build of Godot 4.7+ (the editor plugin uses the 4.7 dock API) and a .NET 8 SDK. A CUDA GPU is optional - it falls back to the SIMD CPU backend, so it runs anywhere.
Try it:
git clone --recursive GitHub - mfagerlund/Bascule: Train game AI inside Godot in pure C# via Tensotron - in-process PPO, no Python, no native runtime. · GitHub
Open in Godot, enable the Bascule plugin (Project Settings - Plugins), open examples/cartpole/CartPoleDemo.tscn, and press Play.
The RL core (Bascule.RL) is Godot-free and on NuGet, if you want in-process PPO in a console sim or another engine.
MIT, free forever. Repo: GitHub - mfagerlund/Bascule: Train game AI inside Godot in pure C# via Tensotron - in-process PPO, no Python, no native runtime. · GitHub - built on Tensotron ( GitHub - mfagerlund/Tensotron · GitHub ), my PyTorch-faithful tensor + autograd library for .NET.
Feedback very welcome - and I am curious what you would train with it.
