首页 > 解决方案 > 决定使用哪个碰撞段作为反射的影响向量

问题描述

问题

我正在开发一个简单的 OpenGL 项目。我的目标是从受影响的物体上正确地反射一个受力的物体。

为此,我使用形状的线段(线段是组成一条线的两个点)进行碰撞检测。例如,正方形的段将是:

self.segments = [
            Segment2D(self.bottom_left, self.bottom_right),  # Bottom side
            Segment2D(self.bottom_right, self.top_right),  # Right side
            Segment2D(self.top_right, self.top_left),  # Top side
            Segment2D(self.top_left, self.bottom_left)  # Left side
        ]

检测的工作原理是检查对象的任何片段是否与外部对象的片段相交。我正在使用 geeksforgeeks 中解释的方法:https ://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/

为了计算反射,我使用公式 r=d−2(d⋅n)n
在此处输入图像描述

然而; 在发现一个物体与另一个物体发生碰撞后,找到这个反射向量就会出现问题。

在此处输入图像描述

例如,当两个正方形发生碰撞(我实现的唯一形状)时,碰撞检测有时会检测到两个段上的碰撞。因为我不知道我应该如何选择哪一个作为受影响的部分,所以反射有时会出现在错误的方向上。

问题

如何确定图像中绿色的哪一部分应该用作反射的影响矢量?

编辑:
我现在已经实施了 Blindman67 建议的解决方案。碰撞检测(决定碰撞哪一侧)似乎在大约 80% 的情况下有效。当靠近角落碰撞时,正方形有时仍会反射到错误的方向。我不确定这是我的错误还是给定的解决方案。

我一直无法解决问题。

此外,当受影响的方格在两侧被吞下时,检查不再成立,使受影响的线彼此平行。

这是我想出的逻辑:

def rect_rect_collision(self, other, force: Vector2D, segments: List[Segment2D]) -> Segment2D:
        """
        Returns the segment that is the most correct one for collision
        :param other: Colliding RigidBody
        :param force: The force applied on the object
        :param segments: The segments that were detected as colliding by the general detection
        :return: The correct collision segment
        """
        if segments[0].angle() == segments[1].angle():
            # TODO: decide which to collide of the remaining 2
            raise Exception("Lines are parallel")

        # Shared corner of the impacted segments
        common_corner = RigidRect2D.get_common_corner(segments[0], segments[1])
        E: Point2D = self.add_width_height_relative_to_center(common_corner, other.shape.center)
        # Segment 0 is EF
        seg_0_other = segments[0].p1 if segments[0].p2 == common_corner else segments[0].p2
        F: Point2D = self.add_width_height_relative_to_center(seg_0_other, other.shape.center)
        # Segment 1 is EJ
        seg_1_other = segments[1].p1 if segments[1].p2 == common_corner else segments[1].p2
        J: Point2D = self.add_width_height_relative_to_center(seg_1_other, other.shape.center)

        A: Point2D = self.shape.center
        ABx: float = force.x
        ABy: float = force.y
        uu = ABx * (A.y - E.y) - ABy * (A.x - E.x)

        EFx = F.x - E.x
        EFy = F.y - E.y
        c = ABx * EFy - ABy * EFx
        if c != 0:
            u = uu / c
            print("U - EF: ", u)
            if 0 <= u <= 1:
                # Hits line E-F
                return segments[0]

        EJx = J.x - E.x
        EJy = J.y - E.y
        c = ABx * EJy - ABy * EJx
        if c != 0:
            u = uu / c
            print("U - EJ: ", u)
            if 0 <= u <= 1:
                # Hits line E-J
                return segments[1]

        raise Exception("Returned no segment from the collision detection")

    @staticmethod
    def get_common_corner(segment1: Segment2D, segment2: Segment2D):
        if segment1.has_point(segment2.p1):
            return segment2.p1
        if segment1.has_point(segment2.p2):
            return segment2.p2
        print(segment1)
        print(segment2)
        raise Exception("No common corner")

    def add_width_height_relative_to_center(self, point: Point2D, center: Point2D) -> Point2D:
        newPoint = Point2D(point.x, point.y)
        if newPoint.y >= center.y:
            newPoint.y += self.shape.height / 2
        else:
            newPoint.y -= self.shape.height / 2

        if newPoint.x >= center.x:
            newPoint.x += self.shape.width / 2
        else:
            newPoint.x -= self.shape.width / 2

        return newPoint

这是一个图像,描述了反射方法从 rect_rect_collision 方法接收到的不正确的影响向量: 不正确的影响向量

绿色轮廓是可碰撞的刚体,红色箭头是反射前的力矢量方向。计算反射后,反射将在错误的方向。

标签: pythoncollision-detectiongame-physicsvector-graphicsvectormath

解决方案


使用下图。

在此处输入图像描述

框沿矢量 AB 移动。以沿 EF 或 EJ 的单位计算 AB 和 EF 和 EJ 之间的截距 图中的截点 C 将位于 AB 和 EJ 线之间

ABx = B.x - A.x
ABy = B.y - A.y
uu = ABx * (A.y - E.y) - ABy * (A.x - E.x)

EFx = F.x - E.x
EFy = F.y - E.y
c = ABx * EFy - ABy * EFx
if (c !== 0) {
    u = uu / c
    if (u >= 0 && u <= 1) {
        // hits line E-F
    }
}

EJx = J.x - E.x
EJy = J.y - E.y
c = ABx * EJy - ABy * EJx
if (c !== 0) {
    u = uu / c
    if (u >= 0 && u <= 1) {
        // hits line E-J
    }
}

如果框是轴对齐的 AABB,那么您可以通过取消零来简化

ABx = B.x - A.x
ABy = B.y - A.y
uu = ABx * (A.y - E.y) - ABy * (A.x - E.x)

c = -ABy * (F.x - E.x)  
if (c !== 0) {
    u = uu / c
    if (u >= 0 && u <= 1) {
        // hits line E-F
    }
}

c = ABx * (J.y - E.y) 
if (c !== 0) {
    u = uu / c
    if (u >= 0 && u <= 1) {
        // hits line E-J
    }
}

请注意,E 点代表公共角,因为测试仅在您要测试的两侧之间。您永远不需要测试超过两个方面。


推荐阅读