feat(client): add tile rendering, fog overlay, and camera smoothing

Sprint 2 rendering pipeline: tiles (#129), fog (#131), camera (#116).

- tile_renderer.gd: programmatic TileSet with floor/wall/door/object
  placeholder tiles, renders from snapshot tile data
- fog_renderer.gd: TileMapLayer overlay with three visibility states
  (visible/fog-edge/hidden), computed from visible_positions data
- Camera2D: smoothing enabled (speed 6.0), 2x zoom, locked to player
- game_state.gd: stores visible_tiles and visible_positions from snapshots
- sim_bridge.gd: test data with 8x8 room, corridor, and Manhattan
  distance visibility for development without server
- Scene render order: Tiles -> FogOverlay -> Entities
- Background clear color set to near-black for unexplored areas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 23:48:02 +01:00
co-authored by Claude Opus 4.6
parent 55b775ee73
commit dcf887cb49
8 changed files with 296 additions and 29 deletions
+12 -1
View File
@@ -1,11 +1,14 @@
extends Node
# Updated each frame from ObserverSnapshot data (Protocol format: {tick, entities}).
# Updated each frame from ObserverSnapshot data (Protocol format: {tick, entities, tiles}).
# Entities use Protocol decoded format: {entity_id, x, y, z, kind: {variant, data}}.
# Tiles use format: [{x, y, z, type}].
var current_snapshot: Dictionary = {}
var current_tick: int = 0
var player_position: Vector2 = Vector2.ZERO
var visible_entities: Array = []
var visible_tiles: Array = []
var visible_positions: Dictionary = {} # Vector2i -> true, for fast fog lookups
# Player entity ID — the first entity is assumed to be the player (will be
# refined when the server assigns explicit player entity IDs).
@@ -24,3 +27,11 @@ func apply_snapshot(snapshot: Dictionary) -> void:
if entity.has("entity_id") and entity.entity_id == player_entity_id:
player_position = Vector2(entity.x, entity.y)
break
if snapshot.has("tiles"):
visible_tiles = snapshot.tiles
if snapshot.has("visible_positions"):
visible_positions.clear()
for pos in snapshot.visible_positions:
visible_positions[Vector2i(pos.x, pos.y)] = true