Nella lezione precedente (vedi qui) abbiamo realizzato il movimento della nostra astronave e dei
proiettili. Introduciamo ora le astronavi aliene alle quali dovremo sparare. Ci accorgiamo che anche per i nemici
dovremo usare la stessa tecnica usata per i proiettili, creandoli e distruggendoli nel ciclo principale. A differenza dei
proiettili, però, i nemici dovranno comparire ad intervalli casuali e in posizioni casuali, scendendo dall'alto verso il basso.
Saranno quindi necessarie le funzioni del modulo random
.
random
;aliens;
Dovremo creare un nuovo nemico ogni volta che il timer scatta: sarà quindi necessario monitorare nel Ciclo degli eventi anche l'evento TIMERSHOT.
elif
nel Ciclo degli eventi, controllando se si è
verificato l'evento TIMERSHOT; nel corpo dell'if
...alien
, aggiungendolo con il costruttore ai Group all_sprites
e aliens
image
la Surface surf_alien
rect
il Rect ricavato dalla Surfacerandrange()
. L'astronave dovrà comunque essere situata entro lo schermo, quindi dovete calcolare
i due parametri (minimo e massimo) da scrivere nella funzione. Potete aiutarvi con questo disegno.Infine facciamo muovere i nostri nemici verso il basso.
for
identico a quello
usato per muovere i proiettili: le astronavi si devono muovere verso il basso di 2 pixel per ciclo,
e devono essere eliminate dalla lista aliens
quando spariscono sotto il lato inferiore di screen
.
Nel ciclo potete usare la variabile alien
come contatore.
Ecco il programma fino a questo punto. Ora si dovrebbero muovere l'astronave, i proiettili e le astronavi aliene.
import pygame, random
from pygame.locals import *
from os.path import join
# --- Inizializzazione
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Aliens")
clk = pygame.time.Clock()
# --- Risorse grafiche
surf_back = pygame.image.load(join("Backgrounds", "Space.png")).convert()
surf_ship = pygame.image.load(join("Sprites", "SpaceArt", "player.png")).convert_alpha()
surf_alien = pygame.image.load(join("Sprites", "SpaceArt", "enemyShip.png")).convert_alpha()
surf_bullet = pygame.image.load(join("Sprites", "SpaceArt", "laserGreen.png")).convert_alpha()
# --- Risorse sonore
sound_laser = pygame.mixer.Sound(join("Effects","smc-wwvi", "synthetic_laser.ogg"))
sound_expl = pygame.mixer.Sound(join("Effects", "smc-wwvi", "synthetic_gunshot_2.ogg"))
sound_end = pygame.mixer.Sound(join("Effects", "smc-wwvi", "big_explosion.ogg"))
pygame.mixer.music.load(join("Music", "SoundImage", "Techno-Gameplay_Looping.ogg"))
# --- Altre variabili
pressed = None
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
aliens = pygame.sprite.Group()
spr_ship = pygame.sprite.Sprite(all_sprites)
spr_ship.image = surf_ship
spr_ship.rect = surf_ship.get_rect()
spr_ship.rect.topleft = (320, 500)
# --- Inizio del gioco
pygame.mixer.music.play(-1)
TIMERSHOT = pygame.event.custom_type()
pygame.time.set_timer(TIMERSHOT, random.randrange(400, 1501))
# --- Ciclo principale
done = False
while not done:
# --- Ciclo degli eventi
for ev in pygame.event.get():
if ev.type == pygame.QUIT:
done = True
elif ev.type == KEYDOWN and ev.key in (K_LEFT, K_RIGHT):
pressed = ev.key
elif ev.type == KEYUP and ev.key == pressed:
pressed = None
elif ev.type == KEYDOWN and ev.key == K_SPACE:
bullet = pygame.sprite.Sprite(all_sprites, bullets)
bullet.image = surf_bullet
bullet.rect = surf_bullet.get_rect()
bullet.rect.midbottom = spr_ship.rect.midtop
sound_laser.play()
elif ev.type == TIMERSHOT:
pygame.time.set_timer(TIMERSHOT, random.randrange(400, 1501))
alien = pygame.sprite.Sprite(all_sprites, aliens)
alien.image = surf_alien
alien.rect = surf_alien.get_rect()
alien.rect.bottomleft = (random.randrange(0, screen.get_width() - alien.rect.width + 1), -1)
# --- Logica del gioco
if pressed == K_LEFT and spr_ship.rect.left > 0:
spr_ship.rect.x -= 5
elif pressed == K_RIGHT and spr_ship.rect.right < screen.get_width():
spr_ship.rect.x += 5
for bullet in bullets:
bullet.rect.y -= 4
if bullet.rect.bottom < 0:
bullet.kill()
for alien in aliens:
alien.rect.y += 2
if alien.rect.top > screen.get_height():
alien.kill()
# --- Aggiornamento dello schermo
screen.blit(surf_back, (0, 0))
all_sprites.draw(screen)
pygame.display.flip()
clk.tick(30)
# --- Uscita
pygame.quit()
Quando centriamo i nemici essi ovviamente devono sparire insieme al proiettile, con il rumore di un'esplosione. Grazie alle
funzioni di pygame che controllano le collisioni tra Sprite tutto questo diventa semplicissimo. Possiamo usare infatti la
pygame.sprite.groupcollide()
(vedi qui), controllando se qualche
Sprite del Group bullets
collide con qualche Sprite del Group aliens
. Inoltre, impostando a
True entrambi i parmetri dokill
possiamo fare in modo che pygame cancelli automaticamente
gli Sprite in collisione.
if pygame.sprite.groupcollide(bullets, aliens, True, True):
sound_expl.play()
Come abbiamo detto, la groupcollide()
restituisce un dizionario di Python contenente tutte le collisioni
trovate. Se non ci sono collisioni restituirà un dizionario vuoto (una sequenza vuota, sia essa string, lista ,tuple, ecc, viene
sempre considerata equivalente al valore booleano False in Python).
Infine dobbiamo far finire il nostro gioco: possiamo stabilire che avremo perso se:
Se siete arrivati sin qui questi due comportamenti non dovrebbero essere difficili da implementare: ricordiamo che per terminare
il gioco basta porre a True la variabile done
che controlla il ciclo principale.
aliens
; trovate questa riga e modificatela in modo che (anzichè eliminare lo Sprite dal Group) il ciclo principale
si interrompa.pygame.sprite.spritecollide()
, indicando come primo parametro lo Sprite spr_ship
, come secondo il
Group aliens
e come terzo True, in modo che l'astronave nemica sia cancellata dallo schermo.
In entrambi i casi fate anche suonare il Sound sound_end
.
Potete fare una prova, ma vedrete che quando perderete non sentirete il suono finale, perchè il programma esce dal ciclo principale e termina immediatamente, con un effetto piuttosto antipatico. Possiamo però ovviare a questo inconveniente facendo aspettare pygame finchè tutti i suoni non sono terminati:
. . .
# --- Uscita
pygame.mixer.music.stop()
while pygame.mixer.get_busy():
clk.tick(30)
pygame.quit()
Nella #84 fermiamo la colonna sonora. Il ciclo while
nella #85
chiama la funzione get_busy()
del sottomodulo mixer
, che restituisce True se
qualche suono del mixer è in esecuzione e False altrimenti. In pratica questo ciclo continua a far
correre l'orologio clk
(nella #86) fino a quando tutti i suoni sono finiti, quindi
esce e Python esegue la quit()
che ferma pygame.if
che determina lo sparo del proiettile e aggiungete con un and
la terza condizione che la lunghezza del Group bullets
sia minore di 8. In questo modo quando sono presenti 8
proiettili sullo schermo la nostra nave smetterà di sparare finchè uno di essi non sia uscito dallo schermo o esploso contro un
alieno, rendendo il gioco più interessante.Bene, abbiamo finito. Potreste lamentarvi del fatto che non c'è un punteggio o qualche scritta iniziale o finale. Questo sarà l'argomento della prossima lezione.
Fine della lezione