What you'll build
A small but complete 2D platformer with:
- A character that moves and jumps with real game feel (coyote time, jump buffer, variable jump height)
- 3 hand-built tilemap levels
- One enemy type that patrols and damages on contact
- Coin pickups with a score display
- A flag at the end of each level that loads the next
- Sound effects for jump, pickup, and hurt
- A pause menu
- A WebGL build uploaded to itch.io that anyone can play in their browser
Nothing is hand-waved. Every script in this guide is generated by Claude using a real prompt that you can copy. By the end, the entire game lives in a single Unity project you can keep building on.
What you need before you start
- Unity 2022.3 LTS installed (free, ~5 GB download from unity.com/download)
- A free Claude account at claude.ai (the free tier is enough for this project; Pro removes the per-day cap if you marathon)
- A free itch.io account (we'll publish to it on Day 2)
- About 12 hours spread however suits you. The original test build was 6 hours Saturday + 6 hours Sunday with breaks.
- Comfort opening Unity and dragging components onto a GameObject. We'll explain everything else.
How to use this guide
Read it sequentially. Don't skip ahead — each step's output becomes the next step's input. If something breaks, the gotchas at the end of each step have the most common fix. If a prompt doesn't return clean output the first time, paste back any errors and ask Claude to fix them. The full prompts here are tested but Claude's outputs will vary slightly run-to-run.
Each step is a self-contained chunk of work with a time estimate. You can pause between any of them.
Create the project
Open Unity Hub. Click New Project. Pick the 2D Core template (NOT 3D, NOT 2D URP — keep it simple). Name it weekendplatformer. Wait for Unity to load (it will take a minute on first run).
In the Project pane, create these folders so things don't end up in a chaotic flat list later:
Scripts/Sprites/Audio/Tiles/Scenes/
The default scene is called SampleScene. Rename it to Level01 and drag it into the Scenes/ folder. Save. (File → Save.)
A placeholder character sprite
You have two free options. Pick whichever is faster for you:
- Use a Pixeldex sprite. Go to the sprite library, download any of the engine mascots as PNG (256 size works well), drag the file into Unity's
Sprites/folder. Unity will auto-import it as a Sprite. - Draw your own. Open Aseprite or Krita. New file, 32×32 pixels, transparent background. Sketch a humanoid blob. Export as PNG. Drag into Unity.
Once the sprite is in the Project pane, drag it into the Scene view. You should see your character on a blank gray background. Don't worry about the art yet. A working blob beats a beautiful nothing.
Movement that feels good
This is the foundation. If movement feels stiff, nothing else you build will be fun. We'll use Pixeldex's coyote-time platformer controller prompt — it bakes in the three "feel" details (coyote time, jump buffer, variable jump height) that separate a playable platformer from a frustrating one.
Open claude.ai/new, go to the prompt page, click Copy prompt, paste into Claude, send. Save the output as Scripts/PlatformerController.cs.
Now wire it up in the scene:
- Select your character GameObject. Click Add Component → Rigidbody 2D. Set Body Type to
Dynamic, Gravity Scale to3, and check Freeze Rotation Z. (No-rotation is what stops the character from tumbling on a slope.) - Add Component → Box Collider 2D. Adjust the size to roughly match the sprite.
- Right-click the character in the Hierarchy → Create Empty. Name it
GroundCheck. Position it at(0, -0.5, 0)relative to the character (so it sits at the bottom). - Add Component → PlatformerController (our script). Drag
GroundCheckinto the script's groundCheck field in the Inspector. - In the top-right of the editor, click the layer dropdown and create a new layer called Ground. Set the script's groundLayer mask to Ground.
- Create a long, thin rectangle GameObject below the character to act as a temporary floor. Set its layer to Ground. Add a
Box Collider 2D.
Press Play. WASD or Arrow keys move you. Space jumps. Walk off the edge of the temporary floor — you should still be able to jump for ~0.1 seconds after leaving the ground. That's coyote time. Hold space briefly = short jump; hold longer = full jump. That's variable jump height.
Tune until it feels right. Don't move on until movement is satisfying. The default values in the SerializeField fields are a starting point. Try speed = 7, jumpForce = 14, coyoteTime = 0.1, jumpBufferTime = 0.1. If it feels floaty, raise gravity scale to 4.
Common gotcha: if the character jitters when moving, set the Rigidbody2D's Interpolation to Interpolate. If you can't jump at all, your GroundCheck is probably overlapping the Ground layer at rest — move it down by 0.1 units.
A real tilemap level
Now we turn the temporary floor into a real level. Unity's Tilemap system handles this nicely.
- Right-click in Hierarchy → 2D Object → Tilemap → Rectangular. This creates a Grid + a Tilemap child. Set the Tilemap's layer to Ground.
- Add a Tilemap Collider 2D component to the Tilemap. Then add a Composite Collider 2D (which auto-adds a Rigidbody2D — set its Body Type to
Static). Check the Tilemap Collider's Used By Composite box. This merges all your tile colliders into one efficient shape. - Create some tile assets. Right-click in
Tiles/→ Create → 2D → Tiles → Rule Tile. Or for the simplest start: drag any 32×32 PNG intoTiles/, then right-click it → Create → 2D → Tiles → Tile. Repeat for a few different colors so you have a tile palette. - Open Window → 2D → Tile Palette. Create a new palette in
Tiles/Palette/. Drag your tile assets into the palette window. - With the Tilemap selected, use the brush in the Tile Palette window to paint a level. Make it small at first — 30 tiles wide, 15 tall. Leave gaps for jumping. Add a high platform that requires variable jump to reach.
Press Play. Walk and jump across the level. Iterate — paint, test, paint, test. Don't worry about pretty art yet. Worry about whether the jump distances feel possible.
Common gotcha: if the character falls through tiles, the Tilemap Collider isn't set up. Re-check that you added Composite Collider, set Rigidbody2D to Static, and checked Used By Composite on the Tilemap Collider.
A goal that loads the next level
Right-click in Hierarchy → Create Empty. Name it Goal. Add a Sprite Renderer with a flag-shape sprite (or any colored rectangle for now). Add a Box Collider 2D and check Is Trigger.
Now we need a script that triggers a scene load when the player touches it. Send this prompt to Claude:
$ Build me a Unity 2022.3+ GoalTrigger MonoBehaviour. // behavior - On OnTriggerEnter2D with a GameObject tagged "Player", load the next scene. - nextSceneName is a public string, SerializeField. - A 0.5s fade-to-black before SceneManager.LoadScene. - Use a CanvasGroup the script finds at runtime (or auto-creates if missing). // constraints - Single file, no dependencies beyond UnityEngine + UnityEngine.SceneManagement + UnityEngine.UI. - No coroutines if avoidable; use a simple float timer in Update. // return format - One file GoalTrigger.cs in a code block. - No prose before or after.
Save as Scripts/GoalTrigger.cs. Attach to the Goal GameObject. Set nextSceneName to Level02.
Tag your character: select it, in the Inspector click the Tag dropdown → Add Tag → create Player → assign to character.
Add Level02 and Level03 scenes (Save Scene As) and add them to File → Build Settings → Scenes In Build. Otherwise SceneManager won't find them.
Test: walk into the goal. Screen fades, next scene loads. (Level02 will be empty for now — that's fine, you'll paint it next time you have an hour.)
An enemy that hurts you
An enemy needs three parts: a sprite, a patrol behavior, and a damage interaction.
Sprite: drag any 24×24 colored PNG into the Sprites folder, or use the PICOMON mascot from the sprite library. Drag it into the scene at a location the player can reach.
Patrol behavior: send this prompt to Claude:
$ Build me a Unity 2022.3+ enemy patrol controller. // shape - Single C# MonoBehaviour, EnemyPatrol.cs. - Requires Rigidbody2D (Kinematic) and BoxCollider2D. - A SerializeField float patrolDistance (default 3) and patrolSpeed (default 2). // behavior - Enemy moves left and right within patrolDistance units of its starting position. - Flips its Sprite Renderer's flipX when changing direction. - Uses Rigidbody2D.MovePosition in FixedUpdate. // damage - A separate method TakeDamage() that destroys the enemy (we'll wire this to player-jumps-on-head later). - A public int contactDamage = 1; (used by the player on collision). // return format - One file. Brief comments only. - No prose before or after.
Save as Scripts/EnemyPatrol.cs. Attach to your enemy. Add Rigidbody2D (Kinematic), Box Collider 2D. Test in Play mode — enemy should patrol back and forth.
Damage interaction: we need the player to take damage when they touch an enemy. Send this prompt:
$ Build me a Unity 2022.3+ PlayerHealth MonoBehaviour. // shape - Single file PlayerHealth.cs. - SerializeField int maxHP = 3. - A public method TakeDamage(int amount). - A public int CurrentHP property. // behavior - On OnCollisionEnter2D with a GameObject containing an EnemyPatrol component, call TakeDamage(enemy.contactDamage) and brief invincibility for 1s. - During invincibility, flash the Sprite Renderer alpha between 1 and 0.3 at 10Hz. - On HP <= 0, reload the current scene. // constraints - No coroutines. Use a float timer in Update. - Single file. Brief comments only on the invincibility flash logic. // return format - One file PlayerHealth.cs in a code block. - No prose before or after.
Save as Scripts/PlayerHealth.cs. Attach to the player. Test: walk into the enemy. Player should flash, take damage, and reload the scene if hit 3 times.
Common gotcha: if nothing happens on contact, you probably need a non-Trigger collider on both objects. Trigger colliders fire OnTriggerEnter, not OnCollisionEnter.
Coin pickups + score display
Send this prompt to Claude:
$ Build me two Unity 2022.3+ scripts: Coin.cs and ScoreManager.cs. // ScoreManager.cs - A singleton MonoBehaviour. Static instance. - Public method AddScore(int amount). - A public int CurrentScore property. - Updates a TextMeshProUGUI label assigned in the inspector. Format: "Score: 123". - Persists across scene loads (DontDestroyOnLoad). // Coin.cs - Requires BoxCollider2D set to Trigger. - SerializeField int value = 10. - On OnTriggerEnter2D with the player, call ScoreManager.instance.AddScore(value), play a quick scale-down animation (0.15s, lerp scale from 1 to 0), then Destroy. // constraints - Two files in two code blocks, with file paths as headers. - Coin animation: simple Update timer, no coroutines. - Brief comments only on the singleton pattern. // return format - Two code blocks, no prose before or after.
Save as Scripts/Coin.cs and Scripts/ScoreManager.cs.
Set up the score UI:
- Right-click Hierarchy → UI → Canvas. (Unity will add an EventSystem too.)
- Right-click the Canvas → UI → Text - TextMeshPro. Pop up appears asking to import TMP Essentials — accept.
- Position the text in the top-left corner. Default text "Score: 0".
- Create an empty GameObject called ScoreManager. Attach
ScoreManager.cs. Drag the TextMeshPro text into the script's label field.
Create coin GameObjects: drag a coin sprite into the scene, add Box Collider 2D (Is Trigger), attach Coin.cs. Duplicate (Cmd+D / Ctrl+D) and place 10 around the level.
Test. Coins should disappear with a scale-down when collected, score should increment.
Sound effects + screen shake
Game feel comes from juice. Two cheap upgrades give you most of it.
Free sound effects: head to sfxr.me (more options on the audio toolkit page) (free, in-browser, CC0). Generate three SFX:
- Jump (use the "Jump" preset, randomize until you like one)
- Coin pickup (use the "Pickup/Coin" preset)
- Hurt (use the "Hit/Hurt" preset)
Download each as WAV. Drag into Audio/.
Send this prompt to Claude:
$ Build me a Unity 2022.3+ AudioBus singleton. // shape - Single file AudioBus.cs. - Static instance, DontDestroyOnLoad. - An AudioSource component (auto-add in Awake). - A public method PlayOneShot(AudioClip clip, float volume = 1). - SerializeField AudioClip references: jumpClip, coinClip, hurtClip. - Convenience methods: PlayJump(), PlayCoin(), PlayHurt() that wrap PlayOneShot with a small pitch jitter (+- 0.05) so identical SFX don't sound mechanical when triggered rapidly. // constraints - Single file. Brief comments only on the pitch jitter logic. // return format - One file AudioBus.cs in a code block. - No prose before or after.
Create an empty GameObject called AudioBus, attach the script, drag in the clips. Now from your other scripts, call AudioBus.instance.PlayJump() in the jump method, PlayCoin() in the coin script, PlayHurt() in PlayerHealth.
Screen shake on hit: send this prompt:
$ Build me a Unity 2022.3+ ScreenShake MonoBehaviour. // shape - Attach to the Main Camera. - Static method ScreenShake.Trigger(float duration, float magnitude). - Stores the camera's resting position on Awake. // behavior - For 'duration' seconds, jitter the camera position by Random.insideUnitCircle * magnitude each frame. - Snap back to resting position when done. // constraints - Single file. No coroutines. // return format - One file ScreenShake.cs in a code block. - No prose before or after.
Attach to Main Camera. From PlayerHealth's TakeDamage method, call ScreenShake.Trigger(0.2f, 0.15f). Damage now feels weighty.
A pause menu
Send this prompt:
$ Build me a Unity 2022.3+ PauseMenu MonoBehaviour. // shape - Attach to a Canvas with a child Panel containing two Buttons: Resume, Quit-to-Title. - SerializeField references to: pausePanel (GameObject), resumeButton, quitButton. // behavior - Press Escape to toggle pausePanel and Time.timeScale (1 / 0). - Resume button hides the panel and sets Time.timeScale = 1. - Quit button loads scene "Title" with timeScale reset to 1. - On scene load, ensures the panel starts hidden and timeScale = 1. // constraints - Single file. No coroutines. // return format - One file PauseMenu.cs in a code block. - No prose before or after.
In the scene: Right-click Canvas → UI → Panel. Add two TextMeshPro buttons inside: Resume, Quit. Style them to taste. Attach PauseMenu.cs to the Canvas, wire the references.
Test: press Escape mid-game. Game pauses, panel appears. Press again to resume.
(For "Title" scene, just Save Scene As → Title with a single TMP text saying "WeekendPlatformer · Press Space to start" and a script that loads Level01 on Space.)
Build and ship to itch.io
Build to WebGL:
- File → Build Settings. Add all your scenes (Title, Level01, Level02, Level03) in order.
- Switch platform to WebGL. (First time will install the WebGL module if you didn't earlier — about 2 GB download.)
- Player Settings (button at bottom): set Compression Format to
Disabled(itch.io doesn't serve compressed builds well by default). Set Resolution to 960×540. - Click Build. Save to a folder called
Build/. Wait ~5 minutes.
Upload to itch.io:
- Zip the entire Build folder.
- itch.io → Upload new project. Name it. Set Kind of project to HTML. Pricing: Free (or "no payments").
- Upload your zip. Check This file will be played in the browser. Set the embed dimensions to 960×540.
- Add a screenshot (just take a Cmd+Shift+4 of your game running in the editor).
- Save and view page. Test by playing in the browser.
Your game is live on the internet. Share the URL on r/gamedev, in your Discord, with three friends.
What to do next
You now have a foundation. Things to add when you have another evening or weekend:
- Save and load. Use the Unity JSON save / load prompt to persist score across sessions.
- More enemy types. The EnemyPatrol script is a starting pattern. Make a JumpingEnemy, ShootingEnemy, BossEnemy by copying and modifying.
- A proper level select. List all the levels with stars based on coins collected.
- Better art. Now that the game is fun, replace the placeholder sprites. Use the sprite spec builder to plan multi-directional character sprites if you want to expand to top-down.
- Music. incompetech.com has free CC-BY tracks. Drop one into Audio/ and play it in a single AudioSource on the Main Camera.
- Submit it to a game jam. itch.io is the home of game jams. The friction to enter one is "you have a playable game on itch." You do.
Why this works
Three things make this 12-hour walkthrough actually finish-able:
- Scope is locked. One enemy type. One pickup type. One goal type. Three short levels. The scope of "what counts as done" is tiny on purpose. Most solo platformer attempts die because scope creeps.
- Claude does the boilerplate. You're not staring at a blank script wondering how to write a state machine. You're reading Claude's output and tweaking. The cognitive load is review, not generation.
- Movement comes first. If movement feels good, everything else is decoration. Step 3 takes 60 minutes on purpose. Don't skimp.
License + sharing
This walkthrough is CC0. Copy from it, fork it, sell something built with it. Same for the prompt outputs Claude returned — they're yours. If you ship something with this guide, a link back to pixeldex.app in your itch description helps the next solo dev find this. No requirement, just appreciated.
If we shut down Pixeldex one day, all of this — guides, prompts, sprites, the spec builder — goes open source. The promise on every page applies to this walkthrough too.