Class: Ruby2D::Tileset

Inherits:
Object
  • Object
show all
Includes:
Renderable
Defined in:
lib/ruby2d/tileset.rb

Overview

An image containing multiple tiles that can be drawn at various positions

Constant Summary collapse

DEFAULT_TINT =
Color.new([1.0, 1.0, 1.0, 1.0])

Constants included from Interactive

Interactive::OBJECT_EVENTS, Interactive::OBJECT_EVENT_FILTER_PREDICATES

Instance Attribute Summary

Attributes included from Renderable

#color, #height, #padding_bottom, #padding_left, #padding_right, #padding_top, #visible, #width, #x, #x_align, #y, #y_align, #z

Instance Method Summary collapse

Methods included from Renderable

#_alignment_anchor_dx, #_alignment_anchor_dy, #_apply_padding, #_extract_alignment, #_point_in_polygon?, #_point_on_segment?, #_require_numeric_position, #_resolve_alignment, #_unrotate, #_validate_dimensions, #add, #colour=, #contains?, flatten_color, flatten_per_vertex, flatten_points, flatten_resolved_color, #hide, #opacity, #opacity=, #padding=, #remove, resolve_color_or_default, resolve_single_color, #show

Methods included from Interactive

#_fire_event, #interactive?, #off, #on

Constructor Details

#initialize(path, tile_width: 32, tile_height: 32, z: 0, padding: 0, spacing: 0, scale: 1, add: true, visible: true) ⇒ Tileset

Create a tileset from an image



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/ruby2d/tileset.rb', line 28

def initialize(path, tile_width: 32, tile_height: 32, z: 0,
               padding: 0, spacing: 0,
               scale: 1, add: true, visible: true)
  # `scale` and the tile dimensions feed the UV denominator
  # (`@scaled_*` sizes); a non-positive value collapses them to zero and
  # divides into NaN/Inf texture coordinates, so reject it up front.
  unless scale.is_a?(Numeric) && scale.positive?
    raise ArgumentError, "Tileset scale must be positive, got #{scale}"
  end
  unless tile_width.is_a?(Numeric) && tile_width.positive?
    raise ArgumentError, "Tileset tile_width must be positive, got #{tile_width}"
  end
  unless tile_height.is_a?(Numeric) && tile_height.positive?
    raise ArgumentError, "Tileset tile_height must be positive, got #{tile_height}"
  end

  @path = path.to_s

  # Initialize the tileset texture image (not added to the window)
  @texture = Image.new(@path, add: false)
  # The tile UV coordinates are normalized against the source texture's
  # true pixel dimensions, so `@width`/`@height` always track the texture.
  @width = @texture.width
  @height = @texture.height
  @z = z

  @tiles = {}
  @tile_definitions = {}
  # Cached per-frame draw batch (geometry only; tint is applied separately
  # in C each frame). Rebuilt lazily in `render` whenever a placement
  # changes — see `mark_batch_dirty`.
  @coords_batch = []
  @tex_coords_batch = []
  @batch_dirty = true
  @padding = padding
  @spacing = spacing
  @x = 0
  @y = 0
  @tile_width = tile_width
  @tile_height = tile_height
  @scale = scale

  calculate_scaled_sizes

  @visible = visible
  self.add if add
end

Instance Method Details

#[](x, y) ⇒ Object

Look up the tile name placed at (x, y), or nil if none



94
95
96
97
# File 'lib/ruby2d/tileset.rb', line 94

def [](x, y)
  placement = @tiles[[x, y]]
  placement && placement.fetch(:name)
end

#[]=(x, y, name) ⇒ Object

Place a single tile at (x, y), replacing any existing placement



89
90
91
# File 'lib/ruby2d/tileset.rb', line 89

def []=(x, y, name)
  place_one(name, x, y)
end

#clearObject

Remove all placements



107
108
109
110
# File 'lib/ruby2d/tileset.rb', line 107

def clear
  @tiles = {}
  mark_batch_dirty
end

#define(name, x, y, rotate: 0, flip: nil) ⇒ Object

Define a named tile type at the given grid position in the tileset image



77
78
79
# File 'lib/ruby2d/tileset.rb', line 77

def define(name, x, y, rotate: 0, flip: nil)
  @tile_definitions[name] = { x: x, y: y, rotate: rotate, flip: flip }
end

#delete(x, y) ⇒ Object

Remove the placement at (x, y)



100
101
102
103
104
# File 'lib/ruby2d/tileset.rb', line 100

def delete(x, y)
  removed = @tiles.delete([x, y])
  mark_batch_dirty if removed
  removed
end

#place(name, coordinates) ⇒ Object

Place a named tile at one or more world coordinates. coordinates is an array of [x, y] pairs. Existing placements at the same coordinate are replaced.



84
85
86
# File 'lib/ruby2d/tileset.rb', line 84

def place(name, coordinates)
  coordinates.each { |x, y| place_one(name, x, y) }
end

#renderObject Also known as: _render_scene

Render the tileset. Called with no arguments from the scene-graph loop, or from a render block for one-shot rendering.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ruby2d/tileset.rb', line 114

def render
  Window.render_ready_check
  return if @tiles.empty?

  # Batch every placed tile into one draw call: they share the source
  # texture and tint, so the per-tile geometry (memoized in each Vertices)
  # is collected and handed to a single `SDL_RenderGeometry` pass. The
  # collected batch is cached and only rebuilt when a placement changes,
  # so a static tile field costs nothing per frame beyond the native draw
  # — worth ~2.6× on the `tiles_static` render slice, so keep the guard.
  if @batch_dirty
    @coords_batch.clear
    @tex_coords_batch.clear
    @tiles.each_value do |placement|
      vertices = placement.fetch(:vertices)
      @coords_batch << vertices.coordinates
      @tex_coords_batch << vertices.texture_coordinates
    end
    @batch_dirty = false
  end

  Ext.image_draw_quads(@texture, @coords_batch, @tex_coords_batch, tint)
end

#tintObject

The tint color, multiplied against the texture. Defaults to white. Copy DEFAULT_TINT on first read so each instance owns its tint — otherwise two default-tinted tilesets would share (and mutate) the single class-level Color.



19
20
21
# File 'lib/ruby2d/tileset.rb', line 19

def tint
  @tint ||= Color.new(DEFAULT_TINT)
end

#tint=(c) ⇒ Object



23
24
25
# File 'lib/ruby2d/tileset.rb', line 23

def tint=(c)
  @tint = Color.new(c)
end