首页 > 解决方案 > ball animation is really junky while running pong game

问题描述

the ball animation while running the program is very stuttery and i can't figure out why. this is just the beggining of the program so ignore the fact that the game isn't ready to play yet.

this is the code: https://www.codepile.net/pile/dqKZa8OG

i want the ball to move smoothly without stuttering. and in addition how do i make it so the program deletes the last location of the ball after each update?

标签: pythonpygamepong

解决方案


It's not "junky" because of the timer, you can only see updates since you're probably moving the mouse in the meantime, then you're updating the ball position everytime you move it (which is wrong, as you're updating the position anytime any events is processed).

The problem is that you're using Clock in the wrong way: the pygame.time.Clock class creates an «object that can be used to track an amount of time», meaning that it is not a timer that can "react" once it times out. The tick method you're calling only updates the current Clock returning how many milliseconds have passed since the last call to tick itself, based on the fps argument you are providing.

What you need is to set a timer, possibly by using a specific eventid just for the updates. Also, since you're updating the ball position based on the events, you'll get more movements if you move the mouse (or any other event is called) making the ball move faster even if it shouldn't - and that's what you'll need the Clock object for.

# I'm using a smaller speed, otherwise it'll be too fast; it should 
# depend on the window size to ensure that bigger windows don't get 
# slower speeds
ball_velocity = .2
fps = 60
UPDATEEVENT = 24

[...]

def start_move_ball(angle):
    ticks = clock.tick()
    speed = ball_velocity * ticks
    # "global ball" is not required, as you are assigning values to an 
    # existing object, not declaring it everytime.
    # You can also use in-place operations to set the speeds, which is 
    # better for readability too.
    ball[0] += angle[0] * speed
    ball[1] += angle[1] * speed

def main_loop():
    angle = choose_angle()
    pygame.time.set_timer(UPDATEEVENT, 1000 // fps)
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return False
            # update only if necessary!
            elif event.type == UPDATEEVENT:
                window.fill((0,0,0))
                start_move_ball(angle)
                draw_mid(window)
                draw_players(window)
                draw_ball(window)
                pygame.display.flip()

The timer is set outside the while cycle, as it automatically sends the event each time interval. You could also leave it within the while (the once=True argument is not really required in this case, as it automatically updates the timer based on the same eventid), but wouldn't make much sense, since set_timer always fires the event after the first time it's set.
I'm using 24 (pygame.USEREVENT) as an eventid, but you can set its id up to pygame.NUMEVENTS - 1, as suggested in the event documentation. If for any reason you want to stop the timer, just use pygame.time.set_timer(eventid, 0) and the timer for that eventid (24 in this case) will not be fired up again.

A couple of suggestions, besides all:

  1. Remove the pygame.display.update() line in draw_mid(window), since it causes flickering while painting.
  2. Avoid using external services to link code (it's not your case, but if the code is too long, first try to reduce it to minimal, reproducible example, eventually leaving all the relevant parts), as they could become unavailable sometime in the future, making your question hard to understand to somebody that is facing a similar issue and reads your answer some time after you posted it.

推荐阅读