This question already has answers here:
Why is my PyGame application not running at all?
(2 answers)
Why is nothing drawn in PyGame at all?
(2 answers)
How to run multiple while loops at a time in Pygame
(1 answer)
Pygame window freezes when it opens
(1 answer)
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 2 years ago.
so my issue is with my player class, I'm still trying to understand classes.
So, basically, I'm using this player movement code from one of Pyganim's examples but I can't seem to implement it into this player class of mine, probably because I don't really understand classes.
For starters. My player never gets displayed now, every thing worked before I tried to add classes. I've probably just over complicated things trying to use this movement. My code is:
import pygame, sys, random, pyganim
from pygame.locals import *
class Scene(object):
screen = pygame.display.set_mode((800, 600))
class Intro(Scene):
def __init__(self):
self.c = (32, 32, 100)
def draw(self):
Scene.screen.fill(self.c)
def update(self):
# since scenes are classes, they have a state that we can modify
r,g,b = self.c
r += 1
g += 1
b += 2
if r > 255: r = 0
if g > 255: g = 0
if b > 255: b = 0
self.c = r, g, b
def handle(self, event):
# move to Menu-scene when space is pressed
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
# returning a scene from handle or update changes the current scene
return Menu()
class Menu(Scene):
def draw(self):
# draw menu
Scene.screen.fill((200, 200, 100))
def update(self):
pass
# do something
def handle(self, event):
# handle menu input
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
return Game()
if event.key == pygame.K_b:
return Intro()
class Game(Scene):
def draw(self):
Scene.screen.fill((0,0,0))
bg = pygame.image.load('C:\\Users\\PAx\\Desktop\\stuff\\fort.png')
Scene.screen.blit(pygame.transform.scale(bg,(400,300)),(0,0))
def update(self):
pass
def handle(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
return meun()
class Player(pygame.sprite.Sprite):
def __init__(self):
self.UP = 'up'
self.DOWN = 'down'
self.LEFT = 'left'
self.RIGHT = 'right'
self.direction = DOWN # player starts off facing down (front)
# load the "standing" sprites (these are single images, not animations)
self.playerWidth, playerHeight = front_standing.get_size()
# creating the PygAnimation objects for walking/running in all directions
self.animTypes = 'back_run back_walk front_run front_walk left_run left_walk'.split()
self.animObjs = {}
for animType in animTypes:
self.imagesAndDurations = [('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_%s.%s.gif' % (animType, str(num).rjust(3, '0')), 1) for num in range(6)]
self.animObjs[animType] = pyganim.PygAnimation(imagesAndDurations)
# create the right-facing sprites by copying and flipping the left-facing sprites
self.animObjs['right_walk'] = animObjs['left_walk'].getCopy()
self.animObjs['right_walk'].flip(True, False)
self.animObjs['right_walk'].makeTransformsPermanent()
self.animObjs['right_run'] = animObjs['left_run'].getCopy()
self.animObjs['right_run'].flip(True, False)
self.animObjs['right_run'].makeTransformsPermanent()
# have the animation objects managed by a conductor.
# With the conductor, we can call play() and stop() on all the animtion
# objects at the same time, so that way they'll always be in sync with each
# other.
self.moveConductor = pyganim.PygConductor(animObjs)
self.x = 100
self.y = 100
self.WALKRATE = 10
self.RUNRATE = 18
self.pygame.sprite.Sprite.__init__(self) #init the Pygame sprite
#load all images
#load the player image
self.front_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_front.gif')
self.back_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_back.gif')
self.left_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_left.gif')
self.right_standing = pygame.transform.flip(left_standing, True, False)
self.running = moveUp = moveDown = moveLeft = moveRight = False
def update(self):
for event in pygame.event.get():
if event.key == KEYDOWN:
if event.key in (K_LSHIFT, K_RSHIFT):
# player has started running
running = True
if event.key == K_UP:
moveUp = True
moveDown = False
if not moveLeft and not moveRight:
# only change the direction to up if the player wasn't moving left/right
direction = UP
elif event.key == K_DOWN:
moveDown = True
moveUp = False
if not moveLeft and not moveRight:
direction = DOWN
elif event.key == K_LEFT:
moveLeft = True
moveRight = False
if not moveUp and not moveDown:
direction = LEFT
elif event.key == K_RIGHT:
moveRight = True
moveLeft = False
if not moveUp and not moveDown:
direction = RIGHT
elif event.type == KEYUP:
if event.key in (K_LSHIFT, K_RSHIFT):
# player has stopped running
running = False
if event.key == K_UP:
moveUp = False
# if the player was moving in a sideways direction before, change the direction the player is facing.
if moveLeft:
direction = LEFT
if moveRight:
direction = RIGHT
elif event.key == K_DOWN:
moveDown = False
if moveLeft:
direction = LEFT
if moveRight:
direction = RIGHT
elif event.key == K_LEFT:
moveLeft = False
if moveUp:
direction = UP
if moveDown:
direction = DOWN
elif event.key == K_RIGHT:
moveRight = False
if moveUp:
direction = UP
if moveDown:
direction = DOWN
if moveUp or moveDown or moveLeft or moveRight:
# draw the correct walking/running sprite from the animation object
moveConductor.play() # calling play() while the animation objects are already playing is okay; in that case play() is a no-op
if running:
if direction == UP:
animObjs['back_run'].blit(screen, (x, y))
elif direction == DOWN:
animObjs['front_run'].blit(screen, (x, y))
elif direction == LEFT:
animObjs['left_run'].blit(screen, (x, y))
elif direction == RIGHT:
animObjs['right_run'].blit(screen, (x, y))
else:
# walking
if direction == UP:
animObjs['back_walk'].blit(screen, (x, y))
elif direction == DOWN:
animObjs['front_walk'].blit(screen, (x, y))
elif direction == LEFT:
animObjs['left_walk'].blit(screen, (x, y))
elif direction == RIGHT:
animObjs['right_walk'].blit(screen, (x, y))
# actually move the position of the player
if running:
rate = RUNRATE
else:
rate = WALKRATE
if moveUp:
y -= rate
if moveDown:
y += rate
if moveLeft:
x -= rate
if moveRight:
x += rate
else:
# standing still
moveConductor.stop() # calling stop() while the animation objects are already stopped is okay; in that case stop() is a no-op
if direction == UP:
Scene.screen.blit(back_standing, ((x, y)))
elif direction == DOWN:
Scene.screen.blit(front_standing, ((x, y)))
elif direction == LEFT:
screen.blit(left_standing, ((x, y)))
elif direction == RIGHT:
screen.blit(right_standing, (x, y))
# make sure the player does move off the screen
if x < 0:
x = 0
if x > WINDOWWIDTH - playerWidth:
x = WINDOWWIDTH - playerWidth
if y < 0:
y = 0
if y > WINDOWHEIGHT - playerHeight:
y = WINDOWHEIGHT - playerHeight
pygame.init()
clock = pygame.time.Clock()
Scene.screen = pygame.display.set_mode((800, 600))
scene = Intro()
while True:
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
scene = scene.handle(e) or scene
scene = scene.update() or scene
scene.draw()
Player.update(scene)
pygame.display.flip()
clock.tick(20)
This is the new and working code.
I changed handle and update and added a lot of self where needed. Wasn't sure how to add in draw() this could probably be cleaned up and written better but at least it works.
import pygame, sys, random, pyganim
from pygame.locals import *
class Scene(object):
screen = pygame.display.set_mode((800, 600))
class Intro(Scene):
def __init__(self):
self.c = (32, 32, 100)
def draw(self):
Scene.screen.fill(self.c)
def update(self):
# since scenes are classes, they have a state that we can modify
r,g,b = self.c
r += 1
g += 1
b += 2
if r > 255: r = 0
if g > 255: g = 0
if b > 255: b = 0
self.c = r, g, b
def handle(self, event):
# move to Menu-scene when space is pressed
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
# returning a scene from handle or update changes the current scene
return Menu()
class Menu(Scene):
def draw(self):
# draw menu
Scene.screen.fill((200, 200, 100))
def update(self):
pass
# do something
def handle(self, event):
# handle menu input
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
return Game()
if event.key == pygame.K_b:
return Intro()
class Game(Scene):
def draw(self):
Scene.screen.fill((0,0,0))
bg = pygame.image.load('C:\\Users\\PAx\\Desktop\\stuff\\fort.png')
Scene.screen.blit(pygame.transform.scale(bg,(400,300)),(0,0))
def update(self):
pass
def handle(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
return Menu()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.UP = 'up'
self.DOWN = 'down'
self.LEFT = 'left'
self.RIGHT = 'right'
self.front_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_front.gif')
self.back_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_back.gif')
self.left_standing = pygame.image.load('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_left.gif')
self.right_standing = pygame.transform.flip(self.left_standing, True, False)
self.running = self.moveUp = self.moveDown = self.moveLeft = self.moveRight = False
self.direction = self.DOWN # player starts off facing down (front)
# load the "standing" sprites (these are single images, not animations)
self.playerWidth, self.playerHeight = self.front_standing.get_size()
# creating the PygAnimation objects for walking/running in all directions
self.animTypes = 'back_run back_walk front_run front_walk left_run left_walk'.split()
self.animObjs = {}
for self.animType in self.animTypes:
self.imagesAndDurations = [('C:\\Users\PAx\\Desktop\\stuff\\gameimages\\crono_%s.%s.gif' % (self.animType, str(num).rjust(3, '0')), 1) for num in range(6)]
self.animObjs[self.animType] = pyganim.PygAnimation(self.imagesAndDurations)
# create the right-facing sprites by copying and flipping the left-facing sprites
self.animObjs['right_walk'] = self.animObjs['left_walk'].getCopy()
self.animObjs['right_walk'].flip(True, False)
self.animObjs['right_walk'].makeTransformsPermanent()
self.animObjs['right_run'] = self.animObjs['left_run'].getCopy()
self.animObjs['right_run'].flip(True, False)
self.animObjs['right_run'].makeTransformsPermanent()
# have the animation objects managed by a conductor.
# With the conductor, we can call play() and stop() on all the animtion
# objects at the same time, so that way they'll always be in sync with each
# other.
self.moveConductor = pyganim.PygConductor(self.animObjs)
self.x = 100
self.y = 100
self.WALKRATE = 10
self.RUNRATE = 18
self.WINDOWWIDTH = 640
self.WINDOWHEIGHT = 480
#load all images
#load the player image
def update(self, scene):
if self.moveUp or self.moveDown or self.moveLeft or self.moveRight:
# draw the correct walking/running sprite from the animation object
self.moveConductor.play() # calling play() while the animation objects are already playing is okay; in that case play() is a no-op
if self.running:
if self.direction == self.UP:
self.animObjs['back_run'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.DOWN:
self.animObjs['front_run'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.LEFT:
self.animObjs['left_run'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.RIGHT:
self.animObjs['right_run'].blit(Scene.screen, (self.x, self.y))
else:
# walking
if self.direction == self.UP:
self.animObjs['back_walk'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.DOWN:
self.animObjs['front_walk'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.LEFT:
self.animObjs['left_walk'].blit(Scene.screen, (self.x, self.y))
elif self.direction == self.RIGHT:
self.animObjs['right_walk'].blit(Scene.screen, (self.x, self.y))
# actually move the position of the player
if self.running:
rate = self.RUNRATE
else:
rate = self.WALKRATE
if self.moveUp:
self.y -= rate
if self.moveDown:
self.y += rate
if self.moveLeft:
self.x -= rate
if self.moveRight:
self.x += rate
else:
# standing still
self.moveConductor.stop() # calling stop() while the animation objects are already stopped is okay; in that case stop() is a no-op
if self.direction == self.UP:
Scene.screen.blit(self.back_standing, ((self.x, self.y)))
elif self.direction == self.DOWN:
Scene.screen.blit(self.front_standing, ((self.x, self.y)))
elif self.direction == self.LEFT:
Scene.screen.blit(self.left_standing, ((self.x, self.y)))
elif self.direction == self.RIGHT:
Scene.screen.blit(self.right_standing, (self.x, self.y))
# make sure the player does move off the screen
if self.x < 0:
self.x = 0
if self.x > self.WINDOWWIDTH - self.playerWidth:
self.x = self.WINDOWWIDTH - self.playerWidth
if self.y < 0:
self.y = 0
if self.y > self.WINDOWHEIGHT - self.playerHeight:
self.y = self.WINDOWHEIGHT - self.playerHeight
def handle(self, event):
if event.type == KEYDOWN:
if event.key in (K_LSHIFT, K_RSHIFT):
# player has started running
running = True
if event.key == K_UP:
self.moveUp = True
self.moveDown = False
if not self.moveLeft and not self.moveRight:
# only change the direction to up if the player wasn't moving left/right
self.direction = self.UP
elif event.key == K_DOWN:
self.moveDown = True
self.moveUp = False
if not self.moveLeft and not self.moveRight:
self.direction = self.DOWN
elif event.key == K_LEFT:
self.moveLeft = True
self.moveRight = False
if not self.moveUp and not self.moveDown:
self.direction = self.LEFT
elif event.key == K_RIGHT:
self.moveRight = True
self.moveLeft = False
if not self.moveUp and not self.moveDown:
self.direction = self.RIGHT
elif event.type == KEYUP:
if event.key in (K_LSHIFT, K_RSHIFT):
# player has stopped running
self.running = False
if event.key == K_UP:
self.moveUp = False
# if the player was moving in a sideways direction before, change the direction the player is facing.
if self.moveLeft:
self.direction = self.LEFT
if self.moveRight:
self.direction = self.RIGHT
elif event.key == K_DOWN:
self.moveDown = False
if self.moveLeft:
self.direction = self.LEFT
if self.moveRight:
self.direction = self.RIGHT
elif event.key == K_LEFT:
self.moveLeft = False
if self.moveUp:
self.direction = self.UP
if self.moveDown:
self.direction = self.DOWN
elif event.key == K_RIGHT:
self.moveRight = False
if self.moveUp:
self.direction = self.UP
if self.moveDown:
self.direction = self.DOWN
pygame.init()
clock = pygame.time.Clock()
Scene.screen = pygame.display.set_mode((800, 600))
player = Player()
scene = Intro()
while True:
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
scene = scene.handle(e) or scene
player.handle(e) or scene
scene = scene.update() or scene
scene.draw()
player.update(scene)
pygame.display.flip()
clock.tick(20)
Related
I'm trying out Pygame and started with a classic, creating the Snake game, the issue is that I try to blit the 'apple' on the board but the apple doesn't appear up until I crash in the walls this is when I set fps = 0 and I freeze the game. I've noticed that when I increase the fps to something like 100 or more the apple starts appearing and disappearing.
import random
import pygame
class Game:
def __init__(self):
pygame.init()
self.board = pygame.display.set_mode((400, 400))
self.apple = Apple(self.board)
self.snake = Snake(self.board, self)
self.direction = 'right'
self.going = True
self.fps = 5
def play(self):
clock = pygame.time.Clock()
while self.going:
clock.tick(self.fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.going = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.direction = 'left'
if event.key == pygame.K_RIGHT:
self.direction = 'right'
if event.key == pygame.K_UP:
self.direction = 'up'
if event.key == pygame.K_DOWN:
self.direction = 'down'
self.apple.spawn()
self.snake.move(self.direction)
def game_over(self):
self.fps = 0
def get_fps(self):
return self.fps
class Snake:
def __init__(self, board, game):
self.board = board
self.image = pygame.Surface((10, 10))
self.game = game
self.length = 5
self.x = [10] * self.length
self.y = [10] * self.length
def move(self, direction):
for i in range(self.length-1, 0, -1):
self.x[i] = self.x[i-1]
self.y[i] = self.y[i-1]
if direction == 'right':
self.x[0] += 10
if direction == 'left':
self.x[0] -= 10
if direction == 'up':
self.y[0] -= 10
if direction == 'down':
self.y[0] += 10
if self.x[0] == self.board.get_width() or self.x[0] == -10 or\
self.y[0] == self.board.get_height() or self.y[0] == -10:
self.game.game_over()
if self.game.get_fps() != 0:
self.draw()
def draw(self):
self.board.fill((0, 255, 0))
for i in range(self.length):
self.board.blit(self.image, (self.x[i], self.y[i]))
pygame.display.flip()
class Apple:
def __init__(self, board):
self.board = board
self.image = pygame.Surface((10, 10))
self.image.fill(pygame.color.Color('red'))
self.x = random.randint(0, self.board.get_width())
self.y = random.randint(0, self.board.get_height())
def spawn(self):
self.board.blit(self.image, (self.x, self.y))
pygame.display.flip()
if __name__ == '__main__':
game = Game()
game.play()
Clear the display at the begin of the application loop and update the dispaly at the end of the application loop:
class Game:
# [...]
def play(self):
clock = pygame.time.Clock()
while self.going:
clock.tick(self.fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.going = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.direction = 'left'
if event.key == pygame.K_RIGHT:
self.direction = 'right'
if event.key == pygame.K_UP:
self.direction = 'up'
if event.key == pygame.K_DOWN:
self.direction = 'down'
self.board.fill((0, 255, 0)) # <---
self.apple.spawn()
self.snake.move(self.direction)
pygame.display.flip() # <---
class Snake:
# [...]
def draw(self):
# self.board.fill((0, 255, 0)) <-- DELETE
for i in range(self.length):
self.board.blit(self.image, (self.x[i], self.y[i]))
# pygame.display.flip() <-- DELETE
class Apple:
# [...]
def spawn(self):
self.board.blit(self.image, (self.x, self.y))
#pygame.display.flip() <-- DELETE
fill clears the entire Surface. Everything that was previously drawn is lost.
One update of the display at the end of the application loop is sufficient. Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering.
This is my first post so please let me know if I should alter anything.
I am attempting to create a simple test 2D platformer, this is the first pygame project I've undertaken without a tutorial. The issue I am encountering is due to this can_jump variable. I am using this variable to stop the player from being able to jump when not on a platform or the bottom of the screen. I had attempted other methods but this is where I have ended up. I feel I've gotten so close.
The issue at the moment appears to be that the variable self.can_jump1 continuously swaps between True and False in the platform loop (at exactly 3 Falses for every True I have noticed) when standing on a platform, I have isolated it to the commented section in collision_check(). I don't know why this happens, and as far as I can tell the rest of the code works as I want it.
I would be happy to provide any extra information/clarification if need be.
Here's my code:
import pygame, sys, random
#---Classes----------------------------------------#
class Block(pygame.sprite.Sprite): #inherited by rest of classes
def __init__(self,path,x_pos,y_pos,size_x,size_y):
super().__init__()
self.image = pygame.transform.scale(pygame.image.load(path).convert_alpha(),(size_x,size_y))
self.rect = self.image.get_rect(center = (x_pos,y_pos))
class PlatformRect(pygame.sprite.Sprite):
def __init__(self,x_pos,y_pos,size_x,size_y,colour,players):
super().__init__()
self.center_x = x_pos
self.center_y = y_pos
self.size_x = size_x
self.size_y = size_y
self.colour = colour
self.players = players
self.can_jump1 = None
self.image = pygame.Surface((self.size_x,self.size_y))
self.image.fill(self.colour)
self.rect = self.image.get_rect(center = (self.center_x,self.center_y))
def collision_check(self):
if pygame.sprite.spritecollide(self,self.players,False):
collision_paddle = pygame.sprite.spritecollide(self,self.players,False)[0].rect
if abs(self.rect.top - collision_paddle.bottom) < 10:
collision_paddle.bottom = self.rect.top
player.movement_y = 0
self.can_jump1 = True
else: #error isolated to here,consistently fluctuates between True and False, not sure why
self.can_jump1 = False
print(self.can_jump1) #if standing on platform should only produce True, everywhere else produce False
def can_jump_check(self):
return self.can_jump1
def update(self):
self.collision_check()
class Player(Block):
def __init__(self,path,x_pos,y_pos,size_x,size_y,speed_x,acceleration_y):
super().__init__(path,x_pos,y_pos,size_x,size_y)
self.size_x = size_x
self.size_y = size_y
self.speed_x = speed_x
self.acceleration_y = acceleration_y
self.movement_x = 0
self.movement_y = 0
def screen_constrain(self):
if self.rect.bottom >= sresy:
self.rect.bottom = sresy
if self.rect.left <= 0:
self.rect.left = 0
if self.rect.right >= sresx:
self.rect.right = sresx
def update(self):
if abs(self.movement_y) <= 9:
self.movement_y += GRAVITY
self.rect.centery += self.movement_y
self.rect.centerx += self.movement_x
self.screen_constrain()
class GameManager:
def __init__(self,player_group,platform_group):
self.player_group = player_group
self.platform_group = platform_group
self.can_jump = True
def run_game(self):
#---drawing---#
self.player_group.draw(screen)
self.platform_group.draw(screen)
#---updating---#
self.player_group.update()
self.platform_group.update()
def game_checking(self):
#---checking---#
for sprite_platform in self.platform_group.sprites():
if not sprite_platform.can_jump_check() == True:
self.can_jump = False
else:
self.can_jump = True
return self.can_jump
#---Setup------------------------------------------#
#---constants-----#
global GRAVITY
GRAVITY = 0.25
#---Gamevariables-----#
can_jump = True
#---colour---#
bg_colour = (50,50,50)
white_colour = (255,255,255)
black_colour = (0,0,0)
#---res---#
resx = 900
resy = 675
sresx = resx - 50
sresy = resy - 50
#---start window-----#
clock = pygame.time.Clock()
screendisplay = pygame.display.set_mode((resx,resy))
screendisplay.fill(bg_colour)
pygame.display.set_caption("PlatformerGame1 started 09/02/2021")
screen = pygame.Surface((sresx,sresy))
screen.fill(white_colour)
#---startgame-----#
player = Player("assets\\pics\\TestPlayerFigure1_256512.png",100,100,32,64,10,30)
player_group = pygame.sprite.GroupSingle()
player_group.add(player)
platform1 = PlatformRect(100,550,100,10,black_colour,player_group)
platform_group = pygame.sprite.Group()
platform_group.add(platform1)
game_manager = GameManager(player_group,platform_group)
#---Loop--------------------------------------------#
while True:
#---events-----#
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_SPACE:
if can_jump == True:
player.movement_y = 0
player.movement_y -= player.acceleration_y * GRAVITY
if event.key == pygame.K_a:
player.movement_x -= player.speed_x
if event.key == pygame.K_d:
player.movement_x += player.speed_x
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
if player.movement_y < 0:
player.movement_y = 0
if event.key == pygame.K_a:
player.movement_x += player.speed_x
if event.key == pygame.K_d:
player.movement_x -= player.speed_x
#---background---#
screendisplay.blit(screen,(25,25))
screen.fill(white_colour)
#---running---#
game_manager.run_game()
if not game_manager.game_checking() == True and player.rect.bottom < sresy:
can_jump = False
else:
can_jump = True
#print(can_jump)
#---updating-----#
pygame.display.update()
clock.tick(60)
When the player is standing on the platform, the player does not collide with the platform and self.can_jump1 is set False. Test if the player is on the platform when the player does not collide with the platform:
self.can_jump1 = False
self.can_jump1 = self.rect.top == self.players.sprites()[0].rect.bottom
Method collision_check:
class PlatformRect(pygame.sprite.Sprite):
# [...]
def collision_check(self):
collide_list = pygame.sprite.spritecollide(self,self.players,False)
if collide_list:
collision_paddle = collide_list[0].rect
if abs(self.rect.top - collision_paddle.bottom) < 10:
collision_paddle.bottom = self.rect.top
player.movement_y = 0
self.can_jump1 = True
else:
self.can_jump1 = self.rect.top == self.players.sprites()[0].rect.bottom
GameManager.game_checking needs to check if the plyer collides with any platform:
class GameManager:
# [...]
def game_checking(self):
#---checking---#
self.can_jump = False
for sprite_platform in self.platform_group.sprites():
if sprite_platform.can_jump_check() == True:
self.can_jump = True
return self.can_jump
This can be simplified with the any function:
class PlatformRect(pygame.sprite.Sprite):
# [...]
def game_checking(self):
#---checking---#
self.can_jump = any(p.can_jump_check() for p in self.platform_group)
return self.can_jump
I am currently making a top down racing game and need a way to detect when a vehicle has completed a full lap. I have chosen to do this by adding images around the circuit, acting as checkpoints, which match with the track surface. When driven over, they output true, all must have output true in order for a lap to count. However, I cannot find a way to detect a collision between my vehicles and an image.
I have tried adding rects to the vehicles and checking if an output can be produced when the two vehicles collide but I just get this error:
AttributeError: 'pygame.Surface' object has no attribute 'rect'
Is there any way I can do this? My code can be seen below.
import pygame
from pygame.locals import *
import math
import time
pygame.init()
F1image = pygame.image.load("F1image.png")
sportsimage = pygame.image.load("sportsimage.png")
bikeimage = pygame.image.load("bikeimage.png")
muscleimage = pygame.image.load("muscleimage.png")
truckimage = pygame.image.load("truckimage.png")
screen = pygame.display.set_mode((1280,720))
xpos = 280
xpos_2 = 280
ypos = 50
ypos_2 = 85
keys = [False, False, False, False]
keys_2 = [False, False, False, False]
direction = 0
direction_2 = 0
forward = 0
forward_2 = 0
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('track.png', [0,0])
class Vehicle:
'Base class for all vehicles (Cars and Motorbikes) in the game'
vehicleCount = 0
def __init__(self, max_speed, acceleration, turning_radius, image):
pygame.sprite.Sprite.__init__(self)
self.max_speed = max_speed
self.acceleration = acceleration
self.turning_radius = turning_radius
self.image = image
self.rect = self.image.get_rect()
Vehicle.vehicleCount = Vehicle.vehicleCount + 1
def displayAmount():
print ("Total number of Vehicle enteries: ", Vehicle.vehicleCount)
def displayVehicle(self):
print ("max speed: ", self.max_speed, "acceleration: ", self.acceleration, "turning radius: ", self.turning_radius)
def checkCollision(self, sprite1, sprite2):
col = pygame.sprite.collide_rect(sprite1, sprite2)
if col == True:
print ("True")
F1 = Vehicle(5.0, 0.1, 2.84, F1image)
sportscar = Vehicle(4.5, 0.2, 2.01, sportsimage)
bike = Vehicle(4.0, 0.15, 2.64, bikeimage)
musclecar = Vehicle(3.5, 0.25, 1.76, muscleimage)
truck = Vehicle(3.0, 0.3, 1.20, truckimage)
print (F1.max_speed)
player1choice = input("Input player 1 choice").lower()
player2choice = input("Input player 2 choice").lower()
if player1choice == ("f1"):
choice1 = F1
elif player1choice == ("sports"):
choice1 = sportscar
elif player1choice == ("muscle"):
choice1 = musclecar
elif player1choice == ("truck"):
choice1 = truck
else:
choice1 = bike
if player2choice == ("f1"):
choice2 = F1
elif player2choice == ("sports"):
choice2 = sportscar
elif player2choice == ("muscle"):
choice2 = musclecar
elif player2choice == ("truck"):
choice2 = truck
else:
choice2 = bike
running = True
while running:
pygame.display.set_caption("Speed Wars")
WHITE = (255, 255, 255)
screen.fill(WHITE)
screen.blit(BackGround.image, BackGround.rect)
#Vehicle 1
if keys[0] == True:
direction += (choice1).turning_radius
if keys[1] == True:
direction -= (choice1).turning_radius
if keys[2] == True and forward <= (choice1).max_speed:
forward += (choice1).acceleration
if keys[3] == True and forward >= 0:
forward -= (choice1).acceleration
#Vehicle 2
if keys_2[0] == True:
direction_2 += (choice2).turning_radius
if keys_2[1] == True:
direction_2 -= (choice2).turning_radius
if keys_2[2] == True and forward_2 <= (choice2).max_speed:
forward_2 += (choice2).acceleration
if keys_2[3] == True and forward_2 >= 0:
forward_2 -= (choice2).acceleration
movex = math.cos(direction / 57.29) * forward
movey = math.sin(direction / 57.29) * forward
xpos += movex
ypos -= movey
movex_2 = math.cos(direction_2 / 57.29) * forward_2
movey_2 = math.sin(direction_2 / 57.29) * forward_2
xpos_2 += movex_2
ypos_2 -= movey_2
rotation = pygame.transform.rotate((choice1).image, direction)
rotation_2 = pygame.transform.rotate((choice2).image, direction_2)
screen.blit(rotation, (xpos, ypos))
screen.blit(rotation_2, (xpos_2, ypos_2))
pygame.display.flip()
time.sleep(0.01)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key == K_LEFT:
keys[0] = True
elif event.key == K_RIGHT:
keys[1] = True
elif event.key == K_UP:
keys[2] = True
elif event.key == K_DOWN:
keys[3] = True
if event.key == K_a:
keys_2[0] = True
elif event.key == K_d:
keys_2[1] = True
elif event.key == K_w:
keys_2[2] = True
elif event.key == K_s:
keys_2[3] = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
keys[0] = False
elif event.key == pygame.K_RIGHT:
keys[1] = False
elif event.key == pygame.K_UP:
keys[2] = False
elif event.key == pygame.K_DOWN:
keys[3] = False
if event.key == pygame.K_a:
keys_2[0] = False
elif event.key == pygame.K_d:
keys_2[1] = False
elif event.key == pygame.K_w:
keys_2[2] = False
elif event.key == pygame.K_s:
keys_2[3] = False
#Collision detection
(choice1).checkCollision((choice2).image, (choice1).image)
The issue is that your code passes in two images into the checkCollision method in your Vehicle class. Then you are passing these two images into the collide_rect function which expects two Sprites.
As a result, you get an error telling you that the two objects passed in(Surfaces in this case), do not contain rects.
To fix this problem:
Use the superclass Sprite for your Vehicle class.
Simply pass in the other sprite into the checkCollision method.
As a result, your checkCollision function should look something like this:
def checkCollision(self, sprite2):
col = pygame.sprite.collide_rect(self, sprite2)
if col == True:
print ("True")
And the call to it should look something like this:
choice1.checkCollision(choice2)
Also, your Vehicle class header should look like this:
class Vehicle(pygame.sprite.Sprite)
Some other issues in your code that should be fixed:
You are receiving input from the keyboard. This is very weird in a game. Instead, you should look at handling this through keyboard input.
You use brackets around choice1 and choice2. This is not needed.
There is code in your main game loop that doesn't need to be ran every frame such as pygame.display.set_caption(). This again is not needed and code like this should go before the main game loop.
The order of you main game loop is different to how it is normally done. First, you should do event handling. Second, do your logic and lastly, do your rendering.
Also, you are making 5 objects and loading many images where only two will be used. Instead, create and load the objects that will be used in the game after the user has decided which car they will play as.
Never use time.sleep() inside a pygame script. This function is evil when used with pygame and causes many errors and bugs. If you would like to use a framerate cap then use a Clock.
I highly recommend you follow these items.
I hope this answer helped you and if you have any further questions please feel free to post a comment below!
To implement checkpoints in a game I would use a solution similar to this: Define a list, group, etc. which contains your checkpoints and set the start point and the active point to the first checkpoint in the list. You can use an itertools.cycle iterator to easily cycle through the points. When the player touches a checkpoint, you set the active_checkpoint to the next point in the iterator and check if it was the start point, if yes, increment your laps counter.
If you want pixel-perfect collision detection, you can give the sprites a self.mask attribute and use pygame.sprite.collide_mask.
Here's a simplified example. I just swap out the images of the sprites here to show which one is active.
import itertools
import pygame as pg
CHECKPOINT_IMG = pg.Surface((120, 20), pg.SRCALPHA)
CHECKPOINT_IMG.fill((120, 60, 0))
CHECKPOINT2_IMG = pg.Surface((120, 20), pg.SRCALPHA)
CHECKPOINT2_IMG.fill((220, 110, 0))
class Player(pg.sprite.Sprite):
def __init__(self, pos, checkpoints):
super().__init__()
self.image = pg.Surface((60, 60), pg.SRCALPHA)
pg.draw.polygon(self.image, (0, 100, 240), [(30, 0), (60, 60), (0, 60)])
self.rect = self.image.get_rect(center=pos)
self.mask = pg.mask.from_surface(self.image)
self.checkpoints = itertools.cycle(checkpoints)
self.active_checkpoint = next(self.checkpoints)
self.start_point = self.active_checkpoint
self.active_checkpoint.image = self.active_checkpoint.image_active
self.laps = -1 # I start at -1 because the start is the first checkpoint.
def handle_event(self, event):
if event.type == pg.MOUSEMOTION:
self.rect.center = event.pos
if pg.sprite.collide_mask(self, self.active_checkpoint):
if self.active_checkpoint == self.start_point: # Completed a round.
self.laps += 1
pg.display.set_caption('Laps: {}'.format(self.laps))
# I change the images of the previous and next checkpoint
# to show which one is active.
self.active_checkpoint.image = self.active_checkpoint.image_inactive
# Switch to the next checkpoint.
self.active_checkpoint = next(self.checkpoints)
self.active_checkpoint.image = self.active_checkpoint.image_active
class Checkpoint(pg.sprite.Sprite):
def __init__(self, pos, angle=0):
super().__init__()
self.image_inactive = pg.transform.rotate(CHECKPOINT_IMG, angle)
self.image_active = pg.transform.rotate(CHECKPOINT2_IMG, angle)
self.image = self.image_inactive
self.rect = self.image.get_rect(center=pos)
self.mask = pg.mask.from_surface(self.image)
class Game:
def __init__(self):
self.screen = pg.display.set_mode((640, 480))
self.done = False
self.clock = pg.time.Clock()
self.checkpoints = (
Checkpoint((100, 200), 0),
Checkpoint((300, 100), 60),
Checkpoint((500, 300), 10),
Checkpoint((200, 300), 30),
)
self.player = Player((20, 20), self.checkpoints)
self.all_sprites = pg.sprite.Group(self.player)
self.all_sprites.add(self.checkpoints)
def run(self):
while not self.done:
self.event_loop()
self.update()
self.draw()
pg.display.flip()
self.clock.tick(60)
def event_loop(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
self.player.handle_event(event)
def update(self):
pass
def draw(self):
self.screen.fill((30, 30, 30))
self.all_sprites.draw(self.screen)
if __name__ == '__main__':
pg.init()
game = Game()
game.run()
pg.quit()
So I'm just learning how to work with classes and getting them to work between each different class. I'm trying to design a game where the user moves around and picks up food and each time the user picks up a piece of food the size of the character increases. I've done something similar before but now that there are classes involved I seem to have a hard time finding which class this should be part of. I added within the sprite update method that if it collides with a cherry then the size of the player should increase by 5 pixels each time. using the code :
self.Player.surface = pygame.transform.scale(self.Player.surface, (pwidth+5, pheight+5))
self.rect = self.Player.surface.get_rect()
Each time the game runs the player size doesn't change and for some reason the game no longer ends after the player has eaten a certain amount of cherries so I was just wondering if I was using a wrong method of changing the size of the player perhaps there may be an easier way to do so? Heres the rest of the code incase it helps at all.
import pygame, glob, random, time
from pygame.locals import *
from LabelClass import *
# CONSTANTS
WIDTH = 400 # Window width
HEIGHT = 400 # Window height
BLACK = (0,0,0) # Colors
WHITE = (255,255,255)
BACKGR = BLACK # Background Color
FOREGR = WHITE # Foreground Color
FPS = 40 # Frames per second
pwidth = 40
pheight = 40
class Food:
def __init__(self,screen,centerx,centery):
self.screen = screen
self.surface = pygame.image.load('cherry.png')
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
def draw(self):
self.screen.blit(self.surface,self.rect)
#pygame.display.update([self.rect])
class Player:
def __init__(self, screen, centerx,
centery, speed, backcolor):
self.surface = pygame.image.load('player.png')
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
self.speed = speed
self.screen = screen
self.backcolor = backcolor
self.dir = ''
def draw(self):
self.screen.blit(self.surface,self.rect)
#pygame.display.update([self.rect])
def move(self):
if self.dir != '':
if self.dir == 'd' and self.rect.bottom < HEIGHT:
self.rect.top += self.speed
if self.dir == 'u' and self.rect.top > 0:
self.rect.top -= self.speed
if self.dir == 'l' and self.rect.left > 0:
self.rect.left -= self.speed
if self.dir == 'r' and self.rect.right < WIDTH:
self.rect.right += self.speed
def jump(self,top,left):
self.rect.top = top
self.rect.left = left
class SpritesGame:
def __init__(self,screen):
self.screen = screen
screen.fill(BLACK)
pygame.display.update()
music_file = getRandomMusic()
pygame.mixer.music.load(music_file)
pygame.mixer.music.play(-1,0.0)
self.music = True
self.Foods = [ ]
self.Eaten = 0
for i in range(20):
self.Foods.append(
Food(self.screen,
WIDTH*random.randint(1,9)//10,
HEIGHT*random.randint(1,9)//10))
for f in self.Foods:
f.draw()
self.Player = Player(screen,WIDTH//2,HEIGHT//2,6,BLACK)
self.PickUpSound = pygame.mixer.Sound('pickup.wav')
self.PlaySound = True
self.startTime = time.clock()
self.endTime = -1
self.Won = False
def update(self):
self.screen.fill(BLACK)
pickedUp = False
for f in self.Foods[:]:
if self.Player.rect.colliderect(f.rect):
self.Foods.remove(f)
self.Foods.append(Food(self.screen,WIDTH*random.randint(1,9)//10,HEIGHT*random.randint(1,9)//10))
pickedUp = True
self.Eaten += 1
self.Player.surface = pygame.transform.scale(self.Player.surface, (pwidth+5, pheight+5))
self.rect = self.Player.surface.get_rect()
#self.rect.center = center
print self.Eaten
if pickedUp and self.PlaySound:
self.PickUpSound.play()
for f in self.Foods:
f.draw()
if self.Eaten == 40:
self.Won = True
self.endTime = time.clock()
self.Player.move()
self.Player.draw()
pygame.display.update()
def toggleMusic(self):
self.music = not self.music
if self.music:
pygame.mixer.music.play(-1,0.0)
else:
pygame.mixer.music.stop()
def run(self):
stop = False
while not stop:
for event in pygame.event.get():
if event.type == QUIT:
stop = True
if event.type == KEYDOWN: # Keeps moving as long as key down
if event.key == K_LEFT or event.key == ord('a'):
self.Player.dir = 'l'
if event.key == K_RIGHT or event.key == ord('d'):
self.Player.dir = 'r'
if event.key == K_UP or event.key == ord('w'):
self.Player.dir = 'u'
if event.key == K_DOWN or event.key == ord('s'):
self.Player.dir = 'd'
if event.type == KEYUP:
if event.key == ord('q'):
stop = True
if event.key == K_ESCAPE:
stop = True
if event.key == K_LEFT or event.key == ord('a'): # End repetition.
self.Player.dir = ''
if event.key == K_RIGHT or event.key == ord('d'):
self.Player.dir = ''
if event.key == K_UP or event.key == ord('w'):
self.Player.dir = ''
if event.key == K_DOWN or event.key == ord('s'):
self.Player.dir = ''
if event.key == ord('x'):
top = random.randint(0,
HEIGHT - self.Player.rect.height)
left = random.randint(0,
WIDTH - self.Player.rect.width)
self.Player.jump(top,left)
if event.key == ord('m'):
self.toggleMusic()
if event.key == ord('p'):
self.PlaySound = not self.PlaySound
mainClock.tick(FPS)
self.update()
if self.Won:
stop = True # END OF WHILE
if self.Won:
self.screen.fill(BLACK)
pygame.display.update()
msg = (str((int(self.endTime)
-int(self.startTime)))
+" seconds to finish. Hit Q.")
L2 = Label(display,WIDTH//2,HEIGHT*7//8,26,msg,WHITE,BLACK)
L2.draw()
stop = False
while not stop:
for event in pygame.event.get():
if event.type == KEYUP:
if event.key == ord('q'):
stop = True
pygame.event.get()
pygame.mixer.music.stop()
def getRandomMusic():
mfiles = glob.glob("*.wav")
mfiles.append(glob.glob("*.mid"))
r = random.randint(0,len(mfiles)-1)
return mfiles[r]
def OpeningScreen(screen):
screen.fill(BLACK)
pygame.display.update()
L1 = Label(display,WIDTH//2,HEIGHT*7//8,26,"Hit Q to Quit, P to Play.",WHITE, BLACK)
L1.draw()
# Properly initiate pygame
pygame.init()
# pygame.key.set_repeat(INT,INT)
# Set the clock up
mainClock = pygame.time.Clock()
# Initialize Display
display = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Sprites and Sounds V06')
OpeningScreen(display)
stop = False
while not stop:
for event in pygame.event.get():
if event.type == QUIT:
stop = True
if event.type == KEYUP:
if event.key == ord('p'):
game = SpritesGame(display)
game.run()
OpeningScreen(display)
if event.key == ord('q'):
stop = True
pygame.quit()
Surface.get_rect() will always return a rect starting at (0,0), and you also are modifying SpritesGame.rect. I think you should change
self.rect = self.Player.surface.get_rect()
to
self.Player.rect.inflate_ip(5, 5)
I'm having trouble flipping a sprite in pygame (I can get it to go right but I want the img to flip on the left key),
I researched how to flip an image and found the pygame.transform.flip, but as a beginner to pygame, I'm not sure how to use it, and the tutorials aren't making sense to me.
Can anyone help me with the code below (I'm not sure if I even put the self.img1 for the flip in the right place)?
import pygame, sys, glob
from pygame import *
class Player:
def __init__(self):
self.x = 200
self.y = 300
self.ani_speed_init = 6
self.ani_speed = self.ani_speed_init
self.ani = glob.glob("walk\Pinks_w*.png")
self.ani.sort()
self.ani_pos = 0
self.ani_max = len(self.ani)-1
self.img = pygame.image.load(self.ani[0])
self.img1 = pygame.transform.flip(self.ani[0], True, False)
self.update(0)
def update(self, pos):
if pos != 0:
self.ani_speed-=1
self.x+=pos
if self.ani_speed == 0:
self.img = pygame.image.load(self.ani[self.ani_pos])
self.ani_speed = self.ani_speed_init
if self.ani_pos == self.ani_max:
self.ani_pos = 0
else:
self.ani_pos+=1
screen.blit(self.img,(self.x,self.y))
h = 400
w = 800
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
player1 = Player()
pos = 0
while True:
screen.fill((0,0,0))
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_RIGHT:
pos = 1
elif event.type == KEYUP and event.key == K_RIGHT:
pos = 0
elif event.type == KEYDOWN and event.key == K_LEFT:
pos = -1
elif event.type == KEYUP and event.key == K_LEFT:
pos = 0
player1.update(pos)
pygame.display.update()
To flip, you would do:
# input
if event.type == KEYDOWN:
if event.key == K_RIGHT:
flip_x = True
elif event.key == K_LEFT:
flip_x = False
elif event.key == K_UP:
flip_y = True
elif event.key == K_DOWN:
flip_y = False
# then to flip
new_image = pygame.transform.flip(original_image, flip_x, flip_y)
Your Player class is not very well readable. As, your names are not easy to understand.
In my version of your code, all I have changed is the names and I have added a check for the value of pos and applied the flip if needed. So, you may need to change the check (if condition), to get the desired results.
And Yes, you don't have a pygame.Sprite, so, the word sprite is misleading, in this case.
My version of your Player class:
class Player:
def __init__(self):
self.x = 200
self.y = 300
self.speed_init = 6
self.images = [pygame.image.load(img) for img in glob.glob("walk\Pinks_w*.png")]
self.index = 0
self.max_index = len(self.images)-1
self.speed = 0
self.img = self.images[self.index]
def update(self, pos):
if pos != 0:
self.speed -= 1
self.x += pos
if not self.speed:
self.speed = self.speed_init
if self.index < self.max_index:
self.index += 1
else:
self.index = 0
self.img = self.images[self.index]
# change True to False if needed, or change the operator.
if pos > 0:
self.img = pygame.transform.flip(self.img,True,False)
screen.blit(self.img,(self.x,self.y))
Update
There was a small problem with the update function. The problem was that since speed was always constant and not 0, the if not self.speed: did not work. So, change the update function to this:
def update(self, pos):
if pos != 0:
self.speed -= 1
self.x += pos
# no more self.speed checks
if self.index < self.max_index:
self.index += 1
else:
self.index = 0
self.img = self.images[self.index]
# change True to False if needed, or change the operator.
if pos < 0:
self.img = pygame.transform.flip(self.img,True,False)
screen.blit(self.img,(self.x,self.y))
Update 2
It seems that there is some kind of typo in your code,
Here's (my version of) the Code, The whole thing.
import pygame, sys, glob
from pygame import *
class Player:
def __init__(self):
self.x = 200
self.y = 300
self.speed_init = 6
self.images = [pygame.image.load(img) for img in glob.glob("walk\Pinks_w*.png")]
self.index = 0
self.max_index = len(self.images)-1
self.speed = 0
self.img = self.images[self.index]
print self.max_index
def update(self, pos):
if pos != 0:
self.speed -= 1
self.x += pos
print self.index, self.max_index
if self.index < self.max_index:
self.index += 1
else:
self.index = 0
self.img = self.images[self.index]
# change True to False if needed, or change the operator.
if pos < 0:
self.img = pygame.transform.flip(self.img,True,False)
screen.blit(self.img,(self.x,self.y))
h = 400
w = 800
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
player1 = Player()
pos = 0
while True:
screen.fill((0,0,0))
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_RIGHT:
pos = 2
elif event.type == KEYUP and event.key == K_RIGHT:
pos = 0
elif event.type == KEYDOWN and event.key == K_LEFT:
pos = -2
elif event.type == KEYUP and event.key == K_LEFT:
pos = 0
player1.update(pos)
pygame.display.update()