Pygame level/menu states - pygame

With my code below what would be the simplest and easiest way to implement game states to control levels? If I wanted to start with a title screen then load a level, and proceed to the next level on completion? If someone could explain the easiest way to handle this that would be great!
import pygame
from pygame import *
WIN_WIDTH = 1120 - 320
WIN_HEIGHT = 960 - 320
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)
DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 0
FLAGS = 0
CAMERA_SLACK = 30
def main():
global level
pygame.init()
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
timer = pygame.time.Clock()
level = 0
bg = Surface((32,32))
bg.convert()
bg.fill(Color("#0094FF"))
up = left = right = False
entities = pygame.sprite.Group()
player = Player(32, 32)
enemy = Enemy(32,32)
platforms = []
x = 0
y = 0
if level == 0:
level = [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",]
#background = pygame.image.load("Untitled.png")
total_level_width = len(level[0]) * 32
total_level_height = len(level) * 32
# build the level
for row in level:
for col in row:
if col == "P":
p = Platform(x, y)
platforms.append(p)
entities.add(p)
if col == "E":
e = ExitBlock(x, y)
platforms.append(e)
entities.add(e)
x += 32
y += 32
x = 0
camera = Camera(complex_camera, total_level_width, total_level_height)
entities.add(player)
entities.add(enemy)
while 1:
timer.tick(60)
for e in pygame.event.get():
if e.type == QUIT: raise SystemExit, "QUIT"
if e.type == KEYDOWN and e.key == K_ESCAPE:
raise SystemExit, "ESCAPE"
if e.type == KEYDOWN and e.key == K_UP:
up = True
if e.type == KEYDOWN and e.key == K_LEFT:
left = True
if e.type == KEYDOWN and e.key == K_RIGHT:
right = True
if e.type == KEYUP and e.key == K_UP:
up = False
if e.type == KEYUP and e.key == K_LEFT:
left = False
if e.type == KEYUP and e.key == K_RIGHT:
right = False
# draw background
for y in range(20):
for x in range(25):
screen.blit(bg, (x * 32, y * 32))
# draw background
#screen.blit(background, camera.apply((0,0)))
#draw entities
for e in entities:
screen.blit(e.image, camera.apply(e))
# update player, update camera, and refresh
player.update(up, left, right, platforms)
enemy.update(platforms)
camera.update(player)
pygame.display.flip()
class Camera(object):
def __init__(self, camera_func, width, height):
self.camera_func = camera_func
self.state = Rect(0, 0, width, height)
def apply(self, target):
try:
return target.rect.move(self.state.topleft)
except AttributeError:
return map(sum, zip(target, self.state.topleft))
def update(self, target):
self.state = self.camera_func(self.state, target.rect)
def complex_camera(camera, target_rect):
l, t, _, _ = target_rect
_, _, w, h = camera
l, t, _, _ = -l + HALF_WIDTH, -t +HALF_HEIGHT, w, h
l = min(0, l) # stop scrolling left
l = max(-(camera.width - WIN_WIDTH), l) # stop scrolling right
t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom
return Rect(l, t, w, h)
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Player(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.xvel = 0
self.yvel = 0
self.onGround = False
self.image = Surface((32,32))
self.image.fill(Color("#0000FF"))
self.image.convert()
self.rect = Rect(200, 1200, 32, 32)
def update(self, up, left, right, platforms):
if self.rect.top > 1440 or self.rect.top < 0:
main()
if self.rect.left > 1408 or self.rect.right < 0:
main()
if up:
if self.onGround:
self.yvel = 0
self.yvel -= 10 # only jump if on the ground
if left:
self.xvel = -10
if right:
self.xvel = 10
if not self.onGround:
self.yvel += 0.3 # only accelerate with gravity if in the air
if self.yvel > 80: self.yvel = 80 # max falling speed
if not(left or right):
self.xvel = 0
self.rect.left += self.xvel # increment in x direction
self.collide(self.xvel, 0, platforms) # do x-axis collisions
self.rect.top += self.yvel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yvel, platforms) # do y-axis collisions
def collide(self, xvel, yvel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if isinstance(p, ExitBlock):
pygame.event.post(pygame.event.Event(QUIT))
if xvel > 0: self.rect.right = p.rect.left
if xvel < 0: self.rect.left = p.rect.right
if yvel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yvel < 0:
self.rect.top = p.rect.bottom
class Enemy(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.yVel = 0
self.xVel = 0
self.image = Surface((32,32))
self.image.fill(Color("#00FF00"))
self.image.convert()
self.rect = Rect(300, 1200, 32, 32)
self.onGround = False
self.right_dis = False
def update(self, platforms):
if not self.onGround:
self.yVel += 0.3
if self.rect.left == 96:
self.right_dis = False
if self.rect.right == 480:
self.right_dis = True
if not self.right_dis:
self.xVel = 2
if self.right_dis:
self.xVel = -2
self.rect.left += self.xVel # increment in x direction
self.collide(self.xVel, 0, platforms) # do x-axis collisions
self.rect.top += self.yVel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yVel, platforms) # do y-axis collisions
def collide(self, xVel, yVel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if xVel > 0: self.rect.right = p.rect.left
if xVel < 0: self.rect.left = p.rect.right
if yVel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yVel < 0:
self.rect.top = p.rect.bottom
class Platform(Entity):
def __init__(self, x, y):
Entity.__init__(self)
#self.image = Surface([32, 32], pygame.SRCALPHA, 32) #makes blocks invisible for much better artwork
self.image = Surface((32,32)) #makes blocks visible for building levels
self.image.convert()
self.rect = Rect(x, y, 32, 32)
def update(self):
pass
class ExitBlock(Platform):
def __init__(self, x, y):
Platform.__init__(self, x, y)
self.image = pygame.image.load("end.png")
if __name__ == "__main__":
main()

First of all, let's get rid of these ugly if-blocks:
for e in pygame.event.get():
if e.type == QUIT: raise SystemExit, "QUIT"
if e.type == KEYDOWN and e.key == K_ESCAPE:
raise SystemExit, "ESCAPE"
if e.type == KEYDOWN and e.key == K_UP:
up = True
if e.type == KEYDOWN and e.key == K_LEFT:
left = True
if e.type == KEYDOWN and e.key == K_RIGHT:
right = True
if e.type == KEYUP and e.key == K_UP:
up = False
if e.type == KEYUP and e.key == K_LEFT:
left = False
if e.type == KEYUP and e.key == K_RIGHT:
right = False
We can rewrite them as:
for e in pygame.event.get():
if e.type == QUIT: raise SystemExit, "QUIT"
if e.type == KEYDOWN and e.key == K_ESCAPE:
raise SystemExit, "ESCAPE"
pressed = pygame.key.get_pressed()
up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]
This will come in handy later.
Back to topic: What we want is a bunch of different Scenes. Each Scene has to be responsible for its own rendering of the screen and event-handling.
Let's try to extract the existing code into a game scene, so that it will be possible to add other scenes later on. We start by creating an empty Scene class that will be the base class of our scenes:
class Scene(object):
def __init__(self):
pass
def render(self, screen):
raise NotImplementedError
def update(self):
raise NotImplementedError
def handle_events(self, events):
raise NotImplementedError
Our plan is to overwrite each method in each sub-class, so we raise NotImplementedErrors in the base class so we easily discover if we forget to do so (we could also use ABCs, but let's keep it simple).
Now let's put everything related to the running-game state (which is basically everything) into a new GameScene class.
class GameScene(Scene):
def __init__(self):
super(GameScene, self).__init__()
level = 0
self.bg = Surface((32,32))
self.bg.convert()
self.bg.fill(Color("#0094FF"))
up = left = right = False
self.entities = pygame.sprite.Group()
self.player = Player(32, 32)
self.enemy = Enemy(32,32)
self.platforms = []
x = 0
y = 0
if level == 0:
level = [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",]
#background = pygame.image.load("Untitled.png")
total_level_width = len(level[0]) * 32
total_level_height = len(level) * 32
# build the level
for row in level:
for col in row:
if col == "P":
p = Platform(x, y)
self.platforms.append(p)
self.entities.add(p)
if col == "E":
e = ExitBlock(x, y)
self.platforms.append(e)
self.entities.add(e)
x += 32
y += 32
x = 0
self.camera = Camera(complex_camera, total_level_width, total_level_height)
self.entities.add(self.player)
self.entities.add(self.enemy)
def render(self, screen):
for y in range(20):
for x in range(25):
screen.blit(self.bg, (x * 32, y * 32))
for e in self.entities:
screen.blit(e.image, self.camera.apply(e))
def update(self):
pressed = pygame.key.get_pressed()
up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]
self.player.update(up, left, right, self.platforms)
self.enemy.update(self.platforms)
self.camera.update(self.player)
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_ESCAPE:
pass #somehow go back to menu
Not perfect yet, but a good start. Everything related to the actual gameplay is extracted to its own class. Some variables have to be instance variable, so they have to be accessed via self.
Now we need to alter the main-function to actually use this class:
def main():
pygame.init()
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
timer = pygame.time.Clock()
running = True
scene = GameScene()
while running:
timer.tick(60)
if pygame.event.get(QUIT):
running = False
return
scene.handle_events(pygame.event.get())
scene.update()
scene.render(screen)
pygame.display.flip()
Note that I changed two little things: I use pygame.event.get(QUIT) to only get a possible QUIT-event first, since this is the only event that we are interesset in in our main-loop. All other events are passed directly to the current scene: scene.handle_events(pygame.event.get()) .
At this point, we could think about extracting some classes to thier own files, but let's going on instead.
Let's create a title menu:
class TitleScene(object):
def __init__(self):
super(TitleScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
self.sfont = pygame.font.SysFont('Arial', 32)
def render(self, screen):
# beware: ugly!
screen.fill((0, 200, 0))
text1 = self.font.render('Crazy Game', True, (255, 255, 255))
text2 = self.sfont.render('> press space to start <', True, (255, 255, 255))
screen.blit(text1, (200, 50))
screen.blit(text2, (200, 350))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_SPACE:
self.manager.go_to(GameScene(0))
This just displays a green background and some text. If the player presses SPACE, we want to start the first level. Note this line:
self.manager.go_to(GameScene(0))
Here I pass the argument 0 to the GameScene class, so let's alter it so it accepts this parameter:
class GameScene(Scene):
def __init__(self, level):
...
the line level = 0 can be removed, as you already guessed.
So what it's self.manager? It's just a little helper class that manages the scenes for us.
class SceneMananger(object):
def __init__(self):
self.go_to(TitleScene())
def go_to(self, scene):
self.scene = scene
self.scene.manager = self
It starts with the title scene and sets each scenes manager field to itself to allow the changing of the current scene. There are a lot of possibilities of how to implement such a scene manager, and this is the simpliest approach. A disadvantage is that each scene has to know which scene comes after it, but that should not bother us right now.
Let's use our new SceneMananger:
def main():
pygame.init()
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
timer = pygame.time.Clock()
running = True
manager = SceneMananger()
while running:
timer.tick(60)
if pygame.event.get(QUIT):
running = False
return
manager.scene.handle_events(pygame.event.get())
manager.scene.update()
manager.scene.render(screen)
pygame.display.flip()
straightforward. Let's quickly add a second level and a winning/losing screen and we are done.
class CustomScene(object):
def __init__(self, text):
self.text = text
super(CustomScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
def render(self, screen):
# ugly!
screen.fill((0, 200, 0))
text1 = self.font.render(self.text, True, (255, 255, 255))
screen.blit(text1, (200, 50))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN:
self.manager.go_to(TitleScene())
Below is the complete code. Note the changes to the Player class: instead of calling the main function again, it calls methods on the scene to indicate the player reached the exit or died.
Also, I changed the placement of the player and enemies. Now, you specify where the entity will appear in the level. E.g. Player(5, 40) will create the player at column 5, row 40 in the level. As a bonus, the enemies to proper collision detection.
I extracted the description of the levels to a dictionary named levels, so it will be easy to alter and add levels as needed (later, you probably want one file per level, so this is a good start). It could be extended to hold the player starting position, but you could also create a special tile, like * for starting position and E for enemy in the level description.
import pygame
from pygame import *
WIN_WIDTH = 1120 - 320
WIN_HEIGHT = 960 - 320
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)
DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 0
FLAGS = 0
CAMERA_SLACK = 30
levels = {0: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38)]},
1: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPPPPPPPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPP PPPP",
"PPP P PPPPPPPPPPPPPPPPPP",
"PPP P PPPPPPPPPPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38), (18, 38), (15, 15)]}}
class Scene(object):
def __init__(self):
pass
def render(self, screen):
raise NotImplementedError
def update(self):
raise NotImplementedError
def handle_events(self, events):
raise NotImplementedError
class GameScene(Scene):
def __init__(self, levelno):
super(GameScene, self).__init__()
self.bg = Surface((32,32))
self.bg.convert()
self.bg.fill(Color("#0094FF"))
up = left = right = False
self.entities = pygame.sprite.Group()
self.player = Player(5, 40)
self.player.scene = self
self.platforms = []
self.levelno = levelno
levelinfo = levels[levelno]
self.enemies = [Enemy(*pos) for pos in levelinfo['enemies']]
level = levelinfo['level']
total_level_width = len(level[0]) * 32
total_level_height = len(level) * 32
# build the level
x = 0
y = 0
for row in level:
for col in row:
if col == "P":
p = Platform(x, y)
self.platforms.append(p)
self.entities.add(p)
if col == "E":
e = ExitBlock(x, y)
self.platforms.append(e)
self.entities.add(e)
x += 32
y += 32
x = 0
self.camera = Camera(complex_camera, total_level_width, total_level_height)
self.entities.add(self.player)
for e in self.enemies:
self.entities.add(e)
def render(self, screen):
for y in range(20):
for x in range(25):
screen.blit(self.bg, (x * 32, y * 32))
for e in self.entities:
screen.blit(e.image, self.camera.apply(e))
def update(self):
pressed = pygame.key.get_pressed()
up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]
self.player.update(up, left, right, self.platforms)
for e in self.enemies:
e.update(self.platforms)
self.camera.update(self.player)
def exit(self):
if self.levelno+1 in levels:
self.manager.go_to(GameScene(self.levelno+1))
else:
self.manager.go_to(CustomScene("You win!"))
def die(self):
self.manager.go_to(CustomScene("You lose!"))
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_ESCAPE:
self.manager.go_to(TitleScene())
class CustomScene(object):
def __init__(self, text):
self.text = text
super(CustomScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
def render(self, screen):
# ugly!
screen.fill((0, 200, 0))
text1 = self.font.render(self.text, True, (255, 255, 255))
screen.blit(text1, (200, 50))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN:
self.manager.go_to(TitleScene())
class TitleScene(object):
def __init__(self):
super(TitleScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
self.sfont = pygame.font.SysFont('Arial', 32)
def render(self, screen):
# ugly!
screen.fill((0, 200, 0))
text1 = self.font.render('Crazy Game', True, (255, 255, 255))
text2 = self.sfont.render('> press space to start <', True, (255, 255, 255))
screen.blit(text1, (200, 50))
screen.blit(text2, (200, 350))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_SPACE:
self.manager.go_to(GameScene(0))
class SceneMananger(object):
def __init__(self):
self.go_to(TitleScene())
def go_to(self, scene):
self.scene = scene
self.scene.manager = self
def main():
pygame.init()
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
timer = pygame.time.Clock()
running = True
manager = SceneMananger()
while running:
timer.tick(60)
if pygame.event.get(QUIT):
running = False
return
manager.scene.handle_events(pygame.event.get())
manager.scene.update()
manager.scene.render(screen)
pygame.display.flip()
class Camera(object):
def __init__(self, camera_func, width, height):
self.camera_func = camera_func
self.state = Rect(0, 0, width, height)
def apply(self, target):
try:
return target.rect.move(self.state.topleft)
except AttributeError:
return map(sum, zip(target, self.state.topleft))
def update(self, target):
self.state = self.camera_func(self.state, target.rect)
def complex_camera(camera, target_rect):
l, t, _, _ = target_rect
_, _, w, h = camera
l, t, _, _ = -l + HALF_WIDTH, -t +HALF_HEIGHT, w, h
l = min(0, l) # stop scrolling left
l = max(-(camera.width - WIN_WIDTH), l) # stop scrolling right
t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom
return Rect(l, t, w, h)
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Player(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.xvel = 0
self.yvel = 0
self.onGround = False
self.image = Surface((32,32))
self.image.fill(Color("#0000FF"))
self.image.convert()
self.rect = Rect(x*32, y*32, 32, 32)
def update(self, up, left, right, platforms):
if self.rect.top > 1440 or self.rect.top < 0:
self.scene.die()
if self.rect.left > 1408 or self.rect.right < 0:
self.scene.die()
if up:
if self.onGround:
self.yvel = 0
self.yvel -= 10 # only jump if on the ground
if left:
self.xvel = -10
if right:
self.xvel = 10
if not self.onGround:
self.yvel += 0.3 # only accelerate with gravity if in the air
if self.yvel > 80: self.yvel = 80 # max falling speed
if not(left or right):
self.xvel = 0
self.rect.left += self.xvel # increment in x direction
if self.collide(self.xvel, 0, platforms): # do x-axis collisions
self.rect.top += self.yvel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yvel, platforms) # do y-axis collisions
def collide(self, xvel, yvel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if isinstance(p, ExitBlock):
self.scene.exit()
return False
if xvel > 0: self.rect.right = p.rect.left
if xvel < 0: self.rect.left = p.rect.right
if yvel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yvel < 0:
self.rect.top = p.rect.bottom
return True
class Enemy(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.yVel = 0
self.xVel = 2 # start moving immediately
self.image = Surface((32,32))
self.image.fill(Color("#00FF00"))
self.image.convert()
self.rect = Rect(x*32, y*32, 32, 32)
self.onGround = False
def update(self, platforms):
if not self.onGround:
self.yVel += 0.3
# no need for right_dis to be a member of the class,
# since we know we are moving right if self.xVel > 0
right_dis = self.xVel > 0
# create a point at our left (or right) feet
# to check if we reached the end of the platform
m = (1, 1) if right_dis else (-1, 1)
p = self.rect.bottomright if right_dis else self.rect.bottomleft
fp = map(sum, zip(m, p))
# if there's no platform in front of us, change the direction
collide = any(p for p in platforms if p.rect.collidepoint(fp))
if not collide:
self.xVel *= -1
self.rect.left += self.xVel # increment in x direction
self.collide(self.xVel, 0, platforms) # do x-axis collisions
self.rect.top += self.yVel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yVel, platforms) # do y-axis collisions
def collide(self, xVel, yVel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if xVel > 0:
self.rect.right = p.rect.left
self.xVel *= -1 # hit wall, so change direction
if xVel < 0:
self.rect.left = p.rect.right
self.xVel *= -1 # hit wall, so change direction
if yVel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yVel < 0:
self.rect.top = p.rect.bottom
class Platform(Entity):
def __init__(self, x, y):
Entity.__init__(self)
#self.image = Surface([32, 32], pygame.SRCALPHA, 32) #makes blocks invisible for much better artwork
self.image = Surface((32,32)) #makes blocks visible for building levels
self.image.convert()
self.rect = Rect(x, y, 32, 32)
def update(self):
pass
class ExitBlock(Platform):
def __init__(self, x, y):
Platform.__init__(self, x, y)
self.image = Surface((32,32)) #makes blocks visible for building levels
self.image.convert()
self.rect = Rect(x, y, 32, 32)
if __name__ == "__main__":
main()

I have also made a game in which I have a game menu, a levels menu, a loading part, and a playing part.
The way I did it was in the main game loop,
I went through a bunch of elif statements to determine what "mode" the game is in, and does the appropriate actions.
It seemed to work very well, and I suggest you try the same.
I realize My Code is really long, but if you go to where it says #game loop (use ctrl+f to find it), you can see the elifs to determine the mode. Hope this helps.
#basic stuff
import pygame, sys, random
pygame.init()
window=pygame.display.set_mode((1500, 800), pygame.FULLSCREEN)
winrect=window.get_rect()
#colors
GREEN=(19, 225, 30)
BLUE=(41, 2, 245)
YELLOW=(251, 240, 32)
WHITE=(255, 255, 255)
BLACK=(0, 0, 0)
RED=(255, 0, 0)
#text
bigfont=pygame.font.SysFont('calibri', 75)
font=pygame.font.SysFont('calibri', 40)
texts={}
so=bigfont.render('Ball Bounce', True, BLUE)
rect=so.get_rect()
rect.top=winrect.top+100
rect.centerx=winrect.centerx
texts['title']=[so, rect]
so=font.render('Start', True, BLUE)
rect=so.get_rect()
so1=pygame.Surface((400, 50))
so2=pygame.Surface((400, 50))
rect1=so1.get_rect()
so1.fill(YELLOW)
so2.fill(RED)
pygame.draw.rect(so1, BLACK, rect1, 5)
pygame.draw.rect(so2, BLACK, rect1, 5)
rect.center=rect1.center
so1.blit(so, rect)
so2.blit(so, rect)
rect1.centerx=winrect.centerx
rect1.top=texts['title'][1].top+300
texts['start']=[so1, rect1, so2]
so=bigfont.render('Levels', True, BLUE)
rect=so.get_rect()
rect.centerx=winrect.centerx
rect.top=winrect.top+100
texts['levels']=[so, rect]
#levels [locked, unlocked, completed/mouseover, rect, state(locked, unlocked, completed)]
levels=[]
lock=pygame.image.load('images/lock.png').convert()
lock=pygame.transform.scale(lock, (100, 100))
lock.set_colorkey(lock.get_at((1, 1)))
for i in range(1, 21):
so=pygame.Surface((100, 100))
so.fill(YELLOW)
rect=so.get_rect()
pygame.draw.rect(so, BLACK, rect, 5)
so1=pygame.Surface((100, 100))
so1.fill(RED)
pygame.draw.rect(so1, BLACK, rect, 5)
text=font.render(str(i), True, BLUE)
textrect=text.get_rect()
textrect.center=rect.center
so.blit(text, textrect)
so1.blit(text, textrect)
locked=pygame.Surface((100, 100))
locked.blit(so, rect)
locked.blit(lock, lock.get_rect())
if i<=5:
rect.top=texts['levels'][1].bottom+25
elif i<=10:
rect.top=levels[0][3].bottom+50
elif i<=15:
rect.top=levels[7][3].bottom+50
else:
rect.top=levels[12][3].bottom+50
if i==1 or i==6 or i==11 or i==16:
rect.right=winrect.centerx-200
elif i==2 or i==7 or i==12 or i==17:
rect.right=winrect.centerx-75
elif i==3 or i==8 or i==13 or i==18:
rect.centerx=winrect.centerx
elif i==4 or i==9 or i==14 or i==19:
rect.left=winrect.centerx+75
else:
rect.left=winrect.centerx+200
if i==1:
levels.append([locked, so, so1, rect, 1])
else:
levels.append([locked, so, so1, rect, 1])
#Wall class (0=horizontal, 1=vertical)
class cwall(pygame.Rect):
'orientation (hor, vert), location, holesize, winrect'
def __init__(self, orientation, location, holesize, winrect):
self.orientation=orientation
if orientation==0:
self.height=5
self.width=winrect.width
self.centery=location
if orientation==1:
self.width=5
self.height=winrect.height
self.centerx=location
self.holesize=holesize
self.bbottomright=round(pygame.mouse.get_pos()[self.orientation]+self.holesize/2)
self.ttopleft=round(pygame.mouse.get_pos()[self.orientation]-self.holesize/2)
def update(self):
self.bbottomright=round(pygame.mouse.get_pos()[self.orientation]+self.holesize/2)
self.ttopleft=round(pygame.mouse.get_pos()[self.orientation]-self.holesize/2)
if self.bbottomright<self.holesize:
self.bbottomright=self.holesize
if self.ttopleft>self.right-self.holesize and self.orientation==0:
self.ttopleft=self.right-self.holesize
if self.ttopleft>self.bottom-self.holesize and self.orientation==1:
self.ttopleft=self.bottom-self.holesize
#Ball Class
class cball(pygame.Rect):
'diameter, speed, color, winrect'
def __init__(self, diameter, speed, color, winrect):
self.width=diameter
self.height=diameter
self.speed=speed
self.color=color
self.direction=random.randint(1, 4)
self.center=(random.randint(round(diameter/2), round(winrect.right-diameter/2)), random.randint(round(diameter/2), round(winrect.bottom-diameter/2)))
def update(self, winrect, walls):
if self.direction/2==round(self.direction/2):
self.right+=self.speed
else:
self.right-=self.speed
if self.direction<=2:
self.top+=self.speed
else:
self.top-=self.speed
for wall in walls:
if wall.collidepoint(self.center):
if wall.orientation==0 and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright):
if self.direction==1:
self.direction=3
self.bottom=wall.top
elif self.direction==2:
self.direction=4
self.bottom=wall.top
elif self.direction==3:
self.direction=1
self.top=wall.bottom
else:
self.direction=2
self.top=wall.bottom
elif wall.orientation==1 and (self.centery<wall.ttopleft or self.centery>wall.bbottomright):
if self.direction==1:
self.direction=2
self.left=wall.right
elif self.direction==2:
self.direction=1
self.right=wall.left
elif self.direction==3:
self.direction=4
self.left=wall.right
else:
self.direction=3
self.right=wall.left
elif wall.orientation==0:
if self.bottom>wall.top and self.centery<wall.top and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright):
if self.direction==1:
self.direction=3
self.bottom=wall.top
elif self.direction==2:
self.direction=4
self.bottom=wall.top
elif self.top<wall.bottom and self.centery>wall.bottom and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright):
if self.direction==3:
self.direction=1
self.top=wall.bottom
if self.direction==4:
self.direction=2
self.top=wall.bottom
else:
if self.left<wall.right and self.centerx>wall.right and (self.centery<wall.ttopleft or self.centery>wall.bbottomright):
if self.direction==1:
self.direction=2
self.left=wall.right
elif self.direction==3:
self.direction=4
self.left=wall.right
elif self.right>wall.left and self.centerx<wall.left and (self.centery<wall.ttopleft or self.centery>wall.bbottomright):
if self.direction==2:
self.direction=1
self.right=wall.left
if self.direction==4:
self.direction=3
self.right=wall.left
if self.top<0:
if self.direction==3:
self.direction=1
self.top=0
elif self.direction==4:
self.direction=2
self.topn=0
if self.bottom>winrect.bottom:
if self.direction==1:
self.direction=3
self.bottom=winrect.bottom
elif self.direction==2:
self.direction=4
self.bottom=winrect.bottom
if self.left<0:
if self.direction==1:
self.direction=2
self.left=0
elif self.direction==3:
self.direction=4
self.left=0
if self.right>winrect.right:
if self.direction==2:
self.direction=1
self.right=winrect.right
if self.direction==4:
self.direction=3
self.right=winrect.right
for box in boxes:
if box[0].collidepoint(self.center) and self.color==box[1]:
return True
return False
#Game loop setup
mode='title'
#Game loop
while True:
if mode=='title':
#event loop
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
if event.type==pygame.MOUSEBUTTONDOWN:
if texts['start'][1].collidepoint(event.pos):
mode='levels'
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
pygame.quit()
sys.exit()
#screen update
window.fill(GREEN)
mouse=pygame.mouse.get_pos()
if texts['start'][1].collidepoint(mouse):
window.blit(texts['start'][2], texts['start'][1])
else:
window.blit(texts['start'][0], texts['start'][1])
window.blit(texts['title'][0], texts['title'][1])
pygame.display.update()
elif mode=='levels':
#event loop
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
if event.type==pygame.MOUSEBUTTONDOWN:
for level in levels:
if level[3].collidepoint(event.pos) and level[4]!=0:
mode='loading'
loadinglevel=levels.index(level)+1
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
pygame.quit()
sys.exit()
#screen update
window.fill(GREEN)
for level in levels:
if level[3].collidepoint(pygame.mouse.get_pos()) and level[4]==1:
window.blit(level[2], level[3])
else:
window.blit(level[level[4]], level[3])
window.blit(texts['levels'][0], texts['levels'][1])
pygame.display.update()
elif mode=='loading':
if loadinglevel==1:
walls=[cwall(1, winrect.width/2, 100, winrect)]
balls=[]
for i in range(2):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(2):
balls.append(cball(20, 3, YELLOW, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), winrect.height), GREEN), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), YELLOW))
elif loadinglevel==2:
walls=[cwall(1, winrect.width/2, 100, winrect)]
balls=[]
for i in range(4):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(4):
balls.append(cball(20, 3, YELLOW, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), winrect.height), GREEN), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), YELLOW))
elif loadinglevel==3:
walls=[cwall(1, winrect.width/3, 100, winrect), cwall(1, 2*winrect.width/3, 100, winrect)]
balls=[]
for i in range(2):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(2):
balls.append(cball(20, 3, YELLOW, winrect))
for i in range(2):
balls.append(cball(20, 3, BLUE, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/3), winrect.height), GREEN), (pygame.Rect(round(winrect.width/3), 0, round(winrect.width/3), winrect.height), YELLOW),
(pygame.Rect(round(2*winrect.width/3), 0, round(winrect.width/3), winrect.height), BLUE))
elif loadinglevel==4:
walls=[cwall(1, winrect.width/3, 100, winrect), cwall(1, 2*winrect.width/3, 100, winrect)]
balls=[]
for i in range(4):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(4):
balls.append(cball(20, 3, YELLOW, winrect))
for i in range(4):
balls.append(cball(20, 3, BLUE, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/3), winrect.height), GREEN), (pygame.Rect(round(winrect.width/3), 0, round(winrect.width/3), winrect.height), YELLOW),
(pygame.Rect(round(2*winrect.width/3), 0, round(winrect.width/3), winrect.height), BLUE))
elif loadinglevel==7:
walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)]
balls=[]
for i in range(2):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(2):
balls.append(cball(20, 3, YELLOW, winrect))
for i in range(2):
balls.append(cball(20, 3, BLUE, winrect))
for i in range(2):
balls.append(cball(20, 3, RED, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), GREEN),
(pygame.Rect(0, round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), RED),
(pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), round(winrect.height/2)), YELLOW),
(pygame.Rect(round(winrect.width/2), round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), BLUE))
elif loadinglevel==8:
walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)]
balls=[]
for i in range(4):
balls.append(cball(20, 3, GREEN, winrect))
for i in range(4):
balls.append(cball(20, 3, YELLOW, winrect))
for i in range(4):
balls.append(cball(20, 3, BLUE, winrect))
for i in range(4):
balls.append(cball(20, 3, RED, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), GREEN),
(pygame.Rect(0, round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), RED),
(pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), round(winrect.height/2)), YELLOW),
(pygame.Rect(round(winrect.width/2), round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), BLUE))
elif loadinglevel==5:
walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)]
balls=[]
for i in range(10):
balls.append(cball(20, 3, RED, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), RED),
(pygame.Rect(0, round(winrect.height/2), winrect.width, round(winrect.height/2)), WHITE),
(pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), WHITE))
elif loadinglevel==6:
walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)]
balls=[]
for i in range(20):
balls.append(cball(20, 3, RED, winrect))
boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), RED),
(pygame.Rect(0, round(winrect.height/2), winrect.width, round(winrect.height/2)), WHITE),
(pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), WHITE))
mode='playing'
elif mode=='playing':
while True:
#event loop
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()
#updates
updates=[]
for wall in walls:
wall.update()
for ball in balls:
updates.append(ball.update(winrect, walls))
#Seeing if won
won=True
for update in updates:
if not update:
won=False
break
if won:
if levels[loadinglevel][4]==0:
levels[loadinglevel][4]=1
levels[loadinglevel-1][4]=2
mode='levels'
break
#blitting
window.fill(WHITE)
for box in boxes:
pygame.draw.rect(window, box[1], box[0])
for wall in walls:
if wall.orientation==0:
pygame.draw.rect(window, BLACK, (wall.left, wall.top, wall.ttopleft, wall.height))
pygame.draw.rect(window, BLACK, (wall.bbottomright, wall.top, wall.right-wall.bbottomright, wall.height))
else:
pygame.draw.rect(window, BLACK, (wall.left, wall.top, wall.width, wall.ttopleft))
pygame.draw.rect(window, BLACK, (wall.left, wall.bbottomright, wall.width, wall.bottom-wall.holesize))
for ball in balls:
pygame.draw.circle(window, ball.color, ball.center, round(ball.width/2))
pygame.draw.circle(window, BLACK, ball.center, round(ball.width/2), 2)
pygame.display.update()
pygame.time.Clock().tick(100)

Related

How to check if a button is clicked in pygame [duplicate]

This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 1 year ago.
I am making a spaceship game where you fire bullets at enemy spaceships and collect coins. When you click the start button, the game is supposed to start. However, when I try clicking the start button, the game doesn't start! Is something wrong with my if statement to identify if the start button is clicked?
This is my current code:
import pygame
from pygame.locals import *
from random import randint, choice
from tools import *
pygame.init()
pygame.font.init()
SCREEN_X = 800
SCREEN_Y = 500
CENTER_POS = (400, 225)
screen = pygame.display.set_mode((SCREEN_X, SCREEN_Y))
class Spaceship(pygame.sprite.Sprite):
"""A spaceship object. Used for the player."""
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Updates the spaceship rect"""
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
"""A bullet object. Appears when the player clicks."""
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("bullet.png")
self.image = pygame.transform.scale(self.image, (100, 100))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Let's the bullet move upward."""
self.y -= 5
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
"""An enemy object. The player's job is to destroy enemies."""
def __init__(self, s, x, y, t):
pygame.sprite.Sprite.__init__(self)
self.type = t
self.screen, self.x, self.y = s, x, y
self.image = pygame.image.load(get_enemy_image()[self.type])
# There is an if statement because the
# N1 Galaxy Fighter and M7 Comet Glider need different sizes
if self.type == "N1 Galaxy Fighter":
self.image = pygame.transform.scale(self.image, (235, 215))
elif self.type == "M7 Comet Glider":
self.image = pygame.transform.scale(self.image, (155, 215))
self.rect = self.image.get_rect()
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
self.score_given = get_enemy_given_score()[self.type]
def update(self):
if self.y < 0:
self.kill()
self.y += 3
self.rect.center = (self.x, self.y)
class GameOverBackground(pygame.sprite.Sprite):
"""The game over background object."""
def __init__(self, s, x, y, size=(100, 100)):
pygame.sprite.Sprite.__init__(self)
self.screen, self.x, self.y = s, x, y
self.size = size
self.image = pygame.image.load("Game_Over.jpg")
self.image = pygame.transform.scale(self.image, self.size)
self.rect = self.image.get_rect()
def blitme(self):
"""Blits the game over image on the screen"""
self.screen.blit(self.image, self.rect)
class Coin(pygame.sprite.Sprite):
"""A coin object."""
def __init__(self, pos=(0, 0), size=(100, 100)):
pygame.sprite.Sprite.__init__(self)
self.x, self.y = pos[0], pos[1]
self.size = size
self.image = pygame.image.load("coin.png")
self.image = pygame.transform.scale(self.image, self.size)
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Updates the coin rect"""
self.rect.center = (self.x, self.y)
class StartButton(pygame.sprite.Sprite):
def __init__(self, s, x, y, size=None):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x = x
self.y = y
self.image = pygame.image.load("start_button.png")
if size is not None:
self.image = pygame.transform.scale(self.image, size)
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def blitme(self):
self.screen.blit(self.image, self.rect)
bg = GameOverBackground(screen, 0, 0, size=(800, 500))
spaceship = Spaceship(screen, 400, 400)
start_button = StartButton(screen, CENTER_POS[0], CENTER_POS[1], size=(300, 195))
game_started = False
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
coins = pygame.sprite.Group()
clock = pygame.time.Clock()
enemy_interval = 2000
enemy_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_event, enemy_interval)
coin_interval = 3500
coin_event = pygame.USEREVENT + 1
pygame.time.set_timer(coin_event, coin_interval)
score = 0
lives = 3
with open("high_score.txt", "r") as file:
highscore = file.read()
font = pygame.font.SysFont("Arial", 30)
score_text_surface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
lives_text_surface = font.render("HP: %s" % lives, True, (0, 0, 0))
high_score_text_surface = font.render("High score: %s" % highscore, True, (0, 0, 0))
spaceship_collided = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if game_started and event.type == MOUSEBUTTONDOWN:
bullet = Bullet(screen, spaceship.x, spaceship.y - 20)
bullets.add(bullet)
if game_started and event.type == enemy_event and not lives <= 0:
enemy = Enemy(screen, randint(-100, 725), 0, choice(["N1 Galaxy Fighter", "M7 Comet Glider"]))
enemies.add(enemy)
if game_started and event.type == coin_event and not lives <= 0:
if len(coins) < 100:
coins.add(Coin((randint(-125, 750), randint(-200, 400))))
screen.fill((255, 255, 255)) # DO NOT DRAW ANYTHING IN FRONT OF THIS LINE, I'M WARNING YOU
if not game_started:
start_button.blitme()
mouse_pos = pygame.mouse.get_pos()
if CENTER_POS[0] < mouse_pos[0] < CENTER_POS[0]+250 and CENTER_POS[1] > mouse_pos[1] > CENTER_POS[1] + 460:
game_started = True
else:
bullets.update()
key = pygame.key.get_pressed()
amount = 5
if key[pygame.K_a]:
spaceship.x -= amount
elif key[pygame.K_d]:
spaceship.x += amount
elif key[pygame.K_w]:
spaceship.y -= amount
elif key[pygame.K_s]:
spaceship.y += amount
spaceship.update()
if not lives <= 0:
screen.blit(spaceship.image, spaceship.rect)
if not lives <= 0:
bullets.draw(screen)
enemies.draw(screen)
coins.update()
coins.draw(screen)
for i in enemies:
i.update()
if pygame.sprite.spritecollide(i, bullets, True):
score += i.score_given
i.kill()
if spaceship_collided and lives <= 0:
bg.blitme()
if score > int(highscore):
with open("high_score.txt", "w") as file:
file.write(str(score))
if score >= 99999:
score = 99999
if not lives <= 0:
score_text_surface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
screen.blit(score_text_surface, (590, 0))
lives_text_surface = font.render("HP: %s" % lives, True, (0, 0, 0))
screen.blit(lives_text_surface, (260, 0))
high_score_text_surface = font.render("High score: %s" % highscore, True, (0, 0, 0))
screen.blit(high_score_text_surface, (360, 0))
if pygame.sprite.spritecollide(spaceship, enemies, dokill=True):
lives -= 1
spaceship_collided = True
if pygame.sprite.spritecollide(spaceship, coins, dokill=True):
score += 10
pygame.display.update()
clock.tick(60)
Use a pygame.Rect object and the method collidepoint:
button_rect = start_button.image.get_rect(topleft = (start_button.x, start_button.y))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MOUSEBUTTONDOWN:
if not game_started:
if button_rect.collidepoint(event.pos):
game_started = True
else:
bullet = Bullet(screen, spaceship.x, spaceship.y - 20)
bullets.add(bullet)
See Pygame mouse clicking detection

Problem creating new enemies when player levels up

i'm working on a simple clone of Space Invaders for an University project. The idea is that when the players levels up, the amount of enemies increase, but when i try to do it, the enemies creates infinitly while the stablished condition is true. I can't dettect the problem, so if one of you can help me, i'll apreciate it.
import random
import csv
import operator
import pygame
import time
from pygame import mixer
# Intialize the pygame
pygame.init()
# create the screen
screen = pygame.display.set_mode((800, 600))
# Background
background = pygame.image.load('background.png')
# Sound
mixer.music.load("background.wav")
mixer.music.play(-1)
#Reloj
clock = pygame.time.Clock()
# Caption and Icon
pygame.display.set_caption("Space Invader")
icon = pygame.image.load('ufo.png')
pygame.display.set_icon(icon)
# Score
score_value = 0
level_value = 1
level_up_value = ""
font = pygame.font.Font('freesansbold.ttf', 20)
game_over = False
high_scores = []
names = []
with open('high_scores.csv') as csvfile:
reader = csv.reader(csvfile)
sortedlist = sorted(reader, key=operator.itemgetter(1), reverse=True)
for i in sortedlist:
names.append(i[0])
high_scores.append(i[1])
scoreX = 10
scoreY = 10
level_textX = 10
level_textY = 80
levelX = 10
levelY = 40
# Game Over
over_font = pygame.font.Font('freesansbold.ttf', 64)
menu_font = pygame.font.Font('freesansbold.ttf', 48)
scores_font = pygame.font.Font('freesansbold.ttf', 45)
#Número de vidas
num_vidas = 3
font = pygame.font.Font('freesansbold.ttf', 32)
vidasX = 650
vidasY = 10
# Player
playerImg = pygame.image.load('player.png')
playerX = 370
playerY = 480
playerX_change = 0
# Enemy
enemyImg = []
bichoImg = []
meteoroImg = []
enemyX = []
enemyY = []
enemyY_change = []
bichoX = []
bichoY = []
bichoY_change = []
meteoroX = []
meteoroY = []
meteoroY_change = []
num_of_enemies_10 = 5
num_of_enemies_20 = 4
num_of_enemies_30 = 3
for i in range(num_of_enemies_10):
enemyImg.append(pygame.image.load('enemy.png'))
enemyX.append(random.randint(0, 736))
enemyY.append(random.randint(50, 150))
enemyY_change.append(0.5)
for i in range(num_of_enemies_20):
bichoImg.append(pygame.image.load('bicho.png'))
bichoX.append(random.randint(0, 736))
bichoY.append(random.randint(10, 120))
bichoY_change.append(0.5)
for i in range(num_of_enemies_30):
meteoroImg.append(pygame.image.load('meteoro.png'))
meteoroX.append(random.randint(0, 736))
meteoroY.append(random.randint(0, 150))
meteoroY_change.append(0.5)
# Bullet
# Ready - You can't see the bullet on the screen
# Fire - The bullet is currently moving
bulletImg = pygame.image.load('bullet.png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 10
bullet_state = "ready"
def show_score(x, y):
score = font.render("Score : " + str(score_value), True, (255, 255, 255))
screen.blit(score, (x, y))
def show_level(x, y):
level = font.render("Level: " + str(level_value), True, (255, 255, 255))
screen.blit(level, (x, y))
def level_up_text(x, y):
level_text = font.render(level_up_value, True, (255, 255, 0))
screen.blit(level_text, (level_textX, level_textY))
def show_vidas(x, y):
vidas = font.render("Vidas : " + str(num_vidas), True, (255, 255, 255))
screen.blit(vidas, (x, y))
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
screen.blit(over_text, (200, 250))
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def bicho(x, y, i):
screen.blit(bichoImg[i], (x, y))
def meteoro(x, y, i):
screen.blit(meteoroImg[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "fire"
screen.blit(bulletImg, (x + 16, y + 10))
#Colisiones para cada tipo de enemigo
def isCollision1(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt(math.pow(enemyX - bulletX, 2) + (math.pow(enemyY - bulletY, 2)))
if distance < 27:
return True
else:
return False
def isCollision2(bichoX, bichoY, bulletX, bulletY):
distance = math.sqrt(math.pow(bichoX - bulletX, 2) + (math.pow(bichoY - bulletY, 2)))
if distance < 27:
return True
else:
return False
def isCollision3(meteoroX, meteoroY, bulletX, bulletY):
distance = math.sqrt(math.pow(meteoroX - bulletX, 2) + (math.pow(meteoroY - bulletY, 2)))
if distance < 27:
return True
else:
return False
# Pause Loop
def show_pause():
paused = True
while paused:
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
pause_title = menu_font.render("PAUSED", True, (255, 255, 0))
pauseRect = pause_title.get_rect()
pauseRect.centerx = screen.get_rect().centerx
pauseRect.centery = screen.get_rect().centery - 50
screen.blit(pause_title, pauseRect)
high_title = menu_font.render("HIGH SCORES", True, (255, 255, 255))
highRect = high_title.get_rect()
highRect.centerx = screen.get_rect().centerx
highRect.centery = screen.get_rect().centery + 50
screen.blit(high_title, highRect)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
paused = False
break
if event.type == pygame.MOUSEBUTTONDOWN:
mpos = pygame.mouse.get_pos()
if highRect.collidepoint(mpos):
show_high_scores_menu()
break
pygame.display.update()
# High Scores Loop
def show_high_scores_menu():
high_scores_menu = True
global high_scores, names
high_scores.clear()
names.clear()
with open('high_scores.csv') as csvfile:
reader = csv.reader(csvfile)
sortedlist = sorted(reader, key=operator.itemgetter(1), reverse=True)
for i in sortedlist:
names.append(i[0])
high_scores.append(i[1])
while high_scores_menu:
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
high_title = menu_font.render("HIGH SCORES", True, (255, 255, 0))
highRect = high_title.get_rect()
highRect.centerx = screen.get_rect().centerx
highRect.centery = 100
screen.blit(high_title, highRect)
score1 = scores_font.render(names[0] + " : " + str(high_scores[0]), True, (255, 255, 255))
score1Rect = score1.get_rect()
score1Rect.centerx = screen.get_rect().centerx
score1Rect.centery = 250
screen.blit(score1, score1Rect)
score2 = scores_font.render(names[1] + " : " + str(high_scores[1]), True, (255, 255, 255))
score2Rect = score1.get_rect()
score2Rect.centerx = screen.get_rect().centerx
score2Rect.centery = 300
screen.blit(score2, score2Rect)
score3 = scores_font.render(names[2] + " : " + str(high_scores[2]), True, (255, 255, 255))
score3Rect = score3.get_rect()
score3Rect.centerx = screen.get_rect().centerx
score3Rect.centery = 350
screen.blit(score3, score3Rect)
score4 = scores_font.render(names[3] + " : " + str(high_scores[3]), True, (255, 255, 255))
score4Rect = score4.get_rect()
score4Rect.centerx = screen.get_rect().centerx
score4Rect.centery = 400
screen.blit(score4, score4Rect)
score5 = scores_font.render(names[4] + " : " + str(high_scores[4]), True, (255, 255, 255))
score5Rect = score1.get_rect()
score5Rect.centerx = screen.get_rect().centerx
score5Rect.centery = 450
screen.blit(score5, score5Rect)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
high_scores_menu = False
show_pause()
break
pygame.display.update()
def save_high_scores():
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
name = ""
while len(name) < 3:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.unicode.isalpha():
name += event.unicode
elif event.key == pygame.K_BACKSPACE:
name = name[:-1]
name = name.upper()
screen.fill((0, 0, 0))
screen.blit(background, (0, 0))
high_title = menu_font.render("High Score!", True, (255, 255, 0))
highRect = high_title.get_rect()
highRect.centerx = screen.get_rect().centerx
highRect.centery = 100
screen.blit(high_title, highRect)
inpt = font.render("Enter a 3-letter name", True, (255, 255, 255))
inptRect = inpt.get_rect()
inptRect.centerx = screen.get_rect().centerx
inptRect.centery = 200
screen.blit(inpt, inptRect)
namedisplay = menu_font.render(name, True, (255, 255, 255))
namedisplayRect = namedisplay.get_rect()
namedisplayRect.center = screen.get_rect().center
screen.blit(namedisplay, namedisplayRect)
pygame.display.update()
#replace lowest
lowest = high_scores[0]
lowest_index = 0
for i in range(len(high_scores)):
if high_scores[i] <= lowest:
lowest = high_scores[i]
lowest_index = i
high_scores[lowest_index] = score_value
names[lowest_index] = name
with open('high_scores.csv', 'w',newline='') as csvfile:
writer = csv.writer(csvfile)
for i in range(len(names)):
writer.writerow([str(names[i]), str(high_scores[i])])
show_high_scores_menu()
BLACK = (0, 0, 0)
#Agregar imágenes para explosión
explosión_anim = []
for i in range(9):
file = "Explosión/Explosión 0{}.png".format(i)
img = pygame.image.load(file).convert()
img.set_colorkey(BLACK)
img_scale = pygame.transform.scale(img, (70, 70))
explosión_anim.append (img_scale)
def explosión1():
image = explosión_anim[0]
center = image.get_rect()
frame = 0
last_update = pygame.time.get_ticks()
frame_rate = 50 #Velocidad de la explosión
screen.blit(image, (enemyX[i], enemyY[i]))
now = pygame.time.get_ticks()
if now - last_update > frame_rate:
last_update = now
frame += 1
if frame == len(explosión_anim):
kill()
else:
center = rect.center
image = explosión_anim[frame]
rect = image.get_rect()
rect.center = center
def explosión2():
image = explosión_anim[0]
center = image.get_rect()
frame = 0
last_update = pygame.time.get_ticks()
frame_rate = 50 #Velocidad de la explosión
screen.blit(image, (bichoX[i], bichoY[i]))
now = pygame.time.get_ticks()
if now - last_update > frame_rate:
last_update = now
frame += 1
if frame == len(explosión_anim):
kill()
else:
center = rect.center
image = explosión_anim[frame]
rect = image.get_rect()
rect.center = center
def explosión3():
image = explosión_anim[0]
center = image.get_rect()
frame = 0
last_update = pygame.time.get_ticks()
frame_rate = 50 #Velocidad de la explosión
screen.blit(image, (meteoroX[i], meteoroY[i]))
now = pygame.time.get_ticks()
if now - last_update > frame_rate:
last_update = now
frame += 1
if frame == len(explosión_anim):
kill()
else:
center = rect.center
image = explosión_anim[frame]
rect = image.get_rect()
rect.center = center
# Game Loop
en_partida = True
while en_partida:
clock.tick(60)
en_final = False
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
# if player levels up
if score_value < 40:
level_value = 1
elif score_value <= 80:
level_value = 2
elif score_value <= 120:
level_value = 3
elif score_value <= 160:
level_value = 4
if (score_value >= 40 and score_value <= 42):
level_up_value = "LEVEL UP!"
elif (score_value >= 80 and score_value <= 82):
level_up_value = "LEVEL UP!"
elif (score_value >= 120 and score_value <= 122):
level_up_value = "LEVEL UP!"
else:
level_up_value = ""
for event in pygame.event.get():
if event.type == pygame.QUIT:
en_partida = False
# if keystroke is pressed check whether its right or left
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -5
if event.key == pygame.K_RIGHT:
playerX_change = 5
if event.key == pygame.K_SPACE:
if bullet_state == "ready":
bulletSound = mixer.Sound("laser.wav")
bulletSound.play()
# Get the current x cordinate of the spaceship
bulletX = playerX
fire_bullet(bulletX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# 5 = 5 + -0.1 -> 5 = 5 - 0.1
# 5 = 5 + 0.1
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 736:
playerX = 736
# Enemy Movement
for i in range(num_of_enemies_10):
# Game Over
enemyY[i] += enemyY_change[i]
if enemyY[i] > 440:
for j in range(num_of_enemies_10):
enemyY[j] = 2000
game_over_text()
break
for i in range(num_of_enemies_20):
# Game Over
bichoY[i] += bichoY_change[i]
if bichoY[i] > 440:
for j in range(num_of_enemies_20):
bichoY[j] = 2000
game_over_text()
break
for i in range(num_of_enemies_30):
# Game Over
meteoroY[i] += meteoroY_change[i]
if meteoroY[i] > 440:
for j in range(num_of_enemies_30):
meteoroY[j] = 2000
game_over_text()
break
# Collision
collision1 = isCollision1(enemyX[i], enemyY[i], bulletX, bulletY)
collision2 = isCollision2(bichoX[i], bichoY[i], bulletX, bulletY)
collision3 = isCollision3(meteoroX[i], meteoroY[i], bulletX, bulletY)
enemy(enemyX[i], enemyY[i], i)
bicho(bichoX[i], bichoY[i], i)
meteoro(meteoroX[i], meteoroY[i], i)
if collision1:
explosionSound = mixer.Sound("explosion.wav")
explosionSound.play()
explosión1()
bulletY = 480
bullet_state = "ready"
score_value += 10
num_of_enemies_10 -= 1
if collision2:
explosionSound = mixer.Sound("explosion.wav")
explosionSound.play()
explosión2()
bulletY = 480
bullet_state = "ready"
score_value += 20
num_of_enemies_20 -= 1
if collision3:
explosionSound = mixer.Sound("explosion.wav")
explosionSound.play()
explosión3()
bulletY = 480
bullet_state = "ready"
score_value += 30
num_of_enemies_30 -= 1
#Aumento de enemigos por nivel
enemy_created = False
flag = False
if(level_value == 2 and flag == True):
flag = True
enemyX[i] = random.randint(0,736)
enemyY[i] = random.randint(50,150)
enemy(enemyX[i], enemyY[i], i)
enemy_created = True
# Bullet Movement
if bulletY <= 0:
bulletY = 480
bullet_state = "ready"
if bullet_state == "fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
player(playerX, playerY)
show_score(scoreX, scoreY)
show_level(levelX, levelY)
level_up_text(levelX + 100, levelY)
show_vidas(vidasX, vidasY)
pygame.display.update()
pygame.quit()
Set the flag before the game loop:
# Game Loop
flag = True # set flag here
en_partida = True
while en_partida:
clock.tick(60)
....................
#Aumento de enemigos por nivel
enemy_created = False
if(level_value == 2 and flag == True): # check flag
flag = False # reset flag
enemyX[i] = random.randint(0,736)
enemyY[i] = random.randint(50,150)
enemy(enemyX[i], enemyY[i], i)
enemy_created = True

Enemy Sprite is hit and disappears but is not fixed later if it was hit on the sides

I have a code solved for this issue Image does not remain on screen after executing for loop in pygame. So I decided to put some platforms as a result of what I learned from pygame. I also decided to make the cowboy move to the right and left and go down to the bottom of the screen.
But when a collision occurs on the right or left side of the cowboy as shown in the Diagram, the class PlayerShip is not executed as expected in the main loop through the def draw_reaction () function because I do not see the image of the cowboy represented by player_sprite fixed in position (60, 48). It only works correctly if the cowboy is hit from below.
Assets
missile.png
cowboy.png
police.png
Diagram:
|missile| <--<-- (is touched its right side by the missile)|cowboy| <--<-- direction
^
|
^
|
(fire missile direction vertical)
^
|
d
i
r
e
c
t
i
o
n
My MWE coding:
import pygame
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
GRAY = (128,128,128)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class PlayerShip( pygame.sprite.Sprite ):
def __init__(self):
super().__init__()
self.image = pygame.image.load( "cowboy.png" )
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.topleft = ( 60, 48 )
player_sprite = PlayerShip()
class Platform(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(GREEN)
self.rect = self.image.get_rect()
class MovingPlatform(Platform):
change_x = 0
change_y = 0
boundary_top = 0
boundary_bottom = 0
boundary_left = 0
boundary_right = 0
player = None
level = None
def update(self):
self.rect.x += self.change_x
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_x < 0:
self.player.rect.right = self.rect.left
else:
self.player.rect.left = self.rect.right
self.rect.y += self.change_y
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_y < 0:
self.player.rect.bottom = self.rect.top
else:
self.player.rect.top = self.rect.bottom
if self.rect.bottom > self.boundary_bottom or self.rect.top < self.boundary_top:
self.change_y *= -1
cur_pos = self.rect.x - self.level.world_shift
if cur_pos < self.boundary_left or cur_pos > self.boundary_right:
self.change_x *= -1
class Level(object):
def __init__(self, player):
self.platform_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
self.player = player
self.background = None
self.world_shift = 0
self.level_limit = -1000
def update(self):
self.platform_list.update()
self.enemy_list.update()
def draw(self, screen):
screen.fill(GRAY)
self.platform_list.draw(screen)
self.enemy_list.draw(screen)
def shift_world(self, shift_x):
self.world_shift += shift_x
for platform in self.platform_list:
platform.rect.x += shift_x
for enemy in self.enemy_list:
enemy.rect.x += shift_x
class Room(object):
wall_list = None
enemy_sprites = None
def __init__(self):
self.wall_list = pygame.sprite.Group()
self.enemy_sprites = pygame.sprite.Group()
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -1500
level = [[210, 70, 500, 500],
[210, 70, 800, 400],
[210, 70, 1000, 500],
[210, 70, 1120, 280],
[210, 70, -120, 500],
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
block = MovingPlatform(70, 40)
block.rect.x = 1350
block.rect.y = 280
block.boundary_left = 1350
block.boundary_right = 1600
block.change_x = 10
block.player = self.player
block.level = self
self.platform_list.add(block)
class Level_02(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -1000
level = [[210, 70, 500, 550],
[210, 70, 800, 400],
[210, 70, 1000, 500],
[210, 70, 1120, 280],
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
block = MovingPlatform(70, 70)
block.rect.x = 1500
block.rect.y = 300
block.boundary_top = 100
block.boundary_bottom = 550
block.change_y = -1
block.player = self.player
block.level = self
self.platform_list.add(block)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
width = 40
height = 60
self.image = pygame.image.load("police.png").convert()
self.rect = self.image.get_rect()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.change_x = 0
self.change_y = 0
self.level = None
def update(self):
self.calc_grav()
self.rect.x += self.change_x
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.change_x > 0:
self.rect.right = block.rect.left
elif self.change_x < 0:
self.rect.left = block.rect.right
self.change_y += 0
self.rect.y += self.change_y
block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.change_y > 0:
self.rect.bottom = block.rect.top
elif self.change_y < 0:
self.rect.top = block.rect.bottom
self.change_y = 0
if isinstance(block, MovingPlatform):
self.rect.x += block.change_x
def calc_grav(self):
if self.change_y == 0:
self.change_y = 1
else:
self.change_y += .35
if self.rect.y >= SCREEN_HEIGHT - self.rect.height and self.change_y >= 0:
self.change_y = 0
self.rect.y = SCREEN_HEIGHT - self.rect.height
def jump(self):
self.rect.y += 2
platform_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
self.rect.y -= 2
if len(platform_hit_list) > 0 or self.rect.bottom >= SCREEN_HEIGHT:
self.change_y = -10
def go_left(self):
self.change_x = -6
def go_right(self):
self.change_x = 6
def stop(self):
self.change_x = 0
class Cowboy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("cowboy.png").convert()
self.rect = self.image.get_rect()
self.image.set_colorkey(BLACK)
class Missile(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("missile.png").convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.y += -3
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
block_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
pygame.display.set_caption("Platformer with moving platforms")
apple = pygame.image.load("missile.png").convert()
block = Cowboy()
player_image = pygame.image.load("cowboy.png").convert()
player = Player()
level_list = []
level_list.append(Level_01(player))
level_list.append(Level_02(player))
current_level_no = 0
current_level = level_list[current_level_no]
block_list.add(block)
all_sprites_list.add(block)
all_sprites_list.add(player)
active_sprite_list = pygame.sprite.Group()
player.level = current_level
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
x_speed = 0
y_speed = 0
rect_x = 50
rect_y = 50
rect_change_x = 1
rect_change_y = 90
x = rect_x
y = rect_y
player.rect.y = rect_x
player.rect.y = 480
score = 0
size = (1366, 768)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
while not done:
x += x_speed
y += y_speed
rect_x += rect_change_x
if rect_x > 280:
rect_change_x *= -1
rect_x += rect_change_x
rect_y += rect_change_y
if rect_x < 0:
rect_change_x *= -1
rect_x += rect_change_x
rect_y += rect_change_y
block.rect.x = rect_x
block.rect.y = rect_y
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Missile()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
all_sprites_list.add(bullet)
bullet_list.add(bullet)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
if event.key == pygame.K_UP:
player.jump()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and player.change_x < 0:
player.stop()
if event.key == pygame.K_RIGHT and player.change_x > 0:
player.stop()
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
def draw_reaction():
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
all_sprites_list.add( player_sprite )
break
all_sprites_list.update()
active_sprite_list.update()
current_level.update()
if player.rect.right >= 1500:
diff = player.rect.right - 500
player.rect.right = 500
current_level.shift_world(-diff)
if player.rect.left <= 120:
diff = 120 - player.rect.left
player.rect.left = 120
current_level.shift_world(diff)
current_position = player.rect.x + current_level.world_shift
if current_position < current_level.level_limit:
if current_level_no < len(level_list)-1:
player.rect.x = 120
current_level_no += 1
current_level = level_list[current_level_no]
player.level = current_level
else:
done = True
current_level.draw(screen)
active_sprite_list.draw(screen)
x += x_speed
y += y_speed
all_sprites_list.draw(screen)
draw_reaction()
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
UPDATE EDIT: If current_level.draw(screen) is not used in the main loop then the code works as expected. But I still don't know what causes it.
I simply added all_sprites_list.add (player_sprite) to the for block in block_hit_list:. In my view this would be necessary to track self.image.get_rect (). Then:
Before:
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
Later:
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
all_sprites_list.add(player_sprite)
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print(score)
screen.blit(player_image, [60, 48])
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
But I would like someone to explain to me how it really happened behind the code by making more experienced references to the documentation or the style of the code itself. Thankful if anyone can make that explanation.

The continue button doesn't work when the game is paused

I have made a game where you have to dodge objects. You can also pause the game by pressing p. Once you pause the game you get an option if you want to continue or quit. For me, the quit button is working but the continue button isn't. Where it says define unpause() is where the code starts for the pause button.
import time
import random
pygame.init()
display_width = 800
display_height = 600
red = (200,0,0)
orange = (255, 165, 0)
yellow = (255, 242, 0)
green = (0,200,0)
blue = (0,0,255)
indigo = (75, 0, 130)
violet = (238, 130, 238)
black = (0,0,0)
white = (255,255,255)
bright_red = (255,0,0)
bright_green = (0,255,0)
car_width = 86
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Racing Game")
clock = pygame.time.Clock()
carImg = pygame.image.load("download.png")
carImg = pygame.transform.scale(carImg,(100,160))
pause = False
def things_dodged(count):
font = pygame.font.SysFont(None, 25)
text = font.render ("Dodged: "+str(count), True, black)
gameDisplay.blit(text,(0,0))
def things(thingx, thingy, thingw, thingh, color):
pygame.draw.rect(gameDisplay, color, [thingx, thingy, thingw, thingh])
def car(x,y):
gameDisplay.blit(carImg, (x,y))
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font("freesansbold.ttf",100)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
pygame.display.update()
game_loop()
time.sleep(2000)
def crash():
message_display ("YOU CRASHED")
def button(msg,x,y,w,h,ic,ac,action = None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
print(click)
if x + w > mouse [0] > x and y + h > mouse [1] > y:
pygame.draw.rect(gameDisplay, ac, (x,y,w,h))
if click[0] == 1 and action != None:
if action == "play":
game_loop()
elif action == "quit":
pygame.quit()
quit()
else:
pygame.draw.rect(gameDisplay, ic, (x,y,w,h))
smallText = pygame.font.Font("freesansbold.ttf",20)
textSurf, textRect = text_objects(msg, smallText)
textRect.center = ( (x+(w/2)), (y+(h/2)) )
gameDisplay.blit(textSurf, textRect)
def quitgame():
pygame.quit()
quit
def unpause():
global pause
pause = False
def paused():
while paused:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font("freesansbold.ttf",100)
TextSurf, TextRect = text_objects("PAUSED", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
button ("Continue!",150,450,100,50,green,bright_green,unpause)
button ("Quit!",550,450,100,50,red,bright_red,"quit")
pygame.display.update()
clock.tick(15)
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font("freesansbold.ttf",100)
TextSurf, TextRect = text_objects("Racing Game", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
button ("GO!",150,450,100,50,green,bright_green,"play")
button ("QUIT!",550,450,100,50,red,bright_red,"quit")
pygame.display.update()
clock.tick(15)
def game_loop():
global pause
x = (display_width * 0.45)
y = (display_height * 0.72)
x_change = 0
thing_startx = random.randrange(0, display_width)
thing_starty = -600
thing_speed = 7
thing_width = 100
thing_height = 100
thingCount = 1
dodged = 0
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
if event.key == pygame.K_RIGHT:
x_change = 5
if event.key == pygame.K_p:
pause = True
paused()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
x += x_change
gameDisplay.fill(white)
things(thing_startx, thing_starty, thing_width, thing_height, black)
thing_starty += thing_speed
car (x,y)
things_dodged(dodged)
if x > display_width - car_width or x < -10:
crash()
if thing_starty > display_height:
thing_starty = 0 - thing_height
thing_startx = random.randrange(0,display_width)
dodged += 1
thing_speed += 1
#thing_width += (dodged * 0.5)
if y < thing_starty + thing_height:
if x > thing_startx and x < thing_startx + thing_width or x + car_width > thing_startx and x + car_width < thing_startx + thing_width:
crash()
pygame.display.update()
clock.tick(100)
game_intro()
game_loop()
pygame.quit()
quit()
Please try to help. I am saying Thank You in advance for all your guy's help.
You have typo in while paused: in def paused()- it has to be while pause:
def paused():
while pause: # <- pause instead of paused

Pygame sprite not displaying for unknown reasons

Okay, so I have now tried to fix my Pygame code for 5 days so I can get the monster sprite drawn just like the platform sprites are already working.
I can't find an error with my code and so do my friends neither.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame, random, sys, os
from pygame.locals import *
pygame.init() #pygame initieres
#skærm indstillinger
window_size = [800, 600]
window_mode = 0
window_color_rate = 32
screen = pygame.display.set_mode(window_size, window_mode, window_color_rate)
background_image = 'background.jpg'
background = (pygame.transform.scale(pygame.image.load(background_image).convert(), (800, 600)))
pygame.display.set_caption('I Wanna Be the Reindeer')
clock = pygame.time.Clock()
class Platform(pygame.sprite.Sprite):
def __init__(self, color, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
platform_image = 'platform.jpg'
self.image = pygame.transform.scale(pygame.image.load(platform_image).convert(), ([width, height]))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Monster(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
monster_image = 'platform.jpg'
self.image = pygame.transform.scale(pygame.image.load(monster_image).convert(), ([width, height]))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Goal(pygame.sprite.Sprite):
def __init__(self, color, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Player(pygame.sprite.Sprite):
#variabler spilleren ændre
change_x = 0
change_y = 0
jump_ready = False
frame_since_collision = 0
frame_since_jump = 0
levelpoints = 0
player_right = 'mobright.png'
player_left = 'mobleft.png'
def __init__(self, color, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(pygame.image.load(Player.player_right).convert_alpha(), [30, 30])
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def changespeed_x(self, x):
self.change_x = x
def changespeed_y(self, y):
self.change_y = y
def jump(self):
self.jump_ready = True
self.frame_since_jump = 0
def update(self, platforms):
old_x = self.rect.x
new_x = old_x + self.change_x
self.rect.x = new_x
old_y = self.rect.y
new_y = old_y + self.change_y
self.rect.y = new_y
collide = pygame.sprite.spritecollide(self, platforms, False)
if collide:
self.rect.x = old_x
platform_hit_list = pygame.sprite.spritecollide(self, platforms, False)
for platform in platform_hit_list:
self.rect.x = old_x
self.rect.y = old_y
self.change_y = 0
self.frame_since_collision = 0
if self.frame_since_collision < 1 and self.frame_since_jump < 6:
self.frame_since_jump = 5
self.change_y -= 10
self.frame_since_collision += 1
self.frame_since_jump += 1
def gravity(self):
self.change_y += 0.35
if self.rect.y >= 570 and self.change_y >= 0:
self.change_y = 0
self.rect.y = 570
self.frame_since_collision = 0
def goalcheck(self, goals):
goal_hit_list = pygame.sprite.spritecollide(self, goals, True)
for goals in goal_hit_list:
Player.levelpoints += 1
'''class Monster(pygame.sprite.Sprite):
global walk_quota
walk_quota = 0
def __init__(self, x, y, walk_distance, walk_interval):
pygame.sprite.Sprite.__init__(self)
monster_image = 'monsterright.png'
self.image = pygame.transform.scale(pygame.image.load(monster_image).convert_alpha(), [35, 35])
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.walk_distance = walk_distance
self.walk_interval = walk_interval
def walking(self):
global walk_quota
if self.walk_distance > walk_quota:
self.rect.x += self.walk_interval
walk_quota += self.walk_interval
print walk_quota
'''
#spilleren skabes
player = Player([255, 255, 0], 20, 15)
player.rect.x = 700
player.rect.y = 550
#monster funktioner
#def monsterwalk():
# for monster in monster_list:
# monster.walking()
#gruppe indstillinger
all_sprites_list = pygame.sprite.Group()
platform_list = pygame.sprite.Group()
goal_list = pygame.sprite.Group()
monster_list = pygame.sprite.Group()
all_sprites_list.add(player)
all_sprites_list.add(platform_list)
all_sprites_list.add(goal_list)
all_sprites_list.add(monster_list)
#level 1
def level1():
if Player.levelpoints == 0:
goal = goal_list.add(Goal((0, 255, 0), 50, 80, 20, 20))
monster = monster_list.add(Monster(400, 600, 200, 200))
for i in range(1, 6):
platform = platform_list.add(
[Platform((0, 0, 255), 50, 100, i * 20, 20)])
for i in range(1, 6):
platform = platform_list.add(
[Platform((0, 0, 255), 150, 200, i * 20, 20)])
for i in range(1, 6):
platform = platform_list.add(
[Platform((0, 0, 255), 250, 300, i * 20, 20)])
for i in range(1, 6):
platform = platform_list.add(
[Platform((0, 0, 255), 350, 400, i * 20, 20)])
for i in range(1, 6):
platform = platform_list.add(
[Platform((0, 0, 255), 450, 500, i * 20, 20)])
Player.levelpoints += 1
#level2
def level2():
if Player.levelpoints == 2:
platform_list.empty()
platform = platform_list.add(
[Platform((0, 0, 255), 600, 200, 100, 20)],
[Platform((0, 0, 255), 600, 300, 100, 20)],
[Platform((0, 0, 255), 600, 400, 100, 20)])
Player.levelpoints += 1
#*------Main loop------*
while True:
#styring af spiller
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == K_LEFT:
player.changespeed_x(-4)
player.image = pygame.transform.scale(pygame.image.load(Player.player_left).convert_alpha(), [30, 30])
if event.key == K_RIGHT:
player.changespeed_x(4)
player.image = pygame.transform.scale(pygame.image.load(Player.player_right).convert_alpha(), [30, 30])
if event.key == K_DOWN:
player.changespeed_y(6)
if event.key == K_UP:
player.jump()
if event.type == KEYUP:
if event.key == K_LEFT:
player.changespeed_x(0)
if event.key == K_RIGHT:
player.changespeed_x(0)
if player.rect.x >= 800:
player.rect.x = -15
if player.rect.x <= -20:
player.rect.x = 799
#initiering af levels
level1()
level2()
#funktioner til opdating af objekter
player.gravity()
player.update(platform_list)
player.goalcheck(goal_list)
platform_list.update()
monster_list.update()
#monsterwalk()
#sprites og billeder der skal tegnes på skærmen
screen.fill((0, 0, 0))
screen.blit(background, [0, 0])
all_sprites_list.draw(screen)
platform_list.draw(screen)
monster_list.draw(screen)
goal_list.draw(screen)
clock.tick(500)
print Player.levelpoints
pygame.display.update()
As it is now I can get my player, goal and platforms to show but not the monster.
Note: The monster uses the platform image just as a placeholder.
You simply draw it outside the screen.
The screen is 800x600, and you draw it at 400, 600, hence it is right beneath the lower edge of the screen:
Try 400, 600 - height_of_monster_image instead, for example.