Creating Rugby Runner with Claude Code, Godot MCP and Luna
September 15, 2026•11 min read•2081 words•#ai-modified,#godot,#gamedev,#rugby
I started Rugby Runner because I wanted a game small enough to finish, but interesting enough to expose the parts of game development that are easy to avoid when an idea is still living in a notebook.
The pitch is straightforward: control a rugby ball carrier running upfield, read the defensive wave ahead, evade the tackle, and keep the run alive for as long as possible. It is an arcade endless runner with rugby-shaped decisions layered on top. Side-step a defender. Fend off a lighter tackler. Find a support runner and offload before the collision arrives.
The first version is not a finished game. It is a playable foundation and a useful experiment in how I want to work with AI while making something real. This time the assistant was not just answering questions in a chat window. I used Claude Code alongside a Godot MCP server, with Luna as the model in the workflow, and let the running project decide whether an idea actually worked.
Claude Code helped me move between design notes, scenes, scripts, and debugging. Godot MCP provided a bridge into the project. Luna was useful when it stayed close to the current code and helped with the next testable step. None of those pieces replaced opening the game and playing it.
Starting with a deliberately small game
The original design document called the project Rugby Alley. The name may still change, but the central loop has stayed consistent:
Run forward → read the defensive wave → evade or offload → score distance → repeatThat loop gave me a useful boundary. I did not need a full rugby simulation. I did not need fifteen players, a complete ruleset, a season mode, or realistic physics. I needed one carrier, a pitch that could continue scrolling, a reason to move left and right, and a clear consequence for mistiming a tackle.
The touch-first controls came from that same decision. Dragging steers the carrier across the pitch. Swipes trigger evasion actions. A swipe left or right performs a side-step, while a swipe up activates a short fend window. A downward gesture is reserved for a grubber-style escape. Taps on the left or right side of the screen are intended to trigger an offload to a support runner.
It is a compact list, but it is enough to make the prototype answer an important question: does running through a defensive wave feel like a game, or just like moving a sprite up a screen?
Why Claude Code needed project context
A normal chat is good at explaining a single GDScript function. It is less good at remembering how that function fits into a scene tree, which node owns a signal, or whether a path already exists in the project.
Godot projects are relational. A scene references a script. A script expects a node with a particular name. A signal connects two systems. A manager may need to know which player is currently carrying the ball. If I only paste one file into a chat, the assistant can produce perfectly plausible code that does not belong in the project I actually have.
This is where the MCP part became useful. The Godot MCP server gives the assistant a defined way to inspect and work with the project. It is a bridge, not a magical understanding of the editor. Claude Code still needs to ask sensible questions, use the available tools carefully, and interpret what Godot reports back.
The rough workflow looks like this:
Design notes
↓
Claude Code
↓
Godot MCP server
↓
Godot project and editor
↓
Run the game and inspect the resultLuna sits in the model part of that loop. I had already written about getting Godot MCP and OpenRouter connected, and this project gave me a better test than a configuration prompt. A model can sound convincing while describing a project it has not actually inspected. The useful test is whether it can make a small change, respond to the resulting error, and keep the project structure intact.
The loop that worked better than “build the game”
My early prompts were too broad. Asking an assistant to “build the runner” created too many decisions at once. Was the problem the scene structure, the input model, the movement math, the prompt, or the model's assumptions? When everything changes together, it is difficult to learn anything from the result.
The better loop became:
- State one goal. For example, “make the carrier move forward and stay inside the pitch.”
- Inspect before editing. Ask which scene and script currently own the behavior.
- Describe the proposed change. Keep the intended files and untouched systems explicit.
- Make one focused edit. A controller, scene connection, or small manager is enough.
- Run Godot. The engine is the source of truth, not the confidence of the response.
- Report the actual result. Include the error message, visual problem, or behavior that occurred.
- Only then expand the scope. Do not add the next system to a foundation that has not been tested.
This sounds methodical because it is. It also feels faster than accepting a large first pass and then trying to work out which parts I understand.
The MCP connection does not make every generated change safe. I still treat a write as a change with consequences. Read the scene first, name the assumptions, and make the smallest edit that can answer the current question.
The first project structure
The prototype is configured as a portrait Godot project with a 720 × 1280 viewport and a smaller window override for development. The folder layout is intentionally ordinary:
scenes/
scripts/
resources/
assets/
documents/
addons/godot_mcp/That simplicity matters when working with an assistant. A predictable path is easier to inspect, easier to describe, and easier to review than a clever structure that only exists in my head.
The design document and implementation roadmap live in documents/. The main scene brings together the player, track manager, wave spawner, score manager, run controller, and HUD. Individual scenes describe the player, pitch tile, mud hazard, defender, and the other pieces of the runner.
The project is still gray-boxed. The visuals are placeholders and the important thing is the behavior. The player controller moves the carrier forward along the track, steers toward a horizontal target, and clamps the position to the playable pitch. The camera follows the run so the player can see the next part of the lane rather than watching the whole level from a static view.
That gave me the first satisfying milestone: pressing play did not just open an empty scene. There was a carrier, a direction of travel, a bounded pitch, and a run that could be observed and tested.
From movement to a runner loop
Once movement existed, the supporting systems could be added one at a time.
The track manager handles the repeating pitch segments. The point is not to generate an infinite world with endless complexity. It is to keep enough track in front of the carrier that the run feels continuous, while recycling what has fallen behind.
Mud patches are the first simple environmental hazard. Entering one temporarily reduces the player's forward speed. That is a small mechanic, but it gives the track a reason to contain more than decorative grass and lines. It also creates a useful test for communication between an area in a scene and a property on the player controller.
Defenders and the wave spawner introduce the real pressure. A defender moves into the carrier's path, and the player has to choose whether to change lanes, use an evasion window, or eventually pass the ball. The score manager tracks distance and successful actions, while the HUD exposes enough information to understand whether the run is still active.
Each of these systems is simple on its own. The interesting work is in the boundaries: who owns the run state, how a tackle reaches the game-over flow, and which node is allowed to change the score. Those are exactly the details that are easy to gloss over in a generated code sample and obvious once the game is running.
The next difficult piece: offloads
The most rugby-specific mechanic is also the part that is still in progress. The current development work adds two support runners behind the carrier and listens for taps on the left or right side of the screen. When a teammate is in a valid position, the offload manager can promote that teammate to the active carrier and tell the run controller which player should now receive tackle events.
That sounds like a straightforward hand-off. It is not.
The previous carrier needs to stop being controlled without breaking the rest of the scene. The selected teammate needs to behave like the new player. The remaining support runner needs to follow the new carrier. The camera and HUD need to continue presenting one coherent run. A successful pass should feel like momentum continuing, not like a scene reload disguised as a mechanic.
This was a good example of why project-aware assistance helps but does not remove design work. Claude Code can inspect the relevant scripts and help connect the signals. MCP can expose the scene and project context. Luna can suggest a focused implementation. I still need to decide what an offload should feel like, what happens when the target is too far away, and whether the transition is readable at game speed.
That percentage is not a production estimate. It is a reminder that the core runner exists, while the game still needs repeated playtesting, clearer feedback, and polish before I can call the loop finished.
What the first playable milestone taught me
The first lesson was that “playable” is a much better feedback tool than “complete.” A small runner with placeholder art can immediately reveal whether the speed is too high, whether the pitch feels too narrow, and whether a gesture is likely to be understood. A design document cannot answer those questions by itself.
The second lesson was that AI is most useful when the task has a visible feedback loop. “Create a fun rugby game” is too vague to validate. “Make a swipe-up fend last half a second and expose its state to the defender collision” is specific enough to inspect, run, and correct.
The third lesson was that the assistant should help preserve decisions, not quietly make all of them. I want to know why the run controller owns game-over state. I want to know which script is responsible for speed modifiers. I want the next change to fit the project rather than introducing a new architecture because it sounds cleaner in isolation.
Finally, errors are not a failure of the workflow. They are part of the workflow. A wrong node path, a signal connected twice, or a typed value that does not match its recipient gives me information. The useful response is to read the error and make the next correction smaller, not to hide it under a larger rewrite.
What comes next
The next milestone is to make the complete run loop reliable before adding more content:
- verify defender collisions and the game-over transition in repeated runs;
- finish the offload hand-off and make the carrier change visually clear;
- tune the distance, speed, and wave difficulty curves;
- add better feedback for a fend, side-step, mud slowdown, and successful pass;
- test the touch controls on an actual portrait device rather than relying only on mouse input;
- add visual and audio polish only after the interactions are stable.
I also want to keep comparing the experience of working through Claude Code, MCP, and Luna against working directly in Godot. The goal is not to maximize generated code. It is to reduce the distance between an idea, a small implementation, and a playable test.
Rugby Runner is still a modest prototype. That is exactly why it is useful. Every system is small enough to inspect, every bug is close enough to reproduce, and every new feature teaches me something about both Godot and the assistant workflow.
The process is still the same one I keep returning to: inspect, plan, change one thing, run the game, learn, and repeat. The difference now is that the thing being tested is no longer just a code sample. It is a player trying to get past a defender with the ball still in hand.