【问题标题】:python collision detection resolution circular dependenciespython碰撞检测解决循环依赖
【发布时间】:2014-01-27 15:55:11
【问题描述】:

我正在使用 pygame 在 python 中开发一个简单的游戏(我对它作为一种语言还很陌生),似乎 python 真的很讨厌循环依赖(尽管我知道有一些方法可以解决这个问题)。

一般来说,对于碰撞检测,一旦检测到碰撞,我会为碰撞中涉及的每个对象调用一个回调函数。问题在于,这样做时,涉及碰撞的每个对象都需要了解另一个对象,以便以正确的方式解决碰撞,从而导致我希望避免的循环依赖(见下文)。

这里是 Enemy.py 模块:

from Player include * #dependency on player

class Enemy():
    def handle_collision(other_object):
        if isinstance(other_object,Player) #this check requires the Enemy class to know about Player

这是 Player.py 模块:

from enemy include * #dependency on enemy

class Player():
    def handle_collision(other_object):
        if isinstance(other_object,Wall): 
            #do what we need to do to resolve a wall collision
        elif isinstance(other_object,Enemy): #this check requires that we include the Enemy class
            #do what we need to do to resolve an enemy collision    

任何关于通常如何处理的建议或见解都会很棒。

【问题讨论】:

  • 这里并没有真正看到循环性。你能解释更多吗?
  • 不确定您在问什么,但 (1) 在Player.handle_collision处理玩家的碰撞结果,而不是其他对象(处理他们handle_collision 方法); (2) 为“动作”或“更新”创建一个队列,并在检测到所有冲突后应用这些
  • 具体来说,依赖来自回调中的 if 检查。例如if isinstance(other_object, "Wall"):
  • 我想从技术上讲,我可以向处理程序传递一个字符串,指示它与哪个对象发生碰撞?
  • 当两个对象发生碰撞时,调用两者的处理程序,另一个作为参数。仍然看不到周期性。也许您应该包括依赖关系的双方以使其清楚。

标签: python collision-detection game-engine circular-dependency


【解决方案1】:

像这样的循环依赖,模块player.py 导入模块enemy.py,反之亦然,在Python 中确实相当棘手。但是有(至少)两种方法可以解决这些问题:

首先,您无需导入模块即可使用该模块中的类。即使不导入 enemy.py 模块,您也可以使用 Enemy 类的实例,例如在它被其他模块作为参数传递给handle_collision 方法之后。然后,您可以通过检查来解决您的问题。对于other_object.__class__.__name__ == 'Enemy'

这可行,但不是很好,并且会导致问题,例如,Enemy 的子类。

其次,您确实必须将每个类放在它自己的模块/文件中,因为它在 Java 等中很常见。在 Python 中,将 许多相关的类 放在同一个模块中是完全正常且良好的做法。 (在 Python 中不鼓励模块之间的循环依赖的原因之一是因为需要它们被认为是糟糕的系统设计的标志。)

所以我的建议是将所有“实体”类放入同一个模块中,例如entities.py

class Entity:
    def handle_collision(self, other_object):
        pass

class Player(Entity):
    def handle_collision(self, other_object):
        if isinstance(other_object, Enemy):
            # do stuff

class Enemy(Entity):
    def handle_collision(self, other_object):
        if isinstance(other_object, Player):
            # do other stuff

请注意,循环导入 实际上在 Python 中是可能的,但应谨慎使用。关于这方面的更多信息,您可以查看thesetwo相关帖子。

【讨论】:

  • 啊,我来自 C++/Java 背景,其中类通常被分离到它们自己的文件中。我希望将它们保存在单独的文件中以实现可重用性/划分。我认为类名检查可能是我在这种情况下最终要走的路线,因为我真的想保持这种分离,但我会记住你的另一点。感谢您的帮助。
  • 解决循环导入错误的另一种方法是使用基本的import module 语法(以及稍后的module.Class),而不是from module import Class。即使导入的模块将我们导入回来,前者也会起作用。
  • @Blckknght 似乎我的第一句话,关于那些“不可能”有点草率。正如 jdhideb 和您自己所指出的,这似乎很有可能。尽管如此,我认为我不会改变我的答案,因为它首先是关于循环进口的替代品。 ;-)
【解决方案2】:

您通常可以通过将导入从模块级别移动到您实际需要的函数或方法来消除循环依赖错误。

在 Player.py 中,尝试将 from enemy include *(应该是 IMPORT,而不是 INCLUDE)移动到您的 handle_collision 方法中:

def handle_collision(other_object):
    from enemy import Enemy
    if isinstance(other_object,Wall): 
        #do what we need to do to resolve a wall collision
        pass
    elif isinstance(other_object,Enemy): 
        #this check requires that we include the Enemy class
        pass
        #do what we need to do to resolve an enemy collision    

此外,您应该尽量避免导入 *,因为它会使您难以查看各种导入名称的来源。

【讨论】:

  • 我听说这通常是不好的做法。
  • @user3241047:我没听说过,但我可以看出您希望尽量减少对这种技术的使用,即通过重构和移动代码来消除循环依赖。
猜你喜欢
  • 2020-11-12
  • 2014-06-26
  • 2014-04-13
  • 1970-01-01
  • 2011-10-21
  • 1970-01-01
相关资源
最近更新 更多