Python 2D Sprite Animation - python

I'm trying to animate a sprite using a loop such that each time the loop runs through the position number for an image in an array increases by one. I keep getting "UnboundLocalError: local variable 'Antic' referenced before assignment". There is
Antic = 0
Antic = int(Antic)
# Global constants
StAnmtn = ["Images/PlayerVampireStanding1.png", "
Images/PlayerVampireStanding2.png",
"Images/PlayerVampireStanding3.png","Images/PlayerVampireStanding4.png",
"Images/PlayerVampireStanding5.png", "Images/PlayerVampireStanding6.png",
"Images/PlayerVampireStanding7.png", "Images/PlayerVampireStanding8.png"]
`
at the start and
def main():
""" Main Program """
pygame.init()
clock = pygame.time.Clock() # creates clock to limit frames per
second
FPS = 60 # sets max speed of min loop
SCREENSIZE = SCREENWIDTH, SCREENHEIGHT = 1300, 700 # sets size of
screen/window
screen = pygame.display.set_mode(SCREENSIZE) # creates window and game
screen
pygame.display.set_caption("Count Acheron")
# Create the player
player = Player()
# Create all the levels
level_list = []
level_list.append( Level_01(player) )
# Set the current level
current_level_no = 0
current_level = level_list[current_level_no]
active_sprite_list = pygame.sprite.Group()
player.level = current_level
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
if event.key == pygame.K_UP:
player.jump()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and player.change_x < 0:
player.stop()
if event.key == pygame.K_RIGHT and player.change_x > 0:
player.stop()
if Antic > 6:
Antic = 0
else:
Antic += 1
# Update the player.
active_sprite_list.update()
# Update items in the level
current_level.update()
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
current_level.draw(screen)
active_sprite_list.draw(screen)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Limit to 60 frames per second
clock.tick(60)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
if __name__ == "__main__":
main()
as the loop. What it doesn't seem to like is the snippet of code
if Antic > 6:
Antic = 0
else:
Antic += 1
How do I fix this?

Personally I've never used pygame's sprite module.
import pygame
class character:
def __init__(self, x, y):
self.x = x
self.y = y
self.sprites = [pygame.image.load("img1.png"), pygame.image.load("img2.png")]
self.frame = 0
def draw(self, surface):
surface.blit(self.sprites[self.frame], (self.x, self.y))
self.frame += 1
if self.frame > len(self.sprites) - 1: self.frame = 0
pygame.init()
DS = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
c = character(640, 360)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
pygame.quit()
c.draw(DS)
pygame.display.update()
clock.tick(30)
DS.fill((0, 0, 0))

Needed to globalise the function ("global Antic" in the main loop)

Related

Some of the objects in sprite group are getting stuck on screen instead of moving in pygame

import pygame
from sys import exit
import random
WIDTH, HEIGHT = 1400,750
FPS = 60
HEADWAY_GAP = 40
vehicleColor = {0:"red",1:"blue",2:"yellow"}
directions = ["Right", "Left"]
classofVehicle = ["Car","Bike","Bus"]
coord = {"Right":(0,300), "Left":(1400,350)}
objects = {"Right":[],"Left":[]}
pygame.init()
myWindow = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
road = pygame.image.load("Required_images/Road/Road_final.png")
class Vehicle(pygame.sprite.Sprite):
def __init__(self, ClassofVehicle, Color_Code, direction, pos):
super().__init__()
self.Class = ClassofVehicle
self.Color_Code = Color_Code
self.direction = direction
self.pos = pos
path = "Required_images/"+ClassofVehicle+"/"+ClassofVehicle+"_"+Color_Code+".png"
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
if self.direction == "Right":
self.rect.midright = pos
else:
self.rect.midleft = pos
self.index = len(objects[direction])-1
def movingVehicles(self):
if self.direction == "Right":
if self.index == 0 or (self.rect.x + self.rect.width < objects[self.direction][self.index].rect.x - HEADWAY_GAP):
self.rect.x += 1
elif self.direction == "Left":
if self.index == 0 or (self.rect.x - self.rect.width > objects[self.direction][self.index].rect.x + HEADWAY_GAP):
self.rect.x -= 1
def update(self):
self.movingVehicles()
Object_timer = pygame.USEREVENT+1
pygame.time.set_timer(Object_timer, 1000)
objectsGroup = pygame.sprite.Group()
def generatingObjects():
Dir = random.choice(directions)
po = coord[Dir]
color_code = random.choice([0,1,2])
cla = random.choice(classofVehicle)
object = Vehicle(cla,vehicleColor[color_code],Dir,po)
objectsGroup.add(object)
objects[Dir].append(object)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
pygame.display.update()
clock.tick(FPS)
I have created a sprite class and a sprite group. I also created an user defined event to generate vehicles for every 1000ms. To display the vehicles on the screen instead of looping through all the elements of the sprite group separately I used group_name.draw() method. But when I run the code, some of the images are getting stuck on the screen for some time and are moving after sometime. I tried to look for any error in the logic but I couldn't find it. If you have any knowledge or able to find the error, please help and also any kind of suggestions are appreciated.
It is a matter of Indentation. You have to draw the scene in the application loop instead of after the application loop:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
# INDENTATION
#->|
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
pygame.display.update()
clock.tick(FPS)
make sure to indent the elements in your while loop and use a pygame.display.flip() at the very end of your loop.
so it looks like
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == Object_timer:
generatingObjects()
myWindow.fill((0,0,0))
objectsGroup.update()
objectsGroup.draw(myWindow)
clock.tick(FPS)
pygame.display.flip()

If player doesn't move for x seconds, do something in pygame

I'm trying to execute an action if player is not moved in certain amount of time.
I tried something like this:
while True:
dt = self.clock.tick(30)
time_since_last_action += dt
moved = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
moved = True
if event.key == pygame.K_LEFT:
player_position -= player_size
elif event.key == pygame.K_RIGHT:
player_position += player_size
if time_since_last_action > 1000 and not moved:
#----action----
time_since_last_action = 0
It doesn't work and I understand why but I have no other idea how to code what I want. It would be very helpful to see an example of what my code should look like. Thank you!
Minimal working example.
When player doesn't move then it start shaking (make random moves up and down)
I use do_something = True/False to control if it should shake or not.
When I press key LEFT or RIGH then I set do_something = False and reset timer time_since_last_action.
When I don't press keys and it does nothing then timer change value.
When timer > 1000 and it doesn't move and doesn't do something then I set do_something = True
# https://github.com/furas/python-examples/
import pygame
import random
# --- constants --- (UPPER_CASE_NAMES)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 25 # for more than 220 it has no time to update screen
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# --- classes --- (CamelCaseNames)
class Player(pygame.sprite.Sprite):
def __init__(self, x=SCREEN_WIDTH//2, y=SCREEN_HEIGHT//2):
super().__init__()
self.image = pygame.Surface((100,100))
self.image.fill(WHITE)
self.rect = self.image.get_rect(centerx=x, centery=y)
def update(self):
#move_x = random.randint(-5, 5)
#move_y = random.randint(-5, 5)
#self.rect.move_ip(move_x,move_y)
pass
def draw(self, surface):
surface.blit(self.image, self.rect)
# --- functions --- (lower_case_names)
# --- main ---
pygame.init()
screen = pygame.display.set_mode( (SCREEN_WIDTH, SCREEN_HEIGHT) )
player = Player()
# --- mainloop ---
clock = pygame.time.Clock()
# default values at start
do_something = False
time_since_last_action = 0
running = True
while running:
# --- FPS ---
dt = clock.tick(30)
time_since_last_action += dt
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
running = False
keys = pygame.key.get_pressed()
moved = False
if keys[pygame.K_LEFT]:
player.rect.x -= 10 # player.rect.width
moved = True
# reset other values
do_something = False
time_since_last_action = 0
elif keys[pygame.K_RIGHT]:
player.rect.x += 10 # player.rect.width
moved = True
# reset other values
do_something = False
time_since_last_action = 0
if not do_something and not moved and time_since_last_action > 1000:
do_something = True
# reset other values
time_since_last_action = 0
if do_something:
# - action -
# shaking
player.rect.y -= random.randint(-5, 5)
# --- changes/moves/updates ---
# empty
# --- draws ---
screen.fill(BLACK)
player.draw(screen)
pygame.display.flip()
# --- end ---
pygame.quit()
I put more complex version on Github. It saves position, changes image and start shaking. When you press LEFT or RIGHT then it restore original position and image.

Pointer not moving up or down despite using earlier and already working code

been trying for a while to find the problem with the following code. I'm trying to have the pointer move up and down like the title of the question states but it just won't move. Any and all help is welcome.
Code for Pause Screen event processing:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
print(str(pointer.Pointer.getPosition(self.pointer)))
return True
return False
Code for displaying which shows the pointer in the same place.
self.active_sprite_list.draw(screen)
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, constants.WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
pygame.display.flip()
And for reference the same code in the Menu which has the pointer moving up and down:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
#print(str(self.selection))
return True
###Some code later###
screen.fill(constants.BLACK)
font = pygame.font.SysFont("serif", 25)
for counter in range(1,5):
text = font.render(self.options[counter-1], True, constants.WHITE)
center_x = 150
center_y = (counter * 120) - (text.get_height() // 2) + (self.pointer.image.get_height() // 2)
screen.blit(text, [center_x, center_y])
self.active_sprite_list.draw(screen)
pygame.display.flip()
And before you suggest, the screen for the pause has been declared before here:
while notPaused == False:
#print("Received")
notPaused = pause.processEvents()
print(str(notPaused))
if firstTime == True:
self.pauseScreen.fill(constants.ABLACK)
pause.displayFrame(self.pauseScreen)
self.pauseScreen.set_alpha(128)
screen.blit(self.pauseScreen, [0,0])
firstTime = False
pause.displayFrame(self.pauseScreen)
clock.tick(60)
As per requested, here is the MoveUp and MoveDown functions in the Pointer Class:
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
And as suggested, the modular/self-contained code that can be run on its own as long as you have some kind of image in a Resources Folder next to the saved code file.
import pygame, sys
"""
Global constants
"""
# Colors
ABLACK = ( 0, 0, 0, 125)
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
YELLOW = ( 255, 255, 0)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Pointer(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Resources/Pointer.png")
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 120
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def getPosition(self):
self.position = self.rect.y / 120
return self.position
class Pause(object):
def __init__(self,screen):
self.selection = 4
self.options = ["Resume Game","Review Controls","Back to Menu","Quit"]
self.active_sprite_list = pygame.sprite.Group()
self.pointer = Pointer()
self.active_sprite_list.add(self.pointer)
def processEvents(self):
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(Pointer.getPosition(self.pointer))
print(str(Pointer.getPosition(self.pointer)))
return False
return True
def displayFrame(self,screen):
self.active_sprite_list.draw(screen)
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
pygame.display.flip()
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
Paused = True
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pauseScreen.fill(ABLACK)
pause = Pause(screen)
pauseScreen.set_alpha(128)
Paused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while Paused:
notPaused = pause.processEvents()
print(str(Paused))
pause.displayFrame(pauseScreen)
#screen.blit(pauseScreen, [0,0])
clock.tick(60)
Your issue is in the main game loop, first off, you had the blitting of Pause Screen to Screen commented out. readding that in gave the pointer seeming to multiply and go all over the place (getting closer!).
The reason it does that is you did not update your pauseScreen in each pass of the loop. Your displayFrame will add your pointer to the proper location, but the one from last frame, and 2 frames ago, and... are still there. by moving the lines
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
And placing them within your game loop, the pause screen is reset ever frame and only the latest pointer is displayed. Here is the updated game loop:
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
notPaused = False
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pause = Pause(screen)
notPaused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while not notPaused:
notPaused = pause.processEvents()
print(str(notPaused))
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
clock.tick(60)

Problems making pygame target image (scope) move with arrow keys

When we run our pygame's code, our target scope image will NOT move, but our do robots generate. We are trying to use our arrow keys to move them and I included all of our code.
Commented out under our newest trial code for moving are two other things we tried.
import pygame, sys
from graphics import *
import time
import random
pygame.init()
level=1
bg = pygame.image.load('bg.png')
bg_width = 800
pygame.display.set_caption('Robot Apocalypse')
surfacew = 1054
surfacel = 562
surface = pygame.display.set_mode((surfacew,surfacel))
black = (0, 0, 0)
score = 0
#player_health = 99
alive=True
targetImg = pygame.image.load('target.png')
targetImg = pygame.transform.scale(targetImg, (40, 40))
targetxy = targetImg.get_rect()
targetx = targetxy[0]
targety = targetxy[1]
def move_target(targetImg):
pygame.event.clear()
while alive == True:
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
targetx -= 5
if keys_pressed[pygame.K_RIGHT]:
targetx += 5
if keys_pressed[pygame.K_UP]:
targety -= 5
if keys_pressed[pygame.K_DOWN]:
targety += 5
pygame.display.update()
# pygame.event.clear()
# for event in pygame.event.get():
# if event.type ==KEYDOWN:
# if event.key == K_LEFT:
# direction = MOVE_LEFT
# elif event.key == K_RIGHT:
# direction = MOVE_RIGHT
# elif event.type == KEYUP:
# if event.key == K_LEFT:
# direction = 0
# elif event.key == K_RIGHT:
# direction = 0
# if(direction == MOVE_LEFT):
# targetx-=10
# elif(direction == MOVE_RIGHT):
# targetx+=10
# for event in pygame.event.get():
# print(event)
# if event.type==QUIT:
# pygame.quit()
# sys.exit()
# if event.type == KEYDOWN:
# if event.key == K_LEFT:
# targetx-=5
# elif event.key == K_RIGHT:
# targetx+=5
# elif event.key == K_UP:
# targety-=5
# elif event.key == K_DOWN:
# targety+=5
# pygame.display.update()
def shoot():
#while True:
shot = False
pos = (targetx, targety)
t = screen.blit(robot, (64,64))
if t.collidepoint(pos):
shot = True
return shot
def generate_robot(x,y):
#while displayrobot == True
robot=pygame.draw.rect(surface, (255,0,0), (x,y,64,64), 0)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
shoot()
if shot == True:
displayrobot = False
cover = surface.blit(bg, (x,y))
pygame.display.update()
return x, y
#if shot == True:
def die():
message("YOU DIED")
pygame.display.update()
def win(level):
level+=1
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def message(text):
largeText = pygame.font.Font('freesansbold.ttf',60)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((surfacew/6),(surfacel/2))
surface.blit(TextSurf, TextRect)
pygame.display.update()
time.sleep(2)
def main(alive):
#displayrobot = True:
robot=0
score=0
surface.fill(black)
surface.blit(bg, (0,0))
message("Level "+ str(level))
mouse = pygame.mouse.get_pos()
target = surface.blit(targetImg, (mouse))
while alive==True:
# robot = enemy(64, 64)
x=random.randint(40,760)
y=random.randint(40,560)
generate_robot(x,y)
pygame.display.update()
robot+=1
time.sleep(8/level)
if robot>50 and robot>score:
alive=False
# if pygame.mouse.get_pressed()==True:
# shoot() #maybe??
if shot==True:
score+=1
robot-=1
if robot==10/(level/2): #if 10- robots on screen then...
die()
if score>25*(level/2):
win()
move_target(targetImg)
main(alive)
pygame.quit()
quit()
.
There are no error messages, but it won't move. We've tried a ton of different things (that aren't included) and looked up a lot of websites so please help us. Thanks
To move object you have to not only change x,y and update screen (send buffer to video card which will display it) but also clean buffer, draw image in new place in buffer (blit()).
This code shows only working move_target. I skiped rest of code.
I keep position in target_rect which is pygame.Rect. You can use it to blit(img,rect) but later you can also use to check collision rect.colliderect(other_rect)
import pygame
# --- constants --- (UPPER_CASE_NAMES)
BLACK = (0, 0, 0)
SURFACE_WIDTH = 1054
SURFACE_HEIGHT = 562
# --- functions --- (lower_case_names)
def move_target(target_img, target_rect):
alive = True
clock = pygame.time.Clock()
while alive:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
alive = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
alive = False
# --- updates/changes ---
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
target_rect.x -= 5
if keys_pressed[pygame.K_RIGHT]:
target_rect.x += 5
if keys_pressed[pygame.K_UP]:
target_rect.y -= 5
if keys_pressed[pygame.K_DOWN]:
target_rect.y += 5
# --- draws ---
surface.fill(BLACK)
surface.blit(target_img, target_rect)
pygame.display.update()
# the same game's speed on all computers = 60 FPS
clock.tick(60)
# --- main --- (lower_case_names)
pygame.init()
pygame.display.set_caption('Robot Apocalypse')
surface = pygame.display.set_mode((SURFACE_WIDTH, SURFACE_HEIGHT))
target_img = pygame.image.load('target.png')
target_img = pygame.transform.scale(target_img, (40, 40))
target_rect = target_img.get_rect()
move_target(target_img, target_rect)
pygame.quit()
It looks like my original functional comment is still "in force": your code doesn't move any game object.
targetxy = targetImg.get_rect()
targetx = targetxy[0]
targety = targetxy[1]
At this point, targetxy is a reference to the bounding rectangle for your game object.
targetx and targety are copies of the values of the rect position.
def move_target(targetImg):
pygame.event.clear()
while alive == True:
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
targetx -= 5
...
You've change the local copy of the x-coordinate. This does not affect the position of targetImg. You need to change the object's attributes, such as
targetxy.x -= 5
After this, you need to update the game screen.

Pygame running super slow (~2 fps)

My pygame game runs super slowly, about 2 fps. You are supposed to be able to move around with WASD and always be centered in the screen. I know it has something to do with my tilemap and the way I do my math, but I can't pin it down. Also, if there is a better way to keep the player in the center while moving the map behind it, I would love to know how to do that.
import pygame, sys, numpy
#create fps clock
clock = pygame.time.Clock()
#
MAPHEIGHT = 80
MAPWIDTH = 80
TILESIZE = 40
TILESONSCREENW = 13
TILESONSCREENH = 13
#set screen size
SCREENH = TILESONSCREENH*TILESIZE
SCREENW = TILESONSCREENW*TILESIZE
#create character vars
circleRad = 40
circleSpeed = 4
#create circle pos vars
circleX = 250
circleY = 250
#create keyboard button vars
rightP = False
leftP = False
upP = False
downP = False
#
playerOnTileS = pygame.Surface((MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE))
#constants for the tilemap
GRASS = pygame.image.load("grass.png")
#tilemap
tilemap = [[GRASS for i in range(MAPHEIGHT)] for j in range(MAPWIDTH)]
#create window
DISPLAYSURF = pygame.display.set_mode((SCREENW, SCREENH))
#set window name
pygame.display.set_caption("Snowball Fight!")
#---------------------------------------------------
class Player:
def __init__(self, playX, playY, size):
self.playerX = playX
self.playerY = playY
self.size = size
self.playerSurface = pygame.Surface((size, size))
pygame.draw.rect(self.playerSurface, (19,135,67), (0,0,size, size))
#------------------------------------------------
def update(self):
playerOnTileS.blit(self.playerSurface, (self.playerX, self.playerY))
DISPLAYSURF.blit(playerOnTileS, (SCREENW/2-self.playerX-self.size ,SCREENH/2-self.playerY-self.size))
#game loop
myPlayer = Player(0,0,circleRad)
while True:
DISPLAYSURF.fill((0,0,0))
for event in pygame.event.get():
#if the user closed the window
if event.type == pygame.QUIT:
#close pygame
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
leftP = True
if event.key == pygame.K_d:
rightP = True
if event.key == pygame.K_w:
upP = True
if event.key == pygame.K_s:
downP = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
leftP = False
if event.key == pygame.K_d:
rightP = False
if event.key == pygame.K_w:
upP = False
if event.key == pygame.K_s:
downP = False
if leftP:
myPlayer.move(-circleSpeed,0,True)
if rightP:
myPlayer.move(circleSpeed,0,True)
if downP:
myPlayer.move(0,circleSpeed,True)
if upP:
myPlayer.move(0,-circleSpeed,True)
for row in range(len(tilemap)):
for column in range(len(tilemap[row])):
playerOnTileS.blit(tilemap[row][column],(column*TILESIZE,row*TILESIZE))
myPlayer.update()
pygame.display.update()
clock.tick(30)
Images/pygame.Surfaces should usually be converted with the pygame.Surface.convert or convert_alpha methods. The performance of unconverted surfaces is abysmal. When I convert the grass image, I get nearly 30 FPS.
The next problem is the nested for loop. Python's function call overhead is rather big, so it would be a good idea to blit all the tiles onto one large background surface before the main loop starts and then blit this background surface onto the DISPLAYSURF once per frame to clear it. With that change I get pygame's apparent maximum FPS, that means clock.get_fps() jumps between 2000 and 1666.666 FPS.
# Ahead of the while loop.
playerOnTileS = pygame.Surface((MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE))
for row in range(len(tilemap)):
for column in range(len(tilemap[row])):
playerOnTileS.blit(tilemap[row][column],(column*TILESIZE,row*TILESIZE))
You have to change the update method (by the way, better call it draw) and blit the player surface onto the DISPLAYSURF instead.

Categories