Module: Zif::Layers::Bitmaskable

Included in:
ActiveBitmaskedTiledLayer, BitmaskedTiledLayer
Defined in:
lib/zif/layers/bitmaskable.rb

Overview

A mixin to extend Tileable functionality. Sprites are chosen via bitmasked adjacency rules on the presence data layer - otherwise known as Autotiling. This is a bit opinionated! Here is how the bitmask is calculated:

+-----+----+-----+
| 128 | 1  |  16 |
+-----+----+-----+
|  8  | <> |  2  |
+-----+----+-----+
|  64 | 4  |  32 |
+-----+----+-----+

(Cardinal directions clockwise, then diagonal directions clockwise)

So if a tile has a neighbor to the north, east and northeast, it’ll try to load the sprite “mytiles_19” – 1+2+16 from the SpriteRegistry. See the SpriteRegistry for more info on filenames and aliasing.

Some background (note the author uses a different numbering scheme): gamedevelopment.tutsplus.com/tutorials/how-to-use-tile-bitmasking-to-auto-tile-your-level-layouts–cms-25673

See Also:

Instance Attribute Summary collapse

1. Public Interface collapse

2. Private-ish methods collapse

Instance Attribute Details

#bitmask_dataArray<Array<Integer>> (readonly)

Returns A 2-dimensional array of bitmasked integers calculated from #presence_data This array is the size of your map’s logical height, containing arrays the size of the logical width.

Returns:

  • (Array<Array<Integer>>)

    A 2-dimensional array of bitmasked integers calculated from #presence_data This array is the size of your map’s logical height, containing arrays the size of the logical width.



32
33
34
# File 'lib/zif/layers/bitmaskable.rb', line 32

def bitmask_data
  @bitmask_data
end

#bitmasked_sprite_name_prefixString

Returns The prefix to use when referencing image paths.

Returns:

  • (String)

    The prefix to use when referencing image paths



35
36
37
# File 'lib/zif/layers/bitmaskable.rb', line 35

def bitmasked_sprite_name_prefix
  @bitmasked_sprite_name_prefix
end

#presence_dataArray<Array<Boolean>>

Returns A 2-dimensional array of true and false values This array is the size of your map’s logical height, containing arrays the size of the logical width.

Returns:

  • (Array<Array<Boolean>>)

    A 2-dimensional array of true and false values This array is the size of your map’s logical height, containing arrays the size of the logical width.



28
29
30
# File 'lib/zif/layers/bitmaskable.rb', line 28

def presence_data
  @presence_data
end

Instance Method Details

#add_at(x, y) ⇒ Object

Add presence at this logical position, then redraw

Parameters:

  • x (Integer)

    logical_x position to add

  • y (Integer)

    logical_y position to add



60
61
62
63
# File 'lib/zif/layers/bitmaskable.rb', line 60

def add_at(x, y)
  @presence_data[y][x] = true
  redraw_at(x, y)
end

#exclude_from_serializeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



154
155
156
# File 'lib/zif/layers/bitmaskable.rb', line 154

def exclude_from_serialize
  %w[presence_data bitmask_data sprites primitives]
end

#recalculate_bitmask(from_x: 0, from_y: 0, to_x: @map.logical_width - 1, to_y: @map.logical_height - 1) ⇒ Object

This will recalculte the #bitmask_data for a rectangle of logical positions, from #presence_data

Parameters:

  • from_x (Integer) (defaults to: 0)

    Lower left logical X position of the rectangle to update

  • from_y (Integer) (defaults to: 0)

    Lower left logical Y position of the rectangle to update

  • to_x (Integer) (defaults to: @map.logical_width - 1)

    Upper right logical X position of the rectangle to update

  • to_y (Integer) (defaults to: @map.logical_height - 1)

    Upper right logical Y position of the rectangle to update



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/zif/layers/bitmaskable.rb', line 78

def recalculate_bitmask(from_x: 0, from_y: 0, to_x: @map.logical_width - 1, to_y: @map.logical_height - 1)
  from_x = [from_x, 0].max
  from_y = [from_y, 0].max
  to_x   = [to_x, @map.logical_width - 1].min
  to_y   = [to_y, @map.logical_height - 1].min

  (from_y..to_y).each do |i|
    (from_x..to_x).each do |j|
      @bitmask_data[i][j] = nil
      next unless @presence_data[i][j]

      map_north = (i + 1) <= @map.logical_height
      map_south = (i - 1).positive?

      map_east  = (j + 1) <= @map.logical_width
      map_west  = (j - 1).positive?

      bitmask = 0

      # The presence of a bit in the bitmask indicates the presence of a tile relative to this one
      # Edges
      # Edges - North
      bitmask += 1 if map_north && @presence_data[i + 1][j]
      # Edges - East
      bitmask += 2 if map_east  && @presence_data[i][j + 1]
      # Edges - South
      bitmask += 4 if map_south && @presence_data[i - 1][j]
      # Edges - West
      bitmask += 8 if map_west  && @presence_data[i][j - 1]

      # Corners
      # Corners - NE
      bitmask += 16  if map_north && map_east && @presence_data[i + 1][j + 1]
      # Corners - SE
      bitmask += 32  if map_south && map_east && @presence_data[i - 1][j + 1]
      # Corners - SW
      bitmask += 64  if map_south && map_west && @presence_data[i - 1][j - 1]
      # Corners - NW
      bitmask += 128 if map_north && map_west && @presence_data[i + 1][j - 1]

      @bitmask_data[i][j] = bitmask
    end
  end
end

#redraw_at(x, y) ⇒ Object

Recalculates bitmask and sets sprites for the specified location and the 9 tiles surrounding it

Parameters:

  • x (Integer)

    Logical X location

  • y (Integer)

    Logical Y location



68
69
70
71
# File 'lib/zif/layers/bitmaskable.rb', line 68

def redraw_at(x, y)
  recalculate_bitmask(from_x: x - 1, from_y: y - 1, to_x: x + 1, to_y: y + 1)
  set_sprites_from_bitmask(from_x: x - 1, from_y: y - 1, to_x: x + 1, to_y: y + 1)
end

#reinitialize_spritesObject

Overrides Tileable#reinitialize_sprites Clears @sprites and resets #presence_data and #bitmask_data



42
43
44
45
46
# File 'lib/zif/layers/bitmaskable.rb', line 42

def reinitialize_sprites
  super()
  @presence_data = Array.new(@map.logical_height) { Array.new(@map.logical_width, false) }
  @bitmask_data  = Array.new(@map.logical_height) { Array.new(@map.logical_width, nil) }
end

#remove_at(x, y) ⇒ Object

Remove the tile / presence at this logical position, then redraw

Parameters:

  • x (Integer)

    logical_x position to remove

  • y (Integer)

    logical_y position to remove



51
52
53
54
55
# File 'lib/zif/layers/bitmaskable.rb', line 51

def remove_at(x, y)
  @presence_data[y][x] = false
  remove_tile(x, y)
  redraw_at(x, y)
end

#set_sprites_from_bitmask(from_x: 0, from_y: 0, to_x: @map.logical_width - 1, to_y: @map.logical_height - 1) ⇒ Object

This will reset the tiles for a rectangle, based on the #bitmask_data

Parameters:

  • from_x (Integer) (defaults to: 0)

    Lower left logical X position of the rectangle to update

  • from_y (Integer) (defaults to: 0)

    Lower left logical Y position of the rectangle to update

  • to_x (Integer) (defaults to: @map.logical_width - 1)

    Upper right logical X position of the rectangle to update

  • to_y (Integer) (defaults to: @map.logical_height - 1)

    Upper right logical Y position of the rectangle to update



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/zif/layers/bitmaskable.rb', line 128

def set_sprites_from_bitmask(from_x: 0, from_y: 0, to_x: @map.logical_width - 1, to_y: @map.logical_height - 1)
  raise 'BitmaskedTiledLayer: Please set @bitmasked_sprite_name_prefix' unless @bitmasked_sprite_name_prefix

  from_x = [from_x, 0].max
  from_y = [from_y, 0].max
  to_x   = [to_x, @map.logical_width - 1].min
  to_y   = [to_y, @map.logical_height - 1].min

  (from_y..to_y).each do |i|
    (from_x..to_x).each do |j|
      remove_tile(j, i)
      bitmask = @bitmask_data[i][j]
      next unless bitmask

      full_name = "#{@bitmasked_sprite_name_prefix}_#{bitmask}".to_sym
      sprite = $services[:sprite_registry].construct(full_name)

      add_positioned_sprite(sprite: sprite, logical_x: j, logical_y: i)
    end
  end
end