test: full builder rewrite with player state machine, camera, and manual _ready()

- _build_player: creates Player (CharacterBody3D) with capsule,
  MovementStateMachine + 6 state children, attaches movement script
- Calls player._ready() manually after set_script (GDScript
  set_script does not auto-trigger _ready)
- Camera parented under Player so it follows movement
- Restores _build_camera() which was accidentally dropped
This commit is contained in:
2026-06-03 00:28:56 -04:00
parent eeb074e58a
commit bb6cac9d7a
+33 -8
View File
@@ -1,7 +1,7 @@
extends Node3D
class_name TestLevelBuilder
## Builds the test level ONLY from code — no .tscn SubResource references.
## Automatically builds when attached to the TestLevel root node.
## One-click playtest: attach to TestLevel root, F5.
func _ready() -> void:
build()
@@ -10,14 +10,15 @@ func build() -> void:
_build_floor()
_build_walls()
_build_lighting()
_build_player()
_build_camera()
_build_ui()
func _box_static(pos: Vector3, size: Vector3, color: Color) -> StaticBody3D:
var body := StaticBody3D.new()
body.position = pos
body.name = "Static_" + str(pos)
body.position = pos
add_child(body)
var shape := CollisionShape3D.new()
shape.shape = BoxShape3D.new()
@@ -38,7 +39,6 @@ func _build_floor() -> void:
func _build_walls() -> void:
# Thin, tall walls for wallrun / walljump testing
_box_static(Vector3(0, 3, -15), Vector3(30, 6, 0.4), Color(0.55, 0.45, 0.35))
_box_static(Vector3(15, 3, 0), Vector3(0.4, 6, 30), Color(0.55, 0.45, 0.35))
_box_static(Vector3(-15, 3, 0), Vector3(0.4, 6, 30), Color(0.55, 0.45, 0.35))
@@ -55,18 +55,43 @@ func _build_lighting() -> void:
add_child(sun)
func _build_player() -> void:
var player := CharacterBody3D.new()
player.name = "Player"
player.position = Vector3(0, 1.5, 8)
add_child(player)
var shape := CollisionShape3D.new()
shape.shape = CapsuleShape3D.new()
shape.shape.radius = 0.4
shape.shape.height = 1.8
player.add_child(shape)
var sm := Node.new()
sm.name = "MovementStateMachine"
player.add_child(sm)
for state_name in ["ground", "air", "wall_run", "wall_cling", "slide", "dash"]:
var st := Node.new()
st.name = "state_" + state_name
sm.add_child(st)
var mover_script = load("res://movement/player_movement_controller.gd")
if mover_script:
player.set_script(mover_script)
# set_script after node is in tree does not auto-call _ready()
# Manually initialize the controller
if player.has_method("_ready"):
player._ready()
func _build_camera() -> void:
var cam := Camera3D.new()
cam.name = "FpsCamera"
cam.current = true
cam.position = Vector3(0, 1.7, 0)
cam.fov = 90.0
var player_node = get_node_or_null("Player")
if player_node:
player_node.add_child(cam)
else:
add_child(cam)
# Spawn marker
var spawn := Node3D.new()
spawn.name = "PlayerSpawn"
spawn.position = Vector3(0, 1.5, 8)
add_child(spawn)
func _build_ui() -> void: