Class: Chingu::GameObjectMap
- Inherits:
-
Object
- Object
- Chingu::GameObjectMap
- Defined in:
- lib/chingu/game_object_map.rb
Overview
** This class is under heavy development, API will most likely change! **
GameObjectMap can map any set of game objects into a 2D-array for fast lookup. You can choose gridsize with the :grid-parameter, defaults to [32,32]. The smaller the grid the more memory GameObjectMap will eat.
The game objects sent to GameObjectMap must respond to #bb (as provided by trait :bounding_box) This is needed to calcuate what cells in the grid each game object covers.
Basic usage:
@map = GameObjectMap.new(:game_objects => TerrainObject.all, :grid => [32, 32])
@map.at(100, 100) # returns one TerrainObject at x/y: 100/100
@map.game_object(player) # returns one TerrainObject which collides with player.bounding_box
A GameObjectMap is ment to be used for static non-moving objects, where a map can be calculated once and then used for fast lookups. This makes GameObjectMap very well suited for terrain for a player to walk on / collide with.
One cell in the GameObjectMap can only be occupied by one game object. If you need many objects at the same cell, use 2 GameObjectMaps, something like:
@terrain = GameObjectMap.new(:game_objects => Terrain.all)
@mines = GameObjectMap.new(:game_objects => Mine.all)
@player.stop_falling if @terrain.at(@player.x, @player)
@player.die if @mine.at(@player.x, @player)
Take note, since there can be only 1 game object per cell a huge game object could very well “cover out” another smaller game objects occupying the same cells.
** This class is under heavy development, API will most likely change! **
Instance Attribute Summary collapse
-
#game_object_positions ⇒ Object
readonly
Returns the value of attribute game_object_positions.
-
#map ⇒ Object
readonly
Returns the value of attribute map.
Instance Method Summary collapse
-
#at(x, y) ⇒ Object
Gets game object from map that resides on real world coordinates x/y.
-
#clear_at(x, y) ⇒ Object
Clear the game object residing in the cell given by real world coordinates x/y.
-
#collisions_with(game_object) ⇒ Object
Returns an array of GameObjects in this grid map that collide with the given GameObject (which is not on the grid).
-
#create_map ⇒ Object
Creates a “tilemap” of game objects using @grid and @game_objects Useful for faster collision detection on a grid-based freeform map created with the Edit game state.
-
#delete(game_object) ⇒ Object
(also: #clear_game_object)
Removes a specific game object from the map, replace the cell-value with nil.
-
#each_collision(game_object) ⇒ Object
Yields all game objects occupying any of the cells that given 'game_object' covers.
-
#each_game_object_between(origin, dest) ⇒ Object
Yields to the block each GameObject in this map's grid which lies between two GameObjects: origin and dest.
-
#from_game_object(game_object) ⇒ Object
Return the first game object occupying any of the cells that given 'game_object' covers.
-
#game_object_at(x, y) ⇒ Object
Return the GameObject at the grid coordinates (not pixel coordinates) x and y.
-
#game_object_between?(origin, dest, options = {}) ⇒ Boolean
Options can contain the keys :target and :only.
-
#grid_spaces_between(origin, dest) ⇒ Object
Returns an array of [x, y] grid coordinate pairs in this map's grid between the GameObjects origin and dest.
-
#initialize(options = {}) ⇒ GameObjectMap
constructor
A new instance of GameObjectMap.
-
#insert(game_object) ⇒ Object
Insert game_object into the map.
Constructor Details
#initialize(options = {}) ⇒ GameObjectMap
Returns a new instance of GameObjectMap.
57 58 59 60 61 62 |
# File 'lib/chingu/game_object_map.rb', line 57 def initialize( = {}) @game_objects = [:game_objects] @grid = [:grid] || [32,32] @debug = [:debug] create_map end |
Instance Attribute Details
#game_object_positions ⇒ Object (readonly)
Returns the value of attribute game_object_positions
55 56 57 |
# File 'lib/chingu/game_object_map.rb', line 55 def game_object_positions @game_object_positions end |
#map ⇒ Object (readonly)
Returns the value of attribute map
55 56 57 |
# File 'lib/chingu/game_object_map.rb', line 55 def map @map end |
Instance Method Details
#at(x, y) ⇒ Object
Gets game object from map that resides on real world coordinates x/y
126 127 128 129 130 |
# File 'lib/chingu/game_object_map.rb', line 126 def at(x, y) lookup_x = (x / @grid[0]).to_i lookup_y = (y / @grid[1]).to_i @map[lookup_x][lookup_y] rescue nil # Benchmark this against @map[lookup_x] && @map[lookup_x][lookup_y] => prob faster end |
#clear_at(x, y) ⇒ Object
Clear the game object residing in the cell given by real world coordinates x/y
117 118 119 120 121 |
# File 'lib/chingu/game_object_map.rb', line 117 def clear_at(x, y) lookup_x = (x / @grid[0]).to_i lookup_y = (y / @grid[1]).to_i @map[lookup_x][lookup_y] = nil end |
#collisions_with(game_object) ⇒ Object
Returns an array of GameObjects in this grid map that collide with the given GameObject (which is not on the grid).
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/chingu/game_object_map.rb', line 172 def collisions_with(game_object) start_x = (game_object.bb.left / @grid[0]).to_i stop_x = (game_object.bb.right / @grid[0]).to_i objects = [] (start_x ... stop_x).each do |x| start_y = (game_object.bb.top / @grid[1]).to_i stop_y = (game_object.bb.bottom / @grid[1]).to_i (start_y ... stop_y).each do |y| obj = game_object_at(x, y) objects << @map[x][y] if obj and obj != game_object # Don't yield collisions with itself end end objects end |
#create_map ⇒ Object
Creates a “tilemap” of game objects using @grid and @game_objects Useful for faster collision detection on a grid-based freeform map created with the Edit game state.
68 69 70 71 72 73 74 75 76 |
# File 'lib/chingu/game_object_map.rb', line 68 def create_map @map = [] @game_object_positions = {} @game_objects.each do |game_object| puts "#{game_object.class} @ #{game_object.x}/#{game_object.y} - #{game_object.bb}" if @debug insert(game_object) end end |
#delete(game_object) ⇒ Object Also known as: clear_game_object
Removes a specific game object from the map, replace the cell-value with nil
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/chingu/game_object_map.rb', line 102 def delete(game_object) range_x, range_y = @game_object_positions[game_object] return unless range_x && range_y range_x.each do |x| range_y.each do |y| @map[x][y] = nil end end end |
#each_collision(game_object) ⇒ Object
Yields all game objects occupying any of the cells that given 'game_object' covers
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/chingu/game_object_map.rb', line 154 def each_collision(game_object) start_x = (game_object.bb.left / @grid[0]).to_i stop_x = (game_object.bb.right / @grid[0]).to_i (start_x ... stop_x).each do |x| start_y = (game_object.bb.top / @grid[1]).to_i stop_y = (game_object.bb.bottom / @grid[1]).to_i (start_y ... stop_y).each do |y| yield @map[x][y] if @map[x] && @map[x][y] && @map[x][y] != game_object # Don't yield collisions with itself end end end |
#each_game_object_between(origin, dest) ⇒ Object
Yields to the block each GameObject in this map's grid which lies between two GameObjects: origin and dest.
193 194 195 196 197 198 |
# File 'lib/chingu/game_object_map.rb', line 193 def each_game_object_between(origin, dest) grid_spaces_between(origin, dest) do |x, y| obj = game_object_at(x, y) yield if obj and obj != origin and obj != dest end end |
#from_game_object(game_object) ⇒ Object
Return the first game object occupying any of the cells that given 'game_object' covers
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/chingu/game_object_map.rb', line 135 def from_game_object(game_object) start_x = (game_object.bb.left / @grid[0]).to_i stop_x = (game_object.bb.right / @grid[0]).to_i (start_x .. stop_x).each do |x| start_y = (game_object.bb.top / @grid[1]).to_i stop_y = (game_object.bb.bottom / @grid[1]).to_i (start_y .. stop_y).each do |y| return @map[x][y] if @map[x] && @map[x][y] end end return nil end |
#game_object_at(x, y) ⇒ Object
Return the GameObject at the grid coordinates (not pixel coordinates) x and
-
If there is no object there, return nil.
237 238 239 240 |
# File 'lib/chingu/game_object_map.rb', line 237 def game_object_at(x, y) return @map[x][y] if @map[x] and @map[x][y] return nil end |
#game_object_between?(origin, dest, options = {}) ⇒ Boolean
Options can contain the keys :target and :only.
Returns true if GameObject options is between GameObjects origin and dest.
If the target is nil, returns true if any GameObject in this map's grid lies between origin and dest.
If options is set, return true only if the matched object is_a? options.
This can be used to find line-of-sight between two objects, for example:
player.sees_enemy if game_object_map.game_object_between?(player, enemy, :only => Wall) # Walls block vision
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/chingu/game_object_map.rb', line 215 def game_object_between?(origin, dest, ={}) grid_spaces_between(origin, dest) do |x, y| if [:target] x_pixels = x * @grid[0] y_pixels = y * @grid[1] return true if [:target].collision_at?(x_pixels, y_pixels) else obj = game_object_at(x, y) if [:only] return true if obj and obj != origin and obj != dest and obj.is_a? [:only] else return true if obj and obj != origin and obj != dest end end end return false end |
#grid_spaces_between(origin, dest) ⇒ Object
Returns an array of [x, y] grid coordinate pairs in this map's grid between the GameObjects origin and dest.
If a block is given, the method will yield x, y to the block for each grid square.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/chingu/game_object_map.rb', line 249 def grid_spaces_between(origin, dest) # Note: x and y here are a Grid location, not pixel coordinates. raise "Expected GameObject as origin, got #{origin.class}" unless origin.is_a? Chingu::GameObject raise "Expected GameObject as dest, got #{dest.class}" unless dest.is_a? Chingu::GameObject start_x = (origin.bb.x/ @grid[0]).to_i stop_x = (dest.bb.x/ @grid[0]).to_i start_y = (origin.bb.y/ @grid[1]).to_i stop_y = (dest.bb.y/ @grid[1]).to_i diff_x = (start_x - stop_x).abs diff_y = (start_y - stop_y).abs x = start_x y = start_y n = 1 + diff_x + diff_y x_inc = 1 #FIXME: do fewer checks x_inc = -1 if start_x > stop_x y_inc = 1 y_inc = -1 if start_y > stop_y error = diff_x - diff_y diff_x *= 2 diff_y *= 2 checked_squares = [] checked_squares << [start_x,start_y] checked_squares << [stop_x,stop_y] n.times do checked_squares << [x,y] yield(x, y) if block_given? if error > 0 x += x_inc error -= diff_y else y += y_inc error += diff_x end end return checked_squares end |
#insert(game_object) ⇒ Object
Insert game_object into the map
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/chingu/game_object_map.rb', line 81 def insert(game_object) start_x = ( game_object.bb.left / @grid[0] ).to_i stop_x = ( game_object.bb.right / @grid[0] ).to_i (start_x ... stop_x).each do |x| start_y = ( game_object.bb.top / @grid[1] ).to_i stop_y = ( game_object.bb.bottom / @grid[1] ).to_i @game_object_positions[game_object] = [(start_x ... stop_x), (start_y ... stop_y)] @map[x] ||= [] (start_y ... stop_y).each do |y| @map[x][y] = game_object puts "#{game_object.class} => map[#{x}][#{y}]" if @debug end end end |