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>
This commit is contained in:
Steven Wroblewski
2026-10-02 11:49:18 +02:00
parent 9921aba63a
commit d2a7973022
10 changed files with 422 additions and 64 deletions
+43 -14
View File
@@ -1,19 +1,27 @@
class_name Figures
extends Node2D
## The figures of the other players, with their names. A new position comes
## over the network about ten times a second; in between, each figure glides
## toward the latest one.
## 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
const COLOR := Color(0.55, 0.8, 0.95)
const OUTLINE := Color(0.05, 0.1, 0.15)
## 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
@@ -27,14 +35,14 @@ func place(player: int, position: Vector2) -> void:
## Removes the figure of `player`.
func forget(player: int) -> void:
_shown.erase(player)
_latest.erase(player)
for figures: Dictionary in [_shown, _latest, _facing, _step, _moving]:
figures.erase(player)
queue_redraw()
func clear() -> void:
_shown.clear()
_latest.clear()
for figures: Dictionary in [_shown, _latest, _facing, _step, _moving]:
figures.clear()
queue_redraw()
@@ -43,21 +51,42 @@ 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:
_shown[player] = _shown[player].lerp(_latest[player], weight)
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]
draw_circle(at, Avatar.RADIUS + 1.0, OUTLINE)
draw_circle(at, Avatar.RADIUS, COLOR)
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, at.y - Avatar.RADIUS - 3.0)
draw_string(font, label, session.player_name(player), HORIZONTAL_ALIGNMENT_CENTER, NAME_WIDTH, NAME_SIZE)
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)