February 5, 2026 · Game Development · 9 min read

How I Built a Mobile Game in a Weekend with Godot

Last month I challenged myself to build a complete, playable mobile game in a single weekend. Not a prototype. Not a "coming soon" placeholder. A finished game with menus, scoring, sound, and an exportable APK. I chose Godot 4.3 as my engine, and here's exactly how it went.

Why Godot?

I've used Unity for years, but the runtime fee debacle in 2023 left a sour taste. Godot is open source, lightweight (the editor is under 40 MB), and its GDScript language feels like Python with types — perfect for rapid prototyping. For a weekend jam, the last thing I want is waiting for a 2 GB editor to compile shaders.

Godot's 2D pipeline is also genuinely excellent. Node-based scene composition, a built-in animation player, and a physics engine that "just works" for simple games. The mobile export pipeline has improved massively since 4.0, though it still has some quirks I'll get into.

Friday Night: The Concept

I started Friday at 9 PM with nothing but a rule: the game must be playable with one thumb. That constraint eliminates complex control schemes and forces simple, addictive gameplay. I settled on a vertical scroller where you tap to switch lanes and dodge obstacles — think a stripped-down version of those subway runner games, but with a twist: the speed increases based on your score multiplier, and obstacles have patterns you can learn.

I sketched the core loop on paper: tap to move, dodge obstacles, collect coins, hit milestones that increase speed. No power-ups, no shop, no social features. Scope is the enemy of weekend projects.

Saturday Morning: Core Mechanics

By 10 AM Saturday I had the project set up. Godot's project structure is file-system based — your scenes are .tscn files, scripts are .gd files, and everything lives in a res:// directory. No hidden metadata, no binary blobs. I organized mine like this:

res://
├── scenes/
│   ├── main.tscn
│   ├── player.tscn
│   ├── obstacle.tscn
│   └── ui/
│       ├── hud.tscn
│       └── game_over.tscn
├── scripts/
│   ├── player.gd
│   ├── obstacle_spawner.gd
│   ├── game_manager.gd
│   └── score_manager.gd
├── assets/
│   ├── sprites/
│   └── audio/
└── export_presets.cfg

The player script was about 40 lines of GDScript:

extends CharacterBody2D

@export var lane_width: float = 120.0
@export var switch_speed: float = 12.0

var current_lane: int = 1  # 0, 1, 2
var target_x: float

func _ready():
    target_x = position.x

func _input(event):
    if event is InputEventScreenTouch and event.pressed:
        if event.position.x < get_viewport_rect().size.x / 2:
            current_lane = max(current_lane - 1, 0)
        else:
            current_lane = min(current_lane + 1, 2)
        target_x = (current_lane - 1) * lane_width

func _physics_process(delta):
    position.x = lerp(position.x, target_x, switch_speed * delta)

Simple, but it feels responsive. The lerp on position gives smooth lane transitions without needing an animation system. I spent about an hour tweaking switch_speed and lane_width until the feel was right — this is the part no tutorial teaches you. Game feel is iteration, not code.

Saturday Afternoon: Obstacles and Scoring

The obstacle spawner uses Godot's timer node to create obstacles at intervals. Each obstacle is a scene with a StaticBody2D, a sprite, and a collision shape. They move down the screen at a speed controlled by the game manager.

I created three obstacle patterns: single block (one lane), double block (two lanes, forcing you into the third), and a "sweeper" that moves horizontally. The spawner picks randomly but weights singles higher at low scores and introduces sweepers only after 500 points.

Scoring is simple: 10 points per obstacle dodged, with a multiplier that increases every 200 points. The multiplier also increases obstacle speed by 8%, which creates a natural difficulty curve. By the time you hit 1000 points, the game is genuinely frantic.

Saturday Evening: UI and Polish

Godot's Control nodes handle UI. I built the HUD as a CanvasLayer with a score label (top center) and a multiplier indicator (top right). The game-over screen is a separate scene that slides in from the bottom using a Tween node.

Polish makes or breaks a game jam project. I spent two hours on things that don't sound impressive but matter enormously: screen shake on collision (4 pixels, 0.2 seconds), a particle burst when you collect coins, a slight slow-motion effect on death (Engine.time_scale = 0.3 for half a second), and audio feedback for every interaction.

For audio, I used sfxr (the online version at sfxr.me) to generate retro sound effects in about 20 minutes. Background music came from a CC0 loop I found on OpenGameArt. Total audio production time: 30 minutes.

Sunday: Mobile Export and the Gotchas

This is where things got interesting. Godot's Android export requires the Android SDK, a debug keystore, and some configuration in Editor Settings. The documentation is decent but assumes you have the SDK already installed. If you're on Linux like me, it's a matter of installing android-tools and pointing Godot to the right paths.

The first gotcha: touch input. My InputEventScreenTouch code worked perfectly in the editor with mouse clicks simulating touches, but on a real phone, multi-touch caused issues. If you accidentally touched with two fingers, both lanes would trigger. Fix: add a flag that ignores input for 100ms after each lane switch.

The second gotcha: screen resolution. I designed for 1080x1920, but phones have notches, rounded corners, and navigation bars. Godot's stretch mode helps — set it to canvas_items with expand aspect — but I still had to add safe area margins. The DisplayServer.get_display_safe_area() function is your friend here.

The third gotcha: performance. My particle effects, which ran at 60 FPS in the editor, dropped to 40 FPS on a mid-range phone. The culprit was GPU particles — Godot's GPU particle system on mobile OpenGL ES 3.0 is slower than CPU particles for small counts. Switching to CPUParticles2D fixed it instantly.

Sunday Afternoon: The Finish Line

By 4 PM Sunday I had a signed APK. The game had a main menu, gameplay, scoring with local high scores (saved via Godot's ConfigFile), a game-over screen with retry, and working touch controls. Total development time: roughly 18 hours of actual coding across the weekend.

Is it a masterpiece? No. But it's a complete game that I built from scratch in a weekend, and that's the point. Weekend projects aren't about perfection — they're about finishing. Every shipped game, no matter how small, teaches you more than a hundred unfinished prototypes.

Tips for Your Own Weekend Game Jam

  • Constrain your input method. One-thumb games have the tightest scope.
  • Skip procedural generation. Hand-crafted obstacle patterns are faster to implement and easier to balance.
  • Use placeholder art until Sunday. Grey boxes that move correctly beat beautiful sprites that don't.
  • Test on a real device by Saturday night. Every mobile-specific bug I found would have been a Sunday crisis if I'd waited.
  • Set a "content lock" time. Mine was Sunday noon — after that, only bug fixes and polish. No new features.

Godot proved itself as a serious rapid-prototyping tool. The editor is fast, GDScript is readable, and the export pipeline — quirks aside — gets you from code to APK without leaving the engine. If you're thinking about game dev but haven't started, give yourself a weekend and see what happens.

💬 Comments

← Back to Blog