Class: RGame::Core::TileMapRenderer

Inherits:
Object
  • Object
show all
Defined in:
lib/rgame/core/tile_map_renderer.rb

Overview

Draws a tile map: the static layers baked once, the animated tiles drawn per frame and culled to the viewport.

tiles = RGame::Core::TileMapRenderer.new(map, tileset_images)

tiles.draw(renderer, camera_x, camera_y, view_w, view_h, elapsed: seconds)
tiles.draw_overlay(renderer, camera_x, camera_y, view_w, view_h, z: 20,
                 elapsed: seconds)

Two bands, with the actors between them

Layers split by the map's own above_layer? flag. The below band — ground, and detail on the actors' level — is drawn under them by #draw; the above band — tree canopies, roofs — over them by #draw_overlay, at a z the scene picks. Two calls rather than one because the scene draws its actors in between, and collapsing them would put every canopy behind every character.

What is baked and what is not

Within each band, every tile that is not animated is baked into one recording, the first time that band is drawn. Scrolling a baked layer is then one call per texture however many thousand tiles went into it. The handful that are animated are drawn individually each frame, culled to the viewport — a map far larger than the screen costs only what is on it.

It loads nothing and holds no clock

The tiles arrive already sliced, so two maps sharing a tileset share one GPU upload — which is only true if something above pulled the image through the asset manager, and is why this class does not load its own.

And elapsed is an argument rather than a clock read, so animation is something the caller advances. Pausing is "stop accumulating"; a spec picks the frame it wants. See CLAUDE.md, "draw renders state; time enters through update".

What it needs of a map

It never names the map's class — the tile map lives a layer above this one and Core may not reach up (CLAUDE.md, "The rule points both ways"). What it calls is the 'a tile map' contract in spec/support/shared_examples/: layer_count, above_layer?, width, height, tile_width, tile_height, gid, and a tileset answering local_id, animations and frame_local_id.

Constant Summary collapse

BELOW_Z =

The ground band sits at the bottom; the scene chooses where the overlay goes, because only it knows what its actors are drawn at.

0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(map, tiles) ⇒ TileMapRenderer

tiles is the tileset image sliced into an Array indexed by local tile id — what Image#tiles returns.



57
58
59
60
61
62
63
64
65
66
# File 'lib/rgame/core/tile_map_renderer.rb', line 57

def initialize(map, tiles)
  @map = map
  @tileset = map.tileset
  @tiles = tiles
  @animated_below, @animated_above = collect_animated_tiles
  # Baked on first draw, not here: recording needs a live frame, and there
  # is no renderer at construction.
  @static_below = nil
  @static_above = nil
end

Instance Attribute Details

#mapObject (readonly)

The map this was built from. A scene reads it for collision and world bounds, which are its business rather than this class's.



53
54
55
# File 'lib/rgame/core/tile_map_renderer.rb', line 53

def map
  @map
end

Instance Method Details

#draw(renderer, camera_x, camera_y, viewport_width, viewport_height, elapsed: 0.0) ⇒ Object



68
69
70
71
72
73
# File 'lib/rgame/core/tile_map_renderer.rb', line 68

def draw(renderer, camera_x, camera_y, viewport_width, viewport_height, elapsed: 0.0)
  @static_below ||= bake(renderer) { |layer| !@map.above_layer?(layer) }
  @static_below.draw(-camera_x, -camera_y, z: BELOW_Z)
  draw_animated(renderer, @animated_below, camera_x, camera_y,
                viewport_width, viewport_height, BELOW_Z, elapsed)
end

#draw_overlay(renderer, camera_x, camera_y, viewport_width, viewport_height, z:, elapsed: 0.0) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/rgame/core/tile_map_renderer.rb', line 75

def draw_overlay(renderer, camera_x, camera_y, viewport_width, viewport_height,
                 z:, elapsed: 0.0)
  @static_above ||= bake(renderer) { |layer| @map.above_layer?(layer) }
  @static_above.draw(-camera_x, -camera_y, z: z)
  draw_animated(renderer, @animated_above, camera_x, camera_y,
                viewport_width, viewport_height, z, elapsed)
end