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()
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