Screen Shake
Part of the Sound and Game Feel section of Coddy's Pygame journey. Lesson 12 of 23.
A heavy crate hits the ground and the whole view jolts for a moment. That is screen shake: for a few frames, everything is drawn a few pixels off, a different way each frame.
To move everything at once, draw the world on a Surface of its own instead of on the screen, then blit that Surface onto the screen at an offset:
world = pygame.Surface((480, 360))In the loop, the drawing calls that used screen now use world. Then pick the offset:
offset = (0, 0)
if shake > 0:
amount = shake // 3
offset = (random.randint(-amount, amount), random.randint(-amount, amount))
shake -= 1
screen.fill((0, 0, 0))
screen.blit(world, offset)shake counts the frames left to shake. A landing sets it to 12. Since amount is shake // 3, the first frames move up to 4 pixels, then 3, 2, 1 and none: the jolt dies down instead of stopping dead. Fill the screen black first, because a world moved 4 pixels leaves a thin strip uncovered.
Challenge
MediumSpace drops a crate, and its landing sets shake = 12, which nothing uses yet. Shake the view:
- Before the loop, make
world = pygame.Surface((480, 360)). - Draw the background, the ground, the post and the crates on
worldinstead ofscreen. - While
shakeis above 0, pick an offset withamount = shake // 3andrandom.randint(-amount, amount)for x and y, then lowershakeby 1. - Fill the screen black and blit
worldat the offset.
Try it yourself
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Crate Drop")
clock = pygame.time.Clock()
thud_sound = pygame.mixer.Sound("assets/sounds/thud.ogg")
falling = []
landed = []
shake = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
falling.append(pygame.Rect(random.randint(140, 400), -40, 40, 40))
for crate in falling:
crate.y += 8
if crate.bottom >= 300:
crate.bottom = 300
landed.append(crate)
thud_sound.play()
shake = 12
falling = [crate for crate in falling if crate.bottom < 300]
screen.fill((30, 30, 46))
pygame.draw.rect(screen, (80, 80, 100), (0, 300, 480, 60))
pygame.draw.rect(screen, (80, 160, 255), (60, 220, 30, 80))
for crate in falling + landed:
pygame.draw.rect(screen, (170, 110, 60), crate)
pygame.display.flip()
clock.tick(60)
pygame.quit()
All lessons in Sound and Game Feel
Practice on your own: Pygame playground