碰撞检测怎么样?

时间:2014-02-06 05:06:59

标签: python python-3.x collision-detection

我必须在collide类中编写一个Rectangle方法,该方法将另一个Rectangle对象作为参数,如果它与执行该方法的矩形发生碰撞,则返回True如果没有,False。我的解决方案是使用for循环遍历一个矩形中的x和y的每个值,以查看它是否属于另一个矩形,但我怀疑可能有更高效或更优雅的方法来执行它。这是方法(我认为所有的名字都是非常自我解释的,只要询问是否有任何不清楚的地方):

def collide(self,target):
    result = False
    for x in range(self.x,self.x+self.width):
        if x in range(target.get_x(),target.get_x()+target.get_width()):
            result = True
    for y in range(self.y,self.y+self.height):
        if y in range(target.get_y(),target.get_y()+target.get_height()):
            result = True
    return result

提前致谢!

2 个答案:

答案 0 :(得分:4)

碰撞检测的问题是众所周知的,所以我想而不是推测我可能会使用着名的搜索引擎搜索工作算法。事实证明,关于矩形重叠的好文献比你想象的要容易得多。在我们继续讨论之前,或许我可以评论一下你对

这样的结构的使用
if x in range(target.get_x(),target.get_x()+target.get_width()):

根据Python的说法,你的想法的这种明显表达实际上是按预期成功的。您可能没有意识到的是(在Python 2中,无论如何)每次使用range()都会创建一个列表(在Python 3中它创建了一个生成器并对其进行迭代;如果您不知道这意味着什么,请接受它的计算方面好一点)。我怀疑你的意思是

if target.get_x() <= x < target.get_x()+target.get_width():

(我使用开放区间测试来反映你对range()的使用)这具有用两个链式比较替换N个等式比较的优点。通过相对简单的数学运算(从比较中的每个项中减去target.get_x()),我们将其转换为

if 0 <= x-target.get_x() < target.get_width():

不要忽视消除这种冗余方法调用的价值,尽管通过赋值保存评估表达式以供将来参考通常更简单。

当然,经过仔细审查后,我们必须重新焕发活力

for x in range(self.x,self.x+self.width):

这设置x的下限和上限,并且对于x的所有值,您编写的不等式必须为false。然而,超越代码进入算法的目的是值得做的。因为内部测试可能已经完成的任何点亮创建现在重复多次(通过对象的宽度,确切地说)。我冒昧释义

for x in range(self.x,self.x+self.width):
    if x in range(target.get_x(),target.get_x()+target.get_width()):
        result = True

进入伪代码:“如果self.x和self.x + self.width之间的任何x位于目标的x和目标的x +宽度之间,那么对象就会发生冲突”。换句话说,两个范围是否重叠。但你肯定要做很多工作才能找到答案。

另外,仅仅因为两个物体在x维度上碰撞并不意味着它们在空间中碰撞。事实上,如果它们也没有在y维度上碰撞,那么对象是不相交的,否则你会将这些矩形评估为碰撞:

+----+
|    |
|    |
+----+

  +----+
  |    |
  |    |
  +----+

所以你想知道它们是否在两个尺寸中碰撞,而不仅仅是一个。理想情况下,人们会定义一维碰撞检测(现在我们只是......),然后应用于两个维度。我也希望那些访问器函数可以被简单的属性访问所取代,而我的代码从现在开始就假设是这种情况。

走了这么远,现在可能是时候快速看一下this YouTube video中的原理了,这使得几何学相对清晰但完全没有表达公式。只要您使用相同的坐标系,它就可以很好地解释原理。如果A的左侧在B的左侧和右侧之间,则基本上两个物体A和B水平重叠。如果B的右边在A的左右之间,它们也会重叠。这两种情况都可能是正确的,但在Python中,您应该考虑使用关键字or来避免不必要的比较。

所以让我们定义一维重叠函数:

def oned_ol(aleft, aright, bleft, bright):
    return (aleft <= bright < aright) or (bleft <= aright < bright)

我要作弊并在两个维度上使用它,因为我的函数内部不知道我用它调用哪个维度的数据。如果我是正确的,以下表述应该做:

def rect_overlap(self, target):
    return oned_ol(self.x, self.x+self.width, target.x, target.x+target.width) \
        and oned_ol(self.y, self.y+self.height, target.y, target.y+target.height

如果您坚持使用这些访问器方法,则必须重新构建代码以包含它们。我已经对1-D重叠功能进行了粗略测试,并且在rect_overlap上完全没有,所以请告诉我 - 告诫lector 。出现了两件事。

  1. 对代码进行表面检查可以导致无效低效算法的“优化”,因此有时最好回到第一原则并更仔细地查看算法。

  2. 如果使用表达式作为函数的参数,则可以在函数体内按名称使用它们,而无需进行明确的赋值。

答案 1 :(得分:2)

def collide(self, target):

    # self left of target?  
    if x + self.width < target.x:
        return False

    # self right of target?
    if x > target.x + target.width :
        return False

    # self above target?
    if y + self.height < target.y:
        return False

    # self below target?
    if y > target.y + target.height:
        return False

    return True

类似的东西(取决于你的坐标系统,即y正向上或向下)