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) ```