Sprite Jittering and Repeatedly Bliting Inside Pygame Project - python

I have been having this issue where when i run my code, the sprites keep jittering even though the screen doesn't force them to do this.
This is where I define the screen size
This is where I update my actions and draw
Here is the part where I load my images from the spritesheet
Any help will be much appreciated.
I tried importing the sprites from the spritesheet onto the game with the animations all working however there are no animations and the sprite continues to jitter up and down
if you would like the code for my fighter class please see below:
def load_images(self, sprite_sheet, animation_steps):
#extract the certain animation images from spritesheet
animation_list = []
for y, animation in enumerate(animation_steps):
temporary_img_list = []
for x in range(animation):
temporaryimg = sprite_sheet.subsurface(x , y , self.size, self.size)
temporary_img_list.append(pygame.transform.scale(temporaryimg, (self.size * self.image_scale, self.image_scale * self.size)))
animation_list.append(temporary_img_list)
print(animation_list)
return animation_list
def move(self, screen_base, screen_height, surface, target):
speed = 10
gravity = 2
dx = 0
dy = 0
self.running = False
self.attack_type = 0
#key-presses recall
key = pygame.key.get_pressed() = pygame.key.get_pressed()
#can only happen if no other attack is occuring
if self.attacking == False:
#movement defined here
if key[pygame.K_a]:
dx = -speed
self.running = True
if key[pygame.K_d]:
dx = speed
self.running = True
#jump is defined here
if key[pygame.K_w] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack is defined here
if key[pygame.K_k] or key[pygame.K_j]:
self.attack(surface, target)
#determine which attack type was used when the key was pressed
if key[pygame.K_k]:
self.attack_type = 1
if key[pygame.K_j]:
self.attack_type = 2
#apply gravity for the jumps
self.vel_y += gravity
dy += self.vel_y
#this makes sure that the character does not go off the screen
if self.rect.left + dx < 0:
dx = -self.rect.left
if self.rect.right + dx > screen_base:
dx = screen_base - self.rect.right
if self.rect.bottom + dy > screen_height - 207:
self.vel_y = 0
self.jump = False
dy = screen_height - 207 - self.rect.bottom
#ensures the players face each other
if target.rect.centerx > self.rect.centerx:
self.flip = False
else:
self.flip = True
#apply the attack cooldown to balance the amount of attacks possible
if self.attack_cooldown > 0:
self.attack_cooldown -= 1
#update the player position constantly
self.rect.x += dx
self.rect.y += dy
#handle the animation updates
def update(self):
#check what action the player is currently doing
if self.health <= 0:
self.health = 0
self.alive = False
self.update_action(6)
elif self.hit == True:
self.update_action(5)
elif self.attacking == True:
if self.attack_type == 1:
self.update_action(5) # 3 = Attack
elif self.attack_type == 2:
self.update_action(6) # 4 = Attack2
elif self.jump == True:
self.update_action(7) # 7 = Jump
elif self.running == True:
self.update_action(2) # 2 = Run
else:
self.update_action(0) # 0 = Idle
animation_cooldown = 50
#update the image
self.image = self.animation_list[self.action][self.frameindex]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.frameindex += 1
self.updatetime = pygame.time.get_ticks()
#check if the animation has finished
if self.frameindex >= len(self.animation_list[self.action]):
#if the player is dead end the animation
if self.alive == False:
self.frameindex = len(self.animation_list[self.action]) - 1
else:
self.frameindex = 0
#check if an attack was executed
if self.action == 5 or self.action == 6:
self.attacking = False
self.attack_cooldown = 20
#check if damage was taken
if self.action == 7:
self.hit = False
#if the player was in the middle on an attack, then the attack is stopped
self.attacking= False
self.attack_cooldown = 20
def attack(self, surface, target):
if self.attack_cooldown == 0:
self.attacking = True
attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect):
target.health -= 5
target.hit = True
pygame.draw.rect(surface, (0, 255, 0), attacking_rect)
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frameindex = 0
self.update_time = pygame.time.get_ticks()
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
#pygame.draw.rect(surface,(255, 0, 0), self.rect)
surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))

Most likely the sprites are not cropped well from the sprite sheet. If there is random spacing on the cropped sprites (for example: the sprites are different sizes or some sprites have a border at the bottom and some sprites have a border at the top), then you get a jittery and wobbly character.

Related

Pygame Error: loot() takes 2 positional arguments but 3 were given

I've been trying to follow this tutorial to include a coin in the game loot_1.png but when I get to the tutorial part that says ...Add the final line from the following code sample to your loop: and i try to compile my game.py file, the code breaks with the following error in my terminal:
#python game.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
run0(0, 528, 3)
run1(300, 400, 3)
run2(500, 528, 4)
Traceback (most recent call last):
File "game.py", line 252, in <module>
loot_list = Level.loot(1,tx,ty)
TypeError: loot() takes 2 positional arguments but 3 were given
I tried to find a solution by looking at the questions but without success: write() takes 2 positional arguments but 3 were given, TypeError: fit_transform() takes 2 positional arguments but 3 were given, TypeError: hook() takes 2 positional arguments but 3 were given, Pygame TypeError: update() takes 1 positional argument but 2 were given.
game.py
import pygame
import sys
import os
'''
Objects
'''
class Platform(pygame.sprite.Sprite):
# x location, y location, img width, img height, img file
def __init__(self,xloc,yloc,imgw,imgh,img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images',img)).convert()
self.image.convert_alpha()
self.rect = self.image.get_rect()
self.rect.y = yloc
self.rect.x = xloc
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
# gravity variables here
self.collide_delta = 0
self.jump_delta = 6
self.score = 1
self.images = []
for i in range(1,9):
img = pygame.image.load(os.path.join('images','spr' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(ALPHA)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
def jump(self,platform_list):
self.jump_delta = 0
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.movey >= 15:
self.movey = 6
if self.rect.bottom > worldy and self.movey >= 0: # <-- uses bottom
self.movey = 0
self.rect.bottom = worldy # <-- uses bottom
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey += y
def update(self):
'''
Update sprite position
'''
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
for p in plat_hit_list:
self.collide_delta = 0 # stop jumping
self.movey = 0
if self.rect.y > p.rect.y:
self.rect.y = p.rect.y+ty
else:
self.rect.y = p.rect.y-ty
if self.collide_delta < 6 and self.jump_delta < 6:
self.jump_delta = 6*2
self.movey -= 33 # how high to jump
self.collide_delta += 6
self.jump_delta += 6
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > ani*3:
self.frame = 0
self.image = self.images[self.frame//ani]
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > ani*3:
self.frame = 0
self.image = self.images[(self.frame//ani)+4]
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
for p in plat_hit_list:
self.rect.bottom = p.rect.top
self.collide_delta = 0 # <----- this is missing
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
for g in ground_hit_list:
self.movey = 0
self.rect.y = worldy-ty-ty
self.collide_delta = 0 # stop jumping
if self.rect.y > g.rect.y:
self.health -= 1
print(self.health)
class Enemy(pygame.sprite.Sprite):
'''
Spawn an enemy
'''
def __init__(self,x,y,img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images',img))
#self.image.convert_alpha()
#self.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0
def move(self):
'''
enemy movement
'''
distance = 80
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance*2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
class Level():
def bad(lvl,eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
enemy_list = pygame.sprite.Group() # create enemy group
enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return enemy_list
def loot(lvl,lloc):
if lvl == 1:
loot_list = pygame.sprite.Group()
loot = Platform(300,ty*7,tx,ty, 'loot_1.png')
loot_list.add(loot)
if lvl == 2:
print(lvl)
return loot_list
def loot(lvl,lloc):
print(lvl)
def ground(lvl,gloc,tx,ty):
ground_list = pygame.sprite.Group()
i=0
if lvl == 1:
while i < len(gloc):
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
ground_list.add(ground)
i=i+1
if lvl == 2:
print("Level " + str(lvl) )
return ground_list
def platform(lvl,tx,ty):
plat_list = pygame.sprite.Group()
ploc = []
i=0
if lvl == 1:
ploc.append((0,worldy-ty-128,3))
ploc.append((300,worldy-ty-256,3))
ploc.append((500,worldy-ty-128,4))
while i < len(ploc):
j=0
while j <= ploc[i][2]:
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
plat_list.add(plat)
j=j+1
print('run' + str(i) + str(ploc[i]))
i=i+1
if lvl == 2:
print("Level " + str(lvl) )
return plat_list
'''
Setup
'''
worldx = 760
worldy = 720
fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)
ALPHA = (0,255,0)
world = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
forwardx = 600
backwardx = 230
eloc = []
eloc = [200,20]
gloc = []
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
tx = 64 #tile size
ty = 64 #tile size
i=0
while i <= (worldx/tx)+tx:
gloc.append(i*tx)
i=i+1
enemy_list = Level.bad( 1, eloc )
ground_list = Level.ground( 1,gloc,tx,ty )
plat_list = Level.platform( 1,tx,ty )
loot_list = Level.loot(1,tx,ty)
'''
Main loop
'''
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
print("LEFT")
player.control(-steps,0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
print("RIGHT")
player.control(steps,0)
if event.key == pygame.K_UP or event.key == ord('w'):
print('jump')
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps,0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps,0)
if event.key == pygame.K_UP or event.key == ord('w'):
player.jump(plat_list)
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# scroll the world forward
if player.rect.x >= forwardx:
scroll = player.rect.x - forwardx
player.rect.x = forwardx
for p in plat_list:
p.rect.x -= scroll
for e in enemy_list:
e.rect.x -= scroll
# scroll the world backward
if player.rect.x <= backwardx:
scroll = backwardx - player.rect.x
player.rect.x = backwardx
for p in plat_list:
p.rect.x += scroll
## scrolling code above
world.blit(backdrop, backdropbox)
player.gravity() # check gravity
player.update()
player_list.draw(world)
enemy_list.draw(world)
ground_list.draw(world)
plat_list.draw(world)
loot_list.draw(world)
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
content folder images:
For enemy (sprit) is yeti.png: https://imgur.com/GNNcU6z
For background is stage.png: https://imgur.com/YyiEJ0q
and the image of the player:
spr.png: https://imgur.com/1fYXa7Y
and the image of the loot_1.png:
https://opengameart.org/sites/default/files/loot_goldcoins_big.png
This appears to be an error in the tutorial itself.
In the previous part (the one about adding loot), they post this as the loot method:
def loot(lvl,lloc):
if lvl == 1:
loot_list = pygame.sprite.Group()
loot = Platform(300,ty*7,tx,ty, 'loot_1.png')
loot_list.add(loot)
if lvl == 2:
print(lvl)
return loot_list
This method takes 2 arguments (lvl and lloc), which you have included in your code (although twice for some reason, and not indented correctly).
Later on in that same part, as well as the part you linked, the same method is found in the "all the code you've written so far" code dump:
class Level():
...
def loot(lvl,tx,ty):
if lvl == 1:
loot_list = pygame.sprite.Group()
loot = Platform(200,ty*7,tx,ty, 'loot_1.png')
loot_list.add(loot)
if lvl == 2:
print(lvl)
return loot_list
Now, the method takes 3 arguments (lvl, tx, and ty). Based on the contents of the two definitions, and the fact that they are the same between them, I believe the second version is correct (def loot(lvl, tx, ty):...).
Finally, to answer the question, the error you're getting is coming from this discrepancy - you implemented the (incorrect) 2-argument version, while the rest of the tutorial code is expecting the 3-argument version.

Pygame - drop enemies randomly in time [duplicate]

This question already has an answer here:
How can one continuously generate and track several random objects with a time delay in pygame? [duplicate]
(1 answer)
Closed 2 years ago.
I'm trying to design my first game with Python and Pygame to improve my coding skills.
Basically, the player has 4 circles and the goal is to prevent the Cube (or enemies) from reaching the middle of the screen, by killing them with the keyboard arrows.
The game works fine : circles move perfectly, the cubes are going faster as the levels go up, the scoring system is ok, etc...
The last step I can't figure out is to make the Cubes drop a bit more randomly, otherwise it's too easy. Currently, when a cube is killed, it reappear instantly on one of the 4 edges of the screen. I figured out how to position the cube randomly, but the timing doesn't work.
I tried Python sleep, but it freezes the screen. I tried adding a line to specify a delay with "delay = random.random()", and adding "delay <0.1" if condition in an if statement. My last idea was to use a time counter, equals to clock.tick(), and adding a condition : timecounter >3000 in an if statement. All these attempts don't work for me.
Any idea ? Here's my code so far:
# ---------- Packages and Inits ----------
import pygame, random, math, sys
pygame.init()
# ---------- Settings ----------
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
FPS = 60
SPEED = 1
CIRCLE_RADIUS = 50
ENEMY_SIZE = 40
# Colors
RED = (255,000,000)
BLUE = (000,000,255)
YELLOW = (255,255,000)
GREEN = (000,128,000)
BLACK = (000,000,000)
# ---------- Classes ----------
class Enemies:
def __init__( self, x, y, size=ENEMY_SIZE, thick=5, color=BLUE, speed=1, position="top"):
self.rect = pygame.Rect(0, 0, size, size)
if ( x == 0 and y == 0 ):
self.randomise()
self.rect.centerx = x
self.rect.centery = y
self.size = size
self.thick = thick
self.color = color
self.speed = speed
self.calcDirection()
self.position = position
def calcDirection( self ):
self.x_float = 1.0 * self.rect.centerx
self.y_float = 1.0 * self.rect.centery
# Determine direction vector from (x,y) to the centre of the screen
self.position_vector = pygame.math.Vector2( self.x_float, self.y_float )
self.velocity_vector = pygame.math.Vector2( SCREEN_WIDTH/2 - self.x_float, SCREEN_HEIGHT/2 - self.y_float )
self.velocity_vector = self.velocity_vector.normalize()
def update( self ):
x_delta = self.speed * self.velocity_vector[0]
y_delta = self.speed * self.velocity_vector[1]
self.x_float += x_delta
self.y_float += y_delta
self.rect.centerx = int( self.x_float )
self.rect.centery = int( self.y_float )
def draw( self, screen):
pygame.draw.rect(screen, self.color, self.rect )
def reachedPoint( self, x, y ):
return self.rect.collidepoint( x, y )
def randomise( self ):
self.rect.centerx = SCREEN_WIDTH//2
self.rect.centery = SCREEN_HEIGHT//2
side = random.randint( 0, 4 )
if ( side == 0 ):
self.rect.centery = SCREEN_HEIGHT
self.color = GREEN
self.position= "bot"
elif ( side == 1 ):
self.rect.centery = 0
self.color = YELLOW
self.position= "top"
elif ( side == 2 ):
self.rect.centerx = 0
self.color = BLUE
self.position= "left"
else:
self.rect.centerx = SCREEN_WIDTH
self.color = RED
self.position= "right"
self.calcDirection()
def set_speed( self, score):
if score < 25 : self.speed = 0.5
elif score < 50 : self.speed = 0.75
elif score < 100: self.speed = 1
elif score < 250: self.speed = 1.25
elif score < 500: self.speed = 1.5
else: self.speed = 2
return self.speed
class Circle:
def __init__(self, x, y, radius=CIRCLE_RADIUS, thick=5, color=BLUE, speed=SPEED, position="top"):
self.rect = pygame.Rect(0, 0, 2*radius, 2*radius)
self.rect.centerx = x
self.rect.centery = y
self.radius = radius
self.thick = thick
self.color = color
self.speed = speed
self.position = position
if speed >= 0:
self.directionX = 'right'
self.direction = 'up'
else:
self.directionX = 'left'
self.direction = 'down'
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.radius, self.thick)
def swing(self):
if self.position == "top":
self.rect.y -= self.speed
if self.rect.top <= 0 and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
elif self.rect.bottom > int(SCREEN_HEIGHT/2) - self.radius and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
if self.position == "bot":
self.rect.y -= self.speed
if self.rect.top < int(SCREEN_HEIGHT/2) + self.radius and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
elif self.rect.bottom >= SCREEN_HEIGHT and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
if self.position == "left":
self.rect.x -= self.speed
if self.rect.right > int(SCREEN_WIDTH/2) - self.radius and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
elif self.rect.left <= 0 and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
if self.position == "right":
self.rect.x -= self.speed
if self.rect.left < int(SCREEN_WIDTH/2) + self.radius and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
elif self.rect.right >= SCREEN_WIDTH and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
def isCollision(self, enemyX, enemyY, circleX, circleY):
distance = math.sqrt((math.pow(enemyX-circleX,2))+(math.pow(enemyY-circleY,2)))
if distance < 65:
return True
else:
return False
# ---------- Other Functions ----------
def set_number_of_enemies(score):
if score < 25 : number_of_enemies = 3
elif score < 50 : number_of_enemies = 4
elif score < 100: number_of_enemies = 5
elif score < 250: number_of_enemies = 6
elif score < 500: number_of_enemies = 7
else: number_of_enemies = 8
return number_of_enemies
# ---------- Main ----------
def main():
# Settings
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
myFont = pygame.font.SysFont("monospace", 25)
clock = pygame.time.Clock()
game_over = False
# Variables
lifes = 5
score = 0
number_of_enemies = 3
# We create an empty list of enemies, as we want them to drop randomly
all_enemies = []
# Start with 4 circles
all_circles = [
Circle ( screen_rect.centerx , screen_rect.centery - 2*CIRCLE_RADIUS , position = "top" ),
Circle ( screen_rect.centerx , screen_rect.centery + 2*CIRCLE_RADIUS , position = "bot" ),
Circle ( screen_rect.centerx + 2*CIRCLE_RADIUS , screen_rect.centery , position = "right"),
Circle ( screen_rect.centerx - 2*CIRCLE_RADIUS , screen_rect.centery , position = "left" )]
while not game_over:
screen.fill(BLACK) # This has to be inside the while not game_over loop
time_counter = clock.tick()
print(time_counter)
# Circles
for c in all_circles:
c.draw(screen) # Place circles on the screen
c.swing() # Move circles from center to edges, and from edges to center, back and forth
# Set number of enemies
number_of_enemies = set_number_of_enemies(score) # Get the number of enemies we want, based on the score
if len(all_enemies) < number_of_enemies and time_counter > 3000: # Add if necessary
all_enemies.append(Enemies(int(SCREEN_WIDTH/2), 0, color = YELLOW, position = "top")) # We add a top enemy, but we randomise it right after
for e in all_enemies:
e.randomise()
time_counter = 0
# Enemies
for e in all_enemies:
e.draw(screen) # Place enemies on the screen
e.set_speed(score) # Set speed difficulty for enemies
e.update() # Move enemies from the edges of the screen towards the center
if ( e.reachedPoint( SCREEN_WIDTH//2, SCREEN_HEIGHT//2 ) ): # If the enemy reaches the middle, you lose a lifepoint and a new enemy is generated
lifes -=1
e.randomise()
# Scoring and lifepoints systems
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
for c in all_circles:
if event.type == pygame.KEYDOWN:
# LEFT
if event.key == pygame.K_LEFT and c.position == "left":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# RIGHT
if event.key == pygame.K_RIGHT and c.position == "right":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# TOP
if event.key == pygame.K_UP and c.position == "top":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# BOT
if event.key == pygame.K_DOWN and c.position == "bot":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# Game Over condition
if lifes == 0:
game_over = True
# Score / Lifes / Number of Enemies
print_lifes = myFont.render("Lifes:" + str(round(lifes)), 1, RED)
screen.blit(print_lifes, (10, SCREEN_HEIGHT-50))
print_score = myFont.render("Score:" + str(round(score)), 1, RED)
screen.blit(print_score, (10, 10))
print_enemies = myFont.render("# of Enemies:" + str(round(number_of_enemies)), 1, RED)
screen.blit(print_enemies, (10, 60))
pygame.display.update()
clock.tick(FPS)
main()
pygame.quit()
I recommend to use a timer event. Use pygame.time.set_timer() to repeatedly create an USEREVENT. e.g.:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to start at pygame.USEREVENT. In this case pygame.USEREVENT+1 is the event id for the timer event, which spawns the enemies.
Create a new enemy when the event occurs in the event loop:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
while not game_over:
# [...]
# Scoring and lifepoints systems
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == enemy_spawn_event and len(all_enemies) < number_of_enemies:
all_enemies.append(Enemies(int(SCREEN_WIDTH/2), 0, color = YELLOW, position = "top"))
for e in all_enemies:
e.randomise()
time_counter = 0

Pygame slow after python update

I am making a python game with pygame on my home computer for school running with python 3.6.3 and pygame 1.9.3. When working on my game I also worked at school where it was working correctly just slower as expected but when I started to load images it randomly choose the images and didn't load the correct ones on school computer not home one (I think this might be due to change in arrays and loading them). I soon found I was running python 3.5.3 at school and then when updating it the python and pygame, The game ran extremely slow and was unplayable. The images loaded correctly but the change in speed was extreme.
Summary:
Pygame at home working great pygame 1.9.3 python 3.6.3.
Working fine at school until loading images python 3.5.3.
Updating school corrected it but is now running unbearably slow.
I would love a fix for this problem either working for other python version or running at a variable speed for school computers.
If there is any optimisation that could be easily implemented that would be very useful as well as I am not sure how to load images effectively
Code:
import pygame
import random
import math
def main():
#load images?:
bg = pygame.image.load("background6.png")
wall = 100
zombies = []
currentnumber = 0
gamepaused = False
moveupx = False
movedownx = False
moveupy = False
movedowny = False
movex = 0
movey = 0
movespeed = 10
time10 = False
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
blue = (0, 0, 255)
fullscreen = pygame.FULLSCREEN
pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 100)
clock = pygame.time.Clock()
clock.tick(30)
#creating sprite groups
heads = pygame.sprite.Group()
bodys = pygame.sprite.Group()
bullets = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
players = pygame.sprite.Group()
playerr = Player()
allsprites.add(playerr)
players.add(playerr)
heads.add()
bodys.add()
gameDisplay = pygame.display.set_mode((1920, 1080), fullscreen)
pygame.display.set_caption("Last Stand")
gameExit = False
pygame.display.update()
#This will get the ticks from inisilising pygame in ms
pretime = pygame.time.get_ticks()
while not gameExit:
time = pygame.time.get_ticks()-pretime
#print (time/1000)
timerounded = round(time/1000)
timedisplay = round(time/1000,1)
spawnbullet = False
mouse = pygame.mouse.get_pos()
# background things
gameDisplay.fill(white)
gameDisplay.blit(bg, (0, 0))
if timerounded % 10 == 0:
if timerounded != currentnumber:
time10 = True
currentnumber = timerounded
else:
time10 = False
#start gameimput look e is event and it loops for each event in pygame
#IT WILL GO THOUGH EACH EVEN!!!!!!! SO CHEACK FOR EACH EVENT
for e in pygame.event.get():
if e.type == pygame.MOUSEBUTTONDOWN:
#so only if it will shoot foward NEED TO ADD FEEDBACK LATER
if mouse[0] < 1500:
spawnbullet = True
#so the problem is that if there is a key up for s then it would kill the key down for w as it would be set
# to 0
if e.type == pygame.KEYDOWN and e.key == pygame.K_w:
moveupy = True
if e.type == pygame.KEYDOWN and e.key == pygame.K_s:
movedowny = True
if e.type == pygame.KEYUP and e.key == pygame.K_w:
moveupy = False
if e.type == pygame.KEYUP and e.key == pygame.K_s:
movedowny = False
if e.type == pygame.KEYDOWN and e.key == pygame.K_a:
moveupx = True
if e.type == pygame.KEYDOWN and e.key == pygame.K_d:
movedownx = True
if e.type == pygame.KEYUP and e.key == pygame.K_a:
moveupx = False
if e.type == pygame.KEYUP and e.key == pygame.K_d:
movedownx = False
if e.type == pygame.QUIT:
gameExit = True
if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE:
gameDisplay = pygame.display.set_mode((1920, 1080), fullscreen)
gameExit = True
# CAHNGE BACK LATER
# END event loop
#LOGIC FOR MOVING THE PLAYER:
if moveupy == True:
movey = -movespeed
if movedowny == True:
movey = movespeed
if movedowny == False and moveupy == False:
movey = 0
if moveupx == True:
movex = -movespeed
if movedownx == True:
movex = movespeed
if movedownx == False and moveupx == False:
movex = 0
# Updating player have to do this at the start as need the angle and the top left cords
for player in players:
#REALLY NOT WOKRING THE MOVEX DOESNT WORK AND THE CHANGING HTE X AND WHY DOESNT WORK!!!
player.move(movex, movey)
angle, topleft = player.update(mouse)
# needed another name for bullet so went for bullett
if spawnbullet == True:
bullett = Bullet(angle, topleft)
#having the bullets only in the bullets class so it can render after the player
bullets.add(bullett)
#creating zombies
if random.randint(0,2) == 1:
ztype = "fast"
else:
ztype = "slow"
#so at the start it spawns one for sure
if time == 0:
startlocation = [5, random.randint(320, 920)]
zombies, heads, bodys = createz(zombies, ztype, startlocation, heads, bodys)
if random.randint(0,10000) >9950-(timerounded**2): #So the time incraeed the odds expernetray
startlocation = [5, random.randint(320, 920)]
# WOULD LIKE THE ZOMBIES TO RUN FASTER AS THE GAME GOES ON!
zombies, heads, bodys = createz(zombies, ztype, startlocation, heads, bodys)
#updating Z
zombies, totaldamnge = updatez(zombies, heads, bodys)
#updating bullets
for bullet in bullets:
bullet.update()
#print(totaldamnge)
wall -= totaldamnge
#Cheacking for hitting bullets
colstions(zombies, heads, bodys, bullets)
if time10 == True:
wall += 25
if wall >100: wall = 100
walltext = myfont.render(str(wall)+"%",False, (0, 0, 0))
gameDisplay.blit(walltext,(1600,100))
#print(wall)
timedisplayer = myfont.render(str(timedisplay) + "Seconds", False, (0, 0, 0))
gameDisplay.blit(timedisplayer, (10, 5))
#drawing:
#so cant use allsprites so might just wont have zomnbie head and zombie body in it
#I CAN CHANGE WHEN WHAT IS DRAW AND WHAT IS ON WHAT
bullets.draw(gameDisplay)
allsprites.draw(gameDisplay)
heads.draw(gameDisplay)
bodys.draw(gameDisplay)
pygame.display.update()
clock.tick(30)
def colstions(zombies, heads, bodys, bullets):
#so as having both hp and having the zombie having a head and body with 2 diffrent hitboxes
headdamage = 140
bodydamge = 60
#so this part had sooooooo many proplems frist with like all the while loops next spend a long time trying to find what was wrong when it was just the head in bodys and vis versa
if len(pygame.sprite.groupcollide(bullets, heads,False, False)) != 0 or len(pygame.sprite.groupcollide(bullets, bodys,False, False)) != 0:
for bullet in bullets:
hitthehead = False
hitthebody = False
zheadkilledzombie = False
zbodykilledzombie = False
counter1 = 0
counter2 = 0
for head in heads:
#so it doesnt make the bullet hit more than one zombie head
if hitthehead == False:
if pygame.sprite.collide_circle(bullet, head):
hp = zombies[counter1].hpordead(headdamage)
hitthehead = True
if hp <= 0:
zheadkilledzombie = True
head.kill()
print("hithead")
counter1 += 1
#so it doesnt check for body hits if it has hit a heah
if hitthehead == False:
for body in bodys:
if hitthebody == False:
#If it colldes and if the zombie is not already dead from the head shots
if pygame.sprite.collide_rect(bullet, body):
print("hitbody")
hp = zombies[counter2].hpordead(bodydamge)
hitthebody = True
if hp <= 0:
zbodykilledzombie = True
body.kill()
counter2 += 1
if hitthehead == True or hitthebody == True:
bullet.kill()
#so killing the head if the body hit killed the zombie
if zheadkilledzombie == True:
zombiekilled = False
counter3 = 0
for body in bodys:
if zombiekilled == False:
hp = zombies[counter3].hpordead(0)
if hp <= 0:
body.kill()
zombies.pop(counter3)
zombiekilled = True
counter3 += 1
#killing the body if killing is arealy dead
if zbodykilledzombie == True:
zombiekilled = False
counter4 = 0
for head in heads:
if zombiekilled == False:
hp = zombies[counter4].hpordead(0)
if hp <= 0:
head.kill()
zombies.pop(counter4)
zombiekilled = True
counter4 += 1
def createz(zombies, ztype, startlocation, heads, bodys, ):
createdzombie = Zombie(startlocation, ztype)
zombies.append(createdzombie)
Hcords = (startlocation[0]+20, startlocation[1]-57)
Bcords = (startlocation[0], startlocation[1])
#need to have it also say what type of z it is.
heads.add(head(Hcords,ztype))
bodys.add(body(Bcords,ztype))
print ("created")
#dont think i need to return?
return zombies, heads, bodys
def updatez(zombies, heads, bodys):
totaldamnge = 0
#somthing ent working
Hcordsx = []
Hcordsy = []
Bcordsx = []
Bcordsy = []
ATWALLupdateanimationN = []
for x in range(len(zombies)):
cords, walldamge, updateanimationN = zombies[x].update()
ATWALLupdateanimationN.append(updateanimationN)
totaldamnge += walldamge
#putting all the cords into arrays just after reading them
Hcordsx.append(cords[0]+ 20)
Hcordsy.append(cords[1] - 57)
Bcordsx.append(cords[0])
Bcordsy.append(cords[1])
counter = 0
for body in bodys:
Bcords = (Bcordsx[counter], Bcordsy[counter])
body.update(Bcords, ATWALLupdateanimationN[counter])
counter += 1
counter = 0
#I have to use the counter as cant do 1 per 1 on the head in heads thing so have to have a seperat counter
for head in heads:
Hcords = (Hcordsx[counter],Hcordsy[counter])
head.update(Hcords,ATWALLupdateanimationN[counter])
counter += 1
return zombies, totaldamnge
class Bullet(pygame.sprite.Sprite):
def __init__(self,angle, playerpos):
pygame.sprite.Sprite.__init__(self)
#Chanig the scale of the image so it doesnt stick out the gun too much
self.image = pygame.transform.rotozoom(pygame.image.load("bulletpixel 6.png"), angle, 0.8)
self.rect = self.image.get_rect()
self.speed = 60
self.angle = angle
self.rect.x = (self.rect.x + playerpos[0])
self.rect.y = (self.rect.y + playerpos[1])
def update(self):
#moging it depending on its angle. so spliting up to horizontal and vertical bny taking thee cos and sin of the angle
self.rect.x -= int(self.speed*math.cos(-self.angle*math.pi/180))
self.rect.y -= int(self.speed*math.sin(-self.angle*math.pi/180))
#could make this better for so when top of screen also deleate
if self.rect.x <= -5:
pygame.sprite.Sprite.kill(self)
if self.rect.y <= -20 or self.rect.y >= 2000:
pygame.sprite.Sprite.kill(self)
def kill(self):
pygame.sprite.Sprite.kill(self)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.angle = 0
#for now sttg to to zombie as didnt save the player image
self.image = pygame.image.load("player2.png")
self.rect = self.image.get_rect()
#need to create an image for player.
#ok so got too choess so i could if e.type == MOUSEMOTION:
#and just have less than more than or like so i can 3 locations it is pointed or could have the player angle towards the mouse pointer and also the shotsneed to be angled anywhay so.
self.rect.x = 1600
self.rect.y = 600
self.center = self.image.get_rect().center
#setting the starting location
self.movingamoutx = 1640
self.movingamouty = 500
self.posforbullet = (1640, 500)
def update(self,mouse):
# SO THIS THIS IS MAD
#Code for making the player follow the mouse:
#First sets the image to the stating image if this is not here the image gets bugged out from rotating many times
self.image = pygame.image.load("player2.png")
#Taking the x and y apart from the mouse
mouse_x, mouse_y = mouse
#calulationg the relative position of the mouse by subtracting the player postion
#I subtracke the previs frame player pos to calulate the new anlge as that is the only way i can get the angle from the gun and not the player and so it will go to the curcer
#This does in fact make it be one frame behind for the angle but as it is 30 fps this will not make a differnce
#subtracking 10 so it comes from a but up from the center and so is on the cetner of the curcer
rel_x, rel_y = mouse_x - self.posforbullet[0], mouse_y - self.posforbullet[1]-10
#calqulating the angle by using atan2
#this takes 2 vectors and calclated the angle inbeween them NOT 100 ON THIS this is in rad so it needs to be degress so is changed to degress by timesing my 180/ pi
# also Return atan(y / x), in radians. The result is between -pi and pi. The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis.
#The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4.
self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
#the angle is inverted so to make it be where it is pointed subtracted 180 degress
self.angle -= 180
#seting self.rotated to the image rotated to by the angle
self.rotated = pygame.transform.rotate(self.image, self.angle)
#getting the cords of the new rotated image
rotRect = self.rotated.get_rect()
#keeping the old center from before
rotRect.center = self.rotated.get_rect().center
#setting the new cords to image rotated cords, the adding is the amout that is getting moved by
self.rect = (rotRect[0]+ self.movingamoutx), (rotRect[1]+ self.movingamouty)
#setting the image to the new rotated one
self.image = self.rotated
#Creating a variable basited of the center of the rotated image so the bullet can shoot from the image then
#adding the move amouts to put it into spot then subtracting to make it really shoot from the barrel and not the
#true center
#self.posforbullet = (rotRect.center[0] + self.movingamoutx-int(math.cos(self.changedangle)*20) ,rotRect.center[1]+ self.movingamouty-40)
self.posforbullet = (rotRect.center[0] + self.movingamoutx-15 ,rotRect.center[1]+ self.movingamouty-45)
return self.angle, self.posforbullet
def move(self,movex, movey):
self.movingamoutx += movex
self.movingamouty += movey
#cheaking to see if the player is trying to go to far away
if self.movingamouty <= 320:
self.movingamouty = 320
elif self.movingamouty >= 840:
self.movingamouty = 840
if self.movingamoutx >= 1700:
self.movingamoutx = 1700
elif self.movingamoutx <= 1500:
self.movingamoutx = 1500
class head(pygame.sprite.Sprite):
def __init__(self,cords, type):
pygame.sprite.Sprite.__init__(self)
if type == "fast":
self.type = "fast"
self.image = pygame.transform.scale(pygame.image.load("fasthead.png"),[70,60])
else:
self.type = "slow"
self.image = pygame.transform.scale(pygame.image.load("slowhead.png"),[70,60])
self.rect = self.image.get_rect()
self.rect.x = cords[0]
self.rect.y = cords[1]
self.ticks = 0
self.putheadonbodyx = -11
self.putheadonbodyy = -2
self.firsttimewall = True
def update(self,newcords,updateanimationNumber):
self.ticks += 1
if updateanimationNumber != 100:
if self.firsttimewall == True:
self.firsttimewall = False
updateanimationNumber = 1
if self.type == "slow":
if updateanimationNumber == 2:
self.image = pygame.image.load("satwallhead1.png")
elif updateanimationNumber == 1:
self.image = pygame.image.load("satwallhead2.png")
if self.type == "fast":
if updateanimationNumber == 2:
self.image = pygame.image.load("fatwallhead1.png")
elif updateanimationNumber == 1:
self.image = pygame.image.load("fatwallhead2.png")
self.rect = self.image.get_rect()
self.rect.x = newcords[0] + self.putheadonbodyx
self.rect.y = newcords[1] + self.putheadonbodyy
def kill(self):
pygame.sprite.Sprite.kill(self)
class body(pygame.sprite.Sprite):
def __init__(self, cords, type):
pygame.sprite.Sprite.__init__(self)
if type == "fast":
self.type = "fast"
self.image = pygame.image.load("fastbody1.png")
self.tickamout = 10
else:
self.type = "slow"
self.image = pygame.image.load("slowbody1.png")
self.tickamout = 15
self.rect = self.image.get_rect()
self.rect.x = cords[0]
self.rect.y = cords[1]
self.ticks = 0
self.number = 0
self.firsttimewall = True
def update(self, newcords, Atwallnumber):
self.ticks += 1
if Atwallnumber == 100:
#This is so it only trys to change image it if is time to changer
if self.ticks % self.tickamout == 0:
self.number += 1
if self.number == 5:
self.number = 1
if self.type == "slow":
if self.number == 1:
self.image = pygame.image.load("slowbody1.png")
elif self.number == 2:
self.image = pygame.image.load("slowbody2.png")
elif self.number == 3:
self.image = pygame.image.load("slowbody3.png")
elif self.number == 4:
self.image = pygame.image.load("slowbody4.png")
elif self.type == "fast":
if self.number == 1:
self.image = pygame.image.load("fastbody1.png")
elif self.number == 2:
self.image = pygame.image.load("fastbody2.png")
elif self.number == 3:
self.image = pygame.image.load("fastbody3.png")
elif self.number == 4:
self.image = pygame.image.load("fastbody4.png")
self.rect = self.image.get_rect()
else:
self.atwall(Atwallnumber)
#Dont think that chaning the codes from the get rect will be too much of a problem and also could save the
#images beofre having ot load them
self.rect.x = newcords[0]
self.rect.y = newcords[1]
def atwall(self,updateanimationNumber):
#seeing if this is the first time
if self.firsttimewall == True:
self.firsttimewall = False
updateanimationNumber = 1
if self.type == "slow":
if updateanimationNumber == 2:
self.image = pygame.image.load("satwallbody1.png")
elif updateanimationNumber == 1:
self.image = pygame.image.load("satwallbody2.png")
else:
if updateanimationNumber == 2:
self.image = pygame.image.load("fatwallbody1.png")
elif updateanimationNumber == 1:
self.image = pygame.image.load("fatwallbody2.png")
self.rect = self.image.get_rect()
def kill(self):
pygame.sprite.Sprite.kill(self)
class Zombie():
def __init__(self, startlocation, ztype):
if ztype == "fast":
self.speed = 11
self.hp = 100
else:
self.speed = 6
self.hp = 200
self.location = startlocation
self.walldamage = 0
self.ticks = 0
#So it will stay 100 before it gets to the wall
self.updateanimationnumber = 100
def update(self):
if self.location[0] < 1300: #the y direction
self.walldamage = 0
self.location[0] += self.speed #as this should be 30 frams a second it should move 3 pixies each time
#so if at the wall
elif self.location[0] >= 1300:
self.walldamage = 0
#cjamge the % number to change how fast the wall dies
if self.ticks % 30 == 0:
self.walldamage = 5
self.updateanimationnumber = 1
#so it will have to hitting animation when dealing damgage
elif self.ticks % 15 == 0:
self.updateanimationnumber = 2
if self.ticks % 15 != 0:
self.updateanimationnumber = 0
self.ticks += 1
return self.location, self.walldamage, self.updateanimationnumber
def hpordead(self,subtrackhp):
self.hp -= subtrackhp
return self.hp
if __name__ == '__main__':
main()
You should load the images in the global scope or another module and import them and then reuse them in your program. Do not load them in your functions or __init__ methods that get called in the while loop all the time or when you create an instance. Reading from the hard disk is slow.
Also, use the convert or convert_alpha methods of surfaces to improve the performance (converted surfaces get blitted faster), e.g.:
# All uppercase letters indicate that it's a constant and should never be changed.
MY_IMAGE = pygame.image.load('my_image.png').convert() # Or `.convert_alpha()`
# Load the images once at program start. You can also do that in
# another module and import them or load all images automatically
# and put them into a dictionary.
PLAYER_IMAGE = pygame.image.load("player2.png").convert_alpha()
BULLET_IMAGE = pygame.image.load("bulletpixel 6.png").convert_alpha()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = PLAYER_IMAGE # Either assign the global image directly ...
#-----------------------------------
# or pass the image to __init__.
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
self.image = image
player = Player(PLAYER_IMAGE)
In the Bullet class you can rotate the original image. transform.rototzoom creates a new surface/image and doesn't modify the original.
class Bullet(pygame.sprite.Sprite):
def __init__(self, angle, playerpos):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.rotozoom(BULLET_IMAGE, angle, 0.8)
You load images too many times. Load them only once in __init__ and later assign from one variable to another.
For example in Player load image to self.original and later assign
self.image = self.original
or
self.image = pygame.transform.rotate(self.original, self.angle)
Similar with
self.number += 1
if self.number == 5:
self.number = 1
if self.type == "slow":
if self.number == 1:
self.image = pygame.image.load("slowbody1.png")
elif self.number == 2:
self.image = pygame.image.load("slowbody2.png")
elif self.number == 3:
self.image = pygame.image.load("slowbody3.png")
elif self.number == 4:
self.image = pygame.image.load("slowbody4.png")
elif self.type == "fast":
if self.number == 1:
self.image = pygame.image.load("fastbody1.png")
elif self.number == 2:
self.image = pygame.image.load("fastbody2.png")
elif self.number == 3:
self.image = pygame.image.load("fastbody3.png")
elif self.number == 4:
self.image = pygame.image.load("fastbody4.png")
load all images in __init__ to two list
self.slowimages = [
pygame.image.load("slowbody1.png"),
pygame.image.load("slowbody2.png"),
pygame.image.load("slowbody3.png"),
pygame.image.load("slowbody4.png"),
]
self.fastimages = [
pygame.image.load("fastbody1.png"),
pygame.image.load("fastbody2.png"),
pygame.image.load("fastbody3.png"),
pygame.image.load("fastbody4.png"),
]
and later you can assign image without if/elif using one line
if self.type == "slow":
self.image = self.slowimages[self.number - 1]
elif self.type == "fast":
self.image = self.fastimages[self.number - 1]
or even load all in one dictionary with two lists
self.all_images = {
"slow": [
pygame.image.load("slowbody1.png"),
pygame.image.load("slowbody2.png"),
pygame.image.load("slowbody3.png"),
pygame.image.load("slowbody4.png"),
],
"fast": [
pygame.image.load("fastbodu1.png"),
pygame.image.load("fastbodu2.png"),
pygame.image.load("fastbodu3.png"),
pygame.image.load("fastbodu4.png"),
],
]
and later you can assign image without if/elif using one line
self.image = self.all_images[self.type][self.number - 1]
BTW in self.number you could keep values 0..3 instead of 1..4 and then in self.all_images[self.type][self.number - 1] you could use self.number instead of self.number - 1
And then instead of
self.number += 1
if self.number == 5:
self.number = 1
you can use modulo and get one line
self.number = (self.number + 1) % 4
Instead of
self.rect.x = newcords[0]
self.rect.y = newcords[1]
you can do in one line
self.rect.topleft = newcords

Pygame Collision with variable speed

it would be really amazing if you could help us with this problem . We're currently working on a game project at school and we want to create a Bomberman-like labyrinth game.
The problem is the following:
the position and the speed of the bombs depend on the mouse which is tracked using float values but is getting way too high if you click far away so it can skip collision detection because it just goes too fast at times. Now there are some other weird errors when you line up the bomb with the bottom of a block and some other collisions.
Is the method colliderect the error ? Do we need to check for each site or are there any better solutions to this problem. We also thought about just using 4 directions but even that didn't work out properly.
I really hope somebody out there can help.
class Block( pygame.sprite.Sprite):
def __init__(self,color = SCHWARZ, width = 40, height = 40, movable = 0):
super().__init__()
self.width = width
self.height = height
self.color = color
self.movable = movable
self.image = pygame.Surface((width,height))
self.image.fill(color)
self.properties()
def properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_pos(self, x, y):
self.rect.x = x
self.rect.y = y
def render(self,window):
pygame.draw.rect(window,self.color,(self.rect.x,self.rect.y,self.width,self.height))
class Bomb(Block):
def __init__(self,player, target_x, target_y,countdown,zeit,color = BLAU):
super().__init__(color, width = 20, height = 20)
self.target_x = round(target_x)
self.target_y = round(target_y)
self.countdown = countdown
self.zeit = zeit
self.player_x = player.rect.left + player.width/2
self.player_y = player.rect.top + player.height/2
self.set_pos(player.rect.center[0]-self.width/2,player.rect.center[1]-self.height/2)
self.truepos = list(self.rect.center)
self.setspeed(3)
def setspeed(self,factor):
self.speed_x = (self.target_x - self.player_x)/100
self.speed_y = (self.target_y - self.player_y)/100
"""self.direction = [0,0]
if self.target_x - self.player_x < 0:
self.direction[0] = -1
elif self.target_x - self.player_x > 0:
self.direction[0] = 1
if self.target_y - self.player_y > 0:
self.direction[1] = 1
elif self.target_y - self.player_y < 0:
self.direction[1] = -1
self.speed_x = self.direction[0]* factor
self.speed_y = self.direction[1]* factor
print(self.speed_x)
print(self.speed_y)"""
def move(self):
if self.speed_x != 0:
self.collision()
if self.speed_y != 0:
self.collision()
def update(self):
self.move()
if self.countdown > 0:
self.countdown -= self.zeit
elif self.countdown <= 0:
bomblist_list.remove(self)
def collision(self):
for block in block_list:
if block.movable != 1:
if self.rect.colliderect(block.rect):
self.distance = [abs(block.rect.centerx - (self.truepos[0] + self.speed_x)), abs(block.rect.centery - (self.truepos[1] + self.speed_y))]
if self.distance[0] > self.distance[1]:
if self.speed_x < 0:
self.rect.left = block.rect.right - self.speed_x
elif self.speed_x > 0:
self.rect.right = block.rect.left -self.speed_x
self.speed_x = -self.speed_x
elif self.distance[0] < self.distance[1]:
if self.speed_y < 0:
self.rect.top = block.rect.bottom - self.speed_y
elif self.speed_y > 0:
self.rect.bottom = block.rect.top - self.speed_y
self.speed_y = -self.speed_y
self.truepos[0] += self.speed_x
self.truepos[1] += self.speed_y
self.rect.center = self.truepos
# -------- Main Program Loop -----------
while not done:
clock.tick(fps)
millisekunde = float(clock.tick(fps))
zeit = millisekunde /500
vergangen += zeit
# --- Main event loop
for event in pygame.event.get(): # User macht irgendwas
if event.type == pygame.QUIT: # Wenn der User Quit klickt
done = True # Verlässt die Schleife
elif event.type == pygame.KEYDOWN:
pass
#if event.key == pygame.K_UP:
#if e
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
mx,my = pygame.mouse.get_pos()
bomb = Bomb(player,mx,my,3,zeit)
bomblist_list.append(bomb)
planted = True
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.move(speedx,0)
if keys[pygame.K_LEFT]:
player.move(-speedx,0)
if keys[pygame.K_DOWN]:
player.move(0,speedy)
if keys[pygame.K_UP]:
player.move(0,-speedy)
if planted:
for bomb in bomblist_list:
bomb.update()
screen.fill(SCHWARZ)
for block in block_list:
block.render(screen)
if planted:
for bomb in bomblist_list:
bomb.render(screen)
pygame.draw.rect(screen,red,player.rect)
pygame.display.update()
pygame.quit()
When checking collision on fast moving items that can pass an object in one frame, what you want to do is store it's position on the last frame and get it's position on the current frame. With this you can check if a it should have collided with anything between the last and current frame.
Then you can amend it's position by putting it back to where it should have stopped and subsequently draw the object.

Add collison detection to enemy sprites?

i'd like to add the same collision detection used by the player sprite to the enemy sprites or 'creeps' ive added all the relevant code I can see yet collisons are still not being detected and handled, please find below the class, I have no idea what is wrong currently, the list of walls to collide with is 'wall_list'
import pygame
import pauseScreen as dm
import re
from pygame.sprite import Sprite
from pygame import Rect, Color
from random import randint, choice
from vec2d import vec2d
from simpleanimation import SimpleAnimation
import displattxt
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
green = (101,194,151)
global currentEditTool
currentEditTool = "Tree"
global editMap
editMap = False
open('MapMaker.txt', 'w').close()
def draw_background(screen, tile_img):
screen.fill(black)
img_rect = tile_img.get_rect()
global rect
rect = img_rect
nrows = int(screen.get_height() / img_rect.height) + 1
ncols = int(screen.get_width() / img_rect.width) + 1
for y in range(nrows):
for x in range(ncols):
img_rect.topleft = (x * img_rect.width,
y * img_rect.height)
screen.blit(tile_img, img_rect)
def changeTool():
if currentEditTool == "Tree":
None
elif currentEditTool == "Rock":
None
def pauseGame():
red = 255, 0, 0
green = 0,255, 0
blue = 0, 0,255
screen.fill(black)
pygame.display.update()
if editMap == False:
choose = dm.dumbmenu(screen, [
'Resume',
'Enable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("hi")
elif choose ==1:
global editMap
editMap = True
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
else:
choose = dm.dumbmenu(screen, [
'Resume',
'Disable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("Resume")
elif choose ==1:
print("Dis ME")
global editMap
editMap = False
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
class Wall(pygame.sprite.Sprite):
# Constructor function
def __init__(self,x,y,width,height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(green)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertTree(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/tree.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertRock(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/rock.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class Creep(pygame.sprite.Sprite):
""" A creep sprite that bounces off walls and changes its
direction from time to time.
"""
change_x=0
change_y=0
def __init__(
self, screen, creep_image, explosion_images,
field, init_position, init_direction, speed):
""" Create a new Creep.
screen:
The screen on which the creep lives (must be a
pygame Surface object, such as pygame.display)
creep_image:
Image (surface) object for the creep
explosion_images:
A list of image objects for the explosion
animation.
field:
A Rect specifying the 'playing field' boundaries.
The Creep will bounce off the 'walls' of this
field.
init_position:
A vec2d or a pair specifying the initial position
of the creep on the screen.
init_direction:
A vec2d or a pair specifying the initial direction
of the creep. Must have an angle that is a
multiple of 45 degres.
speed:
Creep speed, in pixels/millisecond (px/ms)
"""
Sprite.__init__(self)
self.screen = screen
self.speed = speed
self.field = field
self.rect = creep_image.get_rect()
# base_image holds the original image, positioned to
# angle 0.
# image will be rotated.
#
self.base_image = creep_image
self.image = self.base_image
self.explosion_images = explosion_images
# A vector specifying the creep's position on the screen
#
self.pos = vec2d(init_position)
# The direction is a normalized vector
#
self.direction = vec2d(init_direction).normalized()
self.state = Creep.ALIVE
self.health = 15
def is_alive(self):
return self.state in (Creep.ALIVE, Creep.EXPLODING)
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self, time_passed, walls):
""" Update the creep.
time_passed:
The time passed (in ms) since the previous update.
"""
if self.state == Creep.ALIVE:
# Maybe it's time to change the direction ?
#
self._change_direction(time_passed)
# Make the creep point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
#
self.image = pygame.transform.rotate(
self.base_image, -self.direction.angle)
# Compute and apply the displacement to the position
# vector. The displacement is a vector, having the angle
# of self.direction (which is normalized to not affect
# the magnitude of the displacement)
#
displacement = vec2d(
self.direction.x * self.speed * time_passed,
self.direction.y * self.speed * time_passed)
self.pos += displacement
# When the image is rotated, its size is changed.
# We must take the size into account for detecting
# collisions with the walls.
#
self.image_w, self.image_h = self.image.get_size()
bounds_rect = self.field.inflate(
-self.image_w, -self.image_h)
if self.pos.x < bounds_rect.left:
self.pos.x = bounds_rect.left
self.direction.x *= -1
elif self.pos.x > bounds_rect.right:
self.pos.x = bounds_rect.right
self.direction.x *= -1
elif self.pos.y < bounds_rect.top:
self.pos.y = bounds_rect.top
self.direction.y *= -1
elif self.pos.y > bounds_rect.bottom:
self.pos.y = bounds_rect.bottom
self.direction.y *= -1
# collision detection
old_x=bounds_rect.left
new_x=old_x+self.direction.x
bounds_rect.left = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
bounds_rect.left=old_x
old_y=self.pos.y
new_y=old_y+self.direction.y
self.pos.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.pos.y=old_y
elif self.state == Creep.EXPLODING:
if self.explode_animation.active:
self.explode_animation.update(time_passed)
else:
self.state = Creep.DEAD
self.kill()
elif self.state == Creep.DEAD:
pass
#------------------ PRIVATE PARTS ------------------#
# States the creep can be in.
#
# ALIVE: The creep is roaming around the screen
# EXPLODING:
# The creep is now exploding, just a moment before dying.
# DEAD: The creep is dead and inactive
#
(ALIVE, EXPLODING, DEAD) = range(3)
_counter = 0
def _change_direction(self, time_passed):
""" Turn by 45 degrees in a random direction once per
0.4 to 0.5 seconds.
"""
self._counter += time_passed
if self._counter > randint(400, 500):
self.direction.rotate(45 * randint(-1, 1))
self._counter = 0
def _point_is_inside(self, point):
""" Is the point (given as a vec2d) inside our creep's
body?
"""
img_point = point - vec2d(
int(self.pos.x - self.image_w / 2),
int(self.pos.y - self.image_h / 2))
try:
pix = self.image.get_at(img_point)
return pix[3] > 0
except IndexError:
return False
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def _explode(self):
""" Starts the explosion animation that ends the Creep's
life.
"""
self.state = Creep.EXPLODING
pos = ( self.pos.x - self.explosion_images[0].get_width() / 2,
self.pos.y - self.explosion_images[0].get_height() / 2)
self.explode_animation = SimpleAnimation(
self.screen, pos, self.explosion_images,
100, 300)
global remainingCreeps
remainingCreeps-=1
if remainingCreeps == 0:
print("all dead")
def draw(self):
""" Blit the creep onto the screen that was provided in
the constructor.
"""
if self.state == Creep.ALIVE:
# The creep image is placed at self.pos. To allow for
# smooth movement even when the creep rotates and the
# image size changes, its placement is always
# centered.
#
self.draw_rect = self.image.get_rect().move(
self.pos.x - self.image_w / 2,
self.pos.y - self.image_h / 2)
self.screen.blit(self.image, self.draw_rect)
# The health bar is 15x4 px.
#
health_bar_x = self.pos.x - 7
health_bar_y = self.pos.y - self.image_h / 2 - 6
self.screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
self.screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
elif self.state == Creep.EXPLODING:
self.explode_animation.draw()
elif self.state == Creep.DEAD:
pass
def mouse_click_event(self, pos):
""" The mouse was clicked in pos.
"""
if self._point_is_inside(vec2d(pos)):
self._decrease_health(3)
#begin new player
class Player(pygame.sprite.Sprite):
change_x=0
change_y=0
frame = 0
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
# LOAD PLATER IMAGES
# Set height, width
self.images = []
for i in range(1,17):
img = pygame.image.load("images/player/" + str(i)+".png").convert() #player images
img.set_colorkey(white)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.health = 15
self.image_w, self.image_h = self.image.get_size()
health_bar_x = self.rect.x - 7
health_bar_y = self.rect.y - self.image_h / 2 - 6
screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def update(self,walls):
# collision detection
old_x=self.rect.x
new_x=old_x+self.change_x
self.rect.x = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.x=old_x
old_y=self.rect.y
new_y=old_y+self.change_y
self.rect.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.y=old_y
# right to left
if self.change_x < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
# Grab the image, divide by 4
# every 4 frames.
self.image = self.images[self.frame//4]
# Move left to right.
# images 4...7 instead of 0...3.
if self.change_x > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4]
if self.change_y > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4]
if self.change_y < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4+4]
score = 0
# initialize pyGame
pygame.init()
# 800x600 sized screen
global screen
screen = pygame.display.set_mode([800, 600])
screen.fill(black)
#bg_tile_img = pygame.image.load('images/map/grass.png').convert_alpha()
#draw_background(screen, bg_tile_img)
#pygame.display.flip()
# Set title
pygame.display.set_caption('Test')
#background = pygame.Surface(screen.get_size())
#background = background.convert()
#background.fill(black)
# Create the player
player = Player( 50,50 )
player.rect.x=50
player.rect.y=50
movingsprites = pygame.sprite.RenderPlain()
movingsprites.add(player)
# Make the walls. (x_pos, y_pos, width, height)
global wall_list
wall_list=pygame.sprite.RenderPlain()
wall=Wall(0,0,10,600) # left wall
wall_list.add(wall)
wall=Wall(10,0,790,10) # top wall
wall_list.add(wall)
#wall=Wall(10,200,100,10) # poke wall
wall_list.add(wall)
wall=Wall(790,0,10,600) #(x,y,thickness, height)
wall_list.add(wall)
wall=Wall(10,590,790,10) #(x,y,thickness, height)
wall_list.add(wall)
f = open('MapMaker.txt')
num_lines = sum(1 for line in f)
print(num_lines)
lineCount = 0
with open("MapMaker.txt") as infile:
for line in infile:
f = open('MapMaker.txt')
print(line)
coords = line.split(',')
#print(coords[0])
#print(coords[1])
#print(coords[2])
#print(coords[3])
#print(coords[4])
if "tree" in line:
print("tree in")
wall=insertTree(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4])
wall_list.add(wall)
elif "rock" in line:
print("rock in")
wall=insertRock(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4] )
wall_list.add(wall)
width = 20
height = 540
height = height - 48
for i in range(0,23):
width = width + 32
name = insertTree(width,540,790,10,"tree")
#wall_list.add(name)
name = insertTree(width,height,690,10,"tree")
#wall_list.add(name)
CREEP_SPAWN_TIME = 200 # frames
creep_spawn = CREEP_SPAWN_TIME
clock = pygame.time.Clock()
bg_tile_img = pygame.image.load('images/map/grass.png').convert()
img_rect = bg_tile_img
FIELD_RECT = Rect(50, 50, 700, 500)
CREEP_FILENAMES = [
'images/player/1.png',
'images/player/1.png',
'images/player/1.png']
N_CREEPS = 3
creep_images = [
pygame.image.load(filename).convert_alpha()
for filename in CREEP_FILENAMES]
explosion_img = pygame.image.load('images/map/tree.png').convert_alpha()
explosion_images = [
explosion_img, pygame.transform.rotate(explosion_img, 90)]
creeps = pygame.sprite.RenderPlain()
done = False
#bg_tile_img = pygame.image.load('images/map/grass.png').convert()
#draw_background(screen, bg_tile_img)
totalCreeps = 0
remainingCreeps = 3
while done == False:
creep_images = pygame.image.load("images/player/1.png").convert()
creep_images.set_colorkey(white)
draw_background(screen, bg_tile_img)
if len(creeps) != N_CREEPS:
if totalCreeps < N_CREEPS:
totalCreeps = totalCreeps + 1
print(totalCreeps)
creeps.add(
Creep( screen=screen,
creep_image=creep_images,
explosion_images=explosion_images,
field=FIELD_RECT,
init_position=( randint(FIELD_RECT.left,
FIELD_RECT.right),
randint(FIELD_RECT.top,
FIELD_RECT.bottom)),
init_direction=(choice([-1, 1]),
choice([-1, 1])),
speed=0.01))
for creep in creeps:
creep.update(60,wall_list)
creep.draw()
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.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_UP:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.key == pygame.K_DOWN:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_ESCAPE:
pauseGame()
if event.key == pygame.K_1:
global currentEditTool
currentEditTool = "Tree"
changeTool()
if event.key == pygame.K_2:
global currentEditTool
currentEditTool = "Rock"
changeTool()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_UP:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_DOWN:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
for creep in creeps:
creep.mouse_click_event(pygame.mouse.get_pos())
if editMap == True:
x,y = pygame.mouse.get_pos()
if currentEditTool == "Tree":
name = insertTree(x-10,y-25, 10 , 10, "tree")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
image = pygame.image.load("images/map/tree.png").convert()
screen.blit(image, (30,10))
pygame.display.flip()
f.write(str(x) + "," + str(y) + ",790,10, tree\n")
#f.write("wall=insertTree(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
elif currentEditTool == "Rock":
name = insertRock(x-10,y-25, 10 , 10,"rock")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
f.write(str(x) + "," + str(y) + ",790,10,rock\n")
#f.write("wall=insertRock(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
else:
None
#pygame.display.flip()
player.update(wall_list)
movingsprites.draw(screen)
wall_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Sprite collide is the correct way to do it, you are just handling the return data incorrectly. Basically, sprite collide will no return a boolean of if you are colliding or not with a member of the group. It returns a list of sprites. Basically, it will give you a list of sprites colliding with you in that group. Another thing to watch out for is that the list will also invariably include the sprite itself. After all, it is colliding with itself. Here is a functioning way to test if you are colliding with a wall.
def is_colliding_with_wall(): #this is a function that will determine if you are touching a wall. You have repeated code to test collisions with walls multiple times, so it is best just to have a function
collide = pygame.sprite.spritecollide(self, walls, False) #this gets the list, it is the exact same code as in yours
for collision in collide: #goes through the list, checking each collision
if collision != self: #filters out self collision
return True #if there is a collision that is not self, returns True, that there is a collision with a wall
return False #if it has checked all the collisions and there is only a self collision, then it is not colliding with a wall, so it returns False

Categories

Resources