MicroStudio und Python (Teil 2): Zombie Apokalypse
Zu meinem Wechsel in microStudio von microScript nach Python gibt es ein zweites Tutorial. Dieses Mal ist es schon fast ein echtes kleines Spiel.
Ich habe dafür dieses Tutorial, dessen Idee ich damals schon schamlos bei David Bouchard geklaut hatte, auf seinen Ursprung zurückgeführt und aus der Pumpkin Apokalypse wieder eine Zombie Apokalypse gemacht.
Unser Held wartet im Stadtpark auf ein Rendezvous mit seiner Freundin. Doch stattdessen tauchen plötzlich überall Zombies auf, denen er mit Hilfe der Pfeiltasten (oder den Tasten w
, a
, s
und d
) ausweichen muß. Das Tragische ist: Er kann den Zombies nicht entkommen, nur möglichst lange überleben und dabei Punkte sammeln.
Den Helden habe ich in der Klasse Player
implementiert. Sie ist straightforward und quasi eine Eins-zu-Eins-Übersetzung des microScript Codes:
class Player:
def __init__(self):
self.x = 0
self.y = 0
self.w = 32
self.h = 32
self.im = "hero_idle"
self.dir = "right"
self.speed = 1
def move(self):
if check_input(keyboard, "LEFT"):
self.im = "hero_walk"
self.x -= self.speed
elif check_input(keyboard, "RIGHT"):
self.im = "hero_walk"
self.x += self.speed
elif check_input(keyboard, "UP"):
self.im = "hero_walk"
self.y += self.speed
elif check_input(keyboard, "DOWN"):
self.im = "hero_walk"
self.y -= self.speed
else:
self.im = "hero_idle"
wrap(self)
def display(self):
screen.drawSprite(self.im, self.x, self.y, self.w, self.h)
Gleiches gilt auch für die Klasse Zombie
:
class Zombie:
def __init__(self, _x, _y, _dir):
self.x = _x
self.y = _y
self.w = 32
self.h = 32
self.dir = _dir
self.im = "zombie_idle"
self.speed = 0.1
def move(self):
if self.dir == "right":
self.im = "zombie_walk"
self.x += self.speed
elif self.dir == "left":
self.im = "zombie_walk"
self.x -= self.speed
else:
self.im = "zombie_idle"
wrap(self)
def display(self):
screen.drawSprite(self.im, self.x, self.y, self.w, self.h)
Ein wenig mehr aufpassen mußte ich jedoch im Hauptscript. Erst einmal kennt Python/Brython – was zu erwarten war – nicht die microScript-Module math
und random
. Hier mußte ich die entsprechenden Python-Mudule importieren und sie stattdessen nutzen. Das betraf einmal die beiden microScript-Funktionen random.next()
und random.nextInt()
die ich durch Pythons random.random()
und random.randint()
ersetzt habe. (Achtung: In Python liegen die Werte immer in einem halboffenen Intervall, das heißt der höchste Wert ist nicht eingeschlossen. Und randint()
verlangt nach zwei Parametern, einen minimalen Wert (inklusive) und einen maximalen Wert (exklusive).)
import math, random
timestep = 1
maxtime = 180
def init():
global hero, zombies, timer, score
hero = Player()
zombies = []
timer = 0
score = 0
def update():
global timer, score
timer += timestep
if timer >= maxtime:
create_zombie()
score += 1
timer = 0
hero.move()
for z in zombies:
z.move()
# check collision with hero
if distance(z, hero) < 20:
print("Collision")
init()
def draw():
screen.clear()
screen.fillRect(0, 0, screen.width, screen.height, "rgb(109, 170, 44)")
hero.display()
for z in zombies:
z.display()
screen.drawText("Score: " + str(score), 120, 80, 20, "rgb(250, 25, 25)")
def create_zombie():
choice = random.random()
if choice < 0.5:
dir = "left"
else:
dir = "right"
z = Zombie((-180 + random.randint(10, 360)),
(-100 + random.randint(10, 200)), dir)
zombies.append(z)
Und wie ich im ersten Tutorial schon befürchtet hatte, komme ich bei (m)einem naiven Ansatz an der Deklaration aller Variablen, die in Funktionen verändert werden, als global
(in allen Funktionen, die die Werte verändern) nicht vorbei. Wenn man dies nicht will, muß man alles in Klassen kapseln und die Änderungen in den Methoden vornehmen.
Zu guter Letzt gibt es noch ein paar Hilfsfunktionen, die ich wieder in util
ausgelagert habe:
def check_input(obj, val):
if hasattr(obj, val):
return obj[val] != 0
return 0
def wrap(obj, leeway = 0):
if obj.x + leeway < -screen.width/2:
obj.x = screen.width/2 + leeway
elif obj.x - leeway > screen.width/2:
obj.x = -screen.width/2 - leeway
if obj.y + leeway < -screen.height/2:
obj.y = screen.height/2 + leeway
elif obj.y - leeway > screen.height/2:
obj.y = -screen.height/2 - leeway
def distance(obj1, obj2):
a = abs(obj2.x - obj1.x)
b = abs(obj2.y - obj1.y)
c = math.sqrt(a*a + b*b)
return c
Hier kommt in der Funktion distance()
nicht nur der gute, alte Satz des Pythagoras, sondern auch das Modul math
mit math.sqrt()
zum Einsatz. (Auf math.pow(x, n)
habe ich verzichtet und einfach mit a*a
respektive b*b
quadriert.)
Auch dieses Tutorial habe ich wieder auf meinen microStudio-Account hochgeladen, damit ihr es klonen, weiterentwickeln, damit spielen oder einfach nur daraus lernen könnt.
Nun noch die Credits: Die Bilder sind mal wieder von Kenney, dieses Mal aus seinem freien (CC0) Pack Toon Characters 1. Damit sie von microStudio geschluckt werden, habe ich sie auch dieses Mal mit der Bildverarbeitung meines Vertrauens zu den verlangten Streifen zurechtgeschnitten.
Und das Spielen mit microStudio und Python macht mir mehr Spaß, als ich erwartet habe. Ihr könnt daher noch weitere Tutorials erwarten. Still digging!