简体   繁体   English

PyGame中带有Rect类的与帧无关的运动问题

[英]Frame-independent movement issue in PyGame with Rect class

I'm writing a simple game with PyGame, and I've run into a problem getting things moving properly. 我正在用PyGame编写一个简单的游戏,但是在使事情正常运行时遇到了问题。 I'm not experienced with game programming, so I'm not sure if my approach is even correct.. 我没有游戏编程方面的经验,所以不确定我的方法是否正确。

I have a class Ball, which extends PyGame's Sprite class. 我有一个Ball类,它扩展了PyGame的Sprite类。 Each frame, the Ball object is supposed to move around the screen. 每帧,Ball对象都应该在屏幕上移动。 I'm using frame-independent(?)/time-based movement with the time delta returned by PyGame's Clock object each time around. 我正在使用与帧无关的(?)/基于时间的运动,每次都由PyGame的Clock对象返回的时间增量。 Such as, 如,

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

... and ... ...和...

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's Sprite class is backed by a Rect object (rect) that tracks the sprite's position. PyGame的Sprite类由跟踪该Sprite位置的Rect对象(rect)支持。 And Rect's coordinates are automatically converted to integers whenever a value is updated. 每当更新值时,Rect的坐标都会自动转换为整数。 So, my problem is that each time update() is called, any extra fraction-of-a-pixel movement is lost, instead of being accumulated over time as it should. 所以,我的问题是,每次调用update()时,像素多余的运动都将丢失,而不是随时间累积。 Speed in "pixels per second" isn't accurate this way. 这样,“每秒像素数”的速度是不准确的。 Even worse, if the amount of movement per frame is less than one pixel, the ball doesn't move at all for those frames, because it's always rounded down to zero. 更糟糕的是,如果每帧的移动量少于一个像素,则这些帧的球根本不会移动,因为它总是四舍五入为零。 (ie, no movement if pps < fps) (即,如果pps <fps,则不移动)

I'm not sure how to deal with this. 我不确定如何处理。 I tried adding separate x and y values to Ball which aren't forced to be integers and updating the rect every time those change. 我尝试向Ball添加单独的x和y值,这些值不被强制为整数,并且每次更改时都更新rect。 That way the x and y values accumulate the fractions of a pixel as normal. 这样,x和y值会像常一样累积像素的分数。 As in,

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)

But that ends up being messy: it's easy to update the rect object when x and y change, but not the other way around. 但这最终变得混乱:当x和y更改时,很容易更新rect对象,但反之则不然。 Not to mention the Rect object has lots of other useful coordinates that can be manipulated (like centerx, top, bottomleft, etc. -- which is why one would still want to move the rect directly). 更不用说Rect对象还有很多其他可以操纵的有用坐标(例如centerx,top,bottomleft等),这就是为什么人们仍然想直接移动rect的原因。 I'd more or less end up having to re-implement the whole Rect class in Ball, to store floats before it passes them down to the rect object, or else do everything based on just x and y, sacrificing some of the convenience of the Rect class either way. 我或多或少最终不得不在Ball中重新实现整个Rect类,以在将浮点传递给rect对象之前存储浮点,或者仅基于x和y进行所有操作,从而牺牲了一些便利。 Rect类的任何一种。

Is there a smarter way to handle this? 有没有更聪明的方式来解决这个问题?

I don't know if you still need the answer, but I figured this out a bit ago and thought I'd give this question an answer since I came across it in trying to figure out what was going on. 我不知道您是否仍然需要答案,但是我早就想出了这个问题,并认为我会给这个问题一个答案,因为我在试图找出正在发生的事情时遇到了这个问题。 (Thanks for helping me confirm the issue, by the way.) (顺便说一下,感谢您帮助我确认问题。)

You can make use of Python's round function. 您可以利用Python的round函数。 Pretty much, just throw what you want into that function, and it'll spit out a properly rounded number. 差不多,只要将想要的内容扔进该函数中,它就会吐出一个适当的四舍五入数字。

The first way I got it working was like this: 我让它工作的第一种方式是这样的:

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

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

(Where my deltaTime is a float, being a fraction of a second.) (在我的deltaTime是浮点数的情况下,不到一秒。)

As long as you're putting the float value you get through the round function before applying it, that should do the trick. 只要您将浮点值放在应用前就通过round函数,就可以解决问题。 It doesn't have to use separate variables or anything. 它不必使用单独的变量或其他任何东西。

It may be worth noting that you cannot draw in fractions of a pixel. 可能值得注意的是,您不能绘制像素的分数。 Ever. 永远。 If you're familiar with a Lite-Brite, pixels work in that way, being a single lit up light. 如果您熟悉Lite-Brite,像素就是单个照亮的光源,它可以这种方式工作。 That's why the number you use in the end has to be an integer. 这就是为什么最后使用的数字必须是整数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM