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,31 @@
|
||||
class_name Avatar
|
||||
extends Node2D
|
||||
## The local player's figure. It moves outside the lockstep simulation; only
|
||||
## its pickaxe hits become commands.
|
||||
|
||||
## Walking speed: five tiles per second.
|
||||
const SPEED := 5.0 * GridMotion.TILE_SIZE
|
||||
const RADIUS := 5.0
|
||||
## Longest frame that moves the figure in one go. Longer frames are cut, so
|
||||
## that the figure cannot pass through a wall after a stall.
|
||||
const MAX_STEP_SECONDS := 0.1
|
||||
|
||||
## Callable(x: int, y: int) -> bool that says whether a tile can be walked on.
|
||||
var is_walkable: Callable
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
walk(Input.get_vector("move_left", "move_right", "move_up", "move_down"), delta)
|
||||
|
||||
|
||||
## Moves the figure in `direction` for `delta` seconds, sliding along walls.
|
||||
func walk(direction: Vector2, delta: float) -> void:
|
||||
if direction == Vector2.ZERO or not is_walkable.is_valid():
|
||||
return
|
||||
var motion := direction * SPEED * minf(delta, MAX_STEP_SECONDS)
|
||||
position = GridMotion.move(position, motion, RADIUS, is_walkable)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
draw_circle(Vector2.ZERO, RADIUS + 1.0, Color(0.15, 0.1, 0.05))
|
||||
draw_circle(Vector2.ZERO, RADIUS, Color(0.95, 0.85, 0.6))
|
||||
@@ -0,0 +1 @@
|
||||
uid://bpltng2n5f61g
|
||||
@@ -0,0 +1,44 @@
|
||||
class_name GridMotion
|
||||
extends RefCounted
|
||||
## Movement of a body over the tile grid. Figures move outside the lockstep
|
||||
## simulation, so this code may use floating point.
|
||||
|
||||
## Edge length of a tile, in pixels.
|
||||
const TILE_SIZE := 16.0
|
||||
|
||||
|
||||
## The tile that contains `point`.
|
||||
static func tile_of(point: Vector2) -> Vector2i:
|
||||
return Vector2i(floori(point.x / TILE_SIZE), floori(point.y / TILE_SIZE))
|
||||
|
||||
|
||||
## The centre of `tile`, in pixels.
|
||||
static func tile_center(tile: Vector2i) -> Vector2:
|
||||
return (Vector2(tile) + Vector2(0.5, 0.5)) * TILE_SIZE
|
||||
|
||||
|
||||
## True if a square body of half width `radius` around `center` touches
|
||||
## only walkable tiles. `is_walkable` is a Callable(x: int, y: int) -> bool.
|
||||
static func fits(center: Vector2, radius: float, is_walkable: Callable) -> bool:
|
||||
var first := tile_of(center - Vector2(radius, radius))
|
||||
# The far edge lies just inside its tile, so a body that ends exactly on
|
||||
# a tile border does not count the next tile.
|
||||
var last := tile_of(center + Vector2(radius, radius) - Vector2(0.001, 0.001))
|
||||
for y in range(first.y, last.y + 1):
|
||||
for x in range(first.x, last.x + 1):
|
||||
if not is_walkable.call(x, y):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## Moves a body by `motion`, one axis after the other. An axis that would
|
||||
## end in a wall is skipped, so a body pushed into a wall slides along it.
|
||||
static func move(center: Vector2, motion: Vector2, radius: float, is_walkable: Callable) -> Vector2:
|
||||
var result := center
|
||||
var along_x := result + Vector2(motion.x, 0.0)
|
||||
if fits(along_x, radius, is_walkable):
|
||||
result = along_x
|
||||
var along_y := result + Vector2(0.0, motion.y)
|
||||
if fits(along_y, radius, is_walkable):
|
||||
result = along_y
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
uid://dn0m8pxkigqig
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name TickClock
|
||||
extends RefCounted
|
||||
## Turns frame time into a whole number of simulation ticks.
|
||||
|
||||
## Most ticks one frame may run. After a long stall the simulation catches
|
||||
## up only this far and drops the rest, instead of freezing to catch up.
|
||||
const MAX_TICKS_PER_FRAME := 5
|
||||
|
||||
var seconds_per_tick: float
|
||||
var paused := false
|
||||
|
||||
var _accumulated := 0.0
|
||||
|
||||
|
||||
func _init(ticks_per_second: int) -> void:
|
||||
seconds_per_tick = 1.0 / ticks_per_second
|
||||
|
||||
|
||||
## Returns how many ticks to run for a frame that took `delta` seconds.
|
||||
func advance(delta: float) -> int:
|
||||
if paused:
|
||||
return 0
|
||||
_accumulated += delta
|
||||
var ticks := int(_accumulated / seconds_per_tick)
|
||||
if ticks > MAX_TICKS_PER_FRAME:
|
||||
_accumulated = 0.0
|
||||
return MAX_TICKS_PER_FRAME
|
||||
_accumulated -= ticks * seconds_per_tick
|
||||
return ticks
|
||||
@@ -0,0 +1 @@
|
||||
uid://bmrv5nfb0wu6r
|
||||
Reference in New Issue
Block a user