使用矢量算法与线段碰撞后错误的重新定位圆

Wrong repositioning circle after collision with segment with a vector algorithm

在 2D 游戏中,我试图在圆通过一个线段后重新定位它。以图片为例,我需要将圆重新定位,使B成为圆上的切点。

我掌握的信息是:

我正在做的是计算C点的坐标,然后计算B点的坐标,然后从AC(半径)中减去向量AB,并将x,y重新定位到-BC。

算法有问题,因为之后中心和线之间的正交距离永远不会与半径完全相同。

Len = Sqrt( dirx^2 + diry^2 )
dx = dirx / Len
dy = diry / Len
// these should be coordinates of point C
tangX = x + radius * dx
tangY = y + radius * dy
// these should be coordinates of point B
wrongX = x + distance* dx
wrongY = y + distance* dy
//these should be vector BC
BCx = tangX - wrongX;
BCy = tangY - wrongY;
// now i reposition the center back of BC
x = x - BCx
y = y - BCy

中心在正确的方向上重新定位,但永远不足以匹配距离 AC。

换句话说: 我想移动中心并使 B 成为圆和线之间的切点。让我换句话说给你解释这个问题:如你所见,AC 的长度等于我的半径。半径是 40。在我的日志中,我打印出 A 和 B 之间的距离值。以图像为例,这样的距离大约是 30,在我的算法之后我预计 AB 是 40(半径,AC)但它始终是另一个数字,而不是 40

如果表述得当,这个问题相对容易解决。这似乎是一个简单的碰撞检测案例,您要在检测到碰撞后调整对象的位置。构建它的直观方法是:您有一个事件告诉您您的对象越过了边界。它的最后一个运动向量是 (dirX, dirY) 并且你得到 delta 你的对象越过边界。您需要做的就是将对象向后移动,沿相同的运动矢量越过边界的相同增量。

而不是根据切点来推理问题,你可以简单地根据对象本身的中心坐标来推理。

现在,输入以下参数:

  • x, y 成为对象中心的最后一个坐标,r 其半径
  • (dirX, dirY)成为你的运动矢量
  • alpha 为运动矢量与 x 轴的角度(您可以根据运动矢量本身计算)
  • delta为超出边界的距离


问题的解决方案简化为以下内容:


下面是 python 2 中的一个可执行代码示例,用于实现和测试该解决方案:

from math import sin, cos, atan, fabs as abs, sqrt, pow
from collections import namedtuple

# utility types and functions
Disk = namedtuple('Disk', 'x y radius')
MotionVector = namedtuple('MotionVector', 'x y')

def sqr(d):
    return pow(d, 2)


# the actual implementation of the solution
def reposition_disk(disk, delta, motion_vector):
    # we start by computing the angle to the x axis of the motion vector
    alpha = atan(motion_vector.y / motion_vector.x)

    # then we compute the displacement we should apply
    # to our object along both axes

    dX = cos(alpha) * delta
    dY = sin(alpha) * delta

    # and we update the object's coordinates
    return Disk(disk.x - dX, disk.y - dY, disk.radius)


# a test method to exercise our implementation
def test_reposition_disk():
    # initialiasing our disk at a position where it is supposed
    # to have crossed a boundary
    disk = Disk(80, 70, 40)

    # the disk was moving along the following motion vector:
    motion_vector = MotionVector(10, -5)

    delta = 5

    updated = reposition_disk(disk, delta, motion_vector)

    # now we need to compute the coordinates of the 
    # point of the boundary the disk should have stopped at

    alpha = atan(motion_vector.y / motion_vector.x)
    bX = disk.x + (disk.radius - delta) * cos(alpha)
    bY = disk.y + (disk.radius - delta) * sin(alpha)

    # and finally we have to assert that the distance from
    # the object's new coordinates to the boundary is equal
    # to its radius

    distance = sqrt(sqr(updated.x - bX ) + sqr(updated.y - bY))
    print('Distance to boundary : %f' % distance)
    assert abs(distance - disk.radius) < 0.01


test_reposition_disk()



附录

我检查了您粘贴的代码,除非我弄错了,否则您检查碰撞并计算从圆心到相交线的距离的逻辑存在问题。该逻辑的准确实现意味着求解二次方程(检查此 link 以供将来参考)以找出直线是否与圆相交。然后要计算距离,您必须根据相交线的方向向量与通过圆心的正交线的点积求解方程。

这是一个 python 片段,说明了第一部分(确定线是否与圆相交)并粗略地遵循上面 link 中详述的步骤:

Line = namedtuple('Line', 'x1 y1 x2 y2')

# currying the square out of pow
def sqr(d):
    return pow(d, 2)


# function to compute the line equation
# out of the coordinates of a couple of
# points
def coords_to_affine(line):
    # y = hx + j
    h = (line.y2 - line.y1) / (line.x2 - line.x1)
    j = y1 - (h * x1)
    return (h, j)


# function to compute the quadratic equation
# parameters
def compute_quadratic_params(disk, line):
    (h, j) = coords_to_affine(line)
    (p, q, r) = (disk.x, disk.y, disk.r)

    a = sqr(h) + 1
    b = 2 * ((h * j) - (h * q) - p)
    c = sqr(q) - sqr(r) + sqr(p) - (2 * (j * q)) + sqr(j)
    return (a, b, c)


# function to test whether a given circle
# intersects a line
def disk_intersects_line(a, b, c):
    # true if (b² - 4ac) >= 0
    return (sqr(b) - (4 * a * c)) >= 0