class MagicCloud::CollisionBoard

Pixel-by-pixel collision board

Providen by width and height of the board, allows to check if given shape (array of zero and non-zero pixels) “collides” with any of previosly placed shapes

Attributes

intersections_cache[R]
rects[R]

Public Class Methods

new(width, height) click to toggle source
Calls superclass method MagicCloud::BitMatrix::new
# File lib/magic_cloud/collision_board.rb, line 11
def initialize(width, height)
  super
  @rects = []
  @intersections_cache = {}
end

Public Instance Methods

add(shape) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 99
def add(shape)
  l, t = shape.left, shape.top
  shape.height.times do |dy|
    shape.width.times do |dx|
      put(l + dx, t + dy) if shape.sprite.at(dx, dy)
    end
  end

  rects << shape.rect
end
collides?(shape) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 56
def collides?(shape)
  Debug.stats[:collide_total] += 1

  # nothing on board - so, no collisions
  return false if rects.empty?

  # no point to try drawing criss-crossed words
  # even if they will not collide pixel-per-pixel
  return true if criss_cross_collision?(shape.rect)

  # then find which of placed sprites rectangles tag intersects
  intersections = rects.map{|r| r.intersect(shape.rect)}

  # no need to further check: this tag is not inside any others' rectangle
  if intersections.compact.empty?
    Debug.stats[:rect_no] += 1
    return false
  end

  # most probable that we are still collide with this word
  return true if collides_previous?(shape, intersections)

  # only then check points inside intersected rectangles
  return true if pixels_collision_multi?(shape, intersections)

  Debug.stats[:px_no] += 1

  false
end
collides_previous?(shape, intersections) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 28
def collides_previous?(shape, intersections)
  prev_idx = intersections_cache[shape.object_id]

  if prev_idx && (prev = intersections[prev_idx]) &&
     pixels_collision?(shape, prev)

    Debug.stats[:px_prev_yes] += 1
    true
  else
    false
  end
end
criss_cross_collision?(rect) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 19
def criss_cross_collision?(rect)
  if rects.any?{|r| r.criss_cross?(rect)}
    Debug.stats[:criss_cross] += 1
    true
  else
    false
  end
end
pixels_collision?(shape, rect) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 86
def pixels_collision?(shape, rect)
  l, t = shape.left, shape.top
  (rect.x0...rect.x1).each do |x|
    (rect.y0...rect.y1).each do |y|
      dx = x - l
      dy = y - t
      return true if shape.sprite.at(dx, dy) && at(x, y)
    end
  end

  false
end
pixels_collision_multi?(shape, intersections) click to toggle source
# File lib/magic_cloud/collision_board.rb, line 41
def pixels_collision_multi?(shape, intersections)
  intersections.each_with_index do |intersection, idx|
    next unless intersection
    next if idx == intersections_cache[shape.object_id] # already checked it

    next unless pixels_collision?(shape, intersection)

    Debug.stats[:px_yes] += 1
    intersections_cache[shape.object_id] = idx
    return true
  end

  false
end