Dit is deel drie van een vijfdelige serie tutorials over het maken van spellen met Python 3 en Pygame. In deel twee hebben we de textobject
klas gebruikt om tekst op het scherm weer te geven, het hoofdvenster gemaakt en geleerd om objecten zoals stenen, de bal en de peddel te tekenen.
In dit deel duiken we in het hart van Breakout en leren we hoe we omgaan met evenementen, we ontmoeten de hoofdklasse Breakout en zien we hoe we de verschillende game-objecten moeten verplaatsen.
In Breakout zijn er drie typen gebeurtenissen: toetsdrukgebeurtenissen, muisgebeurtenissen en timergebeurtenissen. De hoofdlus in de klasse Game zorgt voor de belangrijkste pers- en muisgebeurtenissen en levert deze af aan abonnees (door een handler-functie aan te roepen).
Hoewel de Game-klasse zeer generiek is en geen Breakout-specifieke kennis heeft, zijn het abonnement zelf en hoe elk evenement te behandelen zeer specifiek.
In de Breakout-les wordt de meeste domeinkennis over het Breakout-spel beheerd. We zullen de Breakout-klas verschillende keren ontmoeten tijdens deze serie. Hier zijn de regels die de verschillende gebeurtenishandlers registreren.
Let daar op allemaal toetsgebeurtenissen voor zowel de linker- als de rechterpijltoets gaan naar dezelfde handler-methode van de paddle.
# Registreer de handle_mouse_event () methode van een knopobject self.mouse_handlers.append (b.handle_mouse_event) # Registreer de handle () methode van de paddle om belangrijke gebeurtenissen af te handelen self.keydown_handlers [pygame.K_LEFT] .append (paddle.handle) self.keydown_handlers [pygame.K_RIGHT] .append (paddle.handle) self.keyup_handlers [pygame.K_LEFT] .append (paddle.handle) self.keyup_handlers [pygame.K_RIGHT] .append (paddle.handle)
De klasse Game roept de geregistreerde handlers voor elke belangrijke gebeurtenis aan en geeft de sleutel door. Merk op dat het niet de klasse Paddle is. In Breakout is het enige object dat geïnteresseerd is in deze evenementen de paddle. Wanneer een toets wordt ingedrukt of losgelaten, is het handvat()
methode wordt genoemd.
De Paddle hoeft niet te weten of het een key down- of key-up-gebeurtenis was, omdat deze de huidige status beheert via een paar booleaanse variabelen: moving_left
en moving_right
. Als moving_left
is waar, dan betekent dit dat op de linkerpijltoets wordt gedrukt en de volgende gebeurtenis wordt ingedrukt, waardoor deze wordt vrijgegeven. Hetzelfde geldt voor de rechterpijltoets. De logica is net zo eenvoudig als het omschakelen van deze variabelen als reactie op een gebeurtenis.
def handle (self, key): if key == pygame.K_LEFT: self.moving_left = not self.moving_left else: self.moving_right = not self.moving_right
Breakout heeft een spelmenu dat je binnenkort zult ontmoeten. De knop in het menu behandelt verschillende muisgebeurtenissen zoals beweging en klikken (gebeurtenissen met de muis naar beneden en gebeurtenissen met de muis omhoog). Als reactie op deze gebeurtenissen werkt de knop een interne toestandsvariabele bij. Hier is de behandelcode van de muis:
def handle_mouse_event (self, type, pos): if 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 (self, pos): if self.bounds.collidepoint (pos): if self.state! = 'pressed': self.state = 'hover' else: self.state = 'normal' def handle_mouse_down (zelf , pos): if self.bounds.collidepoint (pos): self.state = 'pressed' def handle_mouse_up (self, pos): if self.state == 'pressed': self.on_click (self) self.state = ' zweven'
Merk op dat de handle_mouse_event ()
methode die is geregistreerd om muisgebeurtenissen te ontvangen, controleert het type gebeurtenis en stuurt het door naar de specifieke methode die dat gebeurtenistype verwerkt.
Timergebeurtenissen worden niet verwerkt via de hoofdlus. Aangezien de hoofdlus elk frame wordt genoemd, is het echter eenvoudig om te controleren of een bepaalde tijdwaarschuwing moet plaatsvinden. U zult hier later een voorbeeld van zien als het gaat om getimede special effects.
Een andere situatie is wanneer we de game willen bevriezen, bijvoorbeeld wanneer een bericht wordt weergegeven dat de speler zonder afleiding moet kunnen lezen. De toon bericht()
methode van de Breakout-klasse maakt gebruik van deze benadering en oproepen time.sleep ()
. Hier is de relevante code:
import config als c class Breakout (Game): def show_message (self, text, color = colors.WHITE, font_name = "Arial", font_size = 20, centralized = False): message = TextObject (c.screen_width // 2, c .screen_height // 2, lambda: text, color, font_name, font_size) self.draw () message.draw (self.sface, centralized) pygame.display.update () time.sleep (c.message_duration)
In het gameplay gedeelte komen de regels van Breakout om te spelen (zie wat ik daar heb gedaan?). Gameplay gaat over het verplaatsen van de verschillende objecten als reactie op de gebeurtenissen en het veranderen van de spelstatus op basis van hun interacties.
Je zag eerder dat de Paddle-klasse reageert op de pijltjestoetsen door het updaten van de moving_left
en moving_right
velden. De daadwerkelijke beweging gebeurt in de bijwerken()
methode. Er wordt hier wat berekend als de paddle zich dicht bij de linker- of rechterrand van het scherm bevindt. We willen niet dat de paddle voorbij de randen van het scherm komt (inclusief een vooraf gedefinieerde offset).
Dus als de beweging het voorbij de rand zou hebben genomen, past de code de beweging aan om precies op de rand te stoppen. Omdat de peddel alleen horizontaal beweegt, is de verticale component van de beweging altijd nul.
import pygame import config als c van game-object import GameObject-klasse Paddle (GameObject): def __init __ (zelf, x, y, w, h, kleur, offset): GameObject .__ init __ (zelf, x, y, w, h) zelf. color = color self.offset = offset self.moving_left = False self.moving_right = False ... def update (self): if self.moving_left: dx = - (min (self.offset, self.left)) elif self.moving_right: dx = min (self.offset, c.screen_width - self.right) else: return self.move (dx, 0)
De bal gebruikt alleen de functionaliteit van de GameObject
basisklasse, die game-objecten verplaatst op basis van hun snelheid (met horizontale en verticale componenten). De snelheid van de bal wordt bepaald door vele factoren in de klasse Breakout die je binnenkort zult zien. Omdat verplaatsen alleen maar de snelheid toevoegt aan de huidige positie, wordt de richting waarin de bal beweegt volledig bepaald door de snelheid langs de horizontale en verticale assen.
De bal in Breakout verschijnt uit het niets aan het begin van het spel telkens wanneer de speler een leven verliest. Het materialiseert gewoon uit de ether en begint recht naar beneden of in een kleine hoek te vallen. Wanneer de bal is gemaakt in de create_ball ()
methode ontvangt het een snelheid met een willekeurige horizontale component tussen -2 en 2 en een verticale component, die wordt bepaald in de module config.py (momenteel ingesteld op 3).
def create_ball (self): speed = (random.randint (-2, 2), c.ball_speed) self.ball = Ball (c.screen_width // 2, c.screen_height // 2, c.ball_radius, c.ball_color , snelheid) self.objects.append (self.ball)
In dit gedeelte behandelden we de verwerking van gebeurtenissen, zoals toetsaanslagen, muisbewegingen en muisklikken. We doken ook in op enkele van de gameplay-elementen van Breakout zoals het bewegen van de paddle, het verplaatsen van de bal en het regelen van de snelheid van de bal.
Vergeet ook niet om te zien wat we beschikbaar hebben voor verkoop en om te studeren in de Envato-markt als je meer Python-gerelateerd materiaal wilt bestuderen.
In deel vier behandelen we het belangrijke onderwerp botsingdetectie en zien we wat er gebeurt als de bal verschillende spelobjecten raakt zoals de peddel, stenen en zelfs de muren, het plafond en de vloer.
Daarna zullen we onze aandacht richten op het spelmenu. We maken aangepaste knoppen die we gebruiken als een menu dat we kunnen weergeven en verbergen waar nodig.