Search code examples
pythonanimationpygamespritekeypress

Sprite displays but won't switch animations or move with key presses in PyGame


Building a simple PyGame in which I need the player sprite to move and change image based on arrow key presses. I added the facing function in the Sprite1 class and call it in the if statements for key presses in the game loop hoping it would change image every time a key is pressed but it doesn't seem to want to update the sprite's image after key presses. Is the problem with my facing function? Or with something else?

import pygame 
pygame.init() 

#game Window 
screen_width = 800
screen_height = 400

screen = pygame.display.set_mode((screen_width, screen_height)) 
pygame.display.set_caption('Warrior Quest') 

#game variables
main_menu = True

#background image
background_img =  pygame.image.load('background.png')
#button images 
start_img = pygame.image.load('startbutton.PNG')
cancel_img = pygame.image.load('cancelbutton.PNG')
title_img = pygame.image.load('warriorquestTile.PNG')

#background function 
def draw_bg(): 
  screen.blit(background_img, (0,0)) 

 
class Sprite1(pygame.sprite.Sprite):
  def __init__(self): 
    super().__init__()  
    self.faceUp = True
    self.faceDown = False
    self.faceLeft = False 
    self.faceRight = False
    self.image = pygame.image.load('player.png').convert_alpha()
    self.rect = self.image.get_rect()
  
  def draw(self):
    screen.blit(self.image, self.rect)

  def facing(self):
    if self.faceUp == True:
      self.image = pygame.image.load('player.png').convert_alpha()
      self.rect.w = self.image.get_rect().w
      self.rect.h = self.image.get_rect().h
    elif self.faceDown == True:
      self.image = pygame.image.load('playerDown.png').convert_alpha()
      self.rect = self.image.get_rect()
    elif self.faceLeft == True:
      self.image = pygame.image.load('playerLeft.png').convert_alpha()
      self.rect = self.image.get_rect()
    elif self.faceRight == True:
      self.image = pygame.image.load('playerRight.png').convert_alpha()
      self.rect = self.image.get_rect()
  

#setup player 
player = Sprite1()
player.rect.x = 400
player.rect.y = 380

class Button():
  def __init__(self,x,y,image):
    self.image = image
    self.rect = self.image.get_rect() 
    self.rect.x = x 
    self.rect.y = y 
    self.clicked = False 
   
  def draw(self):
    action = False
    
    #get mouse position
    pos = pygame.mouse.get_pos() 
    
    if self.rect.collidepoint(pos):
      if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
        action = True 
        self.clicked = True 

    if pygame.mouse.get_pressed()[0] == 0:
      self.clicked = False 

    #draw Button 
    screen.blit(self.image, self.rect) 

    return action

 
#create buttons 
start_button = Button(screen_width // 2 -350, screen_height // 2, start_img) 
cancel_button = Button(screen_width // 2 + 150, screen_height // 2, cancel_img)
title_button = Button(300,400,title_img)
#game loop running 
running = True
while running:
  
  draw_bg()
  if main_menu == True: 
    
    if start_button.draw():
      main_menu = False
    if cancel_button.draw():
      running = False 
  else:  
    player.draw()
   
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player.rect.x>5:
      player.faceLeft = True 
      player.facing()
      player.rect.x -= 5
    if keys[pygame.K_RIGHT] and player.rect.x<790:
      player.faceRight = True 
      player.facing()
      player.rect.x += 5
    if keys[pygame.K_UP] and player.rect.y>10:
      player.faceUp = True 
      player.facing()
      player.rect.y -= 5
    if keys[pygame.K_DOWN]and player.rect.y<395:
      player.faceDown = True 
      player.facing()
      player.rect.y += 5
  
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False
  
  pygame.display.update() 
pygame.quit()  

    

Solution

  • pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the center of the rectangle can be specified with the keyword argument center. These keyword argument are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a full list of the keyword arguments).

    Keep the position of the sprite by keeping the center position of the rectangle:

    if self.faceUp == True:
        self.image = pygame.image.load('player.png').convert_alpha()
        self.rect = self.image.get_rect(center = self.rect.center)
    

    Additionally, do not load the images in the application loop. Loading an image is very time consuming because the image file has to be read and interpreted. Load the images once at the begin of the application:

    It is not sufficient to set the new face* attribute, you also have to reset the other face* attributes. I recommend to a a single attribute face with different states:

    class Sprite1(pygame.sprite.Sprite):
      def __init__(self): 
        super().__init__()  
        self.player_img = pygame.image.load('player.png').convert_alpha()
        self.player_down_img = pygame.image.load('playerDown.png').convert_alpha()
        self.player_left_img = pygame.image.load('playerLeft.png').convert_alpha()
        self.player_right_img = pygame.image.load('playerRight.png').convert_alpha()
        self.face = "up" 
        self.image = self.player_img
        self.rect = self.image.get_rect()
      
      def draw(self):
        screen.blit(self.image, self.rect)
    
      def facing(self):
        if self.face == "up" :
          self.image = self.player_img
        elif self.face == "down":
          self.image = self.player_down_img
        elif self.face == "left":
          self.image = self.player_left_img
        elif self.face == "right":
          self.image = self.player_right_img
        self.rect = self.image.get_rect(center = self.rect.center)
    
    running = True
    while running:
        # [...]
    
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and player.rect.x>5:
          player.face = "up"
          player.facing()
          player.rect.x -= 5
        if keys[pygame.K_RIGHT] and player.rect.x<790:
          player.face = "down" 
          player.facing()
          player.rect.x += 5
        if keys[pygame.K_UP] and player.rect.y>10:
          player.face = "left"
          player.facing()
          player.rect.y -= 5
        if keys[pygame.K_DOWN]and player.rect.y<395:
          player.face = "right": 
          player.facing()
          player.rect.y += 5