image image


image

GameDev mit TigerJython: DawnHack Improvements, Stage 2

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!


(Kommentieren) 

image image



Ü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!


Werbung


image  image  image
image  image  image


image