複数の衝突判定

Spriteオブジェクトは衝突判定範囲を持ち、そこに画像用のパラメータのスケーリング・ローテーションを反映させて衝突判定を行う。ただ、形状は点、矩形、円、三角に限定されていた。これらを組み合わせて複雑な判定を行いたいという場合がある。
そのような場合に有効な手が無いため、衝突判定範囲の配列をもう一段配列にして、複数の判定範囲を持てるように修正した。
昨日はそのテスト用の五芒星を作っていたわけだ。

ちょっとわかりにくいけど画像の中に五芒星が混ざっている。衝突判定範囲は描画に使ったのと同じ5個の三角形の座標配列で、このどれか1つに当たっていたら衝突とみなされる。
ちょっと長くなってしまったがソースを置いておく。

#!ruby -Ks
require "dxruby"

class CollisionObject < Sprite
  def initialize
    self.x = rand(Window.width-self.image.width)
    self.y = rand(Window.height-self.image.height)
    @hit = false
    @dx = (rand(4)*2-3)/4.0
    @dy = (rand(4)*2-3)/4.0
    super(x, y, self.image)
  end

  def update
    self.x += @dx
    self.y += @dy
    @dx = -@dx if self.x <= 0 or self.x >= Window.width-self.image.width
    @dy = -@dy if self.y <= 0 or self.y >= Window.height-self.image.height
  end
end

# しかく
class Box < CollisionObject
  @@image1 = Image.new(30, 30, [255, 200, 0, 0])
  @@image2 = Image.new(30, 30, [255, 255, 255])
  def initialize
    self.collision = [0, 0, 29, 29]
    self.image = @@image1
    self.scale_x = rand(2.0)+1
    self.scale_y = rand(2.0)+1
    self.center_x = rand(self.image.width)
    self.center_y = rand(self.image.height)
    self.angle = rand(360)
    @rot_speed = (rand(2.0)+1)*2-3
    super
  end

  def update
    super
    self.angle += @rot_speed
    self.image = @@image1
  end

  def hit(d)
    self.image = @@image2
  end
end

# しかく2
class Box2 < CollisionObject
  @@image1 = Image.new(30, 30, [255, 200, 0, 0])
  @@image2 = Image.new(30, 30, [255, 255, 255])
  def initialize
    self.collision = [0, 0, 29, 29]
    self.image = @@image1
    super
  end
  def update
    super
    self.image = @@image1
  end

  def hit(d)
    self.image = @@image2
  end
end

# まる
class Circle < CollisionObject
  @@image1 = Image.new(30, 30).circle_fill(15, 15, 15, [255, 200, 0])
  @@image2 = Image.new(30, 30).circle_fill(15, 15, 15, [255, 255, 255])
  def initialize
    self.collision = [15, 15, 15]
    self.image = @@image1
    self.scale_x = rand(2.0)+1
    self.scale_y = rand(2.0)+1
    self.center_x = rand(self.image.width)
    self.center_y = rand(self.image.height)
    self.angle = rand(360)
    @rot_speed = (rand(2.0)+1)*2-3
    super
  end
  def update
    super
    self.angle += @rot_speed
    self.image = @@image1
  end
  def hit(d)
    self.image = @@image2
  end
end

# まる2
class Circle2 < CollisionObject
  @@image1 = Image.new(30, 30).circle_fill(15, 15, 15, [255, 200, 0])
  @@image2 = Image.new(30, 30).circle_fill(15, 15, 15, [255, 255, 255])
  def initialize
    self.collision = [15, 15, 15]
    self.image = @@image1
    super
  end
  def update
    super
    self.image = @@image1
  end
  def hit(d)
    self.image = @@image2
  end
end

# さんかく
class Triangle < CollisionObject
  @@image1 = Image.new(30, 30).triangle_fill(15,0,29,29,0,29,C_GREEN)
  @@image2 = Image.new(30, 30).triangle_fill(15,0,29,29,0,29,C_WHITE)
  def initialize
    self.collision = [15,0,29,29,0,29]
    self.image = @@image1
    self.scale_x = rand(2.0)+1
    self.scale_y = rand(2.0)+1
    self.center_x = rand(self.image.width)
    self.center_y = rand(self.image.height)
    self.angle = rand(360)
    @rot_speed = (rand(2.0)+1)*2-3
    super
  end

  def update
    super
    self.angle += @rot_speed
    self.image = @@image1
  end
  def hit(d)
    self.image = @@image2
  end
end

# さんかく2
class Triangle2 < CollisionObject
  @@image1 = Image.new(30, 30).triangle_fill(15,0,29,29,0,29,C_GREEN)
  @@image2 = Image.new(30, 30).triangle_fill(15,0,29,29,0,29,C_WHITE)
  def initialize
    self.collision = [15,0,29,29,0,29]
    self.image = @@image1
    super
  end
  def update
    super
    self.image = @@image1
  end
  def hit(d)
    self.image = @@image2
  end
end

# ほし
class Star < CollisionObject
  @@image1 = Image.new(100,100)
  @@image2 = Image.new(100,100)

  bx = 0
  by = -20

  x1 = []
  y1 = []
  (0..4).each do |i|
    x1[i] = bx * Math.cos(Math::PI / 180 * (i * 72 - 36)) - by * Math.sin(Math::PI / 180 * (i * 72 - 36)) + @@image1.width / 2
    y1[i] = bx * Math.sin(Math::PI / 180 * (i * 72 - 36)) + by * Math.cos(Math::PI / 180 * (i * 72 - 36)) + @@image1.height / 2
  end
  sx = x1[0] + (x1[1] - x1[0]) / 2 - @@image1.width / 2
  sy = y1[0] - (x1[1] - x1[0]) / 2 * Math.tan(Math::PI / 180 * 72) - @@image1.height / 2
  x2 = []
  y2 = []
  (0..4).each do |i|
    x2[i] = sx * Math.cos(Math::PI / 180 * i * 72) - sy * Math.sin(Math::PI / 180 * i * 72) + @@image1.width / 2
    y2[i] = sx * Math.sin(Math::PI / 180 * i * 72) + sy * Math.cos(Math::PI / 180 * i * 72) + @@image1.height / 2
  end
  @@cols = [[x2[0], y2[0], x1[2], y1[2], x1[4], y1[4]],
            [x2[1], y2[1], x1[3], y1[3], x1[0], y1[0]],
            [x2[2], y2[2], x1[4], y1[4], x1[1], y1[1]],
            [x2[3], y2[3], x1[0], y1[0], x1[2], y1[2]],
            [x2[4], y2[4], x1[1], y1[1], x1[3], y1[3]]]

  @@cols.each do |ary|
    @@image1.triangle_fill(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], C_CYAN)
    @@image2.triangle_fill(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], C_WHITE)
  end

  def initialize
    self.image = @@image1
    self.collision = @@cols
    super
  end
  def update
    super
    self.image = @@image1
    self.angle += 1
  end
  def hit(d)
    self.image = @@image2
  end
end



Window.width, Window.height = 800, 600

font = Font.new(24)

object = Array.new(20) {Box.new} + Array.new(20) {Box2.new} +
         Array.new(20) {Triangle.new} + Array.new(20) {Triangle2.new} +
         Array.new(20) {Circle.new} + Array.new(20) {Circle2.new} +
         Array.new(5) {Star.new}

Window.loop do
  object.each do |o|
    o.update
  end

  Sprite.check(object)

  object.each do |o|
    o.draw
  end

  break if Input.keyPush?(K_ESCAPE)

  Window.drawFont(0, 0, Window.fps.to_s + " fps", font)
  Window.drawFont(0, 24, Window.getLoad.to_i.to_s + " %", font)
end