Collect one parcel when you are close enough
Use an explicit parcel state and a distance guard so interaction produces one clear result.
Godot 4.7.2 stable · standard edition · GDScript · 25 minutes estimated practiceContinue your working project from the previous lesson. Each download is the completed version of this lesson, for recovery and comparison. Start lesson one with checkpoint 01.
Add the facts the game remembers
Continue from lesson three, or start with checkpoint 03. In game.gd, add these declarations after the WALLS array and before the @onready variables.
The parcel can be waiting, carried or delivered. This lesson implements the first transition. The enum gives those states names, while INTERACT_RADIUS makes the interaction distance an explicit setting.
const PARCEL_START := Vector2(250, 250)
const INTERACT_RADIUS := 38.0
enum ParcelState { WAITING, CARRIED, DELIVERED }
var parcel_state := ParcelState.WAITINGHandle one interaction
Add the two functions below before _draw(), at the same outer indentation as _ready(). _unhandled_key_input listens for the built-in ui_accept action, which includes Space. try_interact checks the current state and distance before changing the parcel to carried.
The return statement stops a distant pickup. Once the parcel is carried, the WAITING branch is no longer eligible, so repeated interaction cannot collect another copy.
func _unhandled_key_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_accept"):
try_interact()
func try_interact() -> void:
if parcel_state == ParcelState.WAITING:
if player.position.distance_to(PARCEL_START) > INTERACT_RADIUS:
status.text = "Move beside parcel [P] before pressing Space."
return
parcel_state = ParcelState.CARRIED
status.text = "Carrying parcel. Delivery arrives in lesson 5."
queue_redraw()Show waiting and carried states
Insert the code below at the end of _draw(), after the wall-drawing loop and before the next func declaration. font is the local font variable already defined earlier in _draw.
When waiting, the parcel is drawn at PARCEL_START. When carried, it is drawn above the courier. The P label provides a text identifier in addition to color.
if parcel_state != ParcelState.DELIVERED:
var parcel_position := PARCEL_START if parcel_state == ParcelState.WAITING else player.position + Vector2(0, -28)
draw_rect(Rect2(parcel_position - Vector2(12, 12), Vector2(24, 24)), Color("d8884d"))
draw_string(font, parcel_position + Vector2(-5, 5), "P", HORIZONTAL_ALIGNMENT_LEFT, -1, 15, Color("3f3325"))Set a useful starting instruction
In _ready(), keep _add_walls() and replace its existing status.text assignment with the line below. Save and run. Move beside P and press Space.
At this checkpoint the courier can carry the parcel, but delivery is added in lesson five. Stop and run again to repeat the pickup test.
- First press Space while far from the parcel. It should stay at its starting location.
- Move near the parcel and press Space. It should follow above the courier and display a carrying message.
- Press Space several more times. There should still be one carried parcel.
status.text = "Find the parcel [P]. Move beside it and press Space to pick it up."Check your work.
- A distant interaction leaves the parcel waiting.
- A nearby interaction changes the parcel to carried.
- The carried parcel follows the courier.
- Repeated pickup interaction does not create extra parcels.
If something went wrong.
- The pickup range compares two positions in the same scene coordinate system.
- Keep the state check before the state assignment. Setting CARRIED before checking distance removes the guard.
- Insert drawing statements inside _draw(), where the local font variable exists.
Ask about one small step.
I am on the pickup lesson of One Small Delivery in Godot 4.7.2. I have WAITING, CARRIED and DELIVERED states, a 38-pixel interaction radius and a try_interact function. Help me test distant pickup, nearby pickup and repeated input. Explain the expected state before and after each case and compare it with what I actually observe.