Questa è la quinta parte di una serie di tutorial in cinque parti su come creare giochi con Python 3 e PyGame. Nella quarta parte abbiamo rilevato collisioni, risposto alla palla colpendo vari oggetti di gioco e creato un menu di gioco con pulsanti personalizzati.
In questa ultima parte, tratteremo diversi argomenti come il gioco finale, la gestione di vite e punteggi, effetti sonori, musica e persino un sistema di effetti speciali flessibile. Per dessert, discuteremo potenziali miglioramenti e indicazioni future.
Alla fine, il gioco deve finire. In questa versione di Breakout, il gioco termina in due modi: o il giocatore perde tutta la vita o colpisce tutti i mattoni. Non esiste un livello successivo (anche se sarebbe facile aggiungere).
Il campo game_over della classe Game è impostato su False in __dentro__()
metodo della classe di gioco. Il ciclo principale va in tondo e rotondo fino al gioco finito
variabile è impostata su True:
gioco di classe: def __init __ (self, caption, width, height, back_image_filename, frame_rate): ... self.game_over = False ... def run (self): while self.game_over: self.surface.blit (self.background_image, (0 , 0)) self.handle_events () self.update () self.draw () pygame.display.update () self.clock.tick (self.frame_rate)
Tutto ciò accade nella classe Breakout nei seguenti casi:
def on_quit (button): self.game_over = True self.is_game_running = False def handle_ball_collisions (self): ... # Hit floor se self.ball.top> c.screen_height: self.lives - = 1 se self.lives == 0 : self.game_over = Vero se non self.bricks: self.show_message ('YOU WIN !!!', centralized = True) self.is_game_running = False self.game_over = True return def update (self): ... se non self. mattoni: self.show_message ('YOU WIN !!!', centralized = True) self.is_game_running = False self.game_over = True return
Di solito, quando il gioco finisce, non vogliamo che la finestra di gioco scompaia nel nulla. L'eccezione è se si fa clic sul pulsante QUIT nel menu. Quando il giocatore perde la sua ultima vita, Breakout mostra il tradizionale 'GAME OVER!' messaggio, e quando il giocatore vince, visualizza "VINCI!"
Il show_message ()
la funzione è utilizzata in entrambi i casi. Mostra il testo in cima alla schermata corrente (il gioco verrà messo in pausa) e attende alcuni secondi prima di tornare. Nella prossima iterazione del ciclo di gioco, il controllo per il gioco finito
il campo determinerà che è True e il programma uscirà.
Ecco il show_message ()
funzione:
def show_message (self, text, color = colors.WHITE, font_name = "Arial", font_size = 20, centralizzato = False): message = TextObject (c.screen_width // 2, c.screen_height // 2, lambda: text, color, font_name, font_size) self.draw () message.draw (self.surface, centralized) pygame.display.update () time.sleep (c.message_duration)
In questa versione, non mantengo il punteggio più alto perché c'è solo un livello e il punteggio di tutti sarà lo stesso se si eliminano tutti i mattoni. In generale, può essere fatto localmente memorizzando il punteggio più alto in un file e quindi visualizzando un altro messaggio se il giocatore ha rotto il punteggio più alto.
I giochi sono un'esperienza audiovisiva. La maggior parte dei giochi ha effetti sonori che sono brevi byte sonori che vengono riprodotti quando il giocatore uccide un mostro, trova un tesoro o esplode in modo orribile. Alcuni giochi hanno anche musica di sottofondo, che contribuisce all'atmosfera. Breakout ha solo effetti sonori, ma ti mostrerò come riprodurre musica di sottofondo nei tuoi giochi.
Hai bisogno di file audio (simili ai file immagine) per riprodurre effetti sonori. Questi file possono essere in formato .wav, .mp3 o .ogg. Breakout mantiene i suoi effetti sonori in effetti sonori
cartella:
~ / git / pygame-breakout> albero sound_effects / sound_effects / ├── brick_hit.wav ├── effect_done.wav ├── level_complete.wav └── paddle_hit.wav
Vediamo come questi effetti sonori vengono caricati e riprodotti al momento giusto. Innanzitutto, per riprodurre effetti sonori (o musica di sottofondo) è necessario inizializzare il sistema audio di Pygame. Questo succede nella classe di gioco: pygame.mixer.pre_init (44100, 16, 2, 4096)
Quindi, nella classe Breakout, tutti gli effetti sonori vengono caricati dalla configurazione nel pygame.mixer.Sound
oggetto e sono memorizzati in un dizionario:
# In config.py sounds_effects = dict (brick_hit = "sound_effects / brick_hit.wav", effect_done = "sound_effects / effect_done.wav", paddle_hit = "sound_effects / paddle_hit.wav", level_complete = "sound_effects / level_complete.wav",) # Nella classe breakout.py Breakout (Gioco): def __init __ (self): ... self.sound_effects = nome: pygame.mixer.Sound (suono) per nome, suono in c.sounds_effects.items () ...
Ora, possiamo suonare gli effetti sonori quando succede qualcosa di interessante. Ad esempio, quando la palla colpisce un mattone:
# Colpire mattone per mattoni in self.bricks: edge = intersecare (brick, self.ball) se non edge: continuare self.sound_effects ['brick_hit']. Play ()
L'effetto sonoro viene riprodotto in modo asincrono, il che significa che il gioco non si blocca mentre il suono è in riproduzione. È possibile riprodurre più effetti sonori contemporaneamente.
Registrare i tuoi effetti sonori è sia facile che gratificante. A differenza del design delle risorse visive, non ci vuole molto talento. Qualcuno può dire "Kaboom!" o "Boing" o urlo "Sei morto. Buona fortuna la prossima volta!"
Spesso chiedo ai miei figli di registrare effetti sonori e messaggi vocali che accompagnano messaggi di testo come "VINCI!" o 'GAME OVER!' La tua immaginazione è l'unica limitazione.
La musica di sottofondo dovrebbe suonare costantemente. In teoria, puoi avere un effetto sonoro molto loooooooongong, ma un approccio più comune è semplicemente quello di riprodurre la musica di sottofondo in un ciclo. I file musicali possono essere in formato .wav, .mp3 o .midi. Ecco come è fatto:
music = pygame.mixer.music.load ('background_music.mp3') pygame.mixer.music.play (-1, 0.0)
Puoi avere solo un brano musicale in sottofondo alla volta. Ma più effetti sonori possono suonare sulla musica di sottofondo. Questo è tutto ciò che riguarda la miscelazione.
Andiamo alla fantasia. Rompere i mattoni con una palla è bello, ma diventa piuttosto veloce. Che ne dici di un sistema di effetti speciali generici? Svilupperemo un sistema estensibile di effetti speciali associati a determinati mattoni e attiveremo quando la palla colpisce il mattone.
Ecco il piano. Gli effetti hanno una vita. L'effetto inizia quando il mattone si rompe e termina quando scade la durata dell'effetto. Cosa succede se la palla colpisce un altro mattone effetto speciale? In teoria, potresti avere effetti di composizione, ma per semplificare le cose per l'implementazione iniziale, l'effetto attivo si fermerà e il nuovo effetto prenderà il suo posto.
Un effetto speciale può essere definito nel modo più generico come due funzioni. La prima funzione attiva l'effetto e la seconda funzione lo ripristina. Vogliamo allegare effetti ai mattoni e rendere chiaro al giocatore quali mattoni sono speciali, in modo che possano provare a colpirli o evitarli in determinati punti.
Il seguente comando del modulo breakout.py definisce i nostri effetti speciali. Ogni effetto ha un nome (ad esempio long_paddle) e un valore, che consiste del colore del mattone e delle due funzioni. Le funzioni sono definite come funzioni lambda che prendono un'istanza di gioco, che include tutto ciò che un effetto speciale in Breakout potrebbe voler cambiare.
special_effects = dict (long_paddle = (colors.ORANGE, lambda g: g.paddle.bounds.inflate_ip (c.paddle_width // 2, 0), lambda g: g.paddle.bounds.inflate_ip (-c.paddle_width // 2 , 0)), slow_ball = (colors.AQUAMARINE2, lambda g: g.change_ball_speed (-1), lambda g: g.change_ball_speed (1)), tripple_points = (colors.DARKSEAGREEN4, lambda g: g.set_points_per_brick (3) , lambda g: g.set_points_per_brick (1)), extra_life = (colors.GOLD1, lambda g: g.add_life (), lambda g: None))
Quando i mattoni vengono creati, a loro viene assegnato un cambiamento per uno degli effetti speciali. Ecco il codice:
def create_bricks (self): w = c.brick_width h = c.brick_height brick_count = c.screen_width // (w + 1) offset_x = (c.screen_width - brick_count * (w + 1)) // 2 mattoni = [] per la riga nel range (c.row_count): per il campo nell'intervallo (brick_count): effect = None brick_color = c.brick_color index = random.randint (0, 10) se l'indice < len(special_effects): x = list(special_effects.values())[index] brick_color = x[0] effect = x[1:] brick = Brick(offset_x + col * (w + 1), c.offset_y + row * (h + 1), w, h, brick_color, effect) bricks.append(brick) self.objects.append(brick) self.bricks = bricks
La classe Brick ha un campo effetto che di solito è Nessuno, ma può ottenere (il 30% di probabilità) uno degli effetti speciali sopra definiti. Si noti che questo codice non è a conoscenza di quali effetti sono disponibili. Ottiene semplicemente l'effetto e il colore del mattone e li assegna se necessario.
In questa versione di Breakout, attiva effetti solo quando viene colpito un mattone, ma puoi immaginare altri scenari che potrebbero attivare eventi. L'effetto precedente viene ripristinato (se ce n'era uno), quindi viene lanciato il nuovo effetto. La funzione di reset e il tempo di inizio dell'effetto vengono memorizzati per dopo.
se brick.special_effect non è None: # Resetta l'effetto precedente se presente se self.reset_effect non è None: self.reset_effect (self) # Trigger effetto speciale self.effect_start_time = datetime.now () brick.special_effect [0] (self) # Imposta la funzione dell'effetto di reset corrente self.reset_effect = brick.special_effect [1]
Se non è stato attivato alcun nuovo effetto, dobbiamo ancora ripristinare l'evento corrente quando scade. Quello succede nel aggiornare()
metodo. In ogni frame, la funzione di reset dell'effetto corrente è stata assegnata al reset_effect
campo. Se il tempo trascorso dall'effetto corrente ha superato la durata dell'effetto, allora il reset_effect ()
la funzione è chiamata e il reset_effect
il campo è impostato su Nessuno (ciò significa che non ci sono effetti attivi al momento).
# Resetta l'effetto speciale se necessario se self.reset_effect: elapsed = datetime.now () - self.effect_start_time se trascorso> = timedelta (seconds = c.effect_duration): self.reset_effect (self) self.reset_effect = Nessuno
Il lungo effetto paddle funziona gonfiando la pagaia del 50%. La sua funzione di reset la ridimensiona semplicemente alla normalità. Il colore del mattone è arancione:
long_paddle = (colors.ORANGE, lambda g: g.paddle.bounds.inflate_ip (c.paddle_width // 2, 0), lambda g: g.paddle.bounds.inflate_ip (-c.paddle_width // 2, 0)),
Un altro effetto che aiuta a inseguire la palla è l'effetto palla lenta, che semplicemente rallenta la velocità della palla di una unità. Il colore del mattone è Acquamarina.
slow_ball = (colors.AQUAMARINE2, lambda g: g.change_ball_speed (-1), lambda g: g.change_ball_speed (1)),
Se vuoi grandi numeri, ti piacerà l'effetto triplo punti che ti dà tre punti per ogni mattone che hai colpito al posto dello standard di un punto. Il colore del mattone è verde scuro.
tripple_points = (colors.DARKSEAGREEN4, lambda g: g.set_points_per_brick (3), lambda g: g.set_points_per_brick (1)),
Infine, un effetto molto utile è l'effetto vita extra. Ti dà solo una vita extra. Nessun reset è necessario davvero. Il colore del mattone è oro.
extra_life = (colors.GOLD1, lambda g: g.add_life (), lambda g: None))
Esistono diverse direzioni naturali per estendere il Breakout. Se sei interessato a provare la tua mano ad aggiungere ulteriori funzionalità e funzionalità, ecco alcune idee.
Per fare di Breakout un gioco serio, ha bisogno di livelli. Giocare solo una schermata non è abbastanza. All'inizio di ogni livello, ripristinerai lo schermo, ma manterrai il punteggio e le vite così come sono. Per rendere il gioco più difficile, puoi aumentare leggermente la velocità della palla ad ogni livello o aggiungere un altro strato di mattoni.
Aggiungere una seconda palla come effetto temporaneo è destinato a creare un sacco di caos. La parte difficile qui è trattare entrambe le sfere come uguali, indipendentemente da quale fosse l'originale. Quando una palla è andata, il gioco continua con la singola palla rimasta. Nessuna vita è persa.
Quando hai livelli con difficoltà crescente, il punteggio più alto diventa un premio ambito. È possibile mantenere il punteggio più alto in un file per persistere tra i giochi. Quando un giocatore rompe il punteggio più alto, puoi aggiungere un po 'di pizazz o lasciare che scrivano il loro nome (tradizionalmente solo tre caratteri).
Nell'attuale implementazione, tutti gli effetti speciali sono legati ai mattoni, ma puoi aggiungere effetti (buoni e cattivi) che cadono dal cielo e il giocatore deve raccoglierli o evitarli.
Sviluppare Breakout usando Python 3 e Pygame è stata un'esperienza super gratificante. È una combinazione molto potente per i giochi 2D (e anche per i giochi 3D). Se ti piace Python e vuoi creare i tuoi giochi, non puoi sbagliare con Pygame.
Ho sicuramente intenzione di fare più giochi con Python e Pygame.
Infine, ricorda che abbiamo un sacco di contenuti Python disponibili per la vendita e per lo studio nel mercato Envato.