PyGame中使用Rect类的帧独立移动问题

1 投票
1 回答
1052 浏览
提问于 2025-04-29 19:15

我正在用PyGame写一个简单的游戏,但在让东西正常移动时遇到了一些问题。我对游戏编程不太熟悉,所以不确定我的方法是否正确。

我有一个叫Ball的类,它是PyGame的Sprite类的一个扩展。每一帧,Ball对象应该在屏幕上移动。我使用的是与帧无关的(?)/基于时间的移动,每次通过PyGame的Clock对象返回的时间增量来更新位置。比如,

class Ball(pygame.sprite.Sprite):
   # . . .
   def update(self, delta):
       self.rect.x += delta * self.speed  # speed is pixels per second
       self.rect.y += delta * self.speed  # ... or supposed to be, anyway

... 还有 ...

class Game(object):
    # . . .
    def play(self):
        self.clock = pygame.time.Clock()
        while not self.done:
            delta = self.clock.tick(self.fps) / 1000.0  # time is seconds
            self.ball.update(delta)

PyGame的Sprite类是由一个Rect对象(rect)支持的,这个对象用来跟踪精灵的位置。而Rect的坐标在每次更新时会自动转换为整数。所以,我的问题是每次调用update()时,任何额外的小数像素的移动都会丢失,而不是像应该的那样随着时间累积。这样“每秒像素数”的速度就不准确了。更糟糕的是,如果每帧的移动量少于一个像素,球在那些帧中根本不会移动,因为它总是被向下舍入为零。(也就是说,如果每秒像素数小于每帧像素数,就没有移动)

我不太确定该怎么处理这个问题。我尝试给Ball添加单独的x和y值,这些值不强制为整数,并在这些值变化时更新rect。这样x和y的值就可以正常累积小数像素。就像,

class Ball(pygame.sprite.Sprite):
    #  . . .
    def update(self, delta):
        self.x += delta * self.speed
        self.y += delta * self.speed
    def getx(self):
        return self._x
    def setx(self, val):
        self._x = val      # this can be a float
        self.rect.x = val  # this is converted to int
    def gety(self):
        return self._y
    def sety(self, val):
        self._y = val      # this can be a float
        self.rect.y = val  # this is converted to int
    x = property(getx,setx)
    y = property(gety,sety)

但这样会变得很麻烦:当x和y变化时,更新rect对象很简单,但反过来就不那么容易了。更不用说Rect对象还有很多其他有用的坐标可以操作(比如centerx、top、bottomleft等等——这就是为什么我们还想直接移动rect)。我最终可能不得不在Ball中重新实现整个Rect类,以便在将浮点数传递给rect对象之前存储它们,或者只能基于x和y来做,这样就牺牲了一些Rect类的便利性。

有没有更聪明的方法来处理这个问题呢?

暂无标签

1 个回答

0

我不知道你是否还需要这个答案,但我之前搞明白了这个问题,想给这个问题一个解答,因为我在研究这个问题时遇到了它。(顺便说一下,感谢你帮我确认了这个问题。)

你可以使用Python的 round 函数。简单来说,就是把你想要的东西放进这个函数里,它就会给你一个正确四舍五入的数字。

我第一次让它工作的方式是这样的:

x = box['rect'].x
if leftDown:
   x -= 300 * deltaTime
if rightDown:
   x += 300 * deltaTime

box['rect'].x = round(x)

(我的 deltaTime 是一个浮点数,也就是小数部分的秒数。)

只要你在使用这个值之前,把浮点数通过 round 函数处理一下,就可以了。你不需要使用单独的变量或者其他复杂的东西。

值得注意的是,你不能在像素上画出小数部分。绝对不行。如果你熟悉Lite-Brite这个玩具,你会发现像素就是这样工作的,每个像素就是一个亮起来的灯。所以最后你用的数字必须是整数。

撰写回答