MicroStudio-Tutorial 2: Pumpkin Apokalypse
Das ist die erste Fortsetzung zu meiner kleinen Serie mit Einführungstutorials zu microStudio, der kleinen, meiner Meinung nach zu Unrecht unterschätzten Game-Engine. Wie schon beim ersten Tutorial dieser Reihe zeige ich nur einen kleinen Ausschnitt dessen, was mit microStudio möglich ist. Ihr könnt Euch dann aus der Summe dieser (hoffentlich!) wachsenden Anzahl von Beispielprogrammen Euer eigenes Spiel selber stricken.
Die Idee zum heutigen Beispiel habe ich schamlos bei David Bouchard geklaut, der in seiner Einführung in P5.play (einer Spielebibliothek zu P5.js) in den beiden Videos Sprites Animation und Game States den Spieler in eine Zombie-Apocalypse schickt, in der er möglichst lange den Zombies ausweichen muß.
Für meine Version dieses Spiels hatte ich mir die freien (CC0) Sprites und Tiles des Dungeon Tileset II des Users 0x72 ausgesucht, denn dort gab es nicht nur Zombies, sondern auch Halloween-Kürbisse als animierte Sprites. Und so wurde aus der Zombie Apokalypse kurzerhand die Pumpkin Apokalypse. Das Spieleprinzip ist jedoch (wenn auch stark vereinfacht) das gleiche geblieben:
Der Spieler kann mit den Pfeiltasten in alle vier Himmelsrichtungen bewegt werden und muß den bösen Halloween-Kürbissen ausweichen, von denen alle drei Sekunden ein neuer das Spielfeld betritt. Die Kürbisse bewegen sich zwar etwas langsamer als der Spieler und können jeweils auch nur horizontal in eine Richtung laufen, da sie jedoch immer mehr werden, ist eine Kollision irgendwann unvermeidlich. Sobald der Spieler mit einem Kürbis kollidiert, beginnt das Spiel von vorne.
Jedesmal wenn ein neuer Kürbis das Spielfeld betritt, bekommt der Spieler – als Belohnung, weil er bis hierher überlebt hat – einen Punkt gutgeschrieben. Einziges Ziel des Spiels ist es, so lange wie möglich den Kürbissen auszuweichen, um dadurch möglichst viele Punkte zu sammeln.
Der Spieler wie die Kürbisse sind sowohl idle
wie auch running
animierte Sprites mit jeweils vier Einzelbildern je Richtung. Ich habe die Streifen wieder mit der Bildverarbeitung meines Vertrauens zusammengestrickt (wobei ich die idle
-Animation der Kürbisse zur Zeit noch nicht nutze).
Der Unterschied zwischen Klassen und Objekten ist in microStudio nicht sehr groß. Daher machen Klassen eigentlich erst dann Sinn, wenn man viele gleichartige Objekte erzeugen will – in diesem Fall also die Kürbisse. Aus Gründen der Gleichbehandlung habe ich aber auch dem Spieler eine eigene Klasse spendiert:
Player = class
constructor = function()
this.x = 0
this.y = 0
this.w = 16
this.h = 23
this.dir = "right"
this.im = "elf_idle_right"
this.vel = 1
end
move = function()
if keyboard.RIGHT then
dir = "right"
im = "elf_running_right"
x += vel
elsif keyboard.LEFT then
dir = "left"
im = "elf_running_left"
x -= vel
elsif keyboard.UP then
y += vel
elsif keyboard.DOWN then
y -= vel
else
if dir == "right" then
im = "elf_idle_right"
else
im = "elf_idle_left"
end
end
wrap(this)
end
Sie ist dem rogue
-Objekt des ersten Tutorials ziemlich ähnlich, lediglich bei der Border-Abfrage habe ich es mir einfacher gemacht und auf die wrap()
-Funktion aus der games-prog library v.02 von mrLman zurückgegriffen.
Da der Spieler nun eine Klasse ist, mußte in der init()
-Funktion des Hauptprogramms mit
das Player-Objket erzugt werden. Wenn nun in der update()
-Funktion player.move()
aufgerufen wird und die draw()
-Funktion so aussieht:
draw = function()
screen.clear()
screen.fillRect(0, 0, screen.width, screen.height, "rgb(109, 170, 44")
kann der Spieler auf dem Spielfeld mit den Pfeiltasten bewegt werden.
Daher ist es nun Zeit, die Kürbisse einzuführen. Auch sie haben eine eigene Klasse bekommen,
Pumpkin = class
constructor = function(_x, _y, _dir)
this.x = _x
this.y = _y
this.dir = _dir
this.w = 16
this.h = 23
this.im = "pumpkin_idle_right"
this.vel = 0.1
end
move = function()
if dir == "right" then
im = "pumpkin_running_right"
x += vel
elsif dir == "left" then
im = "pumpkin_running_left"
x -= vel
else
im = "pumpkin_idle_right"
end
wrap(this)
end
show = function()
screen.drawSprite(im, x, y, w, h)
end
end
der ich neben der Methode move()
auch noch die Methode show()
spendiert habe.
Da sie während des Spiels successive eingeführt, können sie nicht in der init()
-Funktion initialisiert werden, sondern materialisieren sich erst in der `update()-Funktion zu Objekten (oder »Instanzen« der Klasse).
Dafür habe ich in init()
mit timer = 0
erst einmal einen Timer erzeugt, den ich in update()
hochzähle. Alle drei Sekunden (timer == 180
) erstelle ich dann einen neuen Pumpkin und setze (in der Funktion createPumpkin()
) den Timer zurück:
createPumpkin = function()
local choice = random.next()
if choice < 0.5 then dir = "left" else dir = "right" end
p = new Pumpkin((-180 + random.nextInt(360)),
(-100 + random.nextInt(200)), dir)
pumpkins.push(p)
timer = 0
end
Die schon ins Leben gerufenen Kürbisse werden in update()
bewegt
for p in pumpkins
p.move()
// check collision with player
if distance(p.x, p.y, player.x, player.y) < 20 then
print("Collision")
init()
end
end
und auf Kollision mit dem Player überprüft. Dafür setze ich mit distance()
eine weitere Funktion aus der games-prog library v.02 ein.
Das Zeichnen der einzelnen Kürbisse ist dank der show()
-Methode der Klasse Pumpkin
wieder sehr einfach:
Das war es auch schon. Die Punkte werden einfach hochgezählt und mit
auf den Bildschirm gezeichnet. Da es dieses Mal schon ein recht umfangreiches Beispiel ist, drucke ich hier den kompletten Quellcode noch einmal vollständig ab. Zuerst das Hauptprogramm main
:
init = function()
player = new Player()
pumpkins = []
timer = 0
score = 0
end
update = function()
timer += 1
if timer == 180 then
createPumpkin()
score += 1
end
player.move()
for p in pumpkins
p.move()
// check collision with player
if distance(p.x, p.y, player.x, player.y) < 20 then
print("Collision")
init()
end
end
end
draw = function()
screen.clear()
screen.fillRect(0, 0, screen.width, screen.height, "rgb(109, 170, 44")
screen.drawSprite(player.im, player.x, player.y, player.w, player.h)
for p in pumpkins
p.show()
end
screen.drawText("Score: " + score, 120, 80, 20, "rgb(250, 25, 25)")
end
createPumpkin = function()
local choice = random.next()
if choice < 0.5 then dir = "left" else dir = "right" end
p = new Pumpkin((-180 + random.nextInt(360)),
(-100 + random.nextInt(200)), dir)
pumpkins.push(p)
timer = 0
end
Dann die Klasse Player
Player = class
constructor = function()
this.x = 0
this.y = 0
this.w = 16
this.h = 23
this.dir = "right"
this.im = "elf_idle_right"
this.vel = 1
end
move = function()
if keyboard.RIGHT then
dir = "right"
im = "elf_running_right"
x += vel
elsif keyboard.LEFT then
dir = "left"
im = "elf_running_left"
x -= vel
elsif keyboard.UP then
y += vel
elsif keyboard.DOWN then
y -= vel
else
if dir == "right" then
im = "elf_idle_right"
else
im = "elf_idle_left"
end
end
wrap(this)
end
end
und die Klasse Pumpkin
:
Pumpkin = class
constructor = function(_x, _y, _dir)
this.x = _x
this.y = _y
this.dir = _dir
this.w = 16
this.h = 23
this.im = "pumpkin_idle_right"
this.vel = 0.1
end
move = function()
if dir == "right" then
im = "pumpkin_running_right"
x += vel
elsif dir == "left" then
im = "pumpkin_running_left"
x -= vel
else
im = "pumpkin_idle_right"
end
wrap(this)
end
show = function()
screen.drawSprite(im, x, y, w, h)
end
end
Und last but not least die Datei util
mit den beiden Helfermethoden des MrLman:
// makes the object wrap around the screen when moving off the edge
// note: object must have x and y fields (variables)
wrap = function(obj, leeway = 0)
if obj.x + leeway < -screen.width/2 then
obj.x = screen.width/2 + leeway
elsif obj.x - leeway > screen.width/2 then
obj.x = -screen.width/2 - leeway
end
if obj.y + leeway < -screen.height/2 then
obj.y = screen.height/2 + leeway
elsif obj.y - leeway > screen.height/2 then
obj.y = -screen.height/2 - leeway
end
end
// find the distance between object 1 and object 2
// useful for a simple circular collision detection
distance = function(x1, y1, x2, y2)
local a = x2 - x1
local b = y2 - y1
local c = sqrt(pow(a, 2) + pow(b, 2))
return c
end
Außerdem habe ich das »Spiel« auch wieder auf den Seiten von microStudio hochgeladen. Ihr könnt es dort klonen, weiterentwickeln oder einfach nur damit spielen.