Meine Erkundungen von Pygame Zero und dem Roten Baron gehen in die nächste Runde. Zum einen habe ich dem Helden ein Geschoß spendiert, damit er sich verteidigen kann. Doch zur Verteidigung braucht man Gegner, gegen die es sich zu verteidigen gilt. In völliger Mißachtung jeglicher historischer (und politischen) Korrektheit muß sich unser Jagdflieger aus dem 1. Weltkrieg gegen UFO-fliegende Aliens wehren. Diese habe ich ehrlicherweise nur aus dem Grund ausgewählt, weil die (freien) Sprites von Kenney so schön bunt waren.
Und ich mußte eine weitere Entscheidung treffen: Um den Code besser gliedern zu können und die Übersicht zu behalten, habe ich dem Spieler, den Aliens und dem Geschoß jeweils eine eigene, von Actor()
abgeleitete Klasse spendiert, da ich bei ihnen jeweils die bei Actor()
leere Methode update()
überschreiben mußte, weil ich ich diese Methode im Hauptprogramm klein und übersichtlich halten wollte. Für den Roten Baron (Klasse Plane()
) sieht die Methode wie folgt aus:
def update(self):
self.velocity += self.gravity
self.velocity *= 0.9 # Reibung/Luftwiderstand
self.y += self.velocity
if self.y >= groundlevel: # Flugzeug ist am Boden
self.y = groundlevel
self.velocity = 0
if self.y <= 20: # Flugzeug ist am oberen Fensterrand
self.y = 20
self.velocity = 0
self.show()
def up(self):
self.velocity += self.upforce
def show(self):
self.count += 1
if self.count > 9: self.count = 0
if self.count < 3:
self.image = "planered1"
elif self.count < 6:
self.image = "planered2"
else:
self.image = "planered3"
Dabei ist die Methode up()
ein Callback, der beim Drücken der Pfeiltaste nach oben ausgelöst wird. Einen ähnlichen Callback besitzt auch die Klasse Bullet()
für das Geschoß, nur das diese eine Art An-/Ausschalter auslöst: Ist sie ausgelöst, bewegt sich das Geschoß nach vorne, während es sich im Ruhezustand im Doppeldecker versteckt.
def update(self):
if not self.fire:
self.x = plane.x
self.y = plane.y
else:
self.x += self.speed
if self.left >= WIDTH:
self.fire = False
Das hat zur Folge, daß die Aliens, deren Update wegen der Kollisionserkennung zu einem großen Teil doch in der update()
-Methode des Hauptprogramms stattfindet, in der Kollisionserkennung nicht nur prüft, ob eine Kollision mit der Kugel stattindet, sondern ob die Kugel während der Kollision auch abgefeuert wurde (bullet.fire == True
). Denn andernfalls könnte der Rote Baron ungestraft ein unfaires Kamikaze-Verhalten an den Tag legen, denn das im Flieger versteckte Geschoß würde die Aliens ebenfalls erwischen, ohne daß dies Konsequenzen für den Spieler hätte:
for enemy in enemies:
if enemy.colliderect(bullet) and bullet.fire:
enemy.reset()
bullet.fire = False
enemy.update()
Die update()
-Methode von Enemy()
selber ruft, wenn das UFO über den linken Bildschirmrand hinausgeglitten ist, das gleiche reset()
wie oben auf. Durch diese Methode wird das UFO rechts außen mit neuer Besatzung und neuen Farben neu positioniert:
def reset(self):
self.x = randint(WIDTH + 50, WIDTH + 500)
self.y = randint(25, groundlevel)
self.image = choice(enemyships)
Damit steht das Grundgerüst meiner wilden Mixture aus Flappy Bird und Sidescrolling Shooter: Der Rote Baron kann die Aliens abschießen und ihnen aus dem Weg gehen. Natürlich fehlt noch das Salz in der Suppe: Das Spiel sollte zu Ende sein oder zumindest sollte der Rote Baron ein Leben verlieren (das heißt, sein Doppeldecker wird beschädigt), wenn er mit einem UFO kollidiert. Und jeder Abschuß eines UFOs sollte mit Punkten belohnt werden, wärend es Strafpunkte hagelt, wenn ein Alien entkommt. Und sinkt das Punktekonto unter Null, sollte das Spiel ebenfalls zu Ende sein.
Außerdem bedürfen die Konstanten für die Gravitation, die Reibung und die Kraft, die den Flieger nach oben schnellen läßt, ebenfalls noch des Feintunings. Und ein wenig optisches Aufmotzen könnte auch nicht schaden. Ihr seht also, es gibt noch einiges zu tun.
Bis dahin aber erst einmal den vollständigen Quelltext dieser Fassung, damit Ihr ebenfalls experimentieren und Euer eigenes Feintuning vornehmen könnt. Und wie immer gibt es den Quelltext und die Assets auch in meinem GitHub-Repositorium.
import pgzrun
import sys
from random import randint, choice
WIDTH = 800
HEIGHT = 480
TITLE = "The Red Baron 2"
left = WIDTH/2
bottom = HEIGHT/2
bottomground = HEIGHT - 35
BACKGROUND = "background"
GROUND = "groundgrass"
no_enemies = 10
enemyships = ["shipbeige", "shipblue", "shipgreen", "shippink", "shipyellow"]
back0 = Actor(BACKGROUND, (left, bottom))
back1 = Actor(BACKGROUND, (WIDTH + left, bottom))
backs = [back0, back1]
ground0 = Actor(GROUND, (left, bottomground))
ground1 = Actor(GROUND, (WIDTH + left, bottomground))
grounds = [ground0, ground1]
groundlevel = HEIGHT - 85
class Plane(Actor):
def __init__(self, image, x, y):
Actor.__init__(self, image, (x, y))
self.count = 0
self.gravity = 0.1
self.upforce = -15
self.velocity = 0
def update(self):
self.velocity += self.gravity
self.velocity *= 0.9 # Reibung/Luftwiderstand
self.y += self.velocity
if self.y >= groundlevel: # Flugzeug ist am Boden
self.y = groundlevel
self.velocity = 0
if self.y <= 20: # Flugzeug ist am oberen Fensterrand
self.y = 20
self.velocity = 0
self.show()
def up(self):
self.velocity += self.upforce
def show(self):
self.count += 1
if self.count > 9: self.count = 0
if self.count < 3:
self.image = "planered1"
elif self.count < 6:
self.image = "planered2"
else:
self.image = "planered3"
class Bullet(Actor):
def __init__(self):
Actor.__init__(self, "laserred")
self.x = plane.x
self.y = plane.y
self.speed = 25
self.fire = False
def update(self):
if not self.fire:
self.x = plane.x
self.y = plane.y
else:
self.x += self.speed
if self.left >= WIDTH:
self.fire = False
class Enemy(Actor):
def __init__(self, image, x, y):
Actor.__init__(self, image, (x, y))
self.width = 44
self.speed = -1.5
def reset(self):
self.x = randint(WIDTH + 50, WIDTH + 500)
self.y = randint(25, groundlevel)
self.image = choice(enemyships)
def update(self):
self.x += self.speed
if self.x <= -self.width:
self.reset()
plane = Plane("planered1", 100, HEIGHT//2)
bullet = Bullet()
enemies = []
for _ in range(no_enemies):
enemyship = choice(enemyships)
enemy = Enemy(enemyship, randint(WIDTH + 50, WIDTH + 750), randint(50, groundlevel))
enemies.append(enemy)
def update():
for back in backs:
back.x -= 0.4
if back.x <= -left:
back.x = WIDTH + left
for ground in grounds:
ground.x -= 0.6
if ground.x <= -left:
ground.x = WIDTH + left
bullet.update()
for enemy in enemies:
if enemy.colliderect(bullet) and bullet.fire:
enemy.reset()
bullet.fire = False
enemy.update()
plane.update()
def draw():
for back in backs:
back.draw()
for ground in grounds:
ground.draw()
for enemy in enemies:
enemy.draw()
bullet.draw()
plane.draw()
def on_key_down():
## Spielende mit ESC
if keyboard.escape:
sys.exit()
## Hoch mit Pfeiltaste nach oben
if keyboard.up:
plane.up()
## Feuern mit rechter Pfeiltaste
if keyboard.right:
bullet.fire = True
pgzrun.go()
Bei der Entwicklung der Mechanik des Doppeldeckers haben mir folgende P5.js-Video-Tutorials von Daniel Shiffman geholfen:
War sonst noch was? Ach ja, nicht nur Daniel Shiffman, sondern auch der YouTube-User Ihabexe hat eine Flappy-Bird-Version in P5.js programmiert und das Video dazu hochgeladen. Kann man sich zur Inspiration ja auch noch reinziehen.
Über …
Der Schockwellenreiter ist seit dem 24. April 2000 das Weblog digitale Kritzelheft von Jörg Kantel (Neuköllner, EDV-Leiter Rentner, Autor, Netzaktivist und Hundesportler — Reihenfolge rein zufällig). Hier steht, was mir gefällt. Wem es nicht gefällt, der braucht ja nicht mitzulesen. Wer aber mitliest, ist herzlich willkommen und eingeladen, mitzudiskutieren!
Alle eigenen Inhalte des Schockwellenreiters stehen unter einer Creative-Commons-Lizenz, jedoch können fremde Inhalte (speziell Videos, Photos und sonstige Bilder) unter einer anderen Lizenz stehen.
Der Besuch dieser Webseite wird aktuell von der Piwik Webanalyse erfaßt. Hier können Sie der Erfassung widersprechen.
Diese Seite verwendet keine Cookies. Warum auch? Was allerdings die iframes
von Amazon, YouTube und Co. machen, entzieht sich meiner Kenntnis.
Werbung
Diese Spalte wurde absichtlich leergelassen!