PyGame 圆形碰撞问题 - Circle Interior

发布于 2024-11-05 03:20:53 字数 3416 浏览 1 评论 0原文

我正在制作一款益智游戏,要求用户在背景上“画”圆圈以使球到达出口。他们通过按住鼠标按钮来创建圆圈,圆圈会变大;当它足够大时,他们会放手,它会被“击打”到物理空间中,然后球会对其做出反应。

然而,我有一个问题,当两个圆相交时(因此球应该穿过),如果交点不大于球的直径,则球会与圆的内部碰撞照常。

这可能有点难以理解,所以这里有一个显示问题的截屏视频的链接(您无法在 Stack Overflow 上嵌入视频):http://www.youtube.com/watch?v=3dKyPzqTDhs

希望这能让我的问题清楚。以下是 BallCircle 类的 Python / PyGame 代码:

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

    def display(self, surface):
        """Draw the ball"""
        # pygame.gfxdraw.aacircle(screen,cx,cy,new_dist,settings['MINIMAP_RINGS'])
        if self.exited != True:
            pygame.draw.circle(surface, 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) = module_physicsEngine.addVectors((self.angle, self.speed), gravity)
        self.speed *= drag

以及 Circle 类:

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

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

在游戏的主循环中 (> while running == True: 等),此代码用于对每个球执行操作:

for b in balls:
    b.move()
    for i, ball in enumerate(balls):
        for ball2 in balls[i+1:]:
            collideBalls(ball, ball2)
        collideCircle(b) #      <---------------- This is the important line
        collideExit(b)
        b.display(screen)

最后,collideCircle(b) 函数,每个球调用一次检查与圆内部的碰撞,并检查圆是否相交。

def collideCircle(ball):
    """Check for collision between a ball and a circle"""

    hit = False
    closestDist = 0

    for c in circles:
        # Code cannot be replaced with physicsEngine.collideTest because it
        # is slightly differnt, testing if ball [ball] inside a circle [c]
        dx = c.x - ball.x
        dy = c.y - ball.y
        distance = math.hypot(dx, dy)

        if distance <= c.size - ball.size:
            # If BALL inside any CIRCLE
            hit = False
            break
        else:
            # If we're outside of a circle.
            if closestDist < c.size - (distance - ball.size):
                hit = c
                closestDist = (c.size - (distance - ball.size))

    if hit:

        module_physicsEngine.circleBounce(hit, ball)

好的,我知道这是一个有点长且啰嗦的问题,但我认为您已经掌握了所需的所有信息。使球正确交互的解决方案是否与 if distance <= c.size - ball.size: 行有关?

无论如何,提前致谢!

内森出去了。

TL;DR - 观看 YouTube 视频,并告诉我为什么它不起作用。

I'm making a puzzle game that requires the user to 'draw' circles onto a background to get a ball to the exit. They create circles by holding their mouse button, the circle grows; when it is big enough, they let go and it is 'punched' into the physical space and balls then react to it.

I have a problem, however, that when two circles are intersecting (so a ball should pass through), if the intersection is not larger than the diameter of the ball the ball collides with the interior of the circle as usual.

This may be a little hard to comprehend, so here's a link to the screencast showing the problem (You can't embed videos on Stack Overflow): http://www.youtube.com/watch?v=3dKyPzqTDhs

Hopefully that made my problem clear. Here is the Python / PyGame code for the Ball and Circle classes:

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

    def display(self, surface):
        """Draw the ball"""
        # pygame.gfxdraw.aacircle(screen,cx,cy,new_dist,settings['MINIMAP_RINGS'])
        if self.exited != True:
            pygame.draw.circle(surface, 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) = module_physicsEngine.addVectors((self.angle, self.speed), gravity)
        self.speed *= drag

And the Circle class:

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

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

Within the main loop of the game (while running == True: etc.), this code is used to perform actions on each ball:

for b in balls:
    b.move()
    for i, ball in enumerate(balls):
        for ball2 in balls[i+1:]:
            collideBalls(ball, ball2)
        collideCircle(b) #      <---------------- This is the important line
        collideExit(b)
        b.display(screen)

And finally, the collideCircle(b) function, which is called once per ball to check for collisions with the interior of a circle, and also to check if the circles are intersecting.

def collideCircle(ball):
    """Check for collision between a ball and a circle"""

    hit = False
    closestDist = 0

    for c in circles:
        # Code cannot be replaced with physicsEngine.collideTest because it
        # is slightly differnt, testing if ball [ball] inside a circle [c]
        dx = c.x - ball.x
        dy = c.y - ball.y
        distance = math.hypot(dx, dy)

        if distance <= c.size - ball.size:
            # If BALL inside any CIRCLE
            hit = False
            break
        else:
            # If we're outside of a circle.
            if closestDist < c.size - (distance - ball.size):
                hit = c
                closestDist = (c.size - (distance - ball.size))

    if hit:

        module_physicsEngine.circleBounce(hit, ball)

Ok, so I know that this has been a bit of a long and talky question, but I think you have all the information needed. Is the solution to make the balls interact correctly something to do with the line if distance <= c.size - ball.size:?

Anyway, thanks in advance!

Nathan out.

TL;DR - Watch the youtube video, and let me know why it's not working.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半仙 2024-11-12 03:20:53

问题在于意外的命中而不是错过的命中。您真正想要检查的是球的所有部分是否都被某个圆圈覆盖,而您正在执行的检查是任何圆圈是否仅部分重叠 - 但如果任何圆圈完全覆盖球,则覆盖。

我计算出任何潜在的击中点,即圆的最近的内壁,通过检查它与所有其他圆的距离,让该点沿着壁“行走”。如果球离开球,则为假击。

首先,您找到完全接触球的圆圈列表。和以前一样,如果其中任何一个覆盖了它,您可以跳过其余的检查。还要找到距离球最近的墙点。对于每个最近的墙点,如果它与另一个圆重叠,请将其移动到距离球最近但比当前点更远的交点。如果它在球外,则丢弃它。对所有圆圈重复此过程,因为两个以上的圆圈可能会重叠。另请注意,点的移动可能会导致其进入新的圆圈。

您可以预先计算交点并丢弃任何其他圆内的球半径。

这肯定可以改进,但我认为这只是一个开始。我怀疑存在一个错误,涉及以下情况:一对圆的两个交点与球重叠,但步行链将其中一个圆引导到球外。也许初始碰撞点应该仅由两个交点替换,而不是最近的交点。

The problem is with unintended hits rather than missed ones. What you really want to check is if all parts of the ball are covered by some circle, while the check you're doing is if any circle only partially overlaps - but an override if any circle fully covers the ball.

I figure for any potential hit point, i.e. closest inner wall of a circle, let that point "walk" along the wall by checking its distance from all other circles. Should it then leave the ball, it was a false hit.

First you find the list of circles that touch the ball at all. As before, if any of them cover it, you can skip the rest of the checks. Also find the closest wall point to the ball for the circles. For each of those closest wall points, if it overlaps another circle, move it to the intersection point which is closest to the ball but further away than the current point. Discard it if it's outside the ball. Repeat the procedure for all circles, since more than two may overlap. Also note that the moving of the point may cause it to enter new circles.

You could precompute the intersection points and discard any that are a ball radius inside of any other circle.

This can surely be improved on, but it's a start, I think. I suspect a bug involving the case when both intersection points of a pair of circles overlap the ball, but a walk chain leads one of them outside the ball. Perhaps the initial collision points should be replaced only by both intersection points, not the closest.

情归归情 2024-11-12 03:20:53

我看了视频,很喜欢游戏原理。 :)

也许问题在于,一旦遇到包围球的圆圈,您就打破出循环。我指的是片段,

if distance <= c.size - ball.size:
    # If BALL inside any CIRCLE
    hit = False
    break

在这种情况下,为什么你不检查所有其他圆圈呢?可能还有另一个尚未检查的圆圈导致命中

顺便说一句,我不会说 if condition == True:,那是不符合 Python 风格的。只需说if条件:

I watched the video and I like the game principle. :)

Maybe the problem is that you break out of the loop as soon as you encounter a circle that encloses the ball. I'm referring to the snippet

if distance <= c.size - ball.size:
    # If BALL inside any CIRCLE
    hit = False
    break

Why would you not check all of the other circles, in that case? There might be another circle yet unchecked that causes a hit.

Btw, I wouldn't say if condition == True:, that's unpythonic. Just say if condition:.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文