Core Concepts
Inverted Architecture
Traditional Godot applications have Godot control the process lifecycle:
Godot Process → SceneTree → Your Scripts2dog inverts this:
Your .NET Process → twodog.Engine → Godot (as library)You control when Godot starts, when frames iterate, and when it shuts down. Godot becomes a rendering/physics/audio library that you drive.
libgodot Embedding
2dog uses libgodot, a shared library build of Godot Engine. This allows:
- Godot to run as an embedded library within your .NET process
- Direct P/Invoke calls to Godot's native APIs
- Full access to GodotSharp managed bindings
The native library (libgodot.dll, libgodot.so, or libgodot.dylib) is automatically downloaded and cached on first build.
Build Variants
2dog supports three Godot build variants, each optimized for different use cases:
Template Builds (Runtime-Only)
Template Debug (template_debug):
- Debug symbols enabled for troubleshooting
- Assertions and error checking enabled
- No editor features
- Suitable for: Development, debugging game logic
Template Release (template_release):
- Fully optimized for performance
- Minimal binary size
- No debug symbols or editor features
- Suitable for: Production games and applications
Editor Build (Development Tools)
Editor (editor):
- Full editor toolchain enabled (
TOOLS_ENABLED) - Resource import pipeline available
- Editor APIs:
EditorInterface,EditorPlugin,ImportPlugin - Larger binary size, slower than templates
- Suitable for: Asset import tools, editor extensions, build pipelines
Choosing a Build Variant
- Building a game? Use Debug during development, Release for production
- Need to import assets? Use Editor configuration
- Creating build tools? Use Editor configuration
- Running in CI/CD? Use Debug for tests, Release for final builds
Build with different variants:
dotnet build -c Debug # template_debug
dotnet build -c Release # template_release
dotnet build -c Editor # editor with TOOLS_ENABLEDGodotSharp API Access
Once the engine starts, you have full access to the GodotSharp API:
using var engine = new Engine("app", "./project");
using var godot = engine.Start();
// Access the scene tree
SceneTree tree = engine.Tree;
// Load and instantiate scenes
var scene = GD.Load<PackedScene>("res://my_scene.tscn");
var instance = scene.Instantiate();
tree.Root.AddChild(instance);
// Use any Godot API
var viewport = tree.Root.GetViewport();
var physics = PhysicsServer3D.Singleton;The Main Loop
Unlike traditional Godot where _Process callbacks drive your code, you explicitly pump the main loop:
while (!godot.Iteration())
{
// Called every frame
// Godot processes physics, rendering, input, etc.
// Your frame logic here
if (someCondition)
break; // Exit when you decide
}Iteration() returns true when Godot wants to quit (e.g., window closed).
Single Instance Limitation
WARNING
Only one Godot instance can exist per process. This is a Godot limitation, not a 2dog limitation.
// This works
using var engine = new Engine("app", "./project");
using var godot = engine.Start();
// This throws InvalidOperationException
using var engine2 = new Engine("app2", "./project2");
var godot2 = engine2.Start(); // ❌ Error!For testing scenarios, use xUnit's collection fixtures to share a single instance. See Testing.
Resource Paths
Godot's res:// paths resolve relative to the project directory you specify:
// Project at ./my_project
new Engine("app", "./my_project");
// res://scenes/main.tscn → ./my_project/scenes/main.tscn
var scene = GD.Load<PackedScene>("res://scenes/main.tscn");