⭐ My First Video Game · Series 1 · Episode 1 of 6
🏎️ Part 1 · Zoom Zoom 💥 Part 2 · Loops & Explosions 🍄 Ep 2 · Jump Jump 🔫 Ep 3 · Bang Bang 🧚 Ep 4 · Fairy Survivors 🦍 Ep 5 · Barrel Blast 🗡️ Ep 6 · Pixel Quest
← Back to Part 1
🔧 Godot Workshop · Part 2 of 2

Zoom Zoom! Part 2 💥
Loops, Health & Explosions!

You built a car in Part 1 — now let's make a real race! Build a loop track, count laps with checkpoints, give your car health, make it explode, and add an AI opponent to race against! 🏆

👶 Age 7+ with a grown-up ⏱️ About 1 hour 🎮 Godot 4 (free!) ✨ 8 more steps
🏁 Workshop Progress 0 / 8 steps
1
🗺️
Rebuild the Track as a Loop
Replace the straight road with a rectangular circuit — 4 roads + inner grass!
▶ Current
🎯
Goal for this step

Replace your straight road with a rectangular circuit so you can drive in endless loops!

🧒
Little coder: A race with a road that just ends isn't much of a race! Let's build a square track — 4 pieces of grey road arranged in a giant loop. Like a real racing circuit! 🏟️

Delete the old road and rebuild

  1. 1Open track.tscn in Godot. Find your old grey ColorRect road in the Scene panel and delete it (press Delete or right-click → Delete Node).
  2. 2Add a new ColorRect as a child of Track. Make it grey (#444444). Set size to x: 1000, y: 150. Position it at the top of the screen — this is your first straight!
  3. 3Press Ctrl+D (or Cmd+D on Mac) to duplicate it. Move the copy to the bottom of the screen.
  4. 4Duplicate again — this one becomes the left side. Set its size to x: 150, y: 700 and position it on the left edge.
  5. 5Duplicate once more for the right side. Mirror it to the right edge.
  6. 6Add a green ColorRect in the centre to cover any gap in the grass. Size it to fill the inner rectangle.
👨‍👩‍👧
Grown-up: Ctrl+D is your best friend here — it duplicates a node with all the same settings. Let the child drag the rectangles around visually. It doesn't need to be pixel-perfect; a slightly wonky track adds character! Aim for roughly a 1000×700 outer rectangle of road.

Your track layout:

📦 Track (Node2D)
🟩ColorRect ← green background (grass)
RoadTop (ColorRect) ← top straight
RoadBottom (ColorRect) ← bottom straight
RoadLeft (ColorRect) ← left side
RoadRight (ColorRect) ← right side
🟩InnerGrass (ColorRect) ← centre grass patch
Move your Car to sit on one of the road sections when you're done. Press F6 to play and drive around the whole circuit! You should be able to loop forever.
✅ My loop track is built! →
2
🏁
Create the Checkpoint Scene
Build a gate system so cheaters can't just reverse over the finish line!
🔒 Locked
🎯
Goal for this step

Create a checkpoint scene that detects when the car passes through — so laps only count if you drive the whole circuit in order!

🧒
Little coder: Imagine invisible gates on the road. You have to drive through ALL of them in order before your lap counts. No sneaky shortcuts! 🚧
💡
We'll use an Area2DArea2D detects when physics bodies enter it — perfect for finish lines, pickups, and checkpoints! as our checkpoint. Like an invisible photoelectric beam across the road!

Create checkpoint.tscn

  1. 1Go to Scene → New Scene. Click "Other Node" and search for Area2D. Add it and rename it Checkpoint.
  2. 2Add a child: CollisionShape2D. In the Inspector, set Shape to New RectangleShape2D. Make it wide enough to stretch across the road — about x: 200, y: 30.
  3. 3Optionally add a ColorRect (semi-transparent yellow) so you can see the gate while building. You can make it invisible later!
  4. 4Attach a script to Checkpoint. Type this:
 checkpoint.gd
extends Area2D # Each checkpoint gets a number — set this in the Inspector! @export var id = 0 func _ready(): # Fire our function when a car drives through body_entered.connect(_on_body_entered) func _on_body_entered(body): if body.name == "Car": # Tell the track which checkpoint was hit get_parent().hit_checkpoint(id)
  1. 5Press Ctrl+S and save as checkpoint.tscn.
  2. 6Open track.tscn. Drag checkpoint.tscn onto the track 4 times — one on each side of the circuit.
  3. 7Click each checkpoint in the Scene panel. In the Inspector, set their id values to 0, 1, 2, 3 going clockwise around the track.
👨‍👩‍👧
Grown-up: The @export var id line makes the id field appear in the Godot Inspector — a really handy Godot feature! Make sure the ids are assigned in driving order (clockwise or anti-clockwise) so the lap validation works correctly in the next step.
✅ Checkpoints placed on the track! →
3
🔢
Add the Lap Counter
Update track.gd so laps only count when all checkpoints are hit in order!
🔒 Locked
🎯
Goal for this step

Update our track script to count laps when the car passes all checkpoints in the correct order. After 3 laps — you win!

🧒
Little coder: The track is going to be the brain of the race! It remembers which checkpoint you hit last, and when you hit all 4 in order — lap complete! It's like a combination lock! 🔐

Add a Lap Label to the HUD

  1. 1Open track.tscn. In the Scene panel, click CanvasLayer and add a child: Label.
  2. 2Rename it LapLabel. Set its Text to Lap: 1 / 3.
  3. 3Set font size to 32 and position it below the timer — about x: 20, y: 60.

Update track.gd

👨‍👩‍👧
Grown-up: This replaces the previous track.gd content. The key concept is expected_checkpoint — a counter that only advances when the checkpoints are hit in sequence. Hitting them out of order does nothing!
 track.gd
extends Node2D var lap = 1 var elapsed = 0.0 var racing = true var expected_checkpoint = 0 func _process(delta): if racing: elapsed += delta $CanvasLayer/TimerLabel.text = "Time: " + str(snappedf(elapsed, 0.1)) # Called by each checkpoint when the car passes through func hit_checkpoint(id): if id == expected_checkpoint: expected_checkpoint += 1 # All 4 checkpoints hit (0, 1, 2, 3) — lap complete! if expected_checkpoint == 4: lap += 1 expected_checkpoint = 0 $CanvasLayer/LapLabel.text = "Lap: " + str(lap) + "/3" # 3 laps done — you win! if lap > 3: racing = false $CanvasLayer/WinLabel.visible = true
🕹️ Try it!

Press Play and drive the full circuit. Does the Lap counter go up? Now try driving backwards — the lap should NOT count. Then try to complete 3 full laps to see the win screen!

✅ Lap counter is working! →
4
❤️
Give Your Car Health
Add a health variable — crashes slow you down!
🔒 Locked
🎯
Goal for this step

Add a health system to the car. As health drops, the car slows down — driving badly has consequences!

🧒
Little coder: Bumping into walls shouldn't be free! We're going to give our car a health bar. Drive carefully, or your car gets slower and slower — and eventually BOOM! 💥

Update car.gd — new variables at the top

💡
Replace the var speed and var turn_speed lines at the top of your car.gd with these four new variables:
 car.gd — variables
extends CharacterBody2D var health = 100 var max_speed = 400 var turn_speed = 3.5 var is_alive = true

Update _physics_process to use max_speed

In your movement code, change the line that sets velocity so it uses max_speed instead of the old hardcoded value:

 car.gd — velocity line (update this)
# Change this... velocity = Vector2.UP.rotated(rotation) * drive * 300 # ...to this: velocity = Vector2.UP.rotated(rotation) * drive * max_speed

Add a health bar to the HUD

  1. 1Open track.tscn. Add a ProgressBar node to your CanvasLayer.
  2. 2Rename it HealthBar. Set Min Value to 0, Max Value to 100, Value to 100.
  3. 3Position it top-right — about x: 900, y: 20. Set size to x: 200, y: 20.
👨‍👩‍👧
Grown-up: We'll actually wire up the health bar in Step 6 when we add collision damage. For now just placing the ProgressBar in the HUD is enough — it just won't change value yet. The is_alive flag will prevent the car from moving after an explosion.
💡 Health bar preview — drag the slider!
Health: 100 — Speed: 400
✅ Health variables added! →
5
💥
Make Cars Explode!
Add a take_damage() function and GPUParticles2D — BOOM!
🔒 Locked
🎯
Goal for this step

Add a take_damage() function that reduces health and slows the car — and when health hits zero, the car explodes with a particle effect!

🧒
Little coder: This is the most exciting step — we're going to make the car explode! Godot has a special node called GPUParticles2D that sprays glowing sparks everywhere. Let's add it! 🌟

Add GPUParticles2D to car.tscn

  1. 1Open car.tscn. Click the Car node and add a child: search for GPUParticles2D and add it.
  2. 2Rename it ExplosionParticles.
  3. 3In the Inspector, set Emitting to OFF and tick One Shot to ON. (We want it to burst once, not loop.)
  4. 4Click next to Process Material and choose New ParticleProcessMaterial.
  5. 5Click the material to open it. Set Emission Shape to Sphere, and Initial Velocity Min/Max to around 150 / 300. Set the colour to orange/red — this is our fireball!
📦 Car (CharacterBody2D)
🟧ColorRect
🔷CollisionShape2D
📸Camera2D
💥ExplosionParticles ← NEW!

Add take_damage() to car.gd

Add this function to the bottom of car.gd:

 car.gd — take_damage function
func take_damage(amount): if not is_alive: return # Can't take damage if already exploded health -= amount # Speed drops as health drops — 15% penalty feel max_speed = 400 * (health / 100.0) # If health is gone — EXPLODE! if health <= 0: is_alive = false $ExplosionParticles.emitting = true $ColorRect.visible = false # Hide the car body!
Also add if not is_alive: return as the very first line of your _physics_process function — this stops the car moving after it explodes!
👨‍👩‍👧
Grown-up: 400 * (health / 100.0) calculates speed as a percentage of max health. At 100 health you get full speed (400), at 50 health you get 200 — a natural feel without lots of extra maths. Note we use 100.0 not 100 to force floating-point division in GDScript.
✅ Cars can explode! 💥 →
6
💢
Speed-Based Crash Damage
Faster crashes hurt more — wire up collisions to take_damage()!
🔒 Locked
🎯
Goal for this step

Update _physics_process so hitting a wall at high speed deals real damage — gentle taps are fine, big crashes hurt!

🧒
Little coder: A tiny tap against the wall is different from a full-speed smash! We're going to check how fast the car was going when it crashed. Slow tap = no damage. Full speed = OUCH! 🤕

Update _physics_process in car.gd

Here is the complete updated _physics_process function. The new part starts after move_and_slide():

 car.gd — full _physics_process
func _physics_process(delta): if not is_alive: return # Can't drive if exploded! var drive = Input.get_axis("ui_down", "ui_up") var steer = Input.get_axis("ui_left", "ui_right") if drive != 0: rotation += steer * turn_speed * delta velocity = Vector2.UP.rotated(rotation) * drive * max_speed move_and_slide() # Did we hit something while moving? if get_slide_collision_count() > 0: var crash_speed = velocity.length() if crash_speed > 100: # Ignore tiny taps take_damage(10) # Bounce back slightly velocity = -velocity * 0.5

Wire up the health bar

In track.gd, add a line to the _process function to keep the HUD health bar updated:

 track.gd — add inside _process
# Add this inside _process, after the timer update: var car = get_node_or_null("Car") if car: $CanvasLayer/HealthBar.value = car.health
Press Play and drive straight into a wall fast. Does the health bar drop? Drive slower — is it less damage? Survive long enough to complete 3 laps!
👨‍👩‍👧
Grown-up: get_slide_collision_count() is Godot's built-in way to check if move_and_slide() hit anything this frame. velocity.length() gives us the speed as a single number (the magnitude of the velocity vector). This is a clean, game-dev-standard approach!
✅ Crash damage working! →
7
🟢
Add Repair Pickups
Drop glowing wrenches on the track — drive over them to heal!
🔒 Locked
🎯
Goal for this step

Create a glowing green repair pickup. When the car drives over it, health is restored and the pickup disappears!

🧒
Little coder: Every racing game needs power-ups! We're going to place glowing green healing pads on the track. Drive over them and your car gets fixed up! 🔧✨

Create repair_pickup.tscn

  1. 1Go to Scene → New Scene. Add an Area2D node and rename it RepairPickup.
  2. 2Add a child: CollisionShape2D. Set shape to RectangleShape2D, size x: 60, y: 60.
  3. 3Add a child: ColorRect. Size it x: 60, y: 60, position x: -30, y: -30. Set colour to bright green with some transparency (#06d6a0). Glowing!
  4. 4Attach a script and type this:
 repair_pickup.gd
extends Area2D func _ready(): body_entered.connect(_on_body_entered) func _on_body_entered(body): if body.name == "Car" and body.health < 100: body.health += 25 # Clamp health to 100 max if body.health > 100: body.health = 100 # Also restore some speed body.max_speed = 400 * (body.health / 100.0) queue_free() # Delete the pickup!
  1. 5Save as repair_pickup.tscn.
  2. 6Open track.tscn and drag repair_pickup.tscn onto the track 3 or 4 times. Place them around the circuit — ideally after the tight corners!
👨‍👩‍👧
Grown-up: queue_free() is Godot's safe way to delete a node mid-frame — it queues it for removal at the end of the frame rather than deleting it immediately, which prevents crashes. It's used for anything that should disappear: bullets, pickups, explosions.
🕹️ Try it!

Crash into walls on purpose to lower your health, then drive over a green pickup. Does the health bar go back up? You should also feel the car speed up again!

✅ Repair pickups work! →
8
🤖
Build the AI Opponent
ai_car.tscn + ai_car.gd — a bouncy red car to race against!
🔒 Locked
🎯
Goal for this step

Create a red AI car that drives forward and bounces off walls like a pinball. Drop it on the track and race against it!

🧒
Little coder: We're going to build a robot car! It doesn't think — it just zooms forward until it hits a wall, then bounces off like a pinball. It's chaotic and brilliant. Can you dodge it? 🤖🏎️
👨‍👩‍👧
Grown-up: Real racing AI uses navigation meshes and waypoints — way beyond this workshop! But a "pinball" car that bounces off walls is genuinely fun and teaches the exact same collision/physics concepts. The unpredictability is the feature!

Create ai_car.tscn from car.tscn

  1. 1In the FileSystem panel, right-click car.tscn and choose Duplicate. Rename the copy ai_car.tscn.
  2. 2Open ai_car.tscn. Click the ColorRect child and change its colour to red — so we know it's the enemy! 🔴
  3. 3Click the root Car node. Right-click → Detach Script. (We're giving it its own brain!)
  4. 4Right-click the root node again → Attach Script. Name it ai_car.gd. Type this:
 ai_car.gd
extends CharacterBody2D var speed = 250 func _ready(): # Start driving in the direction the car is facing velocity = Vector2.UP.rotated(rotation) * speed func _physics_process(delta): var collision = move_and_collide(velocity * delta) if collision: # Bounce off the wall like a pinball! velocity = velocity.bounce(collision.get_normal()) # Rotate car to face new direction rotation = velocity.angle() + (PI / 2)
  1. 5Save. Open track.tscn and drag ai_car.tscn onto the track. Place it on the road near the start!
  2. 6Press F5 (Play Project) and watch chaos unfold. Dodge the red car — if it hits you it deals crash damage! 😱
Tune the AI: Change the AI speed variable to make it faster or slower. Add two AI cars in different positions for extra mayhem! You can also rotate them to face different directions at the start.

🔴 Make it harder

Change var speed = 250 to 350. Add two AI cars on opposite sides of the track!

💛 Make it easier

Set speed = 150. Or remove the AI car damage from the player (only walls deal damage).

✅ Final Checklist

My track is a full loop circuit
Checkpoints are placed around the track
Laps count up correctly (and can't be cheated!)
The car takes crash damage from walls
The car explodes when health hits zero 💥
Repair pickups heal the car
A red AI car bounces around the track
I completed 3 laps without exploding!
🏆 I finished Part 2! →
🤖🏎️💥🏆🎉
You Mastered Godot Physics!!

You built a real racing circuit with laps, checkpoints, health, explosions, repair pickups, AND an AI opponent. That's serious game dev stuff — be very proud of yourself! 🚀

🔄 Start Again ← Back to Part 1 🍄 Next Workshop →
⭐ My First Video Game · Series 1
You finished the Zoom Zoom series! 🎉
Ready to build a Mario-style platformer next?
🍄 Next: Ep 2 · Jump Jump! →

🛠️ Free In-House Dev Tools

Make It Yours

Use these free browser tools alongside this workshop to create custom sprites, sounds, and colour schemes for your game. No installs. Free forever.

🎨
Pixel Studio
Draw sprites & animations
🗺️
Level Designer
Build 2D tile maps
🎵
SFX Studio
Create custom sound effects
🎨
Colour Palette
Build a game-ready colour scheme
🎲
Game Idea Gen
Random game concepts & prompts
🛠️ See All 20 Free Tools →