Why does flipping my sprites cause it to move weird - python

I'm making a pyGame game and instead of getting sprites to both sides I decided to just flip it horizontally, the thing is it move perfect on the right side but on the left side it moves wrong like the center of the image changes every frame, is there anyway I can controll the images center.
for the record the function draw is being called from Main.py
This is my Player Class:
class Player(pygame.sprite.Sprite):
def __init__(self, char_type, x, y, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.alive = True
self.char_type = char_type
self.speed = speed
self.direction = 1
self.vel_y = 0
self.jump = False
self.in_air = True
self.flip = False
self.attack = False
self.moving_left = False
self.moving_right = False
self.animation_list = []
self.frame_index = 0
self.action = 0
self.update_time = pygame.time.get_ticks()
#load all images for the players
animation_types = ['stand', 'walk', 'jump', 'attack1', 'attack2', 'attack3']
for animation in animation_types:
#reset temporary list of images
temp_list = []
#count number of files in the folder
num_of_frames = len(os.listdir(f'sprites/{self.char_type}/{animation}'))
for i in range(num_of_frames):
img = pygame.image.load(f'sprites/{self.char_type}/{animation}/{i}.png')
img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
def move(self, GRAVITY):
#reset movement variables
dx = 0
dy = 0
#assign movement variables if moving left or right
if self.moving_left:
dx = -self.speed
self.flip = True
self.direction = -1
if self.moving_right:
dx = self.speed
self.flip = False
self.direction = 1
# if self.attack:
# self.play_sound("attack")
#jump
if self.jump == True and self.in_air == False:
self.vel_y = -11
self.jump = False
self.in_air = True
# if self.attack == True:
# self.attack = False
#apply gravity
self.vel_y += GRAVITY
if self.vel_y > 10:
self.vel_y
dy += self.vel_y
#check collision with floor
if self.rect.bottom + dy > 450:
dy = 450 - self.rect.bottom
self.in_air = False
#update rectangle position
self.rect.x += dx
self.rect.y += dy
def update_animation(self):
#update animation
if self.action == 0:
animation_cooldown = 800
elif self.action == 1:
animation_cooldown = 170
elif self.action == 2:
animation_cooldown = 50
elif self.action == 3:
self.moving_left = False
self.moving_right = False
animation_cooldown = 250
# self.action = random.randint(3, 5)
#update image depending on current frame
self.image = self.animation_list[self.action][self.frame_index]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
#if the animation has run out the reset back to the start
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 3:
self.attack = False
self.frame_index = 0
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.frame_index = 0
self.update_time = pygame.time.get_ticks()
def play_sound(self, sound):
soundObj = pygame.mixer.Sound('sprites/sounds/player/' + sound + '.mp3')
soundObj.play()
def draw(self, screen):
screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)

You need to reverse the list for the flipped side. reverse the list in place:
temp_list.reverse()
self.animation_list.append(temp_list)
or append a reversed list:
self.animation_list.append(reversed(temp_list))
If your images are different sizes, you need to create images of the same size. Find the size of the largest image. Create a pygame.Surface for each image and blit the image onto the Surface:
for animation in animation_types:
max_image_size = [0, 0]
temp_list = []
num_of_frames = len(os.listdir(f'sprites/{self.char_type}/{animation}'))
for i in range(num_of_frames):
img = pygame.image.load(f'sprites/{self.char_type}/{animation}/{i}.png')
temp_list.append(img)
max_image_size[0] = max(max_image_size[0], img.get_width())
max_image_size[0] = max(max_image_size[1], img.get_height())
for i in range(num_of_frames):
img = pygame.Surface(max_image_size, pygame.SRCALPHA)
img.blit(temp_list[i], (0, 0))
temp_list[i] = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
self.animation_list.append(temp_list)

Related

Pygame sprite moves up and left faster than down and right [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Ship moves up and left faster than down and right when rotating in pygame
(1 answer)
Closed last month.
I am working on a simple game. I created a pygame sprite and moved it by adding its rect position with a direction (from wasd input). However, it seems to be moving left and up (where all the directions are negative) quicker than right and down (where all the directions are positive)
Here is the code:
import pygame
from settings import *
from support import import_folder
class Player(pygame.sprite.Sprite):
def __init__(self, pos, groups,obstacle_sprites,create_attack,destroy_attack,create_magic):
#for sprites
super().__init__(groups)
self.image = pygame.image.load(f'{abs_path}/graphics/test/player.png').convert_alpha()
self.rect = self.image.get_rect(topleft = pos)
self.hitbox = self.rect.inflate(0,-26)
# graphics setup
self.import_player_assets()
self.status = 'down'
self.frame_index = 0
self.animation_speed = 0.15
#movement
self.direction = pygame.math.Vector2()
self.attacking = False
self.attack_cooldown = 400
self.attack_time = None
self.obstacle_sprites = obstacle_sprites
#weapon
self.create_attack = create_attack
self.destory_attack = destroy_attack
#gets the correct data from weapon dictionary in settings.py also turns keys into list
self.weapon_index = 0
self.weapon = list(weapon_data.keys())[self.weapon_index]
self.can_switch_weapon = True
self.weapon_switch_time = None
self.switch_duration_cooldown = 200
# Magic
self.create_magic = create_magic
self.magic_index = 0
self.magic = list(magic_data.keys())[self.magic_index]
self.can_switch_magic = True
self.magic_switch_time = None
#Stats
self.stats = {'health': 100, 'energy':60, 'attack': 10, 'magic': 4, 'speed': 5}
self.health = self.stats['health']
self.energy = self.stats['energy']
self.exp = 0
self.speed = self.stats['speed']
#For the player animations
def import_player_assets(self):
character_path = f'{abs_path}/graphics/player2/'
self.animations = {'up': [], 'down': [], 'left': [], 'right': [],
'right_idle': [], 'left_idle': [], 'up_idle': [], 'down_idle': [],
'right_attack': [], 'left_attack': [], 'up_attack': [], 'down_attack': []}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
def input(self):
keys = pygame.key.get_pressed()
#Movement Input
#The extra if statement is to make the speed the same even when its going in diagonals, some are set to .8
#because pygame is wierd
if not self.attacking:
if (keys[pygame.K_w] and keys[pygame.K_s]):
self.direction.y = 0
elif keys[pygame.K_w]:
if (keys[pygame.K_a] or keys[pygame.K_d]):
self.direction.y = -.7
else:
self.direction.y = -1
self.status = 'up'
elif keys[pygame.K_s]:
if (keys[pygame.K_a] or keys[pygame.K_d]):
self.direction.y = .7
else:
self.direction.y = 1
self.status = 'down'
else:
self.direction.y = 0
if (keys[pygame.K_d] and keys[pygame.K_a]):
self.direction.x = 0
elif keys[pygame.K_d]:
if (keys[pygame.K_w] or keys[pygame.K_s]):
self.direction.x = .7
else:
self.direction.x = 1
self.status = 'right'
elif keys[pygame.K_a]:
if (keys[pygame.K_w] or keys[pygame.K_s]):
self.direction.x = -.7
else:
self.direction.x = -1
self.status = 'left'
else:
self.direction.x = 0
#Attack Input
#if keys[pygame.K_LSHIFT] and not self.attacking
if keys[pygame.K_SPACE]:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
self.create_attack()
#Spell Input
if keys[pygame.K_LSHIFT]:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
style = list(magic_data.keys())[self.magic_index]
strength = list(magic_data.values())[self.magic_index]['strength'] + self.stats['magic']
cost = list(magic_data.values())[self.magic_index]['cost']
self.create_magic(style, strength, cost)
#Switch weapons Input
if keys[pygame.K_q] and self.can_switch_weapon:
self.can_switch_weapon = False
self.weapon_switch_time = pygame.time.get_ticks()
if self.weapon_index < len(list(weapon_data.keys())) - 1:
self.weapon_index += 1
else:
self.weapon_index = 0
self.weapon = list(weapon_data.keys())[self.weapon_index]
#Switch spells Input
if keys[pygame.K_e] and self.can_switch_magic:
self.can_switch_magic = False
self.magic_switch_time = pygame.time.get_ticks()
if self.magic_index < len(list(magic_data.keys())) - 1:
self.magic_index += 1
else:
self.magic_index = 0
self.magic = list(magic_data.keys())[self.magic_index]
def get_status(self):
#Idle Status:
if self.direction.x == 0 and self.direction.y == 0:
if not 'idle' in self.status and not 'attack' in self.status:
self.status = self.status + '_idle'
if self.attacking:
self.direction.x = 0
self.direction.y = 0
if not 'attack' in self.status:
if 'idle' in self.status:
self.status = self.status.replace('_idle','_attack')
else:
self.status = self.status + '_attack'
else:
if 'attack' in self.status:
self.status = self.status.replace('_attack','')
def move(self, speed):
#Moving the player feat. collision detection
self.hitbox.x += self.direction.x * speed
self.collision('horizontal')
self.hitbox.y += self.direction.y * speed
self.collision('vertical')
self.rect.center = self.hitbox.center
#Pygame cannot tell if something collides but only if it overlaps
#So i check for horizontal/vertical movement because it cant tell where it overlaps
def collision(self, direction):
if direction == 'horizontal':
for sprite in self.obstacle_sprites:
#Checking the hitbox of sprite with hitbox of obstacle
if sprite.hitbox.colliderect(self.hitbox):
if self.direction.x > 0: #checks if player is moving right
#moves the right side of player to left side of obstacle
self.hitbox.right = sprite.hitbox.left
if self.direction.x < 0:
#vice versa
self.hitbox.left = sprite.hitbox.right
if direction == 'vertical':
for sprite in self.obstacle_sprites:
#Checking the hitbox of sprite with hitbox of obstacle
if sprite.hitbox.colliderect(self.hitbox):
if self.direction.y > 0: #checks if player is moving down
#moves the bottom side of player to top side of obstacle
self.hitbox.bottom = sprite.hitbox.top
if self.direction.y < 0: #move up
#vice versa
self.hitbox.top = sprite.hitbox.bottom
#Cooldown function so people cant spam attacks
def cooldowns(self):
current_time = pygame.time.get_ticks()
if self.attacking:
if current_time - self.attack_time >= self.attack_cooldown:
self.attacking = False
self.destory_attack()
if not self.can_switch_weapon:
if current_time - self.weapon_switch_time >= self.switch_duration_cooldown:
self.can_switch_weapon = True
if not self.can_switch_magic:
if current_time - self.magic_switch_time >= self.switch_duration_cooldown:
self.can_switch_magic = True
#Loop the images to make moving sprites
def animate(self):
#Goes into the image dictionary and puts player status as key, the animation = the item
animation = self.animations[self.status]
#loop over frame index
self.frame_index += self.animation_speed
if self.frame_index >= len(animation):
self.frame_index = 0
#Set the image
self.image = animation[int(self.frame_index)]
self.rect = self.image.get_rect(center = self.hitbox.center)
def update(self):
self.input()
self.cooldowns()
self.get_status()
self.animate()
self.move(self.speed)

i have a problem: my enemies are stacked vertically, on top of my player which is weird, and they don't move to their predefined positions [duplicate]

This question already exists:
in my shooting game, my players are positioned on top of my head (player) which is very abnormal and also once the game starts, they start shooting [closed]
Closed 6 months ago.
i have earlier tried to format my question properly but i hope this one is better. am creating a shooter game from https://github.com/russs123/Shooter. however my problem is that my enemies are stacked vertically upwards in a straight line when i run my game and the enemies don't start off at their predefined location. below is the full code i wrote. perhaps if you can run it, it will be better understood.
import pygame, sys
from player import Player
import os
import random
import csv
pygame.init()
screen_width = 600
scroll_thresh = 200
screen_scroll = 0
bg_scroll = 0
screen_height = int(screen_width * 0.8)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Shooter Game")
clock = pygame.time.Clock()
fps = 60
# define game variables
GRAVITY = 0.75
level = 1
ROWS = 16
COLS = 150
TILE_SIZE = screen_height // ROWS
TILE_TYPES = 21
img_list = []
for x in range(TILE_TYPES):
img = pygame.image.load(f"img/tile/{x}.png")
img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
img_list.append(img)
# color variables
bg = (144, 201, 120)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
# define player action variables
moving_left = False
moving_right = False
shoot = False
grenade = False
grenade_thrown = False
# load up images
pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
sky_cloud_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
bullet_img = pygame.image.load("img/icons/bullet.png").convert_alpha()
grenade_img = pygame.image.load("img/icons/grenade.png").convert_alpha()
# pick up boxes
health_box_img = pygame.image.load("img/icons/health_box.png").convert_alpha()
ammo_box_img = pygame.image.load("img/icons/ammo_box.png").convert_alpha()
grenade_box_img = pygame.image.load("img/icons/grenade_box.png").convert_alpha()
# name_dt = type(name)
item_boxes = {
"Health": health_box_img,
"Ammo": ammo_box_img,
"Grenade": grenade_img,
}
# define fonts
font = pygame.font.SysFont("Futura", 30)
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_bg():
screen.fill(bg)
screen.blit(sky_cloud_img, (0, 0))
screen.blit(mountain_img, (0, screen_height - mountain_img.get_height() - 300))
screen.blit(pine1_img, (0, screen_height - pine1_img.get_height() - 150))
screen.blit(pine2_img, (0, screen_height - pine2_img.get_height()))
class Soldier(pygame.sprite.Sprite):
def __init__(self, char_type, x, y, scale, speed, ammo, grenades):
pygame.sprite.Sprite.__init__(self)
self.alive = True
self.char_type = char_type
self.speed = speed
self.ammo = ammo
self.start_ammo = ammo
self.shoot_cooldown = 0
self.grenades = grenades
self.health = 100
self.max_health = self.health
self.direction = 1
self.vel_y = 0
self.jump = False
self.in_air = True
self.flip = False
self.animation_list = []
self.frame_index = 0
self.action = 0
# ai specific variables
self.move_counter = 0
self.vision = pygame.Rect(0, 0, 150, 20)
self.idling = 0
self.idling_counter = 0
self.update_time = pygame.time.get_ticks()
# load all images for the players
animation_types = ["idle", "run", "jump", "death"]
for animation in animation_types:
temp_list = []
# reset temporary list of images
# count number of files in the folder
num_of_frames = len(os.listdir(f"img/{self.char_type}/{animation}"))
for i in range(num_of_frames):
img = pygame.image.load(f"img/{self.char_type}/{animation}/{i}.png").convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.width = self.image.get_width()
self.height = self.image.get_height()
def update(self):
self.update_animation()
self.check_alive()
# update cooldown
if self.shoot_cooldown > 0:
self.shoot_cooldown -= 1
def move(self, moving_left, moving_right):
screen_scroll = 0
dx = 0
dy = 0
if moving_left:
dx = -self.speed
self.flip = True
self.direction = -1
if moving_right:
dx = self.speed
self.flip = False
self.direction = 1
if self.jump == True and self.in_air == False:
self.vel_y = -11
self.jump = False
self.in_air = True
# apply gravity
self.vel_y += GRAVITY
if self.vel_y > 10:
self.vel_y
dy += self.vel_y
# check for collision
for tile in world.obstacle_list:
# check collision in the x direction
if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
dx = 0
# check for collision in the y direction
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
# check if below the ground, i.e jumping
if self.vel_y < 0:
self.vel_y = 0
dy = tile[1].bottom - self.rect.top
# check if above the ground, ie falling
elif self.vel_y >= 0:
self.vel_y = 0
self.in_air = False
dy = tile[1].top - self.rect.bottom
self.rect.x += dx
self.rect.y += dy
# update scroll based on player position
if self.char_type == "player":
if self.rect.right > screen_width - scroll_thresh or self.rect.left < scroll_thresh:
self.rect.x = -dx
screen_scroll = -dx
return screen_scroll
def shoot(self):
if self.shoot_cooldown == 0 and self.ammo > 0:
self.shoot_cooldown = 20
bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery,
self.direction)
bullet_group.add(bullet)
# reduce ammo after each shot
self.ammo -= 1
def ai(self):
if self.alive and player.alive:
if self.idling == False and random.randint(1, 200) == 1:
self.update_action(0)
self.idling = True
self.idling_counter = 50
# check if ai is near the player
if self.vision.colliderect(player.rect):
# stop running and face the player
self.update_action(0)
self.shoot()
else:
if self.idling == False:
if self.direction == 1:
ai_moving_right = True
else:
ai_moving_right = False
ai_moving_left = not ai_moving_right
self.move(ai_moving_left, ai_moving_right)
self.update_action(1)
self.move_counter += 1
# update ai vision as the enemy moves
self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery)
if self.move_counter > TILE_SIZE:
self.direction *= -1
self.move_counter *= -1
else:
self.idling_counter -= 1
if self.idling_counter <= 0:
self.idling = False
# scroll
self.rect.x = screen_scroll
def update_animation(self):
ANIMATION_COOLDOWN = 100
# update image depending on current index
self.image = self.animation_list[self.action][self.frame_index]
# check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
# if animation has run out then restart to the first
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 3:
self.frame_index = len(self.animation_list[self.action]) - 1
else:
self.frame_index = 0
def check_alive(self):
if self.health <= 0:
self.health = 0
self.speed = 0
self.alive = False
self.update_action(3)
def update_action(self, new_action):
# check if new action is different from the previous one
if new_action != self.action:
self.action = new_action
# update animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def draw(self):
screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)
class HealthBar():
def __init__(self, health, x, y, max_health):
self.x = x
self.y = y
self.health = health
self.max_health = max_health
def draw(self, health):
self.health = health
pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24))
pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20))
ratio = self.health / self.max_health
pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20))
class World():
def __init__(self):
self.obstacle_list = []
def process_data(self, data):
for y, row in enumerate(data):
for x, tile in enumerate(row):
if tile >= 0:
img = img_list[tile]
img_rect = img.get_rect()
img_rect.x = x * TILE_SIZE
img_rect.y = y * TILE_SIZE
tile_data = (img, img_rect)
if 0 <= tile <= 8:
self.obstacle_list.append(tile_data)
elif 9 <= tile <= 10:
# water
water = Water(img, x * TILE_SIZE, y * TILE_SIZE, )
water_group.add(water)
pass
elif 11 <= tile <= 14:
# decoration
decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE)
decoration_group.add(decoration)
elif tile == 15:
# create a player
player = Soldier("player", x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 20, 5)
health_bar = HealthBar(10, 10, player.health, player.health)
elif tile == 16:
# create enemy
enemy = Soldier("enemy", x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 0)
enemy_group.add(enemy)
elif tile == 17:
# create ammo box
item_box = ItemBox("Ammo", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 18:
# create grenade box
item_box = ItemBox("Grenade", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 19:
# create health box
item_box = ItemBox("Health", x * TILE_SIZE, y * TILE_SIZE)
item_box_group.add(item_box)
elif tile == 20:
# create exit point
exit = Exit(img, x * TILE_SIZE, y * TILE_SIZE, )
exit_group.add(exit)
return player, health_bar
def draw(self):
for tile in self.obstacle_list:
tile[1][0] += screen_scroll
screen.blit(tile[0], tile[1])
class Decoration(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
self.rect.x += screen_scroll
class Water(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
self.rect.x += screen_scroll
class Exit(pygame.sprite.Sprite):
def __init__(self, img, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = img
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
class ItemBox(pygame.sprite.Sprite):
def __init__(self, item_type, x, y):
pygame.sprite.Sprite.__init__(self)
self.item_type = item_type
self.image = item_boxes[self.item_type]
self.rect = self.image.get_rect()
self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
def update(self):
# scroll
self.rect.x += screen_scroll
# check if player has picked up the box
if pygame.sprite.collide_rect(self, player):
# check what kind of box it was
if self.item_type == "Health":
player.health += 25
if player.health > player.max_health:
player.health = player.max_health
elif self.item_type == "Ammo":
player.ammo += 15
elif self.item_type == "Grenade":
player.grenades += 3
# delete the item box
self.kill()
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 10
self.image = bullet_img
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = direction
def update(self):
self.rect.x += self.direction * self.speed
# check if bullet has left the screen
if self.rect.right < 0 and self.rect.left > screen_width:
self.kill()
# check for collision with level
for tile in world.obstacle_list:
if tile[1].colliderect(self.rect):
self.kill()
# check collision with characters
if pygame.sprite.spritecollide(player, bullet_group, False):
if player.alive:
player.health -= 5
self.kill()
for enemy in enemy_group:
if pygame.sprite.spritecollide(enemy, bullet_group, False):
if enemy.alive:
enemy.health -= 25
self.kill()
class Grenade(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.timer = 100
self.vel_y = -11
self.speed = 7
self.image = grenade_img
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = direction
self.width = self.image.get_width()
self.height = self.image.get_height()
def update(self):
self.vel_y += GRAVITY
dx = self.direction * self.speed
dy = self.vel_y
# check for collision with level
for tile in world.obstacle_list:
# check if grenade has hit a wall
if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
self.direction *= -1
dx = self.direction * self.speed
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
self.speed = 0
# check if below the ground, i.e thrown
if self.vel_y < 0:
self.y = 0
dy = tile[1].bottom - self.rect.top
# check if above the ground, ie falling
elif self.vel_y >= 0:
self.vel_y = 0
dy = tile[1].top - self.rect.bottom
# update grenade position
self.rect.x += dx
self.rect.y += dy
# explosion countdown
self.timer -= 1
if self.timer <= 0:
self.kill()
explosion = Explosion(self.rect.x, self.rect.y, 0.5)
explosion_group.add(explosion)
# do damage to anyone nearby
if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and \
abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2:
player.health -= 50
for enemy in enemy_group:
if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \
abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2:
enemy.health -= 50
class Explosion(pygame.sprite.Sprite):
def __init__(self, x, y, scale):
pygame.sprite.Sprite.__init__(self)
self.images = []
for num in range(1, 6):
img = pygame.image.load(f"img/explosion/exp{num}.png").convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width() * scale), (int(img.get_height() * scale))))
self.images.append(img)
self.frame_index = 0
self.image = self.images[self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.counter = 0
def update(self):
EXPLOSION_SPEED = 4
# update explosion animation
self.counter += 1
if self.counter >= EXPLOSION_SPEED:
self.counter = 0
self.frame_index += 1
# if animation is complete, then delete the explosion
if self.frame_index >= len(self.images):
self.kill()
else:
self.image = self.images[self.frame_index]
# create sprite groups
enemy_group = pygame.sprite.Group()
bullet_group = pygame.sprite.Group()
grenade_group = pygame.sprite.Group()
explosion_group = pygame.sprite.Group()
item_box_group = pygame.sprite.Group()
decoration_group = pygame.sprite.Group()
water_group = pygame.sprite.Group()
exit_group = pygame.sprite.Group()
# create empty tile list
world_data = []
for row in range(ROWS):
r = [-1] * COLS
world_data.append(r)
# load in level data and create world
with open(f"level{level}_data.csv", newline="") as csvfile:
reader = csv.reader(csvfile, delimiter=",")
for x, row in enumerate(reader):
for y, tile in enumerate(row):
world_data[x][y] = int(tile)
print(world_data)
world = World()
player, health_bar = world.process_data(world_data)
run = True
while run:
clock.tick(fps)
draw_bg()
world.draw()
health_bar.draw(player.health)
# show ammo
draw_text("AMMO:", font, WHITE, 10, 35)
for x in range(player.ammo):
screen.blit(bullet_img, (90 + (x * 10), 40))
# show grenade
draw_text("GRENADES: ", font, WHITE, 10, 65)
for x in range(player.grenades):
screen.blit(grenade_img, (130 + (x * 15), 66))
player.update()
player.draw()
for enemy in enemy_group:
enemy.ai()
enemy.update()
enemy.draw()
# update and draw groups
bullet_group.update()
grenade_group.update()
explosion_group.update()
item_box_group.update()
decoration_group.update()
water_group.update()
exit_group.update()
bullet_group.draw(screen)
grenade_group.draw(screen)
explosion_group.draw(screen)
item_box_group.draw(screen)
decoration_group.draw(screen)
water_group.draw(screen)
exit_group.draw(screen)
# update player actions, 1 is run, 0 = idle, 2 = in air
if player.alive:
# shoot bullet
if shoot:
player.shoot()
elif grenade and grenade_thrown == False and player.grenades > 0:
grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction),
player.rect.top, player.direction)
grenade_group.add(grenade)
# reduce grenades
player.grenades -= 1
grenade_thrown = True
if player.in_air:
player.update_action(2)
elif moving_left or moving_right:
player.update_action(1)
else:
player.update_action(0)
screen_scroll = player.move(moving_left, moving_right)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# keyboard presses
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
moving_left = True
if event.key == pygame.K_d:
moving_right = True
if event.key == pygame.K_SPACE:
shoot = True
if event.key == pygame.K_q:
grenade = True
if event.key == pygame.K_w and player.alive:
player.jump = True
if event.key == pygame.K_ESCAPE:
run = False
# keyboard button released
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
moving_left = False
if event.key == pygame.K_d:
moving_right = False
if event.key == pygame.K_SPACE:
shoot = False
if event.key == pygame.K_q:
grenade = False
grenade_thrown = False
pygame.display.update()
pygame.quit()
i have looked at this code for days but cant seem to find out the problem exactly. so, the question once again is how to properly arrange the enemies on the screen at their various predefined positions and not directly on top of my player.

I am having trouble with my stat function in my player class

When I shoot at my cement block sprites I have it set so that self.score is self.score += 1 in my player collision function, but when I shoot my cement blocks and destroy them, either 1 or 2 points is added at random to my score. Why? How can I fix this? A clear example is, I shoot at and destroy 2 cement blocks in a row and 1 point is added for each one destroyed which means my score is 2, which is what I want cause I want to add 1 point to my score whenever I destroy a cement block, but then when I shoot and destroy the third cement block, 2 points are added instead of 1 bringing my score to 4 instead of being a score of 3 points.
Github: https://github.com/Enoc-Mena99/AutoPilot
My code:
import random
import pygame
import pygame.freetype
pygame.init()
#screen settings
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255, 255, 255))
#fps
FPS = 120
clock = pygame.time.Clock()
#load images
bg = pygame.image.load('background/street.png').convert_alpha() # background
bullets = pygame.image.load('car/bullet.png').convert_alpha()
debris_img = pygame.image.load('debris/cement.png')
#define game variables
shoot = False
#player class
class Player(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.bullet_list = []
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
self.score = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image, (self.rect.centerx, self.rect.centery))
#move car
def move(self):
#reset the movement variables
dx = 0
dy = 0
#moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
#update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot
def shoot(self):
bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction)
bullet_group.add(bullet)
#check collision
def collision(self, debris_group):
for debris in debris_group:
if pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
self.score += 1
#player stats
def stats(self):
myfont = pygame.font.SysFont('comicsans', 30)
scoretext = myfont.render("Score: " + str(self.score), 1, (0,0,0))
screen.blit(scoretext, (100,10))
#bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 5
self.image = bullets
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.direction = direction
def update(self):
self.rect.centery -= self.speed
#check if bullet has gone off screen
if self.rect.centery < 1:
self.kill()
#debris class
class Debris(pygame.sprite.Sprite):
def __init__(self,scale,speed):
pygame.sprite.Sprite.__init__(self)
self.scale = scale
self.x = random.randrange(100,800)
self.speed_y = 10
self.y = 15
self.speed = speed
self.vy = 0
self.on_ground = True
self.move = True
self.health = 4
self.max_health = self.health
self.alive = True
self.velocity = random.randrange(1,2)
self.speed_x = random.randrange(-3,3)
self.moving_down = True
self.is_destroyed = False
#load debris
self.image = debris_img
self.rect = self.image.get_rect()
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.rect.center = (self.x,self.y)
#load explosion
self.img_explosion_00 = pygame.image.load('explosion/0.png').convert_alpha()
self.img_explosion_00 = pygame.transform.scale(self.img_explosion_00, (self.img_explosion_00.get_width() * 2,
self.img_explosion_00.get_height() * 2))
self.img_explosion_01 = pygame.image.load('explosion/1.png').convert_alpha()
self.img_explosion_01 = pygame.transform.scale(self.img_explosion_01, (self.img_explosion_01.get_width() * 2,
self.img_explosion_01.get_height() * 2))
self.img_explosion_02 = pygame.image.load('explosion/2.png').convert_alpha()
self.img_explosion_02 = pygame.transform.scale(self.img_explosion_02, (self.img_explosion_02.get_width() * 2,
self.img_explosion_02.get_height() * 2))
self.img_explosion_03 = pygame.image.load('explosion/3.png').convert_alpha()
self.img_explosion_03 = pygame.transform.scale(self.img_explosion_03, (self.img_explosion_03.get_width() * 2,
self.img_explosion_03.get_height() * 2))
#explosion list
self.anim_explosion = [self.img_explosion_00,
self.img_explosion_01,
self.img_explosion_02,
self.img_explosion_03]
self.anim_index = 0
self.frame_len = 10
#spawn new debris
def spawn_new_debris(self):
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.velocity = random.randrange(1, 2)
self.speed_x = random.randrange(-3, 3)
#respawn debris when they go of the screen
def boundaries(self):
if self.rect.left > WIDTH + 10 or self.rect.right < -10 or self.rect.top > HEIGHT + 10:
self.spawn_new_debris()
#update image
def update(self):
self.rect.y += self.velocity
self.rect.x += self.speed_x
self.boundaries()
if self.health <= 0:
max_index = len(self.anim_explosion) - 1
if self.anim_index > max_index:
self.kill()
else:
if self.frame_len == 0:
self.image = self.anim_explosion[self.anim_index]
self.anim_index += 1
self.frame_len = 10
else:
self.frame_len -= 1
#make debris fall down
def falldown(self):
self.rect.centery += self.velocity
if self.moving_down and self.rect.y > 350:
self.kill()
######################CAR/DEBRIS##########################
player = Player(1,5)
##########################################################
#groups
bullet_group = pygame.sprite.Group()
debris_group = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
for x in range(50):
d = Debris(1, 5)
debris_group.add(d)
all_sprites.add(d)
#game runs here
run = True
while run:
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
player.collision(debris_group)
player.stats()
#update all sprites
all_sprites.update()
all_sprites.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
Do not count debris objects with healt <= 0:
class Player(pygame.sprite.Sprite):
# [...]
def collision(self, debris_group):
for debris in debris_group:
# if health > 0 and collision:
if debris.health > 0 and pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
self.score += 1

Enemy collision with pygame.sprite.spritecollide()

In my game, there is a sprite player which I can control. It can move right or left, it can jump, shoot fireballs(bullets) and a breathe fire. I have added an enemy which can move on itself from right to left on a limited distance that I set. What I would like to do now is make my player loose health if it collides with the enemy sprite using pygame.sprite.spritecollide(). However it isn't working out well I don't know how to fix my issue which is the following: if I run my code below it says NameError: name 'enemy_list' is not defined. The errored line is in Sprite1.py in the Player class under the update function. How do I fix my code? I created my Enemy class and Level class with the following website: https://opensource.com/article/18/5/pygame-enemy. I'm open to all suggestions. Thanks beforehand! I separated my code into three files: main.py, settings.py and Sprite1.py. Here's main.py:
import pygame
import os
import sys
import time
from pygame import mixer
from Sprite1 import *
from settings import *
'''
Setup
'''
pygame.init()
clock = pygame.time.Clock()
pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
pygame.mixer.music.play(-1, 0.0)
all_sprites = pygame.sprite.Group()
player = Player(all_sprites)
player.rect.x = 500
player.rect.y = 500
eloc = []
eloc = [400,500]
enemy_list = Level.bad( 1, eloc )
showStartScreen(surface)
x = 0
'''
Main loop
'''
main = True
while main == True:
background = pygame.image.load(os.path.join('images', 'Bg.png')).convert()
surface.blit(background, (0, 0))
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:
player.control(-steps,0)
if event.key == pygame.K_RIGHT:
player.control(steps,0)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.control(steps,0)
if event.key == pygame.K_RIGHT:
player.control(-steps,0)
keys = pygame.key.get_pressed()
if not(isJump):
if keys[pygame.K_UP]:
isJump = True
else:
if jumpCount >= -10:
player.rect.y -= (jumpCount * abs(jumpCount)) * 1
jumpCount -= 2
else:
jumpCount = 10
isJump = False
# dt = time since last tick in milliseconds.
dt = clock.tick(60) / 1000
all_sprites.update(dt)
player.update(dt)
all_sprites.draw(surface) #refresh player position
enemy_list.draw(surface)
for e in enemy_list:
e.move()
pygame.display.flip()
Here's my settings.py:
import pygame
isJump = False
jumpCount = 10
width = 960
height = 720
fps = 40 # frame rate
pygame.display.set_caption('B.S.G.')
surface = pygame.display.set_mode((width, height))
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
PLAYER_LAYER = 2
PLATFORM_LAYER = 1
RED = (255, 0, 0)
steps = 10 # how fast to move
And here's my Sprite1.py:
import pygame
import sys
import os
import time
from pygame import mixer
from pygame.locals import *
from settings import *
vec = pygame.math.Vector2
def showStartScreen(surface):
show = True
while (show == True):
background = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
# rect = surface.get_rect()
surface.blit(background, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
show = False
class Player(pygame.sprite.Sprite):
def __init__(self, all_sprites):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
self.jumping = False
self.images = []
self.imagesleft = []
self.imagesright = []
self.direction = "right"
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.fire_timer = .1
self.bullet_timer = .1
self.pos = vec(40, height - 100)
self.vel = vec(0, 0)
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
def update(self, dt):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in ennemy_hit_list:
self.health -= 1
print(self.health)
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
self.direction = "left"
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
self.direction = "right"
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.bullet_timer -= dt # Subtract the time since the last tick.
if keys[pygame.K_x]:
self.fire_timer -= dt
if self.bullet_timer <= 0:
self.bullet_timer = 100 # Bullet ready.
if keys: # Left mouse button.
# Create a new bullet instance and add it to the groups.
if self.direction == "right":
Bullet([self.rect.x + self.image.get_width(), self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
else:
Bullet([self.rect.x, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
self.bullet_timer = .5 # Reset the timer.
if self.fire_timer <= 0:
self.fire_timer = 100
if keys:
if self.direction == "right":
Fire([self.rect.x + 170, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
else:
Fire([self.rect.x - 90, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
self.fire_timer = .1
if self.health == 0:
self.kill()
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.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0 # counter variable
def move(self):
'''
enemy movement
'''
distance = 20
speed = 15
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 Bullet(pygame.sprite.Sprite):
IMAGE = None
FLIPPED_IMAGE = None
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
# cache images
if not Bullet.IMAGE:
Bullet.IMAGE = pygame.image.load(os.path.join('images','fireball.png'))
Bullet.FLIPPED_IMAGE = pygame.transform.flip(Bullet.IMAGE, True, False)
if direction == "right":
self.vel = pygame.math.Vector2(750, 0)
self.image = Bullet.IMAGE
else:
self.vel = pygame.math.Vector2(-750, 0)
self.image = Bullet.FLIPPED_IMAGE
self.pos = pygame.math.Vector2(pos)
self.rect = self.image.get_rect(center=pos)
def update(self, dt):
# Add the velocity to the position vector to move the sprite
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
class Fire(pygame.sprite.Sprite):
IMAGE = None
FLIPPED_IMAGE = None
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
# cache images
if not Fire.IMAGE:
Fire.IMAGE = pygame.image.load(os.path.join('images','fire_drag.png'))
Fire.FLIPPED_IMAGE = pygame.transform.flip(Fire.IMAGE, True, False)
if direction == "right":
self.image = Fire.IMAGE
self.vel = pygame.math.Vector2(0, 0)
else:
self.image = Fire.FLIPPED_IMAGE
self.vel = pygame.math.Vector2(0, 0)
self.pos = pygame.math.Vector2(pos)
self.rect = self.image.get_rect(center=pos)
def update(self, dt):
self.too = True
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if self.too == True:
self.kill()
class Level():
def bad(lvl,eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'cookie1.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):
print(lvl)
enemy_list is defined in global namespace, in main.py, thus it is not accessible in the module Sprite.py.
Add an additional argument to the update method of the class Player:
class Player(pygame.sprite.Sprite):
# [...]
def update(self, dt, enemy_list):
# [...]
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
# [...]
Since player is a member of all_sprites, you have to add the argument to the update methods of the other sprites (Enemy, Bullet), too.
Pass enemy_list to the update method all_sprites in the main application loop. Note the update method of Player is invoked by all_sprites.update, thus player.update(dt, enemy_list) is superflous:
while main == True:
# [...]
all_sprites.update(dt, enemy_list)
# [...]

Changing Sprite animation Speed (pygame)

I managed to animate my player using my 'animate' function that I had created - Cycles through the list of character images per frame while the character is moving. However, I also found that this happens at the same speed in which the game is running; and was hoping there was a simple way to change it so that the sprite animates at a slower speed than the game FPS.
Here is my sprite class code:
class Civilian(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites, game.player1group, game.bothplayers
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self._layer = PLAYER1_LAYER
self.image = pg.Surface((61, 67))
self.rect = self.image.get_rect()
self.hit_rect = PLAYER_HIT_RECT
self.hit_rect.center = self.rect.center
self.playerspeed = 90
self.vel = vec(0, 0)
self.pos = vec(x , y)
self.move = 0
self.speedboost = False
self.last_dir = 'down'#
self.anim_speed = 0
def animate(self, direction):
if direction == 'right':
self.spritesheet = pg.image.load('walk right civ.png') # Loading the right directional movement spritesheet into the variable
if direction == 'left':
self.spritesheet = pg.image.load('walk left civ.png')
if direction == 'up':
self.spritesheet = pg.image.load('walk up civ.png')
if direction == 'down':
self.spritesheet = pg.image.load('walk down civ.png')
self.frames = [] # List which will contain each cell of the spritesheet
# Adding the cells to the list #
self.frames.append(self.spritesheet.subsurface(pg.Rect(0, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(61, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(122, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(183, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(244, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(305, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(366, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(427, 0, 61, 67)).convert_alpha())
self.frames.append(self.spritesheet.subsurface(pg.Rect(488, 0, 61, 67)).convert_alpha())
# Number of frames/cells
self.frames_number = len(self.frames)
# Current animation frame
self.current_frame = 0
# Frame rectangle
self.frame_rect = self.frames[0].get_rect()
self.last_dir = direction
def get_keys(self):
self.vel= vec(0, 0)
keys = pg.key.get_pressed()
if keys[pg.K_a]: # Const. subtracts player speed from velocity (E.g. Moves sprite to the left)
self.vel.x= -self.playerspeed
self.move += 1
self.moving = 'left' # Uses different spritesheet depending on direction
elif keys[pg.K_d]: # Const. adds player speed value to velocity (E.g. Moves sprite to the right)
self.vel.x= self.playerspeed
self.move += 1
self.moving = 'right'
elif keys[pg.K_w]: # Const. subtracts player speed value from y velocity (Moves player upwards; opposite)
self.vel.y= -self.playerspeed
self.move += 1
self.moving = 'up'
elif keys[pg.K_s]: # Const. adds player speed value to y velocity (Moves player downwards; opposite)
self.vel.y= self.playerspeed
self.move += 1
self.moving = 'down'
def add_speed(self):
pass
def collide_with_player2(self, dir, ifColliding):
if dir == 'x':
collides = pg.sprite.spritecollide(self, self.game.player2group, False, collide_player_hit_rect)
if collides:
if self.vel.x > 0:
self.pos.x = collides[0].hit_rect.left - self.hit_rect.width / 2
if self.vel.x < 0:
self.pos.x = collides[0].hit_rect.right + self.hit_rect.width / 2
self.vel.x = 0
self.hit_rect.centerx = self.pos.x
print("collide x")
if random.randint(0, 100) <= 4:
random.choice(self.game.thief_hit_sounds).play()
self.ifColliding = True
if dir == 'y':
collides = pg.sprite.spritecollide(self, self.game.player2group, False, collide_player_hit_rect)
if collides:
if self.vel.y > 0:
self.pos.y = collides[0].hit_rect.top - self.hit_rect.height / 2
if self.vel.y < 0:
self.pos.y = collides[0].hit_rect.bottom + self.hit_rect.height / 2
self.vel.y = 0
self.hit_rect.centery = self.pos.y
print("collide y")
if random.randint(0, 100) <= 4:
random.choice(self.game.thief_hit_sounds).play()
self.ifColliding = True
def collide_with_walls(self, dir):
if dir == 'x':
collides = pg.sprite.spritecollide(self, self.game.walls, False, collide_hit_rect)
if collides:
if self.vel.x > 0:
self.pos.x = collides[0].rect.left - self.hit_rect.width / 2
if self.vel.x < 0:
self.pos.x = collides[0].rect.right + self.hit_rect.width / 2
self.vel.x = 0
self.hit_rect.centerx = self.pos.x
if dir == 'y':
collides = pg.sprite.spritecollide(self, self.game.walls, False, collide_hit_rect)
if collides:
if self.vel.y > 0:
self.pos.y = collides[0].rect.top - self.hit_rect.height / 2
if self.vel.y < 0:
self.pos.y = collides[0].rect.bottom + self.hit_rect.height / 2
self.vel.y = 0
self.hit_rect.centery = self.pos.y
def update(self):
# frame updates
self.anim_speed += 1
self.moving = 'idle'
self.animate(self.last_dir) # Sets the down spritesheet as default
self.get_keys()
if self.moving == 'up':
self.animate(self.moving) # Uses the up-movement spritesheet if char moving upwards
if self.moving == 'down':
self.animate(self.moving) # Same as above, different direction
if self.moving == 'left':
self.animate(self.moving)
if self.moving == 'right':
self.animate(self.moving)
self.ifColliding = False
self.rect.center = self.pos
self.pos += self.vel * self.game.dt
self.hit_rect.centerx = self.pos.x
self.collide_with_walls('x'), self.collide_with_player2('x', self.ifColliding)
self.hit_rect.centery = self.pos.y
self.collide_with_walls('y'), self.collide_with_player2('y', self.ifColliding)
self.rect.center = self.hit_rect.midtop
if self.ifColliding == True:
Thief.health -= COL_DAMAGE
print(Thief.health)
self.current_frame = (self.current_frame + self.move) % self.frames_number
if self.moving == 'idle':
self.current_frame = 0
self.image = self.frames[self.current_frame] # Image of sprite changes as program cycles through the sheet
You can decouple the animation rate from the frame rate by tying it to time in some way.
One way is to use pygame.time.get_ticks() to return the number of milliseconds since pygame.init() was called. If you store this value, you can measure how much time has elapsed and animate appropriately.
def update(self):
self.elapsed = pygame.time.get_ticks() - self.elapsed
if self.elapsed > 500: # animate every half second
self.animate()
Note: you'll also need to initialise self.elapsed in the constructor.
This is the code I made to animate a sprite having the frame rate at 60 and the sprite animating slower than frame rate
import pygame
import glob
def fps():
fr = "V.3 Fps: " + str(int(clock.get_fps()))
frt = font.render(fr, 1, pygame.Color("coral"))
return frt
class MySprite(pygame.sprite.Sprite):
def __init__(self, action):
super(MySprite, self).__init__()
self.action = action
# This is to slow down animation # takes the frame now and...
self.elapsed = 0
self.images = []
self.temp_imgs = []
self.load_images()
self.count = 0
def load_images(self):
l_imgs = glob.glob(f"png\\{self.action}*.png")
for img in l_imgs:
if len(img) == len(l_imgs[0]):
self.images.append(pygame.image.load(img))
else:
self.temp_imgs.append(pygame.image.load(img))
self.images.extend(self.temp_imgs)
self.index = 0
self.rect = pygame.Rect(5, 5, 150, 198)
def update(self):
self.count += 1
if self.index == len(self.images):
self.index = 0
self.image = self.images[self.index]
if self.count > 2:
#self.image = self.images[self.index]
self.index += 1
self.count = 0
def group_sprites(self):
return pygame.sprite.Group(self)
def group():
"Dictionary of group of sprites"
dici = {}
actions = "idle walk run jump dead"
actions = actions.split()
for action in actions:
dici[action] = MySprite(action).group_sprites()
return dici
def main():
global clock
global font
SIZE = 600, 600
FPS = 60
pygame.init()
action = group()
my_group = action["idle"]
screen = pygame.display.set_mode(SIZE)
pygame.display.set_caption("Game v.3")
font = pygame.font.SysFont("Arial", 60)
clock = pygame.time.Clock()
loop = 1
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
my_group = action["walk"]
if event.key == pygame.K_UP:
my_group = action["jump"]
if event.key == pygame.K_SPACE:
my_group = action["idle"]
if event.key == pygame.K_RIGHT:
my_group = action["run"]
if event.key == pygame.K_DOWN:
my_group = action["dead"]
my_group.update()
screen.fill((0, 0, 0))
my_group.draw(screen)
screen.blit(fps(), (10, 0))
pygame.display.update()
clock.tick(FPS)
pygame.quit()
if __name__ == '__main__':
main()
Read this article... slow down animation without affecting the frame rate
Dowload images and put the in a directory called png

Categories