python - 决定使用哪个碰撞段作为反射的影响向量
问题描述
问题
我正在开发一个简单的 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/
- d : 力向量
- b : 受影响的表面矢量
- n : 撞击表面上的归一化(单位向量)法线向量 90 度
- r : 力向量的反射向量
然而; 在发现一个物体与另一个物体发生碰撞后,找到这个反射向量就会出现问题。
例如,当两个正方形发生碰撞(我实现的唯一形状)时,碰撞检测有时会检测到两个段上的碰撞。因为我不知道我应该如何选择哪一个作为受影响的部分,所以反射有时会出现在错误的方向上。
问题
如何确定图像中绿色的哪一部分应该用作反射的影响矢量?
编辑:
我现在已经实施了 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 方法接收到的不正确的影响向量:
绿色轮廓是可碰撞的刚体,红色箭头是反射前的力矢量方向。计算反射后,反射将在错误的方向。
解决方案
使用下图。
框沿矢量 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 点代表公共角,因为测试仅在您要测试的两侧之间。您永远不需要测试超过两个方面。
推荐阅读
- scala - 使用 Akka Streams 管理共享状态的惯用方式
- python - 遍历内置数字列表的递归函数
- android - Firebase 消息在后台
- r - R如何根据其他列中的字符串值定义新的列变量
- c# - 将 XML 对象解析为具有不同逻辑的多个类
- android - 无法在 v4.app.Fragment 上调用 onRequestPermissionsResult
- wso2 - WSO2 APIM 如何为应用程序订阅所有版本的 API?
- python - 将 keras 模型导出到 tflite
- html - 将图标放在手风琴的右端
- javascript - 对象不支持属性或方法“MaterialSelect”