Retrogaming mit Python: Pyxel-Tutorial Stage 3
Nachdem ich in der letzten Zeit ausgiebig mit microStudio (mit Python/Brython als Programmiersprache) herumgespielt hatte, kam mit in den Sinn, auch meine im August letzten Jahres unterbrochenen Versuche mit der (Python3-) Retrogame-Engine Pyxel wieder aufzunehmen.
Denn Pyxel exportiert (unter anderem) ebenfalls nach HTML, basiert aber auf Pyodide und kann daher fast den gesamten Scientific Stack (und mehr) von Python 3 nutzen (also zumindest Numpy und SciPy) und das ist schon etwas mehr als die Brython-Implementierung von microStudio kann. Dafür unterliegt allerdings Pyxel einigen (gewollten) Einschränkungen (es will halt eine Retro-Fantasy-Spielekonsole sein), die microStudio nicht besitzt.
Ziel war es, eine Pyxel-Version dieses Mini-Games zu erstellen, das ich ebenfalls im August letzten Jahres mit der Retrokonsole TIC-80 in Lua programmiert hatte. Und dies ist das Ergebnis:
Mit den Pfeiltasten könnt Ihr das Pixelmännchen durch das Spielfeld steuern.
Der Quellcode unterscheidet sich kaum von dem des vorherigen Pyxel-Tutorials, lediglich die Fensterbreite habe ich verdoppelt:
import pyxel
# Ein paar nützliche Konstanten
TS = 8 # Tilesize
COLKEY = 0 # Color Key
WALLS = [(3, 0), (4, 0), (0, 1), (2, 1), (3, 1)]
def get_tile(tile_x, tile_y):
# pyxel.tilemaps() gibt ein Tupel mit den x- und y-Koordinaten
# aus der Tilemap des mit pget() identifizierten Tiles zurück
return pyxel.tilemaps[0].pget(tile_x, tile_y)
class Player:
def __init__(self, _x, _y):
self.x = _x*TS
self.y = _y*TS
self.w = self.h = TS
self.u = 0 # Die x-Position des Sprites in der Imagebank
self.v = 0 # Die y-Position des Sprites in der Imagebank
self.imagebank = 0
def move(self):
if (pyxel.btnp(pyxel.KEY_LEFT)
and get_tile((self.x - TS)//TS, self.y//TS) not in WALLS):
self.x -= TS
elif (pyxel.btnp(pyxel.KEY_RIGHT)
and get_tile((self.x + TS)//TS, self.y//TS) not in WALLS):
self.x += TS
elif (pyxel.btnp(pyxel.KEY_UP)
and get_tile(self.x//TS, (self.y - TS)//TS) not in WALLS):
self.y -= TS
elif (pyxel.btnp(pyxel.KEY_DOWN)
and get_tile(self.x//TS, (self.y + TS)//TS) not in WALLS):
self.y += TS
class App:
def __init__(self):
pyxel.init(32*TS, 16*TS, "Pyxel Tutorial Stage 3", display_scale = 4)
pyxel.load("assets/ressources.pyxres")
# Initialisiere den Spieler
# Position in Map-Koordinaten
self.player = Player(1, 1)
def run(self):
print(get_tile(0, 0))
pyxel.run(self.update, self.draw)
def update(self):
self.player.move()
def draw(self):
pyxel.cls(0)
# Zeichen die Map
pyxel.bltm(0, 0, 0, 0, 0, 32*TS, 16*TS, 0)
# Zeichne den Player
pyxel.blt(self.player.x, self.player.y, self.player.imagebank,
self.player.u, self.player.v, self.player.w, self.player.h,
COLKEY)
App().run()
Und da ich dieses Mal unterschiedliche Tiles für die Hindernisse genutzt habe, habe ich ihre Koordinaten aus der Ressourcendatei in der Liste WALLS[]
abgelegt (bei mir sind daher auch Bäume »Mauern«). Die erste Position des Tupels ist die Spalte des entsprechenden Tiles und die zweite Position die Reihe:
Wie Ihr dem Screenshot entnehmen könnt, hat daher die grüne Hintergrundkachel (kein Hindernis) die Position \((0, 0)\) (erste Kachel in der ersten Reihe) und das Wasser (Hindernis) die Position \((3, 0)\) (vierte Kachel in der ersten Reihe), der Baum (ebenfalls ein Hindernis) hingegen die Position \((3, 1)\) (vierte Kachel in der zweiten Reihe) – die Zählung beginnt jeweils bei Null. Die gewünschte Position wird abgefragt und der Player darf sich nur dahin bewegen, wenn das Teil kein Hindernis (not in WALLS
) ist. Die Pixelkoordinaten werden mit Hilfe der Division durch die Kachelgröße ((pixel_x//TS), (pixel_y//TS)
) in die Map-Koordinaten umgerechnet.
Da ich durch mühsame Experimente nun herausgefunden habe, wie bei Pyxel die Umrechnung von Map-Koordinaten in Pixel-Koordinaten funktioniert, hoffe ich, daß ich nun auch in microStudio herausfinde, wie die Map-Koordinaten in Pixel-Koordinaten umgerechnet werden (hier schwimme ich leider immer noch, aber es scheint auch um einiges komplizierter zu sein).
Das Einbinden der HTML-Version in diese Seiten habe ich ebenfalls mit einer neuen Methode bewerkstelligt (die ich hier gefunden hatte): Zuerst habe ich im Terminal mit
eine Pyxel-Standalone-App erzeugt, die mit
aufgerufen werden kann und diese dann mit
in eine HTML-Datei gewandelt. Das Ergebnis ist zwar erstaunlich kompakt (trotz BASE64-Codierung), ruft aber leider immer noch die Webversion von Pyxel über ein CDN auf.
Mit
kann übrigens auch eine Standalone-Runtime Eures Betriebssystems aus der Pyxel-Python-Datei erteugt werden.
Den Quellcode und alle Assets inklusive der .pixapp
und der daraus erzeugten HTML-Datei findet Ihr in meinem GitHub-Repositorium, damit Ihr ebenfalls damit experimentieren könnt.
Jetzt bin ich hin- und hergerissen: Soll ich mit Pyxel erst einmal weiter experimentieren oder soll ich zu microStudio zurückkehren? Das Spielen mit Pyxel macht mir Spaß und speziell mit dem Ressourceneditor möchte ich noch einiges anstellen (so gefällt mir die Default-Farbpalette nicht, ich würde sie gerne entweder durch die Pico-8 Farbpalette oder die TIC-80 Farbpalette ersetzen). Ich habe auch schon testweise dem Player-Sprite eine Animation verpasst.
Außerdem gefällt mir, daß Pyxel einerseits so retromäßig eingeschränkt daherkommt (denn wie ich in diesem Zusammenhang schon 2018 schrieb: In der Beschränkung liegt die Kraft!), aber auf der anderen Seite fast das vollständige Python 3-Ökosystem genutzt werden kann. Ich muß daher noch ein wenig nachdenken und vermutlich auch experimentieren. Still digging!