Dette er del fire av en femdelers serie av opplæringsprogrammer om å lage spill med Python 3 og Pygame. I tredje treffer vi inn i hjertet av Breakout og lærte hvordan vi håndterer hendelser, møtte den største Breakout-klassen, og så hvordan vi kan bevege de forskjellige spillobjektene.
I denne delen vil vi se hvordan vi oppdager kollisjoner og hva som skjer når ballen rammer forskjellige gjenstander som padle, murstein, vegger, tak og gulv. Til slutt vurderer vi det viktige temaet for spillgrensesnitt og spesielt hvordan du lager en meny med våre egne tilpassede knapper.
I spill støter ting inn i hverandre. Breakout er ikke annerledes. Mest sett er det ballen som støter på ting. De handle_ball_collisions ()
Metoden har en nestet funksjon kalt krysse()
, som brukes til å teste om ballen slo et objekt og hvor det rammet objektet. Den returnerer "venstre", "høyre", "topp", "bunn" eller ingen hvis ballen ikke rammet objektet.
def handle_ball_collisions (selv): def kryss (obj, ball): kanter = dikt (venstre = Rect (obj.left, obj.top, 1, obj.height), right = Rect (obj.right, obj.top, 1 , obj.height), topp = Rect (obj.left, obj.top, obj.width, 1), bunn = Rect (obj.left, obj.bottom, obj.width, 1)) kollisjoner = sett kanten, rett i kantene.items () hvis ball.bounds.colliderect (rect)) hvis ikke kollisjoner: retur Ingen hvis len (kollisjoner) == 1: returliste (kollisjoner) [0] hvis "topp" i kollisjoner: hvis ball.centery> = obj.top: return "top" hvis ball.centerx < obj.left: return 'left' else: return 'right' if 'bottom' in collisions: if ball.centery >= obj.bottom: returnere "bunn" hvis ball.centerx < obj.left: return 'left' else: return 'right'
Når ballen treffer padlen, hopper den av. Hvis den treffer toppen av padlen, vil den hoppe opp igjen, men hold den samme horisontale hastighetskomponenten.
Men hvis den treffer siden av padlen, vil den hoppe til motsatt side (venstre eller høyre) og fortsette bevegelsen nedover til den treffer gulvet. Koden bruker kryssingsfunksjonen ().
# Hit padle s = self.ball.speed edge = kryss (selv.paddle, self.ball) hvis kanten ikke er None: self.sound_effects ['paddle_hit']. Spill () hvis kant == 'topp': speed_x = s [0] speed_y = -s [1] hvis selv.paddle.moving_left: speed_x - = 1 elif self.paddle.moving_left: speed_x + = 1 self.ball.speed = speed_x, speed_y elif kanten i ('left' 'høyre'): self.ball.speed = (-s [0], s [1])
Når padlen savner ballen på vei ned (eller hvis ballen rammer padle på siden), vil ballen fortsette å falle og til slutt treffe gulvet. På dette tidspunktet mister spilleren et liv, og ballen gjenopprettes slik at spillet kan fortsette. Spillet er over når spilleren har gått tom for liv.
# Hit gulv hvis self.ball.top> c.screen_height: self.lives - = 1 hvis self.lives == 0: self.game_over = True annet: self.create_ball ()
Når ballen rammer en vegg eller taket, spretter den bare tilbake.
# Hit tak hvis selv.ball.top < 0: self.ball.speed = (s[0], -s[1]) # Hit wall if self.ball.left < 0 or self.ball.right > c.screen_width: self.ball.speed = (-s [0], s [1])
Når en ball treffer en murstein, er det en stor begivenhet i Breakout - murstein forsvinner, spilleren får et poeng, ballen spretter tilbake og noen andre ting skjer (lydeffekt og muligens også en spesiell effekt) som jeg skal diskutere seinere.
For å finne ut om en murstein ble truffet, kontrollerer koden for å se om noen av mursteinene krysser med ballen:
# Hit murstein for murstein i selv.bricks: kant = skjær (murstein, selvball) om ikke kant: fortsett self.bricks.remove (murstein) self.objects.remove (brick) self.score + = self.points_per_brick if kanten i ('topp', 'bunn'): self.ball.speed = (s [0], -s [1]) ellers: self.ball.speed = (-s [0], s [1])
De fleste spill har noen brukergrensesnitt. Breakout har en enkel meny som har to knapper som sier 'PLAY' og 'QUIT'. Menyen vises i begynnelsen av spillet og forsvinner når spilleren klikker 'PLAY'. La oss se hvordan knappene og menyen implementeres og hvordan de integreres med spillet.
Pygame har ikke et innebygd UI-bibliotek. Det er tredjepartsutvidelser, men jeg bestemte meg for å bygge mine egne knapper for menyen. En knapp er et spillobjekt som har tre stater: normal, svever og trykket. Den normale tilstanden er når musen ikke er over knappen, og hover-tilstanden er når musen er over knappen, men venstre museknapp er ikke trykket. Den trykte tilstanden er når musen er over knappen og spilleren har trykket på venstre museknapp.
Knappen er implementert som et rektangel med bakgrunnsfarge og tekst som vises over det. Knappen mottar også en on_click-funksjon (standard til en no-lambda-funksjon) som blir kalt når knappen klikkes.
import pygame fra game_object import GameObject fra text_object import TextObject import config som c klasse Button (GameObject): def __init __ (selv, x, y, w, h, tekst, on_click = lambda x: Ingen, polstring = 0): super .__ init __ (x, y, w, h) self.state = 'normal' self.on_click = on_click self.text = TextObject (x + polstring, y + polstring, lambda: tekst, c.button_text_color, c.font_name, c .font_size) def trekke (selv, overflate): pygame.draw.rect (overflate, self.back_color, self.bounds) self.text.draw (overflate)
Knappen håndterer sine egne mus hendelser og endrer sin interne tilstand basert på disse hendelsene. Når knappen er i trykket tilstand og mottar a MOUSEBUTTONUP
arrangement, betyr det at spilleren klikket på knappen, og ved trykk()
funksjonen er påkalt.
def handle_mouse_event (selv, type, pos): hvis type == pygame.MOUSEMOTION: self.handle_mouse_move (pos) elif type == pygame.MOUSEBUTTONDOWN: self.handle_mouse_down (pos) elif type == pygame.MOUSEBUTTONUP: self.handle_mouse_up pos) def handle_mouse_move (selv, pos): hvis self.bounds.collidepoint (pos): hvis self.state! = 'presset': self.state = 'hover' annet: self.state = 'normal' def handle_mouse_down , pos): hvis self.bounds.collidepoint (pos): self.state = 'presset' def handle_mouse_up (selv, post): hvis selvstart == 'trykket': self.on_click (self) self.state = ' sveve'
De back_color
Egenskapen som brukes til å tegne bakgrunnsrektelet returnerer alltid fargen som samsvarer med gjeldende tilstand på knappen, så det er klart for spilleren at knappen er aktiv:
@property def back_color (selv): return dict (normal = c.button_normal_back_color, hover = c.button_hover_back_color, presset = c.button_pressed_back_color) [selvstart]
De create_menu ()
funksjonen skaper en meny med to knapper med teksten 'PLAY' og 'QUIT'. Den har to nestede funksjoner kalt on_play ()
og on_quit ()
at den gir til den tilsvarende knappen. Hver knapp er lagt til i objekter
liste (skal tegnes) og også til menu_buttons
felt.
def create_menu (selv): for jeg, (tekst, håndterer) i enumerere (((PLAY), on_play), ('QUIT', on_quit))): b = Knapp (c.menu_offset_x, c.menu_offset_y + .menu_button_h + 5) * Jeg, c.menu_button_w, c.menu_button_h, tekst, handler, padding = 5) self.objects.append (b) self.menu_buttons.append (b) self.mouse_handlers.append (b.handle_mouse_event)
Når PLAY-knappen klikkes, på_play () påberopes, som fjerner knappene fra objekter
liste så de ikke er trukket lenger. Også de boolske feltene som utløser starten av spillet-is_game_running
og start_level
-er satt til True.
Når QUIT-knappen er klikket, is_game_running
er satt til Falsk
(pause spillet effektivt) og spillet er slutt
er satt til True, utløser sluttspillets rekkefølge.
def on_play (knapp): for b i self.menu_buttons: self.objects.remove (b) self.is_game_running = True self.start_level = True def on_quit (knapp): self.game_over = True self.is_game_running = False
Viser og gjemmer menyen er implisitt. Når knappene er i objekter
liste, menyen er synlig; Når de er fjernet, er det gjemt. Så enkelt som det.
Det er mulig å lage en nestet meny med egen overflate som gjør underkomponenter som knapper og mer, og legg deretter til / fjern menyelementet, men det er ikke nødvendig for denne enkle menyen.
I denne delen dekket vi kollisjonsdeteksjon og hva skjer når ballen rammer forskjellige gjenstander som padle, murstein, vegger, tak og gulv. Også, vi opprettet vår egen meny med egendefinerte knapper som vi skjuler og viser på kommando.
I den siste delen av serien ser vi inn i sluttspillet, holder faner på poengsum og liv, lydeffekter og musikk.
Deretter vil vi utvikle et sofistikert system med spesialeffekter som vil spice opp spillet. Til slutt vil vi diskutere fremtidens retning og potensielle forbedringer.