extends Node3D
## Minimal creature test — flat plane, single creature, flymode camera.
##
## Controls:
## [1] Spawn GroundCrawler [2] Spawn FlyingSerpent
## [3] Kill all creatures [4] Toggle pause creature AI
## [+/-] Scale creature [F12] Screenshot
## WASD: fly camera Mouse: look Scroll: speed
@onready var stats_label: Label = $CanvasLayer/StatsLabel
@onready var camera: Camera3D = $Camera3D
var _rng := RandomNumberGenerator.new()
var _creature: Node3D = null
var _creatures_paused: bool = false
# Fly camera
var _fly_speed: float = 10.0
var _mouse_sens: float = 0.003
var _cam_yaw: float = 0.0
var _cam_pitch: float = -0.3
func _ready() -> void:
_rng.randomize()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
camera.global_position = Vector3(0, 3, 8)
func _process(delta: float) -> void:
_fly_camera(delta)
_update_hud()
func _fly_camera(delta: float) -> void:
var input := Vector3.ZERO
if Input.is_key_pressed(KEY_W):
input.z -= 1.0
if Input.is_key_pressed(KEY_S):
input.z += 1.0
if Input.is_key_pressed(KEY_A):
input.x -= 1.0
if Input.is_key_pressed(KEY_D):
input.x += 1.0
if Input.is_key_pressed(KEY_E) or Input.is_key_pressed(KEY_SPACE):
input.y += 1.0
if Input.is_key_pressed(KEY_Q) or Input.is_key_pressed(KEY_CTRL):
input.y -= 1.0
var speed := _fly_speed
if Input.is_key_pressed(KEY_SHIFT):
speed *= 3.0
if input.length_squared() > 0.0:
input = input.normalized()
camera.global_position += camera.global_transform.basis * input * speed * delta
func _update_hud() -> void:
if not stats_label:
return
var info: String = "No creature"
if _creature and is_instance_valid(_creature):
var pos: Vector3 = _creature.global_position
if _creature is GroundCrawler:
info = "GroundCrawler (segs=%d legs=%d scale=%.2f)" % [_creature.segment_count, _creature.segment_count * 2, _creature.creature_scale]
elif _creature is FlyingSerpent:
info = "FlyingSerpent (segs=%d scale=%.2f)" % [_creature.segment_count, _creature.creature_scale]
elif _creature is Hopper:
info = "Hopper (segs=%d scale=%.2f)" % [_creature.segment_count, _creature.creature_scale]
info += "\nPos: (%.1f, %.1f, %.1f)" % [pos.x, pos.y, pos.z]
else:
_creature = null
var pause_str: String = " [AI PAUSED]" if _creatures_paused else ""
stats_label.text = "FPS: %d%s\n%s\n\n[1] Crawler [2] Serpent [3] Hopper\n[4] Kill all [5] Pause AI [+/-] Scale\nWASD: fly EQ: up/down Scroll: speed" % [
Engine.get_frames_per_second(), pause_str, info]
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
_cam_yaw -= event.relative.x * _mouse_sens
_cam_pitch = clampf(_cam_pitch - event.relative.y * _mouse_sens, -PI * 0.49, PI * 0.49)
camera.rotation = Vector3(_cam_pitch, _cam_yaw, 0)
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
_fly_speed = minf(100.0, _fly_speed * 1.2)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
_fly_speed = maxf(1.0, _fly_speed / 1.2)
if event.is_action_pressed("ui_cancel"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if not event is InputEventKey or not event.pressed:
return
match event.keycode:
KEY_1:
_spawn_type("crawler")
KEY_2:
_spawn_type("serpent")
KEY_3:
_spawn_type("hopper")
KEY_4:
_kill_all()
KEY_5:
_creatures_paused = !_creatures_paused
for child in get_children():
if child is GroundCrawler or child is FlyingSerpent or child is Hopper:
child.set_process(!_creatures_paused)
child.set_physics_process(!_creatures_paused)
KEY_EQUAL:
_rescale(1.2)
KEY_MINUS:
_rescale(1.0 / 1.2)
KEY_F12:
var img: Image = get_viewport().get_texture().get_image()
var ts: String = Time.get_datetime_string_from_system().replace(":", "-")
var path: String = "/home/fleeky/Desktop/hyper-tps/creature_%s.png" % ts
img.save_png(path)
print("Screenshot saved: %s" % path)
func _spawn_type(type: String) -> void:
_kill_all()
match type:
"crawler":
_creature = GroundCrawler.new()
_creature.name = "TestCrawler"
add_child(_creature)
_creature.global_position = Vector3(0, 0.5, 0)
_creature.initialize(_rng)
print("Spawned GroundCrawler (segs=%d)" % _creature.segment_count)
"serpent":
_creature = FlyingSerpent.new()
_creature.name = "TestSerpent"
add_child(_creature)
_creature.global_position = Vector3(0, 5.0, 0)
_creature.initialize(_rng)
print("Spawned FlyingSerpent (segments=%d)" % _creature.segment_count)
"hopper":
_creature = Hopper.new()
_creature.name = "TestHopper"
add_child(_creature)
_creature.global_position = Vector3(0, 0.1, 0)
_creature.initialize(_rng)
print("Spawned Hopper (segments=%d)" % _creature.segment_count)
func _kill_all() -> void:
for child in get_children():
if child is GroundCrawler or child is FlyingSerpent or child is Hopper:
child.queue_free()
_creature = null
func _rescale(factor: float) -> void:
if _creature and is_instance_valid(_creature):
_creature.scale *= factor
print("Scale: %.2f" % _creature.scale.x)