Takt und Bewegung der Figur
TickClock rechnet Bildzeit in Takte um und holt nach einem Ruckler höchstens fünf Takte auf. GridMotion bewegt einen Körper über das Kachelraster und lässt ihn an Wänden entlanggleiten. Die Figur läuft fünf Felder pro Sekunde, außerhalb des Gleichschritts. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
extends TestCase
|
||||
|
||||
|
||||
func _open_field(x: int, y: int) -> bool:
|
||||
return x >= 0 and y >= 0 and x < 100 and y < 100
|
||||
|
||||
|
||||
func test_the_figure_walks_five_tiles_per_second() -> void:
|
||||
var avatar := Avatar.new()
|
||||
avatar.is_walkable = _open_field
|
||||
avatar.position = GridMotion.tile_center(Vector2i(50, 50))
|
||||
var start := avatar.position
|
||||
for frame in 10:
|
||||
avatar.walk(Vector2.RIGHT, 0.1)
|
||||
check(is_equal_approx(avatar.position.x - start.x, 5.0 * GridMotion.TILE_SIZE), "five tiles in one second, moved %s" % (avatar.position.x - start.x))
|
||||
avatar.free()
|
||||
|
||||
|
||||
func test_a_long_frame_moves_the_figure_no_further_than_a_tenth_of_a_second() -> void:
|
||||
var avatar := Avatar.new()
|
||||
avatar.is_walkable = _open_field
|
||||
avatar.position = GridMotion.tile_center(Vector2i(50, 50))
|
||||
var start := avatar.position
|
||||
avatar.walk(Vector2.DOWN, 3.0)
|
||||
check(is_equal_approx(avatar.position.y - start.y, Avatar.SPEED * Avatar.MAX_STEP_SECONDS), "cut to a tenth of a second")
|
||||
avatar.free()
|
||||
|
||||
|
||||
func test_without_a_map_the_figure_stays() -> void:
|
||||
var avatar := Avatar.new()
|
||||
avatar.walk(Vector2.LEFT, 0.1)
|
||||
check_eq(avatar.position, Vector2.ZERO, "no walkability callable")
|
||||
avatar.free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://b7gc0kkgk5xg0
|
||||
@@ -0,0 +1,46 @@
|
||||
extends TestCase
|
||||
## A 10 x 10 test grid in which tile (3, 1) and every tile outside are walls.
|
||||
|
||||
const SIZE := GridMotion.TILE_SIZE
|
||||
const RADIUS := 5.0
|
||||
|
||||
|
||||
func _walkable(x: int, y: int) -> bool:
|
||||
if x < 0 or y < 0 or x >= 10 or y >= 10:
|
||||
return false
|
||||
return Vector2i(x, y) != Vector2i(3, 1)
|
||||
|
||||
|
||||
func test_tile_of_and_tile_center_agree() -> void:
|
||||
check_eq(GridMotion.tile_of(Vector2(0.0, 0.0)), Vector2i(0, 0), "origin")
|
||||
check_eq(GridMotion.tile_of(Vector2(15.9, 16.0)), Vector2i(0, 1), "tile borders")
|
||||
check_eq(GridMotion.tile_of(Vector2(-0.1, 3.0)), Vector2i(-1, 0), "left of the map")
|
||||
check_eq(GridMotion.tile_center(Vector2i(2, 3)), Vector2(40.0, 56.0), "centre")
|
||||
check_eq(GridMotion.tile_of(GridMotion.tile_center(Vector2i(7, 4))), Vector2i(7, 4), "round trip")
|
||||
|
||||
|
||||
func test_free_movement_covers_the_full_distance() -> void:
|
||||
var start := GridMotion.tile_center(Vector2i(5, 5))
|
||||
var moved := GridMotion.move(start, Vector2(3.0, -2.0), RADIUS, _walkable)
|
||||
check_eq(moved, start + Vector2(3.0, -2.0), "unblocked")
|
||||
|
||||
|
||||
func test_a_wall_stops_the_body_before_it() -> void:
|
||||
var position := GridMotion.tile_center(Vector2i(1, 1))
|
||||
for step in 40:
|
||||
position = GridMotion.move(position, Vector2(1.0, 0.0), RADIUS, _walkable)
|
||||
check(position.x + RADIUS <= 3.0 * SIZE, "the body stays left of the wall at x = 48, ends at %s" % position.x)
|
||||
check(position.x + RADIUS > 3.0 * SIZE - 1.0, "and gets close to it, ends at %s" % position.x)
|
||||
|
||||
|
||||
func test_a_body_slides_along_a_wall() -> void:
|
||||
var start := GridMotion.tile_center(Vector2i(2, 1))
|
||||
var moved := GridMotion.move(start, Vector2(4.0, 2.0), RADIUS, _walkable)
|
||||
check_eq(moved.x, start.x, "the blocked axis stays")
|
||||
check_eq(moved.y, start.y + 2.0, "the free axis moves")
|
||||
|
||||
|
||||
func test_the_edge_of_the_map_is_a_wall() -> void:
|
||||
var start := Vector2(RADIUS, RADIUS)
|
||||
var moved := GridMotion.move(start, Vector2(-1.0, -1.0), RADIUS, _walkable)
|
||||
check_eq(moved, start, "no step off the map")
|
||||
@@ -0,0 +1 @@
|
||||
uid://bjgyllbxp5ty8
|
||||
@@ -0,0 +1,31 @@
|
||||
extends TestCase
|
||||
|
||||
|
||||
func test_one_tick_lasts_a_twentieth_of_a_second() -> void:
|
||||
var clock := TickClock.new(20)
|
||||
check_eq(clock.advance(0.05), 1, "a full tick")
|
||||
check_eq(clock.advance(0.025), 0, "half a tick")
|
||||
check_eq(clock.advance(0.025), 1, "the second half completes the tick")
|
||||
|
||||
|
||||
func test_a_second_of_short_frames_runs_about_twenty_ticks() -> void:
|
||||
var clock := TickClock.new(20)
|
||||
var ticks := 0
|
||||
for frame in 60:
|
||||
ticks += clock.advance(1.0 / 60.0)
|
||||
# Rounding in floating point may leave the last tick for the next frame.
|
||||
check(ticks == 19 or ticks == 20, "19 or 20 ticks, got %d" % ticks)
|
||||
|
||||
|
||||
func test_a_long_stall_runs_at_most_five_ticks_and_forgets_the_rest() -> void:
|
||||
var clock := TickClock.new(20)
|
||||
check_eq(clock.advance(10.0), TickClock.MAX_TICKS_PER_FRAME, "capped")
|
||||
check_eq(clock.advance(0.0), 0, "the rest of the stall is dropped")
|
||||
|
||||
|
||||
func test_a_paused_clock_runs_no_ticks() -> void:
|
||||
var clock := TickClock.new(20)
|
||||
clock.paused = true
|
||||
check_eq(clock.advance(1.0), 0, "paused")
|
||||
clock.paused = false
|
||||
check_eq(clock.advance(0.05), 1, "running again")
|
||||
@@ -0,0 +1 @@
|
||||
uid://bhwedt1ik28sq
|
||||
Reference in New Issue
Block a user