2

I'm trying to "animate" a sprite by simply loading a set of 4 images over and over while the assigned left/right buttons are held down. It mostly works, except the images keep flickering and aren't "switching" as I intended. It also slows down considerably. Being a beginner in Pygame/Python my brain is absolutely fried.

import pygame
import os

from test_menu import drawmenu
from game_over import player1wins
from game_over import player2wins
from game_over import tie
pygame.init()

while True: 
    screen_size = (1080, 400)
    screen = pygame.display.set_mode(screen_size)

    background = (255,255,255)
    image = pygame.image.load("background(1).png")
    imagerect = image.get_rect()
    wall_left = pygame.Rect(0,0,1,950)
    wall_right = pygame.Rect(1080,0,1,950)

    square1 = pygame.Rect(750, 350, 20, 20)
    square2 = pygame.Rect(330, 350, 20, 20)
    IMAGE = pygame.image.load("player1standright.png").convert_alpha()
    rect = IMAGE.get_rect()
    rect.x = 100
    rect.y = 225

    x_movement = 0
    y_movement = 0
    x2_movement = 0
    y2_movement = 0

    accelx1 = 0
    accely1 = 0
    accelx2 = 0
    accely2 = 0

    jumping = False
    jumping2 = False

    #This is the function that does it
    def player1runright(images1right, IMAGE, image, imagerect):
        i = -1
        while i <= 2:
            pygame.time.delay(0)
            i += 1
            print (i)
            IMAGE = pygame.image.load(images1right[i])
            print (images1right[i])
            screen.blit(IMAGE, rect)

    images1right = ["player1runright-1.png","player1runright-2.png","player1runright-3.png","player1runright-4.png"]

    def health_bars(player1_health, player2_health):
        if player1_health > 75:
            player1_health_color = (62, 219, 41)
        elif player1_health > 50:
            player1_health_color = (239, 212, 9)
        else:
            player1_health_color = (239, 9, 9)

        if player2_health > 75:
            player2_health_color = (62, 219, 41)
        elif player2_health > 50:
            player2_health_color = (239, 212, 9)
        else:
            player2_health_color = (239, 9, 9)

        pygame.draw.rect(screen, player1_health_color, (70, 50, player1_health, 25))
        pygame.draw.rect(screen, player2_health_color, (900, 50, player2_health, 25))

    player1_health = int(100)
    player2_health = int(100)

    drawmenu()

    while True:
        screen.fill(background)
        screen.blit(image, imagerect)
        pygame.draw.rect(screen, (255,0,0), square1)
        pygame.draw.rect(screen, (0,0,255), square2)
        pygame.draw.rect(screen, (0,0,0), wall_left)
        pygame.draw.rect(screen, (0,0,0), wall_right)
        health_bars(player1_health, player2_health)
        pygame.display.update()
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                exit()
        keys_pressed = pygame.key.get_pressed()
        if keys_pressed[pygame.K_d] != 1:
            screen.blit(IMAGE, rect)

        #MOVEMENT P1

        if keys_pressed[pygame.K_LEFT] == 1:
            x_movement += -1
        elif keys_pressed[pygame.K_RIGHT] == 1:
            x_movement += 1
        else:
            x_movement = 0

        #MOVEMENT P2

        if keys_pressed[pygame.K_a] == 1:
            x2_movement += -1
        elif keys_pressed[pygame.K_d] == 1:
            x2_movement += 1
            player1runright(images1right, IMAGE, image, imagerect)
        else:
            x2_movement = 0

        #MAX SPEED P1
        if x_movement < -7:
            x_movement = -7
        if x_movement > 7:
            x_movement = 7
        if y_movement < -7:
            y_movement = -7
        if y_movement > 7:
            y_movement = 7

        #MAX SPEED P2
        if x2_movement < -7:
            x2_movement = -7
        if x2_movement > 7:
            x2_movement = 7
        if y2_movement < -7:
            y2_movement = -7
        if y2_movement > 7: 
            y2_movement = 7

        #COLLISION (WALL) P1

        if square1.colliderect(wall_left):
            player2_health -= 0.5
            x_movement = 0
            y_movement = 0
            if keys_pressed[pygame.K_RIGHT] == 1:
                x_movement += 1

        if square1.colliderect(wall_right):
            player2_health -= 0.5
            x_movement = 0
            y_movement = 0
            if keys_pressed[pygame.K_LEFT] == 1:
                x_movement += -1

        #COLLISION (WALL) P2

        if rect.colliderect(wall_left):
            player1_health -= 0.5
            x2_movement = 0
            y2_movement = 0
            rect.x = 0
            rect.y = 0
            if keys_pressed[pygame.K_d] == 1:
                x2_movement += 1
                rect.x += 1

        if rect.colliderect(wall_right):
            player1_health -= 0.5
            x2_movement = 0
            y2_movement = 0
            rect.x = 0
            rect.y = 0
            if keys_pressed[pygame.K_a] == 1:
                x2_movement += -1
                rect.x += -1


        #COLLISION (ATTACK) P1
        if square1.colliderect(rect) and keys_pressed[pygame.K_g] == 1:
            player2_health -= 1

        #COLLISION (ATTACK) P2
        if rect.colliderect(square1) and keys_pressed[pygame.K_p] == 1:
            player1_health -= 1

        #JUMPING P1
        if keys_pressed[pygame.K_w] == 1:
            if jumping2 == False:
                jumping2 = True

                y2_movement = -30
                accely2 = 1

        rect = rect.move(x2_movement, y2_movement)

        x2_movement += accelx2
        y2_movement += accely2

        if jumping2 == True:
            if rect.y >= 225:
                jumping2 = False

                rect.y = 225
                y2_movement = 0
                accely2 = 0

        #JUMPING P2
        if keys_pressed[pygame.K_UP] == 1:
            if jumping == False:
                jumping = True

                y_movement = -30
                accely1 = 1

        square1 = square1.move(x_movement, y_movement)

        x_movement += accelx1
        y_movement += accely1

        if jumping == True:
            if square1.y >= 350:
                jumping = False

                square1.y = 350
                y_movement = 0
                accely1 = 0

        pygame.display.update()
        pygame.time.delay(20)

        if player2_health == 0 and player1_health !=0:
            pygame.time.delay(400)
            player1wins()
            pygame.time.delay(3000)
            drawmenu()
            break
        elif player1_health == 0 and player2_health !=0:
            pygame.time.delay(400)
            player2wins()
            pygame.time.delay(3000)
            drawmenu()
            break
        if player1_health == 0 and player2_health == 0:
            pygame.time.delay(400)
            tie()
            pygame.time.delay(3000)
            drawmenu()
            break

(I know the code is a bit lengthy but I don't know if I can cut off any of it.)

Alisha Jain
  • 75
  • 2
  • 11
  • Usually because you're blitting the wrong thing. – Ignacio Vazquez-Abrams Jan 17 '18 at 18:55
  • I recommend posting your complete program on http://codereview.stackexchange.com/ because there are several things that could be improved. However, make sure that everything works correctly before you post it there. – skrx Jan 18 '18 at 13:44
  • And please reduce your program to a [minimal, complete example](https://stackoverflow.com/help/mcve) before you post it here the next time. You might find the bug yourself in this process. – skrx Jan 18 '18 at 13:46

2 Answers2

4

The flickering occurs because you update the screen twice every frame. Remove the first pygame.display.update() line and it should work. There should usually be only one pygame.display.update or pygame.display.flip call per frame.

I figured that out by consecutively removing parts of your program until only a minimal example was left. Check every time after you've removed something if the error is still there.


The problem with your animation is unrelated, but here's a quick solution. First the while loop in the player1runright function has to be removed because it will immediately blit all images one after the other every frame.

I'd just increment an index variable every frame if the d-key is pressed, get the corresponding image out of the animation list and assign it to a variable that holds the current image and blit it.

import sys
import pygame

pygame.init()
clock = pygame.time.Clock()

# Some surfaces which serve as the player images.
PLAYER_IMAGE = pygame.Surface((30, 50))  # Idle player image.
PLAYER_IMAGE.fill((30, 150, 230))
IMAGE1 = pygame.Surface((30, 55))
IMAGE1.fill((60, 150, 180))
IMAGE2 = pygame.Surface((30, 60))
IMAGE2.fill((90, 150, 130))
IMAGE3 = pygame.Surface((30, 65))
IMAGE3.fill((120, 150, 80))
IMAGE4 = pygame.Surface((30, 70))
IMAGE4.fill((150, 150, 30))
PLAYER1_IMAGES_RIGHT = [IMAGE1,IMAGE2,IMAGE3,IMAGE4]

anim_index = 0

while True:
    screen = pygame.display.set_mode((1080, 400))
    background_image = pygame.Surface(screen.get_size())
    background_image.fill((30, 30, 30))
    background_rect = background_image.get_rect()

    player_image = PLAYER_IMAGE
    player_rect = player_image.get_rect(x=100, y=225)

    while True:
        # Handle the events.
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        keys_pressed = pygame.key.get_pressed()
        if keys_pressed[pygame.K_d]:
            # Increment the index and change the player image.
            anim_index += 1
            anim_index %= len(PLAYER1_IMAGES_RIGHT)*4
            # * 4 in the line above and floor div 4 to slow the animation down.
            player_image = PLAYER1_IMAGES_RIGHT[anim_index//4]
        else:  # Switch back to the idle image.
            player_image = PLAYER_IMAGE
            # Reset the index, so that we don't start in the middle of the anim.
            anim_index = 0

        # Draw everything at the end.
        screen.blit(background_image, background_rect)
        # Just blit the current player image at the player_rect's topleft coords.
        screen.blit(player_image, player_rect)

        pygame.display.update()
        clock.tick(30)

Also, take a look at this answer.

skrx
  • 19,980
  • 5
  • 34
  • 48
0

Your blit rect is wrong for (at least) two reasons:

  1. Inside player1runright, you take a parameter named imagerect but you blit with rect, defined above as the rect from "player1standright.png". That rect will always stay the same.
  2. When you call player1runright, you pass the value of imagerect, which is defined as the rect from "background(1).png". Again, that will never change, AND it is not the rect you want to use.

Both of these problems are due to variables "leaking" into other scopes. Sometimes that is desired, but here it is just causing bugs. I would consider reorganizing your code (don't nest your functions unless you intentionally want to capture your containing scope) to help find these bugs.

Regarding the slowdown: Don't load the images every frame. File loading is slow. Load them once and store them somewhere.

0x5453
  • 12,753
  • 1
  • 32
  • 61