If your lift isn’t moving and you’re staring at “0 nearby interactables,” you’re not alone.
This guide shows a clean setup that uses already-instanced buttons in your scene, while keeping lift logic in a single script.
This approach avoids runtime spawning and instead links existing nodes by NodePath — perfect for level-authored layouts.
0) What Are the Lift Pieces Made Of?
Before wiring, it helps to know what each piece actually is:
- Interactable: Base script (
interactables/interactable.gd) that auto‑adds nodes to theinteractablegroup so the player can detect and use them. - InteractableButton: Reusable button script (
interactables/button.gd) that extendsInteractableand handles interaction prompts + button signals. - LiftButton: Specific lift button script (
interactables/lift_button.gd) that extendsInteractableButtonand calls methods on aLiftFloor. - LiftFloor: The moving platform script (
interactables/lift_floor.gd) that contains movement logic and wires buttons.
Scene composition:
- LiftFloor node:
CharacterBody3Dwithlift_floor.gdattached. - LiftButton scene:
interactables/lift_button.tscnwhose root isNode3Dwithlift_button.gdattached plus anArea3Dfor interaction. - Start/End markers:
Marker3Dnodes used by the lift to know where to move.
This means: the button is a scene, the lift floor is a scripted node, and the player looks for nodes in the interactable group.
1) Scene Layout (Lift Test Example)
In your scenes/lift_test.tscn, you should have:
LiftFloor(the moving platform)LiftButton(main button; child ofLiftFloor)Start Call ButtonandEnd Call Button(in the scene root)StartPointandEndPointmarkers
All buttons are instances of interactables/lift_button.tscn.
2) Lift Floor Script: Accept Existing Nodes
Use NodePath exports to point at existing button instances:
@export var lift_button_path: NodePath
@export var call_button_start_path: NodePath
@export var call_button_end_path: NodePath
Then, in _ready():
_lift_button = _get_button_from_path(lift_button_path, "LiftButton")
_call_button_start = _get_button_from_path(call_button_start_path, "CallButtonStart")
_call_button_end = _get_button_from_path(call_button_end_path, "CallButtonEnd")
_setup_button(_lift_button, "toggle", "Press F to use lift")
_setup_button(_call_button_start, "move_to_start", "Press F to call lift (start)")
_setup_button(_call_button_end, "move_to_end", "Press F to call lift (end)")
This lets you drag existing nodes into the inspector and wire the lift without spawning new scenes.
3) Ensure Buttons Are Properly Scripted
Your button scene (interactables/lift_button.tscn) must have lift_button.gd attached.
If the script is missing, the button shows up as plain Node3D and won’t be “interactable.”
Checklist:
- Root node of
lift_button.tscnuseslift_button.gd lift_button.gdextendsInteractableButtonInteractableButtonextendsInteractableInteractable._ready()adds the node to theinteractablegroup
4) Why “0 nearby interactables” Happens
The player prints:
Player checking 0 nearby interactables in 'interactable' group:
That means one of these is true:
- The button script never ran (wrong script attached)
- The button never got added to the
interactablegroup - The node isn’t the correct type (
LiftButtonvs plainNode3D)
A simple fix is to ensure your setup code calls:
button.add_to_group("interactable")
5) Final Wiring in the Inspector
Select the LiftFloor node and assign:
lift_button_path→LiftButton(child)call_button_start_path→../Start Call Buttoncall_button_end_path→../End Call Buttonstart_position→StartPointend_position→EndPoint
That’s it — no prefab spawning required.
6) Debugging Tips That Actually Help
Add a few debug prints to see what’s happening:
- Lift logs:
- assigned buttons
- target changes
- movement state
- Button logs:
- interaction blocked
- method called
- target lift object
This gives you instant visibility when something is “wired but not working.”
Wrap‑Up
Best practice here is simple:
- Use NodePath for existing nodes
- Use PackedScene only when you’re spawning
- Always verify scripts are attached
- Groups matter for player detection
If you want a cleaner, decoupled version, convert to signals.
If you want a prefab lift, keep the PackedScene path and spawn inside the lift.
Leave a Reply