Optimize wall/fog rendering

This commit is contained in:
gamer147
2026-04-07 10:22:30 -04:00
parent 0882908e4c
commit e42a98fece
3 changed files with 49 additions and 64 deletions

View File

@@ -129,7 +129,9 @@ Both systems independently iterate `get_nodes_in_group("units")`, hook `node_add
--- ---
### J. Sprite spam in `FogRenderer` and `WallRenderer` ### J. Sprite spam in `FogRenderer` and `WallRenderer` — **Resolved 2026-04-07**
Both renderers now override `_draw()` and use `draw_texture_rect_region()` directly. Zero child sprites; layout changes call `queue_redraw()` instead of thrashing the scene tree. Findings **O** (redundant `match` arms) and **P** (undocumented `FOG_RECT`) were folded into the same change.
**Files:** `nodes/fog_renderer.gd:13-43`, `nodes/wall_renderer.gd:44-176` **Files:** `nodes/fog_renderer.gd:13-43`, `nodes/wall_renderer.gd:44-176`
@@ -254,7 +256,7 @@ Findings 2 and 9 from the prior review are the largest gameplay gap. Until that
| G | `remove_unit` leaks signal connections | Medium | Small | | G | `remove_unit` leaks signal connections | Medium | Small |
| H | Dead `process_combat` | Medium | Trivial | | H | Dead `process_combat` | Medium | Trivial |
| I | O(n) unit lookup | Medium | Small | | I | O(n) unit lookup | Medium | Small |
| J | Sprite-per-tile renderers | Medium | Medium | | J | Sprite-per-tile renderers | ~~Medium~~ | **Done** |
| K | `MapLayout.initialize` not guarded | Medium | Small | | K | `MapLayout.initialize` not guarded | Medium | Small |
| L | Highlight updates while modal open | Medium | Trivial | | L | Highlight updates while modal open | Medium | Trivial |
| M | Inline scripts in `unit.tscn` | Medium | Small | | M | Inline scripts in `unit.tscn` | Medium | Small |

View File

@@ -5,39 +5,35 @@ extends Node2D
## that is not part of any room. Future: drive visibility from map state. ## that is not part of any room. Future: drive visibility from map state.
const TILE_SIZE := 100.0 const TILE_SIZE := 100.0
## Fog tile region in aux_terrain.BMP
const FOG_RECT := Rect2(53, 53, 100, 100) const FOG_RECT := Rect2(53, 53, 100, 100)
@export var atlas_texture: Texture2D @export var atlas_texture: Texture2D
var _fog_tiles: PackedVector2Array = []
func draw_fog_for_layout(map_layout: MapLayout) -> void: func draw_fog_for_layout(map_layout: MapLayout) -> void:
_clear() _fog_tiles.clear()
if not map_layout or not atlas_texture: if not map_layout or not atlas_texture:
queue_redraw()
return return
for y in map_layout.size.y: for y in map_layout.size.y:
for x in map_layout.size.x: for x in map_layout.size.x:
var tile := Vector2i(x, y) var tile := Vector2i(x, y)
if map_layout.is_tile_valid(tile): if map_layout.is_tile_valid(tile):
continue continue
_create_fog_sprite(Vector2(tile) * TILE_SIZE) _fog_tiles.append(Vector2(tile) * TILE_SIZE)
queue_redraw()
func _clear() -> void: func _draw() -> void:
for child in get_children(): if not atlas_texture:
child.queue_free() return
var dest_size := Vector2(TILE_SIZE, TILE_SIZE)
for pos in _fog_tiles:
func _create_fog_sprite(pos: Vector2) -> void: draw_texture_rect_region(
var atlas := AtlasTexture.new() atlas_texture,
atlas.atlas = atlas_texture Rect2(pos, dest_size),
atlas.region = FOG_RECT FOG_RECT,
)
var sprite := Sprite2D.new()
sprite.texture = atlas
sprite.centered = false
sprite.position = pos
sprite.scale = Vector2(
TILE_SIZE / FOG_RECT.size.x,
TILE_SIZE / FOG_RECT.size.y,
)
add_child(sprite)

View File

@@ -40,21 +40,28 @@ const HALF_EDGE := TILE_SIZE / 2.0
@export var atlas_texture: Texture2D @export var atlas_texture: Texture2D
# Each entry is [dest_rect: Rect2, source_rect: Rect2]
var _segments: Array = []
func draw_walls_for_layout(map_layout: MapLayout) -> void: func draw_walls_for_layout(map_layout: MapLayout) -> void:
_clear_walls() _segments.clear()
if not map_layout or not atlas_texture: if not map_layout or not atlas_texture:
queue_redraw()
return return
var tile_edges := _collect_tile_edges(map_layout) var tile_edges := _collect_tile_edges(map_layout)
for tile in tile_edges: for tile in tile_edges:
var edges: Array = tile_edges[tile] var edges: Array = tile_edges[tile]
_draw_tile_walls(tile, edges) _build_tile_walls(tile, edges)
queue_redraw()
func _clear_walls() -> void: func _draw() -> void:
for child in get_children(): if not atlas_texture:
child.queue_free() return
for seg in _segments:
draw_texture_rect_region(atlas_texture, seg[0], seg[1])
func _collect_tile_edges(map_layout: MapLayout) -> Dictionary: func _collect_tile_edges(map_layout: MapLayout) -> Dictionary:
@@ -97,28 +104,20 @@ func _direction_to_edge(dir: Vector2i) -> StringName:
return &"" return &""
func _draw_tile_walls(tile: Vector2i, edges: Array) -> void: func _build_tile_walls(tile: Vector2i, edges: Array) -> void:
var tile_origin := Vector2(tile) * TILE_SIZE var tile_origin := Vector2(tile) * TILE_SIZE
for edge in edges: for edge in edges:
match edge: _build_edge_segments(tile_origin, edge)
&"left":
_draw_edge_segments(tile_origin, edge)
&"right":
_draw_edge_segments(tile_origin, edge)
&"top":
_draw_edge_segments(tile_origin, edge)
&"bottom":
_draw_edge_segments(tile_origin, edge)
# TODO: Outer corner segments # TODO: Outer corner segments
_draw_outer_corners(tile, tile_origin, edges) _build_outer_corners(tile, tile_origin, edges)
# TODO: Inner corner segments (for non-rectangular room support) # Inner corner segments (for non-rectangular room support)
_draw_inner_corners(tile, tile_origin, edges) _build_inner_corners(tile, tile_origin, edges)
func _draw_edge_segments(tile_origin: Vector2, edge: StringName) -> void: func _build_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
var seg_a_rect: Rect2 var seg_a_rect: Rect2
var seg_b_rect: Rect2 var seg_b_rect: Rect2
var seg_a_offset: Vector2 var seg_a_offset: Vector2
@@ -156,34 +155,22 @@ func _draw_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
seg_a_offset = Vector2(0, TILE_SIZE - WALL_THICKNESS) seg_a_offset = Vector2(0, TILE_SIZE - WALL_THICKNESS)
seg_b_offset = Vector2(HALF_EDGE, TILE_SIZE - WALL_THICKNESS) seg_b_offset = Vector2(HALF_EDGE, TILE_SIZE - WALL_THICKNESS)
_create_segment_sprite(tile_origin + seg_a_offset, seg_a_size, seg_a_rect) _queue_segment(tile_origin + seg_a_offset, seg_a_size, seg_a_rect)
_create_segment_sprite(tile_origin + seg_b_offset, seg_b_size, seg_b_rect) _queue_segment(tile_origin + seg_b_offset, seg_b_size, seg_b_rect)
func _create_segment_sprite(pos: Vector2, target_size: Vector2, source_rect: Rect2) -> void: func _queue_segment(pos: Vector2, target_size: Vector2, source_rect: Rect2) -> void:
var atlas := AtlasTexture.new() _segments.append([Rect2(pos, target_size), source_rect])
atlas.atlas = atlas_texture
atlas.region = source_rect
var sprite := Sprite2D.new()
sprite.texture = atlas
sprite.centered = false
sprite.position = pos
sprite.scale = Vector2(
target_size.x / source_rect.size.x,
target_size.y / source_rect.size.y
)
add_child(sprite)
func _draw_outer_corners(_tile: Vector2i, _tile_origin: Vector2, _edges: Array) -> void: func _build_outer_corners(_tile: Vector2i, _tile_origin: Vector2, _edges: Array) -> void:
# TODO: Implement outer corner segments # TODO: Implement outer corner segments
# Check pairs of adjacent edges (e.g., "top" + "left" → top-left outer corner) # Check pairs of adjacent edges (e.g., "top" + "left" → top-left outer corner)
# and draw the corner piece from the atlas. # and draw the corner piece from the atlas.
pass pass
func _draw_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -> void: func _build_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -> void:
## Draws decorative corner pieces wherever two perpendicular wall edges meet ## Draws decorative corner pieces wherever two perpendicular wall edges meet
## on the same tile (e.g., a top wall + left wall produces an upper-left corner). ## on the same tile (e.g., a top wall + left wall produces an upper-left corner).
var has_top := edges.has(&"top") var has_top := edges.has(&"top")
@@ -193,25 +180,25 @@ func _draw_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) ->
var corner_size := Vector2(CORNER_SIZE, CORNER_SIZE) var corner_size := Vector2(CORNER_SIZE, CORNER_SIZE)
if has_top and has_left: if has_top and has_left:
_create_segment_sprite( _queue_segment(
tile_origin + Vector2(0, 0), tile_origin + Vector2(0, 0),
corner_size, corner_size,
INNER_CORNER_UPPER_LEFT_RECT INNER_CORNER_UPPER_LEFT_RECT
) )
if has_top and has_right: if has_top and has_right:
_create_segment_sprite( _queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, 0), tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, 0),
corner_size, corner_size,
INNER_CORNER_UPPER_RIGHT_RECT INNER_CORNER_UPPER_RIGHT_RECT
) )
if has_bottom and has_left: if has_bottom and has_left:
_create_segment_sprite( _queue_segment(
tile_origin + Vector2(0, TILE_SIZE - CORNER_SIZE), tile_origin + Vector2(0, TILE_SIZE - CORNER_SIZE),
corner_size, corner_size,
INNER_CORNER_LOWER_LEFT_RECT INNER_CORNER_LOWER_LEFT_RECT
) )
if has_bottom and has_right: if has_bottom and has_right:
_create_segment_sprite( _queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, TILE_SIZE - CORNER_SIZE), tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, TILE_SIZE - CORNER_SIZE),
corner_size, corner_size,
INNER_CORNER_LOWER_RIGHT_RECT INNER_CORNER_LOWER_RIGHT_RECT