ALL POSTS DEVLOG

The Game Mechanic That Takes 20 Minutes to Write – and 3 Days to Get Right

The gap between a prototype and a production-ready gameplay feature is rarely a few extra ifs. It's a small civilization.

The Game Mechanic That Takes 20 Minutes to Write – and 3 Days to Get Right

In short

"Works on my machine" isn't a milestone in gameplay programming. It's more of a dangerously optimistic status report.

In this article we'll take a simple dash mechanic apart to see what turns an idea into a first working prototype, and then into a system a designer can tune, an animator can hit, QA can break, the network can keep in sync, and you'll still dare to touch six months from now.

Prototype Production
You press the button → dash Input policy + state + cooldown + authority
The character moves Movement, collision, slopes, ledges, air state
Looks good on your machine Animation, camera, VFX, audio, frame rate
Done, according to QA: "cute" Professional, relaxed, and dangerously close to the truth.
Day one, 09:12

1. The 20-minute miracle

You get a ticket: "Let the player dash forward." The task description is short, the design seems simple, the coffee is still warm. Every sign points to this being a pleasant morning feature.

At this stage, the prototype's goal isn't to be correct in every situation. Its goal is to quickly answer the most important question: does the idea feel good at all?

The naive implementation

void PlayerCharacter::Dash() { const Vec3 direction = GetForwardVector(); AddImpulse(direction * dashForce); }

You press the button. The character shoots forward. It works. Joy. Someone in the background is already halfway to marking the ticket Done.

First lesson

A prototype proves the idea is capable of working. Production code has to prove the idea doesn't break the rest of the game.

So why is a quick prototype still a good idea?

The first definition of "Done"

At the prototype level, Done means: I pressed the button, something happened, and it was roughly what we wanted. At the production level, Done is a somewhat longer sentence. Long enough that it's about to get its own chapter.

Day one, 10:03

2. The first enemy: the rest of the game

The dash is simple on its own. The dash inside an existing character system no longer is. Because the character doesn't just dash. It runs, jumps, falls, attacks, gets stunned, plays animations, performs interactions, and might just be hanging off a ladder. And every single state has a strong opinion about whether dashing is allowed right now.

Input ≠ immediate execution

void PlayerCharacter::OnDashPressed() { if (!CanDash()) return; StartDash(); }

Suddenly, questions appear behind CanDash() that, naturally, weren't in the ticket:

A state machine: so not everything talks to everything else

enum class MovementState { Grounded, Airborne, Dashing, Knockback }; bool CanDash() const { return state != MovementState::Knockback && dashCooldown <= 0.0f && stamina >= dashCost; }

Architecture

If adding a feature means asking "are we currently dashing?" in twenty separate places, the dash probably isn't what got too complex. Your system is trying to tell you something.

And now there's a lifecycle

Alongside StartDash(), UpdateDash(), EndDash(), and maybe OnDashInterrupted() show up. The feature is no longer a single function. It has state, entry and exit conditions, resources, and side effects. This is the point where the twenty-minute code starts growing up.

Day one, 11:26

3. Movement and collision: the wall has opinions too

In open space, the dash is great. On an actual level, though, there are walls. Slopes. Stairs. Ledges. Moving platforms. Door frames. And that one 7-centimeter-tall decorative rock that somehow becomes more important to the feature than the entire combat system.

Impulse or controlled movement?

In the first prototype, an impulse is convenient, but in production it becomes a question of how much you want to trust physics with the movement. A controlled dash is usually more deterministic, easier to tune, and cooperates better with collision.

Vec3 target = position + dashDirection * dashDistance; HitResult hit; if (SweepCapsule(position, target, hit)) { target = ComputeSafeDashEnd(hit); } MoveCharacterTo(target, dashDuration);

Edge-case bingo

Situation Question
Wall 30 cm away Do we stop? Slide along it? Bounce off?
Downhill slope Do we keep the horizontal direction, or follow the ground?
Ledge Can we fly over it? Fall off? Stop automatically?
Moving platform Do we compute movement in world space or local space?
Narrow gap Do we let it through if there's no room at the end position?
Another character Does it block? Do we ghost through? Shove?

A QA preview

A large share of gameplay bugs aren't born because the feature is bad. They're born because the player took it to a piece of geometry the feature never knew existed.

Frame rate and time-based movement

If you add the dash's speed per frame, you get a different game at 30 FPS than at 144 FPS. That's why movement needs to be time-based, collision needs to be continuous, and the end result should ideally be reproducible. The player won't file a bug report about "a deltaTime handling issue." They'll just say: "sometimes the dash is shorter."

Day one, 14:08

4. Animation, camera, and that certain "feel"

Technically, we're already dashing. Now comes the part where it turns out the mechanic works, it just feels bad to use. One of gameplay programming's most important realizations: a game mechanic isn't just logic. It's a combination of timing, visual feedback, sound, camera, and animation.

Who's driving the movement?

All three can work. What matters is that it's clear which system has authority. If the animation wants to travel 4.2 meters, gameplay wants 4.0, and collision hits a wall at 3.6, someone has to decide. "Hopefully they end up in the same place" is not a stable architecture.

Game feel layers

Layer What it provides
Animation Weight, dynamics, pose, readability
Camera Sense of speed, focus, impact
VFX Direction, intensity, state feedback
Audio Start, peak, landing, cooldown feedback
Input timing Instantaneous, or deliberate anticipation

The numbers aren't sacred

dashDistance might be 4.0 meters in the spec, then become 3.7 because it works better with the animation. dashDuration might be 0.25 seconds, then 0.18, because the game's pacing calls for it. The gameplay programmer's job here isn't to defend the first constant's value. It's to provide a system that can be safely tuned.

Game feel

"Feeling good" isn't some mystical designer magic trick. It's the result of many small, measurable, tunable decisions. The player just has to end up feeling like it was natural all along.

Day two, 09:17

5. Designer tooling: no one should have to call a programmer over 0.05 seconds

If every change to dashDistance, cooldown, or the invulnerability window requires a C++ build, the system technically works, but it's expensive from a production standpoint. Designers need fast iteration. That's why the gameplay programmer builds not just a mechanic, but an editable mechanic.

From hardcoded to data-driven

struct DashConfig { float distance = 4.0f; float duration = 0.22f; float cooldown = 0.8f; float staminaCost = 20.0f; Curve speedCurve; bool allowInAir = false; };

A good config doesn't mean every bool and float gets exposed to the editor. Too many parameters are just as much a problem as too few. You need to give controls that represent real design decisions.

Designer-proof doesn't mean designer-hostile

Tooling

If a designer asks "can I change this somewhere?", the right answer is rarely "sure, on line 847."

Tuning is a feature too

Part of a production-ready gameplay system is iteration speed. A mechanic's quality doesn't just depend on how good the algorithm behind it is, but also on how many attempts it lets the team make in the same amount of time. Good tooling directly improves the game.

Day two, 12:41

6. Networking: now two players can break it

In single-player, your own machine is the truth. In multiplayer, multiple machines need to tell roughly the same story about the same dash. This immediately raises questions of authority, prediction, reconciliation, and bandwidth.

Who decides the dash actually happened?

A typical approach has the client react to input immediately for responsive feel, while the server validates and authorizes the event. If the two diverge, a correction follows.

// Client PredictDash(inputSequence, direction); SendDashRequest(inputSequence, direction); // Server if (ValidateDash(player, direction)) { ExecuteAuthoritativeDash(); ReplicateDashState(); }

New problems, same button

Multiplayer

"Works on my machine" is an especially dangerous sentence for networked gameplay. The question is who it works for, at what ping, and what the server thinks about it.

Not every feature needs the same networking model

A competitive movement mechanic might need stricter authority and prediction, while a cooperative, non-critical traversal feature can tolerate more visual correction. Good engineering here, too, starts from the game's requirements, not a single universal recipe.

Day two, 15:32

7. Save/load, QA, and the long memory

"But why save the dash?" – on its own, maybe there's nothing to save. But the feature, combined with a stamina system, unlocks, perks, or upgrades, might already carry persistent state. Part of production thinking is not just paying attention to the current frame.

Save/load questions

QA isn't the enemy. It's a force of nature.

QA does exactly what the player will do later: combine the feature with everything you didn't combine it with. Preferably on a Friday afternoon.

Test What we're looking for
Input spam Double triggers, cooldown bypass
Pause/unpause Timer, animation, state freezing
Low FPS Distance/timing drift
During save/load Half-finished or illegal state
Wall + attack + dash Priority and interrupt logic
Respawn Resetting state and resources

Reproducibility

A rare bug only becomes fixable once you can trigger it deterministically, or at least consistently. That's why logging, state history, input history, and debug visualization matter. You can't build a stable fix from "QA saw it once." "Frame 18421: Grounded → Dashing → Knockback, stamina=0, wallHit=true" is a completely different story.

Day three, 09:06

8. Debugging, performance, and maintainability

The feature now works in a lot of situations. That still doesn't mean it's a good production system. The question now is: if someone gets a bug on it in six months, will they understand what's happening? And meanwhile, is it needlessly eating CPU time every single frame?

Making the invisible visible

DebugDrawDash( origin, dashDirection, dashDistance, predictedEnd, collisionHit, currentState );

Debug tooling isn't a luxury. For gameplay systems, it often cuts debugging time from hours down to minutes. A good overlay can show the state, input, cooldowns, collision sweep, and the reason for an interruption.

Performance: the small things add up

Readability > cleverness

A brilliantly terse three-line state transition can be elegant. The question is whether it'll still be elegant in six months, when someone adds a new interrupt. Change is natural in gameplay code. The architecture doesn't just need to nicely represent the current feature list – it needs to survive the next five modifications too.

Engineering

Good gameplay code isn't professional because it's complicated. It's professional because it keeps complicated behavior inside an understandable system.

Day three, 16:48

9. So when is it actually done?

Let's look back at the first twenty minutes. You could press the button back then too, and the character moved forward back then too. So what happened over three days? The feature's core idea didn't change. The guarantees built up around it did.

After 20 minutes After 3 days
Input calls a function Input policy + buffering + state validation
Forward impulse Controlled movement + collision + slopes + ledges
One constant Designer config + validation + tuning
One animation Gameplay/animation authority + interrupts
Local state Prediction + server validation + replication
"Looks good to me" QA matrix + debug tools + reproducibility
Looks finished Maintainable and extensible

The definition of production-ready

Production-ready doesn't mean bug-free. No such system exists. It means the feature works according to known rules, handles the relevant edge cases, cooperates with its environment, and can be tuned, tested, debugged, and safely extended.

The point, in one sentence

Summary

Working code proves you can implement the idea. A good gameplay system proves you understand which game it has to live in.

That's why "this takes twenty minutes to write" is misleading. Often, it's completely true. The core of the mechanic really can be written in twenty minutes. It's just that the player isn't going to use the core of the mechanic. The player is going to break the entire system – next to a wall, mid-jump, at 38 FPS, with 120 ms of ping, combined with some other feature.

And that's exactly where gameplay programming really begins.
Prototype → System → Gameplay → Experience