pygame shaking window when loosing lifes

game-engine, pygame, python

Solution

A simple solution is to not draw to the screen `Surface` directly, but to a temporary `Surface`. Then blit that temporary `Surface` to the screen `Surface`.

If you want to shake the screen, simply apply an offset to the position of the temporary `Surface` while blitting.

To generate that offset, you can make use of a generator function.

Here's a complete example. Note how the screen shakes if the player is hit by a falling rock.

import pygame
from random import randint
from itertools import repeat

pygame.init()
org_screen = pygame.display.set_mode((400, 400))
screen = org_screen.copy()
screen_rect = screen.get_rect()
player = pygame.Rect(180, 180, 20, 20)

def get_rock():
    return pygame.Rect(randint(0, 340), 0, 60, 60)

falling = get_rock()
clock = pygame.time.Clock()

# 'offset' will be our generator that produces the offset
# in the beginning, we start with a generator that 
# yields (0, 0) forever
offset = repeat((0, 0))

# this function creates our shake-generator
# it "moves" the screen to the left and right
# three times by yielding (-5, 0), (-10, 0),
# ... (-20, 0), (-15, 0) ... (20, 0) three times,
# then keeps yieling (0, 0)
def shake():
    s = -1
    for _ in xrange(0, 3):
        for x in range(0, 20, 5):
            yield (x*s, 0)
        for x in range(20, 0, 5):
            yield (x*s, 0)
        s *= -1
    while True:
        yield (0, 0)

while True:
    if pygame.event.get(pygame.QUIT): break
    pygame.event.pump()

    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]: player.move_ip(0, -2)
    if keys[pygame.K_a]: player.move_ip(-2, 0)
    if keys[pygame.K_s]: player.move_ip(0, 2)
    if keys[pygame.K_d]: player.move_ip(2, 0)
    player.clamp_ip(screen_rect) 

    falling.move_ip(0, 4)
    org_screen.fill((0, 0, 0))
    screen.fill((255,255,255))
    pygame.draw.rect(screen, (0,0,0), player)
    pygame.draw.rect(screen, (255,0,0), falling)

    if player.colliderect(falling):
        # if the player is hit by the rock,
        # we create a new shake-generator
        offset = shake()
        falling = get_rock()

    if not screen_rect.contains(falling):
        falling = get_rock()

    clock.tick(100)
    # here we draw our temporary surface to the
    # screen using the offsets created by the 
    # generators.
    org_screen.blit(screen, next(offset))
    pygame.display.flip()

Problem

I've been trying to develop a small shooting game and i'd like the window to shake eveytime the hero gets hit or looses a life, i was thinkingto maybe reposition the window 3 times in a very shot time but im not sure how to implement this and still keep the game running. Here is my code for my main game loop: ``` def game(): #set screen width = 800 height = 600 pygame.init() screen = pygame.display.set_mode((width, height)) pygame.display.set_caption('UoN Invaders: Game') background = pygame.Surface(screen.get_size()) background = background.convert() background.fill((000, 000, 000)) screen.blit(background, (0, 0)) pygame.display.flip() #load music with help of helpers.py music = pygame.mixer.music.load ("data/spacequest.mp3") pygame.mixer.music.play(-1) global clock, player, playerSprite, enemySprites, enemiestocreat global enemiesset, lvl2, laserSprites, enemyExplosion, playerExplosion, explosionSprites clock = pygame.time.Clock() i = 0 #setting i that define if the game is just starting of in the middle of the game space1 = Space1(i) space2 = Space2() player = Player() space1 = pygame.sprite.RenderPlain((space1)) space2 = pygame.sprite.RenderPlain((space2)) playerSprite = pygame.sprite.RenderPlain((player)) enemySprites = pygame.sprite.RenderPlain(()) if lvl2 == False: enemiestocreat = int(float(enemiesset)) while enemiestocreat != 0: enemySprites.add(Enemy(enemiestocreat*50)) enemiestocreat -= 1 elif lvl2 == True: enemiesset = int(float(enemiesset)) * 2 enemiestocreat = enemiesset while enemiestocreat != 0: enemySprites.add(Enemy(enemiestocreat*50)) enemiestocreat -= 1 laserSprites = pygame.sprite.RenderPlain(()) enemyExplosion = pygame.sprite.RenderPlain(()) playerExplosion = pygame.sprite.RenderPlain(()) explosionSprites = pygame.sprite.RenderPlain(()) # Main Loop global gameover, enemydead while gameover == False: clock.tick(60) global lives font = pygame.font.Font(None,36) text1 = font.render("Lives:" + str(lives + 1), 1, (250,250,250)) textpos1 = text1.get_rect() textpos1.centerx = 750 textpos1.centery = 590 global enemieskilled text2 = font.render("Score: " + str(enemieskilled), 1, (250,250,250)) textpos2 = text2.get_rect() textpos2.centerx = 55 textpos2.centery = 590 if int(float(enemiesset)) == enemydead: lvl2 = True enemydead = 0 lvl2src() for event in pygame.event.get(): if event.type == KEYDOWN and event.key == K_ESCAPE: main() # Update i += 1 space1.update(i) space2.update() player.update() enemySprites.update() laserSprites.update() enemyExplosion.update() playerExplosion.update() explosionSprites.update() screen.blit(background, (0, 0)) # Draw space1.draw(screen) space2.draw(screen) playerSprite.draw(screen) enemySprites.draw(screen) laserSprites.draw(screen) enemyExplosion.draw(screen) playerExplosion.draw(screen) explosionSprites.draw(screen) screen.blit(text1, textpos1) screen.blit(text2, textpos2) pygame.display.flip() while gameover == True: gameoversrc() ``` And here is my function where i detect if the hero gets hit: ``` class Enemy(pygame.sprite.Sprite): def __init__(self, centerx): global level if level%2: #choosing which lecturer depending on the modulus of the level pygame.sprite.Sprite.__init__(self) self.image, self.rect = load_image("fse.png", -1) self.rect = self.image.get_rect() self.dy = 8 self.reset() elif level%3: pygame.sprite.Sprite.__init__(self) self.image, self.rect = load_image("aps.png", -1) self.rect = self.image.get_rect() self.dy = 8 self.reset() else: pygame.sprite.Sprite.__init__(self) self.image, self.rect = load_image("ust.png", -1) self.rect = self.image.get_rect() self.dy = 8 self.reset() def update(self): self.rect.centerx += self.dx self.rect.centery += self.dy #checking if enemies are outside scrren if self.rect.top > 600: self.reset() # Laser Collisions if pygame.sprite.groupcollide(enemySprites, laserSprites, 1, 1): explosionSprites.add(EnemyExplosion(self.rect.center)) # Ship Collisions if pygame.sprite.groupcollide(playerSprite, enemySprites, 0, 1): global lives if lives == 0: explosionSprites.add(PlayerExplosion(self.rect.center)) elif lives != 0: explosionSprites.add(EnemyExplosion(self.rect.center)) lives -= 1 #randome movement of enemies def reset(self): self.rect.bottom = 0 self.rect.centerx = random.randrange(0, 800) self.dy = random.randrange(5, 10) self.dx = random.randrange(-2, 2) ```

Original source