代码之家  ›  专栏  ›  技术社区  ›  user890167

python和pygame-在这个包装器的执行过程中发生了什么?

  •  7
  • user890167  · 技术社区  · 10 年前

    我希望赏金能吸引一个懂一件事的人 关于PyGame的内部工作原理(我尝试查看 源代码。。有很多),可以告诉我这是不是真的 与threading.py的存在有关,或者如果有一些实践 我可以避免 一般来说 以防止这种情况发生在其他 PyGame项目。

    我为当 obj.kill() 被调用。

    def make_explosion(obj, func):
        def inner(*args, **kwargs):
            newX, newY = obj.x, obj.y
            newBoom = Explosion(newX, newY)
            allqueue.add(newBoom) #an instance of pygame.sprite.Group
            return func(*args, **kwargs)
        return inner
    

    它获取对象的x和y坐标,然后创建 Explosion 在这些坐标处,将其添加到 allqueue 我用它来确保一切 update d,并最终返回其包装的函数-在本例中, obj.kill . obj.kill() 是一个 pygame.sprite.Sprite 方法,该方法将对象从 sprite.Group 它所属的。

    然后,在创建 Enemy .

    newEnemy = Enemy()
    ##other code to add new AI, point values…eventually, the following happens:
    newEnemy.kill = make_explosion(newEnemy, newEnemy.kill)
    

    当我运行游戏时,爆炸出现在随机的地方,而不是物体实际x和y坐标附近的任何地方。有趣的是,它甚至不像是发生在物体的原点(x,y),甚至在它们在屏幕上短暂存在期间所走过的路径上或附近(我很擅长自己的游戏,不是吹牛),所以我觉得我必须排除在包装方法时分配x和y的可能性。

    有点漫无目的,我四处乱逛,把代码改成了这样:

    def make_explosion(obj, func):
        def inner(*args, **kwargs):
            allqueue.add(Explosion(obj.x, obj.y))
            return func(*args, **kwargs)
        return inner
    

    此更改使其“按预期”工作——当对象 self.kill() 方法时,爆炸将出现在正确的坐标上。

    我不明白的是 为什么? 这有效!特别是考虑到PyGame的文档,其中指出 kill() 不一定要删除对象;它只是从它所属的所有组中删除它。

    从…起 https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite.kill --

    基尔()

    从所有组中移除精灵kill()->没有一个

    精灵将从包含它的所有组中移除。这不会改变 关于雪碧的状态。可以继续 调用此方法后使用Sprite,包括添加它 到组。

    所以,尽管我无意中找到了问题的解决方案,但我实际上根本不明白。为什么它会这样?

    编辑:根据早期的一些评论,我试图在不使用PyGame库的情况下重现类似的情况,但我无法做到这一点。

    Par示例:

    >>> class A(object):
    ...     def __init__(self, x, y):
    ...             self.x = x
    ...             self.y = y
    ...     def go(self):
    ...             self.x += 1
    ...             self.y += 2
    ...             print "-go-"
    ...     def stop(self):
    ...             print "-stop-"
    ...             print "(%d, %d)" % (self.x, self.y)
    ...     def update(self):
    ...             self.go()
    ...             if self.x + self.y > 200:
    ...                     self.stop()
    >>> Stick = A(15, 15)
    >>> Stick.go()
    -go-
    >>> Stick.update()
    -go-
    >>> Stick.x
    17
    >>> Stick.y
    19
    >>> def whereat(x, y):
    ...     print "I find myself at (%d, %d)." % (x, y)
    ...
    >>> def wrap(obj, func):
    ...     def inner(*args, **kwargs):
    ...             newX, newY = obj.x, obj.y
    ...             whereat(newX, newY)
    ...             return func(*args, **kwargs)
    ...     return inner
    ... 
    >>> Stick.update = wrap(Stick, Stick.update)
    >>> Stick.update()
    I find myself at (17, 19).
    -go-
    >>> Stick.update()
    I find myself at (18, 21).
    -go-
    >>> Stick.update()
    I find myself at (19, 23).
    -go-
    

    所以,我马上注意到 whereat() 正在呼叫 之前 坐标的变化 Stick.go() 所以它使用 x y 就在递增之前,但这很好;我可以很容易地更改包装,直到 go() 被调用。这个问题不像我的PyGame项目中那样出现在这里;爆炸甚至不是“相邻的敌人”,它们会出现在各种各样的随机位置,而不是精灵先前的(x,y)坐标(如果有,我可能根本没有注意到问题!)。

    更新: 在一条用户评论让我感到疑惑后,我在网上搜索了一下 cProfile 在所讨论的项目上,瞧,PyGame肯定利用了 threading.py 不幸的是,同一个互联网不足以准确地告诉我 怎样 PyGame使用 螺纹.py ,所以显然我需要了解线程是如何工作的。Oi.:)

    在读了一点PyGame源代码之后(bleh),我发现了一个模块,它似乎可以仲裁PyGame何时以及为什么产生线程。从我对线程所知甚少的情况来看,这可能会导致类似种族状况的情况;没有为包装器执行“及时”定义x,因此它( 爆炸 对象)在错误的位置结束。我看到在一个不同的项目中出现了其他错误,这对数学来说有点重;这些更像是核心转储,但它们通常涉及 pulse 无法获取事件,或等待事件超时,或类似情况(我确实有一个v.long堆栈跟踪,以防任何人睡眠困难)。

    最后,我怀疑这是我可以通过坚持某些实践来避免的,但我不知道这些实践实际上会是什么 ,我不知道如何阻止PyGame仲裁哪些进程获得了自己的线程,而这比不这样做更糟糕。如果有一个统一的理论来解释这一切,我肯定很想知道。

    2 回复  |  直到 10 年前
        1
  •  0
  •   KodyVanRy    10 年前

    我假设你使用 pygame.sprite.Group() 对精灵进行分组,然后将其渲染到屏幕上。如果我错了,请纠正我。然后创建一个碰撞,使用精灵的属性创建碰撞,然后“销毁”精灵,这意味着使用 pygame.sprite.Group 然后包含该精灵的将不再包含该精灵。现在考虑到您在问题中添加了链接,我将假设您已经阅读了文档。

    下面是您发布的代码,并对每个代码的功能进行了评论。

    #so here is your wrapper which will call make explosion before it kills the sprite.
    def make_explosion(obj, func):
        #you call kill and so here it goes.
        def inner(*args, **kwargs):
            #you add the explosion to your queue
            allqueue.add(Explosion(obj.x, obj.y)) #create an instance of explosion inside.
            # return to the outside function `make_explosion`
            return func(*args, **kwargs)
        #you call inner second and then go to the inner.
        return inner
        #after it returns inner kill is called
    

    因此,这意味着它调用make_explosion,然后对精灵调用kill,将其从任何组中移除。请告诉我你是否需要对此进行澄清,或者你是否需要一个更好或更深入的答案。或者告诉我你是否还有其他需要的东西。

        2
  •  0
  •   user890167 user890167    10 年前

    不幸的是,我不得不说,对于这个特定的问题,错误是我的。

    Explosion 类,其代码未包含在此处,我没有正确更新其 self.x self.y 在其创建或 self.update() 方法更改代码,与代码I的缩短一致 已经显示在这里,导致该问题和其他问题消失。

    我知道接受你自己的答案并简单地结束这个问题有点愚蠢,特别是在问题实际上出现在代码的不同部分之后, 尤其地 在悬赏之后。我很抱歉。