makepad/examples/godot/resources/template/tools/agent_eye.gd
2026-07-10 12:09:47 +02:00

74 lines
2.4 KiB
GDScript

# Lets the game-making agent peek at the running game without disturbing it.
#
# `tools/gd peek` drops `.agent/peek_request` into the project; this autoload
# notices within a quarter second, saves a short burst of screenshots plus the
# player's state to `.agent/live/`, and finishes with a `done` marker. The kid
# keeps playing the whole time — nothing is paused or restarted.
extends Node
const SNAPS := 4
const SNAP_GAP := 0.4
var _poll := 0.0
var _busy := false
func _ready() -> void:
# Godot's default ui_accept knows Enter and Space but no gamepad button, so
# a kid on a controller could walk (ui_left/right carry D-pad and stick
# bindings) but never jump. Bind A here, where every game gets it for free.
var jump := InputEventJoypadButton.new()
jump.button_index = JOY_BUTTON_A
InputMap.action_add_event("ui_accept", jump)
func _agent_dir() -> String:
return ProjectSettings.globalize_path("res://") + ".agent"
func _process(delta: float) -> void:
_poll += delta
if _poll < 0.25 or _busy:
return
_poll = 0.0
var request := _agent_dir() + "/peek_request"
if FileAccess.file_exists(request):
DirAccess.remove_absolute(request)
_snap_burst()
func _snap_burst() -> void:
_busy = true
var live := _agent_dir() + "/live"
# Start fresh so a sheet never mixes two peeks.
if DirAccess.dir_exists_absolute(live):
for file in DirAccess.get_files_at(live):
DirAccess.remove_absolute(live + "/" + file)
else:
DirAccess.make_dir_recursive_absolute(live)
var report := ""
for index in range(SNAPS):
var image := get_viewport().get_texture().get_image()
image.save_png("%s/f%04d.png" % [live, index])
report += _state_line(index)
if index < SNAPS - 1:
await get_tree().create_timer(SNAP_GAP).timeout
var state := FileAccess.open(live + "/state.txt", FileAccess.WRITE)
if state:
state.store_string(report)
state.close()
var done := FileAccess.open(live + "/done", FileAccess.WRITE)
if done:
done.store_string("ok")
done.close()
_busy = false
func _state_line(index: int) -> String:
var scene := get_tree().current_scene
if scene == null:
return "snap %d: no scene\n" % index
var player := scene.find_child("Player", true, false)
if player == null:
return "snap %d: no node named Player\n" % index
var line := "snap %d: %s pos=%s" % [index, player.name, player.position]
if player is CharacterBody2D:
line += " vel=%s floor=%s" % [player.velocity, player.is_on_floor()]
return line + "\n"