l. zhang
l. zhang

Reputation: 345

How can you make pygame animations run more smoothly?

I am making a spaceship game using pygame. I have a health bar at the top made out of three heart images. When you crash, the first from the right disappears. This means that the health bar needs to be generated in the main loop, but it keeps on flickering when running.

import pygame, random
class SpaceshipClass(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("spaceship.png")
        self.rect = self.image.get_rect()
        self.rect.center = [320, 420]

class ObstacleClass(pygame.sprite.Sprite):
    def __init__(self, image_file, location, type):
        pygame.sprite.Sprite.__init__(self)
        self.image_file = image_file
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.rect.center = location
        self.type = type
        self.passed = False

    def update(self):
        global speed, travelled
        self.rect.centery += speed[1]
        travelled+=0.1
        if self.rect.centery>700:
            self.kill()

def create_map():
    global obstacles
    locations = []
    for i in range(10):
        row = random.randint(0, 9)
        col = random.randint(0, 9)
        location = [col * 64 + 32, row * 64 + 32 + -640]
        if not (location in locations):
            locations.append(location)
            type = random.choice(["rock", "point"])
            if type == "rock": 
                img = "obstacle.png"
            elif type == "point": 
                img = "coin.png"
            obstacle = ObstacleClass(img, location, type)
            obstacles.add(obstacle)
def animate():
    screen.fill([0, 0, 0])
    obstacles.draw(screen)
    screen.blit(ship.image, ship.rect)
    screen.blit(score_surf, score_pos)
    dis_surf=dis_font.render((string_dis + str(("%.2f")%travelled)+metres), 
    1, (255, 255, 255))
    screen.blit(dis_surf, dis_pos)
    pygame.display.flip()
pygame.init()
screen = pygame.display.set_mode([640,640])
clock = pygame.time.Clock()
points = 0
lives=3
speed = [0, 1]
ship= SpaceshipClass()
obstacles = pygame.sprite.Group()
create_map()
map_position = 0
travelled=0
string_dis="distance traveled: "
metres="m"
dis_font=pygame.font.Font(None, 50)
dis_surf=dis_font.render((string_dis + str(travelled)+metres), 1, (255, 
255, 255))
dis_pos=[10, 40]
score=0
string_score="Score: "
score_font=pygame.font.Font(None, 50)
score_surf=score_font.render((string_score + str(score)), 1, (255, 255, 
255))
score_pos=[10, 10]
done=False
running=True
while running:
    clock.tick(180)
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            running=False
        elif event.type==pygame.MOUSEMOTION:
            ship.rect.centerx=event.pos[0]
    if not done:
        map_position += speed[1]
        if map_position >= 640:
            create_map()
            map_position = 0
        obstacles.update()
        animate()
    for i in range(lives+1):
        width=screen.get_width()
        health=pygame.image.load("health.png")
        screen.blit(health, [width-30*i, 20])
        pygame.display.flip()    
    col=pygame.sprite.spritecollide(ship, obstacles, False)
    if col:
        if col[0].type=="rock" and not col[0].passed:
            lives-=1
            if lives==0:
                final_text1="Game Over!"
                final_text2=("Your Score is: "+ str(score))
                final_text3=("you traveled %.2f meters" %travelled)
                ft1_font=pygame.font.Font(None, 70)
                ft1_surf=ft1_font.render(final_text1, 1, (255, 255, 255))
                ft2_font=pygame.font.Font(None, 50)
                ft2_surf=ft2_font.render(final_text2, 1, (255, 255, 255))
                ft3_font=pygame.font.Font(None, 50)
                ft3_surf=ft3_font.render(final_text3, 1, (255, 255, 255))                
                screen.blit(ft1_surf, [screen.get_width()/2- 
                ft1_surf.get_width()/2, 100])
                screen.blit(ft2_surf, [screen.get_width()/2- 
                ft2_surf.get_width()/2, 200])
                screen.blit(ft3_surf, [screen.get_width()/2- 
                ft3_surf.get_width()/2, 300])
                pygame.draw.rect(screen, [0,0,0], [width-30, 20, 90, 90], 
                0)
                pygame.display.flip()
                done=True

            else:
                pygame.time.delay(1000)
                ship.image = pygame.image.load("spaceship.png")
                speed = [0, 1]
                col[0].passed = True
        elif col[0].type=="point" and not col[0].passed:
            score+=100
            score_surf=score_font.render((string_score + str(score)), 1, 
            (255, 255, 255))
            screen.blit(score_surf, score_pos)
            col[0].kill()
            pygame.display.flip()
pygame.quit()

How, not just in this code but in any code, can you make an animation run smoothly?

Upvotes: 0

Views: 781

Answers (1)

Luke B
Luke B

Reputation: 2121

Your problem is that you are calling pygame.display.flip() multiple times per frame. This should only be used once per loop in your main while running: loop.

Instead of calling it whenever you do a drawing, just use it once at the end of your main loop. It is singular, similar to clock.tick(180), just at the end.

When the screen is flipped more than once per frame, it results in flickering, just as you are seeing here.

You should remove pygame.display.flip() from everywhere except the end of your loop.

The code with this change made:

import pygame, random
class SpaceshipClass(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("spaceship.png")
        self.rect = self.image.get_rect()
        self.rect.center = [320, 420]

class ObstacleClass(pygame.sprite.Sprite):
    def __init__(self, image_file, location, type):
        pygame.sprite.Sprite.__init__(self)
        self.image_file = image_file
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.rect.center = location
        self.type = type
        self.passed = False

    def update(self):
        global speed, travelled
        self.rect.centery += speed[1]
        travelled+=0.1
        if self.rect.centery>700:
            self.kill()

def create_map():
    global obstacles
    locations = []
    for i in range(10):
        row = random.randint(0, 9)
        col = random.randint(0, 9)
        location = [col * 64 + 32, row * 64 + 32 + -640]
        if not (location in locations):
            locations.append(location)
            type = random.choice(["rock", "point"])
            if type == "rock": 
                img = "obstacle.png"
            elif type == "point": 
                img = "coin.png"
            obstacle = ObstacleClass(img, location, type)
            obstacles.add(obstacle)
def animate():
    screen.fill([0, 0, 0])
    obstacles.draw(screen)
    screen.blit(ship.image, ship.rect)
    screen.blit(score_surf, score_pos)
    dis_surf=dis_font.render((string_dis + str(("%.2f")%travelled)+metres), 
    1, (255, 255, 255))
    screen.blit(dis_surf, dis_pos)
pygame.init()
screen = pygame.display.set_mode([640,640])
clock = pygame.time.Clock()
points = 0
lives=3
speed = [0, 1]
ship= SpaceshipClass()
obstacles = pygame.sprite.Group()
create_map()
map_position = 0
travelled=0
string_dis="distance traveled: "
metres="m"
dis_font=pygame.font.Font(None, 50)
dis_surf=dis_font.render((string_dis + str(travelled)+metres), 1, (255, 
255, 255))
dis_pos=[10, 40]
score=0
string_score="Score: "
score_font=pygame.font.Font(None, 50)
score_surf=score_font.render((string_score + str(score)), 1, (255, 255, 
255))
score_pos=[10, 10]
done=False
running=True
while running:
    clock.tick(180)
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            running=False
        elif event.type==pygame.MOUSEMOTION:
            ship.rect.centerx=event.pos[0]
    if not done:
        map_position += speed[1]
        if map_position >= 640:
            create_map()
            map_position = 0
        obstacles.update()
        animate()
    for i in range(lives+1):
        width=screen.get_width()
        health=pygame.image.load("health.png")
        screen.blit(health, [width-30*i, 20])
    col=pygame.sprite.spritecollide(ship, obstacles, False)
    if col:
        if col[0].type=="rock" and not col[0].passed:
            lives-=1
            if lives==0:
                final_text1="Game Over!"
                final_text2=("Your Score is: "+ str(score))
                final_text3=("you traveled %.2f meters" %travelled)
                ft1_font=pygame.font.Font(None, 70)
                ft1_surf=ft1_font.render(final_text1, 1, (255, 255, 255))
                ft2_font=pygame.font.Font(None, 50)
                ft2_surf=ft2_font.render(final_text2, 1, (255, 255, 255))
                ft3_font=pygame.font.Font(None, 50)
                ft3_surf=ft3_font.render(final_text3, 1, (255, 255, 255))                
                screen.blit(ft1_surf, [screen.get_width()/2- 
                ft1_surf.get_width()/2, 100])
                screen.blit(ft2_surf, [screen.get_width()/2- 
                ft2_surf.get_width()/2, 200])
                screen.blit(ft3_surf, [screen.get_width()/2- 
                ft3_surf.get_width()/2, 300])
                pygame.draw.rect(screen, [0,0,0], [width-30, 20, 90, 90], 
                0)
                done=True

            else:
                pygame.time.delay(1000)
                ship.image = pygame.image.load("spaceship.png")
                speed = [0, 1]
                col[0].passed = True
        elif col[0].type=="point" and not col[0].passed:
            score+=100
            score_surf=score_font.render((string_score + str(score)), 1, 
            (255, 255, 255))
            screen.blit(score_surf, score_pos)
            col[0].kill()
    pygame.display.flip()
pygame.quit()

Upvotes: 2

Related Questions