Crash
Part of the Game Rules section of Coddy's Pygame journey. Lesson 27 of 29.
Touching an obstacle ends the run. pygame.sprite.spritecollideany(sprite, group) returns the first sprite in the group that touches the sprite, or None.
By default it compares rects. The pictures have see-through corners, the blob most of all, so two rects can overlap while nothing you see touches. A third argument picks a fairer test:
if pygame.sprite.spritecollideany(runner, obstacles, pygame.sprite.collide_mask):
state = "over"collide_mask compares the pictures' drawn pixels and ignores the see-through ones. A crash is then a crash the player can see.
The state works as in Meteor Dodge: updating, jumping and new obstacles happen only while it is "play", and the game over screen draws the frozen game with a message.
Challenge
Medium- Add a font,
assets/fonts/pixel.ttfat size 32, andstate = "play". - Only while the state is
"play": jump, add obstacles, update the runner and the obstacles. - When the runner touches an obstacle (
collide_mask), set the state to"over". - When the game is over, show
Game overin(40, 50, 80)centred at (320, 140).
Try it yourself
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 320))
pygame.display.set_caption("Sky Runner")
clock = pygame.time.Clock()
SKY = (140, 200, 240)
GROUND = (90, 160, 80)
GROUND_Y = 260
GRAVITY = 1
SPAWN = pygame.USEREVENT + 1
class Runner(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.frames = [
pygame.image.load("assets/images/player.png").convert_alpha(),
pygame.image.load("assets/images/player_walk.png").convert_alpha(),
]
self.image = self.frames[0]
self.step = 0
self.rect = self.image.get_rect(midbottom=(100, GROUND_Y))
self.vy = 0
def jump(self):
if self.rect.bottom == GROUND_Y:
self.vy = -16
def update(self):
self.vy += GRAVITY
self.rect.y += self.vy
if self.rect.bottom >= GROUND_Y:
self.rect.bottom = GROUND_Y
self.vy = 0
if self.rect.bottom == GROUND_Y:
self.step += 1
self.image = self.frames[self.step // 8 % 2]
else:
self.image = self.frames[1]
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = random.choice(obstacle_images)
self.rect = self.image.get_rect(midbottom=(664, GROUND_Y))
def update(self):
self.rect.x -= speed
if self.rect.right < 0:
self.kill()
obstacle_images = [
pygame.image.load("assets/images/spike_ball.png").convert_alpha(),
pygame.image.load("assets/images/blob.png").convert_alpha(),
]
runner = Runner()
obstacles = pygame.sprite.Group()
speed = 6
pygame.time.set_timer(SPAWN, 1500)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key in (pygame.K_SPACE, pygame.K_UP):
runner.jump()
if event.type == SPAWN:
obstacles.add(Obstacle())
pygame.time.set_timer(SPAWN, random.randint(1000, 2000))
runner.update()
obstacles.update()
screen.fill(SKY)
pygame.draw.rect(screen, GROUND, (0, GROUND_Y, 640, 60))
screen.blit(runner.image, runner.rect)
obstacles.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
All lessons in Game Rules
Practice on your own: Pygame playground