Files
Steven Wroblewski d2a7973022 Spielfeld über Art zeichnen
Karte, Figuren und Zielmarke zeichnet jetzt Art. Die Karte zeichnet in
Blöcken von 16 mal 16 Kacheln und nach einem Treffer nur die Blöcke um
die Kachel neu; jede Kachel kennt ihre Nachbarn. Figuren stehen
aufrecht in ihrer Kachel, schreiten und schauen in die Richtung, in die
sie gehen. Der eigene Schlag zeigt seine Splitter sofort, auch als Gast,
und die Namen über fremden Figuren werden in Bildschirmgröße gezeichnet
und bleiben scharf.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-10-02 11:49:18 +02:00

93 lines
3.3 KiB
GDScript

class_name Figures
extends Node2D
## The figures of the other players, with their names, drawn by `art`. A new
## position comes over the network about ten times a second; in between,
## each figure glides toward the latest one, stepping and facing the way it
## goes.
## How quickly a figure closes the gap to its latest position, per second.
const GLIDE := 15.0
## Pixels per frame below which a gliding figure counts as standing.
const STANDING := 0.05
## Size and width of the names, in pixels of the playing field. The names
## are drawn at the size they have on screen, so that they stay sharp.
const NAME_SIZE := 8
const NAME_WIDTH := 80.0
const NAME_OUTLINE := Color(0.05, 0.05, 0.06)
var session: GwSession
var art: Art
var _shown: Dictionary[int, Vector2] = {}
var _latest: Dictionary[int, Vector2] = {}
var _facing: Dictionary[int, float] = {}
var _step: Dictionary[int, float] = {}
var _moving: Dictionary[int, bool] = {}
## Moves the figure of `player` toward `position`. A figure that was not
## shown yet appears there at once.
func place(player: int, position: Vector2) -> void:
if not _shown.has(player):
_shown[player] = position
_latest[player] = position
queue_redraw()
## Removes the figure of `player`.
func forget(player: int) -> void:
for figures: Dictionary in [_shown, _latest, _facing, _step, _moving]:
figures.erase(player)
queue_redraw()
func clear() -> void:
for figures: Dictionary in [_shown, _latest, _facing, _step, _moving]:
figures.clear()
queue_redraw()
## Where the figure of `player` is shown, or Vector2.INF if it is not.
func shown_at(player: int) -> Vector2:
return _shown.get(player, Vector2.INF)
## 1.0 if the figure of `player` faces right, -1.0 if it faces left.
func facing_of(player: int) -> float:
return _facing.get(player, 1.0)
func _process(delta: float) -> void:
if _shown.is_empty():
return
var weight := minf(1.0, GLIDE * delta)
for player in _shown:
var before := _shown[player]
_shown[player] = before.lerp(_latest[player], weight)
var moved := _shown[player] - before
if absf(moved.x) > STANDING:
_facing[player] = signf(moved.x)
_step[player] = _step.get(player, 0.0) + moved.length() * Avatar.STEP_PER_PIXEL
_moving[player] = moved.length() > STANDING
queue_redraw()
func _draw() -> void:
if art == null:
return
var font := ThemeDB.fallback_font
# The camera enlarges the field. Text drawn at its size on screen and then
# shrunk stays sharp; text drawn small and enlarged would blur.
var zoom := maxf(1.0, get_global_transform_with_canvas().get_scale().x)
for player in _shown:
var at := _shown[player]
var state := {"player": player, "facing": facing_of(player), "step": _step.get(player, 0.0), "moving": _moving.get(player, false)}
art.draw(self, Art.FIGURE, Avatar.figure_rect(at), state)
if session != null:
var label := Vector2(at.x - NAME_WIDTH / 2.0, Avatar.figure_rect(at).position.y - 2.0)
var text := session.player_name(player)
var size := roundi(NAME_SIZE * zoom)
draw_set_transform(label, 0.0, Vector2.ONE / zoom)
draw_string_outline(font, Vector2.ZERO, text, HORIZONTAL_ALIGNMENT_CENTER, NAME_WIDTH * zoom, size, maxi(2, roundi(zoom * 1.5)), NAME_OUTLINE)
draw_string(font, Vector2.ZERO, text, HORIZONTAL_ALIGNMENT_CENTER, NAME_WIDTH * zoom, size)
draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE)