Python&Pygame:球与ci内部的碰撞

2024-06-21 20:35:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我在做一个游戏,球在一个大得多的圆的内部反弹。大圆圈不动。

下面是我当前用于这些冲突的代码:

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)

它是基于彼得·柯林格里奇的精彩教程over here

圆和球对象都是类,具有(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

提前谢谢你,内森


Tags: theselfsizedefmathintspeedthickness
3条回答

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

我想这对你来说通常是不方便的。例如,在冲突代码中,您最终调用atan2将向量(dxdy)转换为角度,然后调用sincos将角度再次转换为向量。(同样,如果你试图将你的代码推广到三维空间,你会发现自己身处一个痛苦的世界。)所以,除非你有特殊的要求,需要极坐标,我建议你做其他人都做的事情,即用笛卡尔坐标表示球的速度作为矢量(vxvy)。

我还建议改变你的物理方法,从静态的方法(“物体a当前是否与物体B发生碰撞?”)到adynamicone(“对象a在下一个移动步骤中是否会与对象B发生碰撞?”)。在静态物理系统中,你经常在一个运动步骤结束时发现物体相互交叉,然后你必须找出使它们再次分离的最佳方法,这是很难做到的。

如果你同时做这两件事,你很容易在没有任何三角学的情况下弹起球。

第一步。使用Minkowski addition将圆/圆碰撞转换为点/圆碰撞:

Original problem at left shows small circle moving inside a large circle. Transformed problem at right shows point moving in circle whose radius is the difference between the radii of the circles in the original problem.

第二步。考虑一个时间段,在该时间段中,球从p=(px,py)开始并移动v=(vx,vy)。它和圆相交吗?您可以使用standard line segment/circle test来实现这一点,但测试的意义是相反的。

第三步。找到碰撞点c=(cx,cy)。球从圆上反弹的方式与它从与该点的圆相切的线上反弹的方式相同。对于一个以原点为中心的圆,切向量只是(-cy,cx),我相信你可以找出如何计算其他圆的切向量。

Figure described above

如何根据摩擦系数和恢复系数计算球的新路径。

第四步。别忘了,球可能还有一段距离要沿新向量移动。如果时间步长足够大或速度足够高,则可能在同一时间段内再次发生碰撞。

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

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

if distance >= circle.size - ball.size:

因为球的尺寸越大,它的中心和圆心之间的距离就越小。这将使球在正确的位置(圆内)反弹。

然后我想你只需要换x和y的符号,一切都应该正常。

ball.x += math.sin(angle)
ball.y -= math.cos(angle)

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

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

祝你好运

大多数图形包使用左上角作为绘图代码的开始。最有可能需要两组坐标,一组是与之碰撞/移动的坐标,另一组是用于绘制的坐标(x-radius,y-radius)。

另外,在没有考虑太多的情况下,交叉点的检查是否应该是distance + ball.size >= circle.size?如果我正确理解设置的话,球与中心的距离加上它的半径应该小于圆的半径。

相关问题 更多 >