Games · Godot
How we make a game in Godot
We are building SOLOMONAR, a first-person folk-horror game set in a Maramureș village over seven days, with agents doing most of the typing and one person deciding. The method rests on three decisions taken on day one, a world that can be checked without a screen, and one rule: no automated check is trusted until we have seen it fail on a world we broke on purpose.
Updated
What this is for
SOLOMONAR is a first-person folk-horror game made in Godot 4. You arrive in a village in Maramureș, you have seven days, a girl has been found dead at the boundary between two plots, and the village already has a word for what happened. That is as much of the story as our public page tells, and it is as much as we will tell here.
This article is about how the game is produced. One person directs, plays and judges; agents write most of the code, the tools and the tests. Our estimate for the whole game is about 2,750 hours, down from a first estimate of about 6,000 after several rounds of adversarial review. No feature was cut to get there. What changed was the method, not the scope.
About a quarter of those hours belong to the person directing and cannot be handed to a machine or to anyone else: tuning, reading every line of dialogue aloud, and cultural judgement on assets. Agents do not compress that part, so we plan around it rather than hoping it shrinks.
Three decisions on day one
1. The simulation runs without rendering. Keyboard injection under headless Godot has been broken since February 2023 and the issue is still open, so a headless game cannot be driven through input. Our village simulation is therefore a plain data module driven through a named API (tick(dt)), with no nodes, no scene tree, no camera and no input. If the simulation is tangled into nodes, nothing can be tested automatically, and the agents stop saving any time on testing.
2. Freeze the engine version. We build an empty project containing every add-on, open it once on the development machine and once on a server, and only then start. We pin Godot 4.6.3, because 4.6.2 has a regression (issue 118110) in which using TIME in a sky shader breaks the lighting.
3. Collapse the state space instead of repackaging it. Each of the seven days has one fixed weather. With several possible weathers per day, the combinations of day, weather and social state can only be sampled by a tester; with one weather per day, the whole space can be enumerated in a single overnight run. We had first believed that “one day times N states” was cheaper than N days. It is not: a game with exactly that architecture, in 2D, took seven years. The matrix stays the same however it is packed. Fixing one axis is what shrinks it.
From design document to playable build
Every subject has its own design document: architecture, the attested folklore behind each mechanic, the voice licence, and a list of forbidden assets.
Part of the design is executable. At start-up the project checks its own budgets and promises: 10 to 12 scenes in the whole village and 7 to 8 interiors; every clue has two doors, one where you are told and one where you see for yourself; no conclusion depends on a single source; and a solver proves that the investigation can be completed within the seven days on both paths.
Content lives in tables, not scenes. A villager is one row and takes 15 to 40 seconds to add, without touching a scene. A rule refers to a predicate registered by name, never to an expression stored in a string. The line where data-driven design becomes a mistake is the moment you start writing a language: string conditions have no types, no stack trace and no tests.
The working order is: shape decisions in writing; download free-licence assets, each with a licence file next to it; a short hardware probe; a visual loop in which the agent presses Play and reads the error; the pure simulation with a headless soak; and a teleporter that loads a named game state directly. The result today is a village you can walk through on our development machine, knock on doors and call at gates, where calling is public and the neighbours hear whom you are looking for.
Engine traps that fail without an error
scaled() moves the origin. In Godot 4, Transform3D.scaled() works in the parent's frame and multiplies the origin as well as the basis; scaled_local() touches only the basis. A tree-placement script called scaled() after setting the position, and measured trees ended up as high as 56 m in the air, others half buried. The same call is harmless when the origin is zero, which is why “we used it before and it worked” proved nothing.
Two WorldEnvironments, one used. The Sky3D add-on extends WorldEnvironment. Our scene already had one, so there were two valid nodes, and Godot rendered with ours: flat background colour, no sky. The whole sky, sun, moon and clouds were ignored, and the top third of every frame was a flat rectangle. There was no error and no warning. An agent found it by comparing the resource ID of the active environment with each node. The fix is to make both share the same Environment resource. In the same family, a function that handed over to the sky and returned before applying fog and ambient occlusion meant those settings never reached the screen.
No LOD on .obj. Godot generates mesh levels of detail automatically at import, but not for .obj files; the documentation says so. Our terrain was an .obj and had no level of detail at all. We now export everything as glTF.
Smaller ones. Height fog in Godot has no distance term, so our settings put between 28% and 53% fog on the ground at zero metres. Expression with const_calls_only does not block utility functions, so a condition can call randf() and silently destroy the determinism of a soak run; we whitelist functions at parse time. Godot crashes on software Vulkan (issue 82435), so captures made on a server without a GPU use a different renderer and are different images.
Testing a 3D game with agents
Probes and captures run in a copy of the engine that the operating system never brings to the front, with a dummy audio driver, rendering into an off-screen viewport. They do not take the focus or the mouse from the person working.
A world checker measures the scale of every animal against real heights, finds anything inside a wall, floating or buried, and checks gate widths. We tested it by putting the fixed bugs back on purpose: it reported 8 problems, and with the fixes it reported a clean world. That is our fixed rule. No gate ships until it has been seen red on a deliberately broken world. Of nine gates as first written, three would have gone into production lying.
We also check the checker. During a night of shadow optimisation, our probe drew a configuration the game does not draw: 80.8% of the shadow work we were measuring did not exist in the game. Before any rendering verdict we now prove that the probe and the game count the same thing.
Server captures detect regressions; they never approve anything. The final visual verdict is given on the machine with a GPU, by a person. To keep that person from becoming a queue, agents bring a comparison rather than a question: three fog variants on the same frame, ten versions of the same line.
Where we were wrong
We assumed agents would speed the whole project up by 1.8x. Checked against the work mix, it is about 1.5x: high on code and text, 1.0x on artistic judgement, and possibly below 1 on tuning, because more code per hour also means more to tune per hour.
We optimised shadows for a night on false numbers and gave a wrong verdict twice. The real cost of a frame was elsewhere: ground 55%, ambient occlusion 31%, vegetation 1.9%, shadows 0.3%. We blamed tone mapping and saturation for washed-out images before working out the fog formula.
Current status, stated plainly
Built: a walkable village with daily life, voiced villagers and automated checks, running on our development machine. Not built: a release. SOLOMONAR has no players and no reviews, and nothing here is a claim about either. The repair list from the last play-through still had open items, among them a mill wheel that passes through its building, the geometry of the church tower, the trees and most textures. The first planned deliverable is a single complete night, not the whole week.
We also say this: a separate study of the market for games of this kind concluded against building a game as a business. SOLOMONAR is a production method being tested, and it should be read that way.
Checklist
1. Can the simulation run with no node, camera or input? 2. Is the engine version pinned and checked against known regressions? 3. Which axis of the state space have you fixed? 4. Has every automated gate been seen failing on a broken world? 5. Do the probe and the game draw the same thing? 6. Is the final visual verdict given on the player's renderer, by a person? 7. Are you using scaled_local() after setting an origin, one Environment resource, and glTF rather than .obj?
Questions and answers
How do you test a 3D Godot game automatically?
Testing a 3D Godot game automatically starts with a simulation that runs without rendering, driven through a named API rather than input, because keyboard injection under headless Godot is broken. Probes and captures run off-screen so they never take the focus or the mouse. A world checker measures scale, finds objects inside walls, floating or buried, and checks gate widths. Every such gate must first be seen red on a deliberately broken world.
Why does Transform3D.scaled() move my objects in Godot 4?
Transform3D.scaled() in Godot 4 works in the parent's frame and multiplies the origin as well as the basis, so calling it after setting a position moves the object. In one placement script this sent trees high into the air and buried others. Transform3D.scaled_local() touches only the basis. The same scaled() call is harmless when the origin is zero, which is why earlier success with it proves nothing.
Why is my sky not rendering in Godot even though there is no error?
A Godot sky can fail silently when a scene contains two WorldEnvironment nodes, for example when a sky add-on extends WorldEnvironment and the scene already had one. Godot renders with only one of them, so the sky, sun, moon and clouds can be ignored with no error or warning. Comparing the resource ID of the active environment with each node finds the cause; making both share one Environment resource fixes it.
Should I use .obj or glTF for meshes in Godot?
Use glTF rather than .obj for meshes in Godot. Godot generates mesh levels of detail automatically at import, but not for .obj files, and the documentation says so. A terrain imported as .obj therefore gets no level of detail at all. Exporting everything as glTF avoids that silent performance gap.
How much do AI agents speed up making a game?
AI agents speed up making a game unevenly: the expected 1.8x turned out to be about 1.5x once checked against the actual work mix. The gain is high on code and text, 1.0x on artistic judgement, and possibly below 1 on tuning, because more code per hour also means more to tune per hour. The director's share of the work cannot be handed to agents at all.
How to cite this
Going Stellar (2026). How we make a game in Godot. GOING STELLAR S.R.L..
https://goingstellar.ro/methods/how-we-make-a-game-in-godot/