You finish a Godot tutorial. The game runs. Then you try to change the rules and cannot work out which script to open. This exercise starts at that exact point: take a working coin game and give it a score target. You will decide where the rule belongs, change one script, and check what happens when the player cannot win.
Start with a game that already runs
Use the project from our first Godot game tutorial. Before changing it, collect the three coins and check that the score reaches 30. Keep a copy of that working project. If movement or pickups already fail, fix those first; otherwise you will be debugging two problems at once.
This is a Godot editor exercise. The scene explorer below helps you find the files, but it does not run GDScript. If you only have a browser available, try the coding practice first and return to this project when you can open Godot.
The change you are trying to make
Three coins worth 10 each cannot reach 35. Leave them that way for your first run: you need to see the unsuccessful result too. Later, change one coin to 25. Write down the totals you expect before touching the script.
Keep movement, art, sound, and the number of levels as they are. A new rule already gives you several decisions to make. Adding a shop or an enemy now would make it harder to tell whether you understood this change.
Which script owns the win condition?
Select a node to inspect it.
Groups objects in 2D space. Its children inherit its transform.
- Target score
- 35
Attach main.gd here. It counts points and decides which message to show.
A body controlled by your script. It supplies movement and collision methods for a 2D character.
Keep player.gd unchanged. Movement does not need to know the target.
Child of Main
Groups objects in 2D space. Its children inherit its transform.
Contains the three Coin.tscn instances.
Child of Main
An instance of a saved scene. It brings that scene’s root and children into this level.
- Value
- 10
Uses coin.gd with class_name CoinPickup. Reports its value when collected.
Child of Main/Coins
An instance of a saved scene. It brings that scene’s root and children into this level.
- Value
- 10
Keep this coin worth 10.
Child of Main/Coins
An instance of a saved scene. It brings that scene’s root and children into this level.
- Value
- 10
Change this instance to 25 for the second experiment.
Child of Main/Coins
Places its canvas items on a separate drawing layer, useful for a screen-space interface.
Keeps the labels in the interface layer.
Child of Main
Displays text, such as a score or a hint. A script can update its text property.
Main updates this label after a pickup.
Child of Main/HUD
Displays text, such as a score or a hint. A script can update its text property.
Displays the target, success, or insufficient-points message.
Child of Main/HUD
Click a node to inspect its job. Keep these names from the first-game tutorial.
The coin knows its own value and emits collected(value). Main already receives that event and stores the total. Put the target check in Main, where those numbers are available. Changing coin.gd would make each coin responsible for a total it does not own.
There are two different numbers here: score tells you how many points the player has, and remaining tells you how many pickups are left. A 25-point coin increases one number by 25 and decreases the other by one. If that distinction is unclear, trace the signal from the emitter to its listener before continuing.
Try the change before reading the solution
- Open main.gd and add an exported integer named target_score with a default of 35. Keep score and remaining.
- Find the function that receives a coin value. After updating both counters, compare score with target_score using >=. A score of 45 must also count as a win.
- Check success before checking whether coins have run out. The last coin might be the one that wins the game.
- Use a separate function to update the labels, and call it at startup as well as after a pickup. That avoids maintaining two versions of the same display logic.
If you get stuck, narrow the question. “How do I compare two integers in GDScript?” or “Why does this node path return null?” gives you something you can check. “Build the scoring system for me” skips the decisions this exercise is meant to teach. Looking up syntax is part of doing the exercise.
A complete main.gd to compare against
Replace main.gd with this file if you need the reference solution. Keep the original player.gd, coin.gd, node names, and restart input action. This version keeps remaining pickups available after reaching the target, so you can still collect all the points. Winning changes the message; it does not freeze the game.
extends Node2D
@export_range(1, 9999) var target_score: int = 35
var score: int = 0
var remaining: int = 0
@onready var score_label: Label = $HUD/ScoreLabel
@onready var hint_label: Label = $HUD/HintLabel
func _ready() -> void:
for child in $Coins.get_children():
var coin := child as CoinPickup
if coin:
remaining += 1
coin.collected.connect(_on_coin_collected)
_update_hud()
if remaining == 0:
hint_label.text = "Add Coin.tscn instances under Coins."
func _on_coin_collected(value: int) -> void:
score += value
remaining -= 1
_update_hud()
func _update_hud() -> void:
score_label.text = "Score: %d / %d" % [score, target_score]
if score >= target_score:
hint_label.text = "Target reached! Press R to try again."
elif remaining == 0:
hint_label.text = "Not enough points. Press R to try again."
else:
hint_label.text = "Reach %d points. R restarts." % target_score
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("restart"):
get_tree().reload_current_scene()Compare the code with your attempt one decision at a time. If you checked remaining first, explain what would happen when the last pickup also reaches 35. If you used ==, predict the result of jumping from 20 to 45. Those are useful differences to investigate; matching the reference line for line is unnecessary.
Check the awkward cases
- 10 + 10 + 10: the final score is 30 and the message says there were not enough points.
- 10 + 25 + 10: the target is reached on the second pickup. The third raises the score to 45 and the success message stays.
- 10 + 10 + 25: the final pickup jumps past 35 to 45. It must still succeed.
- Set Target Score on Main to 30 and restore all coin values to 10. The last pickup should succeed, even though remaining becomes zero at the same time.
- Remove every coin in a spare copy of the scene. Startup should ask you to add instances, rather than announcing a loss before play begins.
- Press R after a pickup, after success, and after running out of points. Check that the score resets and the coins return.
Use the Inspector on the individual Coin.tscn instance when changing Value. Editing the source scene can change defaults for other instances too. If a result differs from your prediction, record the order of pickups, their values, the target, and the message you actually saw. That is enough to make a useful bug report or a focused help request.
When you need another explanation
If the callback never runs, inspect the connection in _ready() and check that the coins are direct children of Coins. If CoinPickup is unknown, open coin.gd and fix its first parser error. If the score updates but the text is wrong, work inside _update_hud(). You do not need to replace the movement script to fix a label.
For syntax questions, use the GDScript cheat sheet. For help understanding what an existing script does, open the annotated scripts. If an AI assistant helps, ask it to explain the failing condition and suggest a test; compare its answer with the GDScript checks in our AI guide before accepting a rewrite.
Make the next attempt a little less guided
Return to the working copy from before this exercise. Add the target rule again using only the brief and your test list. Keep documentation available. Notice which part still sends you back to the solution: choosing the script, writing the condition, or setting up the scene. That gives you a specific topic to practice next.
Once that works, choose your own target and coin values so the player can win without collecting everything. Explain why you picked those numbers, then have someone try the level. You have made a small design decision and implemented the rule that supports it.
When you are ready to share the game, use the web-export troubleshooting guide to compare a local browser run with an uploaded copy.
Frequently asked questions
- Do I have to stop using tutorials to learn Godot?
- You can keep using tutorials and documentation. After a working example, try a change with a result you can check. In this exercise, that means adding a score target and checking both successful and unsuccessful attempts.
- Should I memorize GDScript before making my own game?
- You can look up syntax as you work. For this exercise, focus on knowing which script owns the score, what event changes it, and how you will recognize a correct result.
Sources and revision context
Published September 8, 2026. Written around a reader question from the linked community discussion; the exercise and explanation are original. Added a link to the web-export troubleshooting guide. Uses the first-game tutorial scene and signal flow. The reference script passed 28 headless checks in Godot 4.7.2 for initialization, signal-driven scoring, exact targets, overshoots, and insufficient points. Restart input and visual rendering were not tested in this review.