# Building Games with Go and Godot: A Developer's Field Notes Godot is one of the friendlier game engines for indie and small team development. While it hasn't really reached the mainstream adoption of Unity or Unreal, at its current velocity and quality, it'll happen soon. There are already several popular games that people might not realise are running on Godot: - Brotato - Cassette Beasts - Unrailed 2 - Slay the Spire 2 The [graphics.gd](https://github.com/grow-graphics/gd) library, which bridges Go to Godot, recently added experimental support for web exports. This development prompted me to start work on my game using this unique combination of technologies. > Go has nothing to do with the Godot engine! What follows are field notes on the issues I encountered and eventually unblocked after considerable head-desking. If you're attempting something similar, these notes might save you some pain. **The main challenge with the graphics.gd library is that the causes of errors are never, ever obvious. Debugging feels like archaeological work most of the time.** ## Entity Component System Architecture ![[Pasted image 20250227131649.png]] The ECS architecture has been discussed to death online, but it's worth a quick refresher since it's central to how I structured this project. Instead of focusing on inheritance and objects, ECS breaks up game state and logic into three distinct parts: - **Entities** - Made up of many components - **Components** - Decoupled data containers - **Systems** - Logic that mutates sets of components In the diagram above: - The character entity has 3 components: health, movement, and transform - The projectile entity has 2 components: movement and transform The systems operate predictably: - The health system only acts upon entities that have a health component - The movement system only acts upon entities that have both movement and transform components This creates a data-driven, testable architecture that scales well as projects become more complex. I'll admit it came with some challenges: - Integrating with Godot's node-based workflow - Initially lower developer productivity The productivity issue resolves over time - I found development gets faster and faster as you internalize the ECS paradigm. For Godot integration, I stored Godot nodes as components within the ECS hierarchy. An alternative approach would be to decouple them completely and communicate via signals, but I found the hybrid approach more practical. **Key principle**: Keep as much data as possible in the ECS world. Use Godot nodes only for heavy lifting like physics, collisions, spawning, rendering, and audio. Think of ECS as your data layer and Godot as your presentation layer. ## Graphics.gd Gotchas and Solutions > How do you get embedded structs to show up in the editor? If you have a custom child node within your custom child node, you need to set it up manually in the editor. The Go code alone isn't sufficient for Godot to recognize the hierarchy. > How do you get nodes in your Go code to show up in the editor? Implement `AsNode()` to make nodes appear in the scene tree. Important caveat: The node itself won't be "configured" with exported fields automatically. You still need to set up the scene in the Godot UI to fully initialize it with the proper configuration. > How do you write Godot signals in Go? Use a write-only channel to expose signals to Godot: ```go type HelloWorld struct { gd.Class[HelloWorld, gd.SceneTree] Something chan<- struct{} `gd:"something"` } ``` **Declaring multiple signals using channel syntax currently causes a panic. For multiple signals, use the Signal package instead. ### Engine References > How do pointers survive between frames? Use `Object.use()` to rehydrate a pointer before usage, otherwise your application will panic. This is probably the most common source of mysterious crashes when starting with `graphics.gd`. ## ECS Implementation Details For the ECS implementation in Go, I used [mlange-42's arche library](https://github.com/mlange-42/arche), which is one of the better ECS libraries available in the Go ecosystem. Developing in an ECS style feels quite unintuitive at first, especially if you're coming from object-oriented game development. The mental shift from "objects that contain data and behavior" to "data flows through systems" takes time to internalise, but its not too bad if you've been developing in Go for long enough. ## Final Thoughts The Go + Godot combination is unconventional but surprisingly powerful once you work through the initial friction. The type safety and performance of Go, combined with Godot's excellent tooling and rapid iteration capabilities, creates a compelling development experience. The graphics.gd library is still experimental, and it shows. Expect to spend time debugging integration issues rather than building gameplay. But if you're comfortable with that trade-off and want to leverage Go's strengths in game development, it's worth exploring. The ECS architecture, while adding initial complexity, pays dividends as your game grows. The clear separation of concerns makes testing easier and helps manage the inevitable complexity creep that happens in game development. The main issue is that Godot itself does NOT like being forced into this architecture, so you end up jamming ECS into a node somewhere and send signals back and forth to the rest of the engine. ...So you get all of the friction and none of the performance benefits. Would I recommend this stack for a commercial project? Nope. But for experimentation and learning, it's a pretty fun and interesting alternative to game architecture.