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)
Related
So the problem I'm encountering is that I have a function in one file, then when I import that file to another file and try to run the function, I get an error message saying: AttributeError: 'function' object has no attribute 'apply_gravity'
This is the code for the file I'm importing from:
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((32, 64))
self.image.fill('red')
self.rect = self.image.get_rect(topleft = pos)
# Player Movement
self.direction = pygame.math.Vector2(0, 0)
self.speed = 8
self.gravity = 0.8
self.jump_speed = -16
def get_input(self):
self.direction.x = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.direction.x = 1
elif keys[pygame.K_LEFT]:
self.direction.x = -1
else:
self.direction.x = 0
if keys[pygame.K_SPACE]:
self.jump()
def apply_gravity(self):
self.direction.y += self.gravity
self.rect.y += self.direction.y
def jump(self):
self.direction.y = self.jump_speed
def update(self):
self.get_input()
and this is the code where I try to import the function:
import pygame
from tiles import Tile, width
from Settings import tile_size, level_map
from player import Player
class level:
def __init__(self, level_data, surface):
# level setup
self.display_surface = surface
self.setup_level(level_data)
self.world_shift = 0
def setup_level(self, layout):
self.tiles = pygame.sprite.Group()
self.player = pygame.sprite.GroupSingle()
for row_index, row in enumerate(layout):
for column_index, column in enumerate(row):
x = column_index * tile_size
y = row_index * tile_size
if column == 'X':
tile = Tile((x, y), tile_size)
self.tiles.add(tile)
if column == 'P':
player_sprite = Player((x, y))
self.player.add(player_sprite)
def scroll_x(self):
player = self.player.sprite
player_x = player.rect.centerx
direction_x = player.direction.x
if player_x < width/4 and direction_x < 0:
self.world_shift = 8
player.speed = 0
elif player_x > width - width/4 and direction_x > 0:
self.world_shift = -8
player.speed = 0
else:
self.world_shift = 0
player.speed = 6
def horizontal_collision(self):
player = self.player.sprite
player.rect.x += player.direction.x * player.speed
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
elif player.direction.x > 0:
player.rect.right = sprite.rect.left
def vertical_collision(self):
player = self.player.sprites
player.apply_gravity()
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.y > 0:
player.rect.bottom = sprite.rect.top
player.diretcion.y = 0
elif player.direction.y < 0:
player.rect.top = sprite.rect.bottom
def run(self):
#Level Tiles
self.tiles.update(self.world_shift)
self.tiles.draw(self.display_surface)
self.scroll_x()
# Player
self.player.update()
self.horizontal_collision()
self.vertical_collision()
self.player.draw(self.display_surface)
self.scroll_x()
Your problem, I believe, is that you are setting player to the sprites method of an object (a method is a "function") when what you want to do is call that method/function and then set player to its return value. So I think you want this:
player = self.player.sprites
player.apply_gravity()
to be this:
player = self.player.sprites()
player.apply_gravity()
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)
When the frisk sprite is colliding with any sprite in npc_group, I want the former to move back the opposite direction of the side it's moving into, so that it doesn't move through it. I already have the collision detection down, but how do I detect the side of the sprites in npc_group being collided with to achieve this effect?
import pygame
from pygame import sprite
pygame.init()
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("UNDERRUNE")
clock = pygame.time.Clock()
keys_pressed = pygame.key.get_pressed() #updates which keys are being pressed
class Frisk(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
friskd1 = pygame.transform.scale(pygame.image.load('assets/frisk_d1.png').convert_alpha(),(38,58))
friskd2 = pygame.transform.scale(pygame.image.load('assets/frisk_d2.png').convert_alpha(),(38,58))
friskd3 = pygame.transform.scale(pygame.image.load('assets/frisk_d3.png').convert_alpha(),(38,58))
friskd4 = pygame.transform.scale(pygame.image.load('assets/frisk_d4.png').convert_alpha(),(38,58))
self.frisk_d = [friskd1,friskd2,friskd3,friskd4]
friskr1 = pygame.transform.scale(pygame.image.load('assets/frisk_r1.png').convert_alpha(),(34,58))
friskr2 = pygame.transform.scale(pygame.image.load('assets/frisk_r2.png').convert_alpha(),(34,58))
self.frisk_r = [friskr1,friskr2,friskr1,friskr2]
frisku1 = pygame.transform.scale(pygame.image.load('assets/frisk_u1.png').convert_alpha(),(38,58))
frisku2 = pygame.transform.scale(pygame.image.load('assets/frisk_u2.png').convert_alpha(),(38,58))
frisku3 = pygame.transform.scale(pygame.image.load('assets/frisk_u3.png').convert_alpha(),(38,58))
frisku4 = pygame.transform.scale(pygame.image.load('assets/frisk_u4.png').convert_alpha(),(38,58))
self.frisk_u = [frisku1,frisku2,frisku3,frisku4]
friskl1 = pygame.transform.scale(pygame.image.load('assets/frisk_l1.png').convert_alpha(),(34,58))
friskl2 = pygame.transform.scale(pygame.image.load('assets/frisk_l2.png').convert_alpha(),(34,58))
self.frisk_l = [friskl1,friskl2,friskl1,friskl2]
self.walkframe = 0
self.image = self.frisk_d[self.walkframe]
self.rect = self.image.get_rect(center = (320,240))
#self.collisionbox = self.get_rect(bottom = (frisk.x,f))
self.facing = 1
self.vel = 3
self.moving = False
self.moving_x = False
self.moving_y = False
#self.movement = {moveup: False, movedown: False, moveleft: False, moveright:}
def player_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and self.rect.top > 0:
self.rect.y -= self.vel
self.facing = 0
self.moving_y = True
elif keys[pygame.K_DOWN] and self.rect.bottom < 480:
self.rect.y += self.vel
self.facing = 1
self.moving_y = True
else:
self.moving_y = False
if keys[pygame.K_LEFT] and self.rect.left > 0:
self.rect.x -= self.vel
self.facing = 2
self.moving_x = True
elif keys[pygame.K_RIGHT] and self.rect.right < 640:
self.rect.x += self.vel
self.facing = 3
self.moving_x = True
else:
self.moving_x = False
# RUNNING
if keys[pygame.K_LSHIFT]: self.vel = 10
else: self.vel = 6
def frisk_anim(self):
if self.facing == 0:
self.image = self.frisk_u[int(self.walkframe)]
elif self.facing == 1:
self.image = self.frisk_d[int(self.walkframe)]
elif self.facing == 2:
self.image = self.frisk_l[int(self.walkframe)]
elif self.facing == 3:
self.image = self.frisk_r[int(self.walkframe)]
self.walkframe += 0.2
if self.walkframe >= len(self.frisk_d): self.walkframe = 0
if self.moving_x == False and self.moving_y == False: self.walkframe = 0
def collision(self):
if pygame.sprite.spritecollide(self,npc_group,False):
self.rect.x += self.vel
def update(self):
self.player_input()
self.frisk_anim()
self.collision()
class NPC(pygame.sprite.Sprite):
def __init__(self, type):
super().__init__()
if type == 'npc1':
npc1_1 = pygame.transform.scale(pygame.image.load('assets/npc1_1.png').convert_alpha(),(38,58))
npc1_2 = pygame.transform.scale(pygame.image.load('assets/npc1_2.png').convert_alpha(),(38,58))
self.npc_image = [npc1_1,npc1_2]
self.location = (100,100)
if type == 'npc2':
npc2 = pygame.transform.scale(pygame.image.load('assets/npc1_2.png').convert_alpha(),(55,58))
self.npc_image = [npc2]
self.location = (250,50)
self.image = self.npc_image[0]
self.rect = self.image.get_rect(topleft = self.location)
# GROUPS
frisk = pygame.sprite.GroupSingle() # group contains sprite
frisk.add(Frisk())
npc_group = pygame.sprite.Group()
npc_group.add(NPC('npc1'))
# GAME LOOP
while True:
# BACKGROUND
#screen.blit(pygame.image.load('assets/bg.png').convert_alpha(),(0,0))
screen.fill((0,0,0))
# QUIT
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
# FRISK
frisk.draw(screen)
frisk.update()
npc_group.draw(screen)
pygame.display.update() # updates the display surface
clock.tick(30) # tells pygame that this while True loop shouldn't run faster then 60 fps
You know which direction you are moving, so you also know which side you hit an object on. The side of the collision depends on the relative movement of the 2 objects. The relative movement is the difference in movement between the player and the obstacle. If the obstacle stands still, it is the player's movement.
if pygame.sprite.spritecollide(self,npc_group,False):
if self.facing == 0:
self.rect.y += self.vel
elif self.facing == 1:
self.rect.y -= self.vel
elif self.facing == 2:
self.rect.y += self.vel
elif self.facing == 3:
self.rect.y -= self.vel
Alternatively, you can estimate the side by calculating the difference in object position and finding the side with the minimum distance. Note pygame.sprite.spritecollide return a list containing all intersecting sprites:
hit = pygame.sprite.spritecollide(self, npc_group, False):
if hit:
hit_rect = hit[0].rect
dr = abs(self.rect.right - hit_rect.left)
dl = abs(self.rect.left - hit_rect.right)
db = abs(self.rect.bottom - hit_rect.top)
dt = abs(self.rect.top - hit_rect.bottom)
if min(dl, dr) < min(dt, db):
if dl < dr:
self.rect.x -= self.vel
else:
self.rect.x += self.vel
else:
if dt < db:
if dl < dr:
self.rect.y -= self.vel
else:
self.rect.y += self.vel
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)
# [...]
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