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:
@@ -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
|
||||
|
||||
@@ -235,7 +235,7 @@ static func _action_enum_to_wire(action: int) -> String:
|
||||
return ""
|
||||
|
||||
# Hardcoded test snapshot matching Protocol format (deterministic per D-010 principle 4).
|
||||
# Uses the same {tick, entities} schema as Protocol.decode_snapshot() returns.
|
||||
# Uses the same {tick, entities, tiles} schema as Protocol.decode_snapshot() returns.
|
||||
func _test_snapshot() -> Dictionary:
|
||||
_test_tick += 1
|
||||
return {
|
||||
@@ -246,7 +246,71 @@ func _test_snapshot() -> Dictionary:
|
||||
"x": 10.0,
|
||||
"y": 10.0,
|
||||
"z": 0,
|
||||
"kind": { "variant": "Player", "data": null },
|
||||
},
|
||||
{
|
||||
"entity_id": 2,
|
||||
"x": 12.0,
|
||||
"y": 10.0,
|
||||
"z": 0,
|
||||
"kind": { "variant": "Npc", "data": null },
|
||||
},
|
||||
],
|
||||
"tiles": _test_tiles(),
|
||||
"visible_positions": _test_visible_positions(),
|
||||
}
|
||||
|
||||
# Generate a small test room: 8x6 room with walls, a door, and floor
|
||||
func _test_tiles() -> Array:
|
||||
var tiles: Array = []
|
||||
var room_x := 7
|
||||
var room_y := 7
|
||||
var room_w := 8
|
||||
var room_h := 8
|
||||
|
||||
for x in range(room_x, room_x + room_w):
|
||||
for y in range(room_y, room_y + room_h):
|
||||
var is_edge := (x == room_x or x == room_x + room_w - 1
|
||||
or y == room_y or y == room_y + room_h - 1)
|
||||
var tile_type: String
|
||||
if is_edge:
|
||||
# Door on the south wall, center
|
||||
if y == room_y + room_h - 1 and x == room_x + room_w / 2:
|
||||
tile_type = "door"
|
||||
else:
|
||||
tile_type = "wall"
|
||||
else:
|
||||
tile_type = "floor"
|
||||
tiles.append({"x": x, "y": y, "z": 0, "type": tile_type})
|
||||
|
||||
# Corridor south of the door
|
||||
var door_x := room_x + room_w / 2
|
||||
for y in range(room_y + room_h, room_y + room_h + 4):
|
||||
tiles.append({"x": door_x - 1, "y": y, "z": 0, "type": "wall"})
|
||||
tiles.append({"x": door_x, "y": y, "z": 0, "type": "floor"})
|
||||
tiles.append({"x": door_x + 1, "y": y, "z": 0, "type": "wall"})
|
||||
|
||||
return tiles
|
||||
|
||||
# Test visibility: player at (10,10) can see tiles within radius 4, blocked by walls
|
||||
func _test_visible_positions() -> Array:
|
||||
var positions: Array = []
|
||||
var player_x := 10
|
||||
var player_y := 10
|
||||
var radius := 4
|
||||
|
||||
# Room bounds (inner floor area)
|
||||
var room_x := 7
|
||||
var room_y := 7
|
||||
var room_w := 8
|
||||
var room_h := 8
|
||||
|
||||
for x in range(player_x - radius, player_x + radius + 1):
|
||||
for y in range(player_y - radius, player_y + radius + 1):
|
||||
var dist := absf(x - player_x) + absf(y - player_y)
|
||||
if dist <= radius:
|
||||
# Walls are visible but block further vision
|
||||
# For test purposes, include all tiles within radius that are inside the room
|
||||
if x >= room_x and x < room_x + room_w and y >= room_y and y < room_y + room_h:
|
||||
positions.append({"x": x, "y": y})
|
||||
return positions
|
||||
|
||||
Reference in New Issue
Block a user