Python和Pygame:球与圆的内部碰撞

nch*_*pmn 7 python pygame physics collision-detection

我正在制作一个游戏,其中球一个更大的圆圈内部反弹.较大的圆圈不会移动.

这是我目前用于这些冲突的代码:

def collideCircle(circle, ball):
    """Check for collision between a ball and a circle"""
    dx = circle.x - ball.x
    dy = circle.y - ball.y

    distance = math.hypot(dx, dy)

    if distance >= circle.size + ball.size:
        # We don't need to change anything about the circle, just the ball
        tangent = math.atan2(dy, dx)
        ball.angle = 2 * tangent - ball.angle
        ball.speed *= elasticity + 0.251

        angle = 0.5 * math.pi + tangent
        ball.x -= math.sin(angle)
        ball.y += math.cos(angle)
Run Code Online (Sandbox Code Playgroud)

它基于Peter Collingridge 在这里的精彩教程.

圆和球对象都是类,具有(x,y),半径,角度和速度.

我有这个方法的两个问题,但是:

  1. 球弹跳(我怀疑)是它的"锚点",它似乎位于圆圈的右上角.
  2. 当与圆的底部5%碰撞时,无法反弹得足够高,因此"下沉"出屏幕.我猜这是因为弹跳不足以将球移到其(错误放置的)"锚点"之上?

看过这里可能已有的解决方案,特别是"快速圈碰撞检测"[链接由于垃圾邮件链接限制而被删除],虽然在Java中使用相同的方法,但这些都处理外部冲突,而我正在寻找弹跳围绕圆圈内部的球.

这里还有Ball()和Circle()的类定义:

class Ball():
    def __init__(self, (x,y), size):
        """Setting up the new instance"""
        self.x = x
        self.y = y
        self.size = size
        self.colour = (0,128,255)
        self.thickness = 0
        self.speed = 0.01
        self.angle = math.pi/2

    def display(self):
        """Draw the ball"""
        pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)

    def move(self):
        """Move the ball according to angle and speed"""
        self.x += math.sin(self.angle) * self.speed
        self.y -= math.cos(self.angle) * self.speed
        (self.angle, self.speed) = addVectors((self.angle, self.speed), gravity)
        self.speed *= drag

class Circle():
    def __init__(self, (x,y), size):
        """Set up the new instance of the Circle class"""
        self.x = x
        self.y = y
        self.size = size
        self.colour = (236, 236, 236)
        self.thickness = 0
        self.angle = 0 # Needed for collision...
        self.speed = 0 # detection against balls

    def display(self):
        """Draw the circle"""
        pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness
Run Code Online (Sandbox Code Playgroud)

内森先生,谢谢你

Gar*_*ees 13

如果不回答您的问题,我想评论您的实施策略并推荐一种新方法.你用极坐标形式表示球的速度,ball.angleball.speed.

我认为这对你来说通常很不方便.例如,在碰撞代码中,您最终调用atan2将矢量(dx,dy)转换为角度,然后调用sincos再次将角度转换回矢量.(另外,如果你试图将你的代码概括为三维,你会发现自己处于一个痛苦的世界.)所以,除非你有特殊的要求需要极坐标,我建议你做其他人做的事,即代表笛卡尔坐标系中球的速度为矢量(vx,vy).

我还建议将物理方法从静态方法("当前与对象B碰撞的对象A?")更改为动态方法("将对象A在下一个移动步骤中与对象B碰撞?").在静态物理系统中,在运动步骤结束时,您经常会遇到彼此相交的物体,然后您必须找出让它们再次分离的最佳方法,这很难做到.

如果你同时进行这两项操作,那么在没有任何三角测量的情况下直接弹球是很简单的.

步骤1.使用Minkowski添加将圆/圆碰撞转换为点/圆碰撞:

左边的原始问题显示在一个大圆圈内移动的小圆圈. 右边的转换问题表示点在圆周中移动,其半径是原始问题中圆的半径之差.

步骤2.考虑一个时间段,其中球在p =(px,py)处开始并且移动v =(vx,vy).它与圆相交吗?除此之外,您可以使用标准线段/圆测试,但测试的意义相反.

步骤3.找到碰撞点c =(cx,cy).球以与在该点处与圆相切的线t反弹的方式反弹离开圆圈.对于以原点为中心的圆,切线向量只是(-cy,cx),我相信你可以弄清楚如何为其他圆计算它.

上图描述

请参阅此答案,了解如何根据摩擦系数和恢复系数计算球的新路径.

步骤4.不要忘记球可能仍然有一些距离沿着新的向量w移动.如果时间步长足够大或速度足够高,则可能在同一时间段内再次发生碰撞.


Pet*_*dge 12

我很高兴你喜欢我的教程.我喜欢你的变化,它实际上应该更简单.

首先,我认为您需要将碰撞测试更改为:

if distance >= circle.size - ball.size:
Run Code Online (Sandbox Code Playgroud)

因为球的尺寸越大,其中心与圆心之间的距离就越小.这应该使球在正确的位置(圆圈内)弹跳.

然后我认为你只需要交换x和y的标志,一切都应该有效.

ball.x += math.sin(angle)
ball.y -= math.cos(angle)
Run Code Online (Sandbox Code Playgroud)

要将球移动正确的距离,您可以计算重叠:

overlap = math.hypot(dx, dy) - (circle.size - ball.size)

if overlap >= 0:
  tangent = math.atan2(dy, dx)
  ball.angle = 2 * tangent - ball.angle
  ball.speed *= elasticity

  angle = 0.5 * math.pi + tangent
  ball.x += math.sin(angle)*overlap
  ball.y -= math.cos(angle)*overlap
Run Code Online (Sandbox Code Playgroud)

祝好运