PyGame 圆形碰撞问题 - 圆内部分
我正在制作一个拼图游戏,玩家需要在背景上“画”圆圈,以便把一个球引导到出口。玩家通过按住鼠标按钮来创建圆圈,圆圈会逐渐变大;当圆圈足够大时,玩家松开鼠标,圆圈就会被“打入”物理空间,球会对这个圆圈产生反应。
不过,我遇到了一个问题,当两个圆圈相交时(也就是说,球应该可以通过),如果它们的交集小于球的直径,球就会像往常一样与圆圈的内部发生碰撞。
这可能有点难以理解,所以这里有一个链接,展示了这个问题的录像(在Stack Overflow上不能嵌入视频):http://www.youtube.com/watch?v=3dKyPzqTDhs
希望这样能让你明白我的问题。以下是Ball
和Circle
类的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:
有关呢?
无论如何,提前谢谢你们!
Nathan 退场。
总结 - 看一下YouTube视频,告诉我为什么它不工作。
2 个回答
我看了那个视频,觉得这个游戏的原理挺有意思的。 :)
可能问题出在你一遇到包围小球的圆圈就立刻break
跳出循环。我指的是这段代码
if distance <= c.size - ball.size:
# If BALL inside any CIRCLE
hit = False
break
那这样的话,为什么不检查其他的圆圈呢?可能还有其他没检查的圆圈会导致hit
发生。
顺便说一下,我不建议用if condition == True:
,这样写不太符合Python的风格。直接写if condition:
就可以了。
这个问题主要是关于意外的碰撞,而不是漏掉的碰撞。你真正想检查的是,球的所有部分是否都被某个圆圈覆盖,而你现在检查的是,任何一个圆圈是否只部分重叠——不过如果有哪个圆圈完全覆盖了球,那就算它。
我想,对于任何可能的碰撞点,也就是圆圈内壁最近的点,可以让这个点沿着墙“走”,通过检查它与其他圆圈的距离来判断。如果它离开了球,那就说明是个假碰撞。
首先,你需要找出所有与球接触的圆圈。如果其中任何一个圆圈完全覆盖了球,那就可以跳过后面的检查。同时,还要找出这些圆圈离球最近的墙面点。对于每个最近的墙面点,如果它与另一个圆圈重叠,就把它移动到离球最近但比当前点更远的交点。如果这个点在球的外面,就把它丢掉。对所有圆圈重复这个过程,因为可能会有多个圆圈重叠。同时要注意,移动这个点可能会让它进入新的圆圈。
你可以提前计算交点,并丢掉任何在其他圆圈内部、距离球半径以内的交点。
这肯定可以进一步改进,但我觉得这是一个开始。我怀疑在一对圆圈的两个交点都重叠在球内的情况下,可能会出现一个bug,但通过“走”的链条让其中一个点在球外。也许最初的碰撞点应该只用两个交点替代,而不是最近的那个。