Nachdem ich im ersten Teil dieses Tutorials ein Raumschiff mit flackerndem Schweif vor einem majestätisch dahingleitenden Sternenhimmel implementiert hatte, möchte ich nun in diesem Teil feindliche Ufos erscheinen lassen und das Raumschiff bewaffnen, damit es sich dieser Feinde entledigen kann. Dazu mußte ich zuerst einmal zwei neue Klassen implementieren. Einmal die Klasse Bullet
für die Torpedos, die der Spieler auf die Ufos abfeuern kann:
class Bullet(Actor): def __init__(self, image): Actor.__init__(self, image) self.state = "ready" self.pos = NIRWANA self.speed = 20 def update(self): if keyboard.space: if self.state == "ready" and player.state == "ready": self.pos = player.pos self.state = "fire" if self.state == "fire": self.x += self.speed if self.x >= WIDTH: self.state = "ready" self.pos = NIRWANA
Die Klasse Bullet
hat zwei Stati, ready
und fire
. Damit soll verhindert werden, daß der Spieler ganze Salven abfeuert, denn nur im Status ready
kann der Spieler feuern und das Torpedo gelangt nur in diesem Status, wenn es entweder ein Ufo trifft (dazu weiter unten mehr) oder es über den rechten Bildschirmrand heraus verschwunden ist.
NIRWANA
ist übrigens eine konstantes Tupel, dessen Position weit außerhalb des sichtbaren Bildschirmfensters liegt. Und abgefeuert wird das Torpedo mit der Leertaste.
Gleichzeitig wollte ich auch verhindern, daß der Spieler feuern kann, wenn er sich bewegt. Denn es sieht in meinen Augen sehr unrealistische aus, wenn sich die Kugel nicht direkt vom Raumschiff entfernt, sondern daß Raumschiff sich schon wieder weiter nach oben oder nach unten bewegt hat. Dazu mußte ich auch der Klasse Ship
zwei Stati verpassen, ready
und moving
. Die Klasse Ship
bekommt initial den Status ready
zugewiesen, doch das kann sich in der update()
-Methode ändern:
def update(self): if keyboard.up: self.y -= 1 self.state = "moving" elif keyboard.down: self.y += 1 self.state = "moving" else: self.state = "ready"
Nun zu den feindlichen Ufos. Ihre Bildchen stammen ebenfalls von Jacob Zinman-Jeanes aus dem gleichen freien (CC-BY-3.0) Sprite-Satz auf Gamedevtuts+, dem ich auch schon die Bildchen der Rakete und den Sternenhimmel entnommen habe. Sie besitzen sechs Animationsphasen und dementsprechen ist die Animation ein wenig aufwendiger als bei der Rakete des Spielers:
class Enemy(Actor): def __init__(self, image, pos): Actor.__init__(self, image) self.pos = pos self.start_pos = pos self.frame = 0 self.speed = 1 def update(self): self.y += self.speed if self.y >= HEIGHT - 20 or self.y <= 20: self.x -= 60 self.speed *= -1 def make_animation(self): if self.frame <= 5: self.image = "enemy1" elif self.frame <= 10: self.image = "enemy2" elif self.frame <= 15: self.image = "enemy3" elif self.frame <= 20: self.image = "enemy4" elif self.frame <= 25: self.image = "enemy5" elif self.frame <= 30: self.image = "enemy6" if self.frame >= 30: self.frame = 0 self.frame += 1 def hit(self, bullet): a = self.x - bullet.x b = self.y - bullet.y d = math.sqrt((a**2) + (b**2)) if d < 20: bullet.state = "ready" bullet.pos = NIRWANA self.pos = NIRWANA player.hitpoints += 1 print("Score " + str(player.hitpoints))
Die hit()
-Methode habe ich in diese Klasse gelegt, weil ich es weniger aufwendig zu implmentieren fand, wenn die Ufos selber registrieren, wenn sie getroffen sind und sich dann auch selber ins NIRWANA
katapultieren. 🤪
Das hat natürlich zur Folge, daß ich im dritten Teil dieser kleinen Reihe, wenn auch das Schiff des Spielers von den Ufos getroffen werden kann, die Klasse Ship
ebenfalls eine eigene hit()
-Methode benötigt. Doch darüber mache ich mir Gedanken, wenn ich die dritte Version dieses Spiels implementiere.
Wie man an der hit()
-Methode erkennt, habe ich auch eine rudimentäre Punktvergabe implementiert, die zur Zeit jedoch nur den Punktestand auf der Konsole und nicht im Pygame Zero-Fenster ausgibt. Auch dies habe ich auf Stage 3 verschoben.
Bei der von mir gewählten Größe des Bildschirmfensters passen neun feindliche Ufos in einer Reihe auf dem Bildschirm, wenn ich ihnen oben und unten noch etwas Luft lasse:
NO_ENEMYS = 9 enemy_pos = [] for i in range(NO_ENEMYS): enemy_pos.append((WIDTH + 20, (i+1)*40)) enemys = [] for i in range(len(enemy_pos)): enemys.append(Enemy("enemy1", enemy_pos[i]))
Die Initialisierung erfolgt in zwei Schleifen. Die erste Schleife füllt eine Liste mit den Positionen der Ufos (als Tupel) und in der zweiten Schleife werden die feindlichen Ufos auf ihren Positionen erzeugt.
So, und nun wie gewohnt der komplette Quellcode:
import pgzrun import math import sys WIDTH = 680 HEIGHT = 400 TITLE = "Shmup 2" TILEWIDTH = 640 NIRWANA = -2000, -2000 NO_ENEMYS = 9 class Layer(Actor): def __init__(self, image, xpos): Actor.__init__(self, image) self.left = xpos self.scroll_speed = 0.3 def update(self): self.left -= self.scroll_speed if self.right < 0: self.left = TILEWIDTH class Ship(Actor): def __init__(self, image, pos): Actor.__init__(self, image, pos) self.state = "ready" self.pos = pos self.frame = 0 self.hitpoints = 0 def make_animation(self): if self.frame <= 5: self.image = "ship1" elif self.frame <= 10: self.image = "ship2" elif self.frame <= 15: self.image = "ship3" elif self.frame <= 20: self.image = "ship4" if self.frame >= 20: self.frame = 0 self.frame += 1 def update(self): if keyboard.up: self.y -= 1 self.state = "moving" elif keyboard.down: self.y += 1 self.state = "moving" else: self.state = "ready" if self.hitpoints == NO_ENEMYS: print("You Won!") sys.exit() def check_edges(self): if self.y >= HEIGHT - 16: self.y = HEIGHT - 16 elif self.y <= 16: self.y = 16 class Bullet(Actor): def __init__(self, image): Actor.__init__(self, image) self.state = "ready" self.pos = NIRWANA self.speed = 20 def update(self): if keyboard.space: if self.state == "ready" and player.state == "ready": self.pos = player.pos self.state = "fire" if self.state == "fire": self.x += self.speed if self.x >= WIDTH: self.state = "ready" self.pos = NIRWANA class Enemy(Actor): def __init__(self, image, pos): Actor.__init__(self, image) self.pos = pos self.start_pos = pos self.frame = 0 self.speed = 1 def update(self): self.y += self.speed if self.y >= HEIGHT - 20 or self.y <= 20: self.x -= 60 self.speed *= -1 def make_animation(self): if self.frame <= 5: self.image = "enemy1" elif self.frame <= 10: self.image = "enemy2" elif self.frame <= 15: self.image = "enemy3" elif self.frame <= 20: self.image = "enemy4" elif self.frame <= 25: self.image = "enemy5" elif self.frame <= 30: self.image = "enemy6" if self.frame >= 30: self.frame = 0 self.frame += 1 def hit(self, bullet): a = self.x - bullet.x b = self.y - bullet.y d = math.sqrt((a**2) + (b**2)) if d < 20: bullet.state = "ready" bullet.pos = NIRWANA self.pos = NIRWANA player.hitpoints += 1 print("Score " + str(player.hitpoints)) bg1 = Layer("bg", 0) bg2 = Layer("bg", 1188) layers = [bg1, bg2] enemy_pos = [] for i in range(NO_ENEMYS): enemy_pos.append((WIDTH + 20, (i+1)*40)) enemys = [] for i in range(len(enemy_pos)): enemys.append(Enemy("enemy1", enemy_pos[i])) player = Ship("ship1", (60, HEIGHT/2)) bullet = Bullet("laserred") def draw(): for layer in layers: layer.draw() for enemy in enemys: enemy.draw() bullet.draw() player.draw() def update(): for layer in layers: layer.update() for enemy in enemys: enemy.make_animation() enemy.update() enemy.hit(bullet) bullet.update() player.make_animation() player.update() player.check_edges() pgzrun.go()
Ihr findet den Quellcode und sämtliche Assets natürlich auch wieder in meinem GitHub-Repositorium. Die Datei dieser Fassung heißt shmup2.py
. Wenn Ihr diese Datei und den images
-Ordner herunterladet und Pygame Zero (größer/gleich Version 1.2) auf Eurem Rechner installiert habt, dürfte das Programm sofort lauffähig sein.
In dieser Fassung kann der Spieler das Spiel nicht verlieren. Dazu müßte man die Anzahl der feindlichen Ufos erhöhen, die dann auch in mehreren Reihen auftreten könnten und/oder die Geschwindigkeit der Ufos steigern. Oder die Ufos schießen zurück und der Spieler muß ihren Torpedos ausweichen. Das programmiere ich vielleicht in einer letzten, endgültigen Fassung des Spieles. Still digging!
Ü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!