Da das Programmieren von Spielen mit TigerJythons GameGrid-Bibliothek wirklich Spaß macht, habe ich mich heute drangesetzt und mein DawnHack Lookalike von gestern (mit dem DawnLike-Tileset) etwas aufgehübscht. Ein Alleinstellungsmerkmal von DawnLike ist, daß viele Figuren und Gegenstände – im Gegensatz zu den Tilesets klassischer Rogue-Likes wie Angband oder NetHack – in zwei Phasen animiert sind. Da GameGrid solche Animationen unterstützt, war dies die erste Änderung, die ich vorgenommen habe.
Dafür mußte ich allerdings aufgeben, daß der Spieler die Blickrichtung ändern kann, denn die meisten animierten Tiles von DawnLike zeigen die Figuren nur in Frontansicht. Aber das ist Standard bei fast allen Rogue-Like-Klassikern, also kann ich damit leben. Gamegrid unterstützt Animationen, wenn die Namen der Sprites mit einem Unterstrich und einer fortlaufenden Nummer versehen sind, also zum Beispiel hero_0.png
und hero_1.png
. Dann kann man die Animation mit folgendem Kommando initialisieren,
Actor.__init__(self, "sprites/hero.png", 2)
vorausgesetzt, die jeweilige Sprite-Klasse ist eine Unterklasse von Actor
. Wichtig ist, daß _x
nicht dem Spritepfad hinzugefügt wird, das ergänzt Actor
selbstständig und daß die Anzahl der Spritebilder als letzter Parameter mitgegeben wird. Wie immer in Python beginnt der Zähler bei Null, eine zweiphasige Animation besteht also aus den Bilder hero_0.png
und hero_1.png
.
Dann besitzt Actor
eine leere Methode act()
, die muß zur Animation überschrieben werden, um die Animation durchzuführen:
def act(self): self.showNextSprite()
Den Hintergrund mit den Bäumen lasse ich jetzt nicht mehr vom Programm zeichnen, sondern lade ihn einfach mit einem Bild, das ich mit dem Programm Tiled erzeugt habe. Die Matrix mit den Nullen und Einsen, die ich für die Kollisionserkennung benötigte, liefert mit Tiled auf Wunsch als CSV-Datei ebenfalls.
Außerdem habe ich zwei schwarze Ränder oben und unten hinzugefügt. Der obere soll später einmal einen Head Up Display (HUD) mit Punktanzeige, Anzeige der Anzahl der Leben und dem Inventory aufnehmen, unten sollen Texte und Dialoge angezeigt werden. Doch das ist noch Zukunftsmusik.
Dann habe ich dem Spieler einen ersten Gegner spendiert. Momentan macht er noch gar nichts, außer dumm herum zu stehen, aber später einmal soll er mit einer Art künstlicher Intelligenz ausgestattet werden und mit dem Spieler (oder besser gegen ihn) als NPC agieren. Die Enemy
-Klasse
class Enemy(Actor): def __init__(self, spritepath, startLoc): Actor.__init__(self, spritepath, 2) self.dir = "right" # Auch die Gegner starten mit Blickrichtung nach rechts self.startLocation = startLoc
unterscheidet sich kaum von der Player
-Klasse, außer – da es ja mehrere und unterschiedliche Gegner geben soll – daß sie einen Pfad zur jeweiligen Bilddatei und einen Startpunkt zur Initialisierung benötigt.
Weil es irgendwann mal mehrere Gegner geben soll, habe ich die Gegner von vorneherein als Liste implementiert:
enemies = [] zombie = Enemy("sprites/zombie.png", Location(8, 10)) enemies.append(zombie) for enemy in enemies: addActor(enemy, enemy.startLocation) enemy.show()
Der Rest entspricht – bis auf Nuancen – der bisherigen Implementierung. Damit Ihr jedoch die Übersicht nicht verliert, hier der kompelette Quellcode dieser (immer noch frühen) Version:
from gamegrid import * ts = 16 # Tilesize width = 25 height = 24 level01 = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] class Player(Actor): def __init__(self): Actor.__init__(self, "sprites/hero.png", 2) self.dir = "right" # Spieler startet mit Blickrichtung nach rechts def act(self): self.showNextSprite() class Enemy(Actor): def __init__(self, spritepath, startLoc): Actor.__init__(self, spritepath, 2) self.dir = "right" # Auch die Gegner starten mit Blickrichtung nach rechts self.startLocation = startLoc def act(self): self.showNextSprite() def enemyMove(): pass def keyCallback(e): keyCode = e.getKeyCode() if keyCode == 37: # left player.setDirection(180) player.show(1) elif keyCode == 38: # up player.setDirection(270) player.show(3) elif keyCode == 39: # right player.setDirection(0) player.show(2) elif keyCode == 40: #down player.setDirection(90) player.show(0) # Kollsionserkennung nextpos = player.getNextMoveLocation() i = nextpos.getX() # Ränderabfrage if i == width: i = 0 elif i == 0: i = width - 1 j = nextpos.getY() if j == height: j = 0 elif j == 0: j = height - 1 # Wenn an der nächsten Position kein Hindernis, dann gehe dorthin. if level01[j][i] != 1: player.move() # Ränderbehandlung für den nächsten Schritt if player.getX() < 0: player.setX(width - 1) elif player.getX() > width - 1: player.setX(0) if player.getY() < 2: player.setY(height - 3) elif player.getY() > height - 3: player.setY(2) for enemy in enemies: enemyMove() makeGameGrid(width, height, ts, None, "sprites/map01.png", False, keyPressed = keyCallback) setTitle("DawnHack") enemies = [] zombie = Enemy("sprites/zombie.png", Location(8, 10)) enemies.append(zombie) for enemy in enemies: addActor(enemy, enemy.startLocation) enemy.show() player = Player() addActor(player, Location(14, 14)) player.show(0) show() doRun()
Als nächstes möchte ich einige Flucht- und Verfolgungs-Algorithmen vorstellen, mit denen der Zombie den Spieler jagt. 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!