0

所以我是 ruby​​ 的初学者,我正在玩 ruby​​ 2d 中的碰撞检测。我有一个可控制的正方形和一个保持静止的正方形。我的问题是如何检测正方形何时与圆形碰撞。

这是我的主要代码,其余的只是移动方块的控件

    @square = Square.new(x: 10, y: 20, size: 25, color: 'blue')

    @circle = Circle.new(x: 100, y: 100, radius: 10, color: 'red')

    @x_speed = 0
    @y_speed = 0

    game = Game.new


    class Game
        @score = 0
    end

这就是正在更新的内容,

    update do
      if game.square_hit_circle?(square.x, square.y)
        puts "hit"
      end
    end

这是什么square_hit_circle?方法

    def square_hit_circle?(x, y)
      @circle_x == x && @circle_y == y
    end
4

1 回答 1

1

我认为提供一个不使用ruby2dgem 的解决方案可能会很有趣,因此请展示如何执行所需的计算。

示例数据

认为

center = [2, 7]
radius = 4

corners = [[1,3], [5,3], [3,1], [3,5]]

这看起来像下面这样,Y圆的中心和X正方形的角。

7    Y
6   
5        X
4         
3  X           X    
2                
1        X              
0  1  2  3  4  5

确定矩形的边

选择这些角中的任何一个(比如第一个角):

first, *rest = corners
  #=> [[1, 3], [5, 3], [3, 1], [3, 5]] 
first
  #=> [1, 3] 
rest
  #=> [[5, 3], [3, 1], [3, 5]] 

确定哪个角离 最远c1

x1, y1 = first
farthest = rest.max_by { |x,y| (x-x1)**2 + (y-y1)**2 }
  #=> [5, 3] 

将正方形的边计算为端点数组:

rest.delete(farthest)
  #=> [5, 3] 
rest
  #=> [[3, 1], [3, 5]] 
sides = [first,farthest].product(rest)  
  #=> [[[1, 3], [3, 1]],
  #    [[1, 3], [3, 5]],
  #    [[5, 3], [3, 1]],
  #    [[5, 3], [3, 5]]]

让我们做一个方法来做到这一点。

def sides(corners)
  first, *rest = corners
  x1, y1 = first
  farthest = rest.max_by { |x,y| (x-x1)**2 + (y-y1)**2 }
  rest.delete(farthest)
  [first,farthest].product(rest)
end

sides(corners)  
  #=> <as above>

计算与正方形边重合的线的截距和斜率

对于这些边中的每一个,在空间中都有一条与边重合的线。这些线中的每一条都由截距i和斜率描述,因此对于点b的任何值,如果 在线上。我们可以计算每条线的截距和斜率。x[x, y]y = i + b*x

def compute_line(side)
  (ux,uy), (vx,vy) = side
  b = ux==uy ? 0.0 : (uy - vy).fdiv(ux - vx)
  [uy - b*ux, b]
end

sides.map { |side| compute_line(side) }
  #=> [[4.0, -1.0], [2.0, 1.0], [-2.0, 1.0], [8.0, -1.0]] 

笔记:

i, b = lines.first
  #=> [4.0, -1.0] 
i + b*0
  #=> 4.0 (the point [0, 4.0]) 
i + b*1
  #=> 3.0 (the point [1, 3.0]) 
i + b*2
  #=> 2.0 (the point [2, 2.0])
i + b*3  
  #=> 1.0 (the point [3, 1.0])
i + b*4
  #=> 0.0 (the point [4, 0.0])

计算圆与边重合的线相交的点

cx, cy = center
  #=> [2, 7]

假设我们考虑重合线具有截距i和斜率的一侧s。然后我们有二次表达式:

(cx-x) 2 + (cy-is*x) 2 = 半径2

通过定义:

e = cy - 我

等式简化为:

cx 2 - 2*cx*x + x 2 + e 2 - 2*e*s*x + s 2 *x 2 = 半径2

或者

(1 + s 2 )*x 2 + 2*(-cx -e*s)*x + cx 2 + e 2 - 半径2 = 0

或者

a x 2 + b x + c = 0

在哪里:

a = (1 + s 2 )

b = -2*(cx + e*s)

c = cx 2 + e 2 - 半径2

一个或多个实根(如果存在)由二次方程给出。首先计算discriminate

d = b 2 - 4*a*c

如果判别为负,则二次没有实根(只有复根)。这意味着圆不够大,无法与与这一边重合的线相交。

如果判别d为正,则有两个实根(仅当d为零时才具有一个实根)。让:

w = d 1/2

根( 的值x)是:

(-b + w)/(2*a)

(-b - w)/(2*a)

让我们将其包装在一个方法中:

def circle_and_line_intersections(center, radius, side)
  i, s = compute_line(side)
  cx, cy = center
  e = cy - i
  a = 1 + s**2
  b = -2*(cx + e*s)
  c = cx**2 + e**2 - radius**2 
  d = b**2 - 4*a*c
  return [] if d < 0
  return [-b/(2*a)] if d.zero?
  w = Math.sqrt(d)
  r1 = (-b + w)/(2*a)
  r2 = (-b - w)/(2*a)
  [[r1, i + s*r1], [r2, i + s*r2]] 
end

sides.map { |side| circle_and_line_intersections(center, radius, side) }
  #=> [[[ 0.82287, 3.17712], [-1.82287, 5.82287]],
  #    [[ 5.89791, 7.89791], [ 1.10208, 3.10208]],
  #    [],
  #    [[4.28388, 3.71611], [-1.28388, 9.28388]]]    

还需要做以下事情。

确定其中一个交点是否在一侧

这很简单明了:

def on_side?(pt, side)
  low, high = side.map(&:first).sort
  (low..high).cover?(pt.first)
end 

例如:

on_side?([0.82287, 3.17712], sides[0])
  #=> false
on_side?([1.10208, 3.10208], sides[1])    
  #=> true

把它们放在一起

def intersect?(center, radius, corners)
  sides(corners).any? do |side|
    circle_and_line_intersections(center, radius, side).any? { |pt|
    on_side?(pt, side) }
  end
end

intersect?(center, radius, corners)
  #=> true
于 2019-08-26T21:18:31.640 回答