Class: MagicCloud::CollisionBoard

Inherits:
BitMatrix
  • Object
show all
Defined in:
lib/magic_cloud/collision_board.rb

Overview

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

Instance Attribute Summary collapse

Attributes inherited from BitMatrix

#bits, #height, #width

Instance Method Summary collapse

Methods inherited from BitMatrix

#at, #dump, #put

Constructor Details

#initialize(width, height) ⇒ CollisionBoard

Returns a new instance of CollisionBoard.



12
13
14
15
16
# File 'lib/magic_cloud/collision_board.rb', line 12

def initialize(width, height)
  super
  @rects = []
  @intersections_cache = {}
end

Instance Attribute Details

#intersections_cacheObject (readonly)

Returns the value of attribute intersections_cache.



18
19
20
# File 'lib/magic_cloud/collision_board.rb', line 18

def intersections_cache
  @intersections_cache
end

#rectsObject (readonly)

Returns the value of attribute rects.



18
19
20
# File 'lib/magic_cloud/collision_board.rb', line 18

def rects
  @rects
end

Instance Method Details

#add(shape) ⇒ Object



102
103
104
105
106
107
108
109
110
111
# File 'lib/magic_cloud/collision_board.rb', line 102

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) ⇒ Boolean

rubocop:enable Lint/HashCompareByIdentity

Returns:

  • (Boolean)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/magic_cloud/collision_board.rb', line 59

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) ⇒ Boolean

rubocop:disable Lint/HashCompareByIdentity – probably should be followed, but don’t have time to test it now

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/magic_cloud/collision_board.rb', line 30

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) ⇒ Boolean

Returns:

  • (Boolean)


20
21
22
23
24
25
26
27
# File 'lib/magic_cloud/collision_board.rb', line 20

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) ⇒ Boolean

Returns:

  • (Boolean)


89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/magic_cloud/collision_board.rb', line 89

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) ⇒ Boolean

Returns:

  • (Boolean)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/magic_cloud/collision_board.rb', line 43

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