====== Drei Minigames ======
== Informatikarbeit von Yeung Paul, Huber Mateo und Gottrau Olivier ==
----
Der vollständige Code sowie ein Download-Link zu der Minecraft-Welt finden sich im Anhang.
----
**Idee und Kurzfassung**
Unser Spiel besteht aus drei Minigames. Im ersten muss ein Parcours mit unsichtbaren Hindernissen absolviert werden und anschliessend mit Pfeilen vier schwer treffbare Knöpfe treffen.
Im zweiten muss ebenfalls ein Parcours abgeschlossen werden, in welchem aber einige Inseln verschwinden, sobald man auf sie steht. Im letzten muss ein Gebäude von 64 Zombie-Pigmen befreit werden, während dem man in Geheimkammern speziell Starke Ausrüstung finden kann.
Der Spieler hat für jedes Level unendlich viele Versuche und muss nicht mit dem ersten Level wieder anfangen, falls er ein Minispiel nicht auf Anhieb erreichen kann.
----
__1. Jump'n'Run mit Zielschiessen:__
In desem Spiel gilt es, den Parcours mit unsichtbaren Hindernissen abzuschliessen. Diese besetehen aus "Barrier"-Blöcken. Dies soll den Spieler reizen und in der nächsten Aufgabe unkonzentriert machen, damit das Spiel schwerer wird.
Danach müssen vier Knöpfe mit Pfeilen abgeschossen werden, welche dem Spieler via Command Block gegeben werden, wenn er auf einen Knopf drückt.
__2. Himmelfahrt:__
Danach absolvieren sie eine tödliche Partie "Himmelfahrt"; dieses Minigame wurde von der Netflixserie "Squid Game" inspiriert und knüpft an das Jump and Run-Theme des vorherigen Spiels.
Der Parcours besteht aus 12 Reihen mit jeweils 2 Inseln.
Der Spieler muss über die Inseln springen, dabei werden einige Inseln zerstört, wenn der Spieler darauf steht, fällt er in den Tod.
Die Inseln, welche zerstört sind, bleiben nach jedem Respawn zerstört.
Wir haben dieses Spiel gewählt, weil es übersetzt so viel wie "Himmelfahrt" heisst und damit zu der Geschichte passt, mit der kirchlichen Einrichtung. Ebenfalls ist es ein top Spielerlebnis, wenn man auf Inseln springen muss, welche plötzlich verschwinden und man somit einfach runter fällt.
__3. Operation Zombie Pigman:__
Im Letzten Level findet man sich in einer Kirche wieder und muss sie von den Monstern befreien.
Sie können auf dem Weg dorthin Waffen und Rüstung bekommen indem sie Kammern finden, welche versteckt sind und nur mit Schlüsseln (Items) geöffnet werden können. Sobald sie den Monsterboss besiegt haben, haben sie das Level geschafft. Es ist ein ziemlich grosses Gebäude mit verschiedensten Einrichtungen, damit der Spieler das perfekte Erlebnis geniessen kann.
----
**Einteilung**
__Paul Yeung:__ Bau und Gestaltung der Levels, Programmierung, Idee, Organisation der Gruppe
__Mateo Huber:__ Bau der Levels, Wiki, Programmierung, Organisation der Gruppe
__Olivier Gottrau:__ Idee, Programmierung, Wiki, Bau und Gestaltung der Levels
**Danksagung**
__Laith Ben Yacoub (2BS):__ Zähl-Funktion im 3. Level, Beratung für Himmelfahrt-Level
__Timo Amacker (2D1):__ Beratung für Himmelfahrt-Level
----
**Bauberichte**
__**1. Jump'n'Run mit Zielschiessen**__
__ Code__
Die Knöpfe leiten ihr Signal an Command-Blocks weiter, welche den Spieler "ziel1", "ziel2" etc. sagen lassen. Der Code erkennt dies mittels player.on_tell_command(). Es wird jeweils eine Variable (insel1, insel2 etc) auf True gesetzt; wenn alle 4 True sind wird das nächste Level gestartet.
Neben dem Schild beim Knopf-Treffe-Spiel befinden sich zwei Commandblocks, welche auf Knopfdruck "give @p"- Befehle ausführen. Sie geben dem Spieler einen Pfeilbogen und 64 Pfeile, was unlimitiert wiederholt werden kann.
__ Phyischer Bau__
Die hohen Mauern rund um den Parcours wurden mit dem folgenden Code erstellt.
fill x y z x y z obsidian
Die restlichen Bauten wurden gänzlich von Hand gebaut.
__ Schwierigkeiten__
Es war zuerst schwer, auf die Idee mit dem Commandblock-Code-Feedback zu kommen.
----
__**2. Himmelfahrt**__
__ Code__
Die Commands in den Command-Blocks am Ende des Spiels:
tp @p[r=2] 18 5 -121
execute @p[r=3] ~ ~ ~ say spiel2
Als Setup werden die Inseln einzeln Block-By-Block gebaut, in dem über player.execute() setblock aufgerufen wird. Dies wird für jeden einzelnen Block wiederholt. Wir können hier die Wiederholungen nicht vermeiden, weil die Befehle zur Verwaltung von Matrizen fehlen. Wir könnten sonst mit for i in range für jedes Element der Matrizen der einzelnen Inseln den setblock-Befehl aufrufen und der Code wäre um einiges kürzer.
Auf dem Dach der Box befinden sich Command Blocks, welche mit dem Befehl testfor die einzelnen Koordinaten der zusammenfallenden Inseln überwachen. Sie sind im "Immer Aktiv"-Modus. Wenn diese Bedingung zutrifft, geben sie über einen Redstone-Comparator ein Signal aus und aktivieren einen anderen Block mit dem Befehl "setblock x y z air". Dies löscht die Insel Block-By-Block und der Spieler fällt ins
Nichts.
__ Phyischer Bau__
Die Box um das Jump'n'Run wurde mit einfachen "fill"-Commands im Chat gebaut.
fill x y z x y z chiseled_polished_blackstone
Wie im vorherigen Level wurden die restlichen Elemente von Hand gebaut.
__ Schwierigkeiten__
Es war sehr aufwändig, die Koordinaten der Inseln zu bestimmen. Zudem haben wir keine Methode gefunden, mit dem Code direkt die Inseln zu zerstören; auch Schüler von anderen Klassen konnten keine funktionierenden Lösungen finden. Die Wiederholungen beim Setup lassen sich unseres Erachtens nach nicht vermeiden.
Wir haben uns von Laith Ben Yacoub (2BS) und Timo Amacker(2D1) in der Programmierung beraten lassen.
----
__**3. Operation Zombie-Pigman**__
Es war ursprünglich geplant, das Gebäude als Kirche zu bauen, weil wir uns so besonders viel Raum für Feingestaltung erhofften (spez. mit programmierbaren Eastereggs und anderen Elementen). Wir haben uns aber dagegen entschieden, weil wir keine religiösen Ansichten anderer Personen verletzen wollten. Das Gebäude besitzt jedoch noch Kirchen-Elemente, weil wir zum Zeitpunkt dieser Entscheidung bereits viel Arbeit in das Gebäude investiert hatten.
__ Code__
Drei einfache Häuser nebeneinander, ein wenig versetzt gebaut. Die Böden noch für die verschiedenen Etagen eingefügt.
def simple_house(xpos,ypos,zpos,breite, hoehe):
blocks.fill(BLOCK_OF_QUARTZ, world(xpos,ypos,zpos),world(xpos+breite,ypos+hoehe, zpos+breite),FillOperation.HOLLOW)
#Tuere
blocks.fill(AIR,world(xpos+4,ypos,zpos),world(xpos+4,ypos+1,zpos))
blocks.place(ACACIA_DOOR,world(xpos+4,ypos,zpos))
def boden(xpos,ypos,zpos,breite,hoehe):
blocks.fill(BLOCK_OF_QUARTZ, world(xpos,ypos,zpos), world(xpos+breite, ypos+hoehe, zpos+breite))
boden(10, 34, -105, 20, 1)
Hier ein Beispiel für die Programmierung __einer__ versteckten Kammer.
found = False
def on_item_interacted():
global found
player.say("Schlüssel für verborgene Kammer gefunden")
found = True
player.on_item_interacted(CLOCK, on_item_interacted)
def on_travelled_walk():
x = player.position().get_value(Axis.X)
y = player.position().get_value(Axis.Y)
z = player.position().get_value(Axis.Z)
if (x == 19) and (y== 5) and (z==-99) and found==True:
blocks.fill(AIR, world(21, 5, -100), world(21, 7, -98))
player.on_travelled(WALK, on_travelled_walk)
Programmierung der verschiedenen Kammern, welche nur durch einige Items geöffnet werden können und in denen verschiedene Rüstungsgegenstände oder andere Sachen verborgen sind. Die Items haben den Namen der jeweiligen Kammer und sollen Schlüssel darstellen, ebenfalls sind die Kammern mit Schildern gekennzeichnet. Hier der __komplette Code für alle Kammern und Räume__.
found = False
def on_clock_interacted():
global found
player.say("Schlüssel für verborgene Kammer gefunden")
found = True
def on_compass_interacted():
global found
player.say("Schlüssel für Rüstungskammer gefunden")
found = True
def on_flint_and_steel_interacted():
global found
player.say("Schlüssel für Kammer gefunden")
found = True
def on_shears_interacted():
global found
player.say("Schlüssel für Treppenkammer gefunden")
found = True
def on_book_interacted():
global found
player.say("Schlüssel für kleine Kammer gefunden")
found = True
def on_travelled_walk():
x = player.position().get_value(Axis.X)
y = player.position().get_value(Axis.Y)
z = player.position().get_value(Axis.Z)
if (x == 19) and (y== 5) and (z==-99) and found==True:
blocks.fill(AIR, world(21, 5, -100), world(21, 7, -98))
if (x == 18) and (y== 13) and (z==-116) and found==True:
blocks.fill(AIR, world(17, 13, -118), world(19, 15, -118))
if (x == 13) and (y== 13) and (z==-119) and found==True:
blocks.fill(AIR, world(13, 13, -118), world(13, 14, -118))
if (x == 58) and (y == 59) and (z == -91) and found==True:
blocks.fill(AIR, world(57, 59, -93), world(59, 61, -93))
if (x == 46) and (y == 47) and (z == -98) and found==True:
blocks.fill(AIR, world(44, 47, -98), world(44, 49, -99))
if (x == 44) and (y == 38) and (z == -94) and found==True:
blocks.fill(AIR, world(45, 38,-94), world(45, 39, -94))
player.on_item_interacted(CLOCK, on_clock_interacted)
player.on_item_interacted(COMPASS, on_compass_interacted)
player.on_item_interacted(FLINT_AND_STEEL, on_flint_and_steel_interacted)
player.on_item_interacted(SHEARS, on_shears_interacted)
player.on_item_interacted(BOOK, on_book_interacted)
player.on_travelled(WALK, on_travelled_walk)
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_12.00.56.png?400 |}}
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_12.01.13.png?400 |}}
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_12.01.50.png?400 |}}
Das Zählen der Mobs findet mit der Funktion mobs.mobs_on_mob_killed statt. Jedes Mal, wenn ein Zombie Pigman getötet wird, wird die Funktion addKill aufgerufen, welche 1 von der Variable "alive" subtrahiert. Wenn diese Variable den Wert 0 erreicht, wird die Schlaufe mit "break" unterbrochen und der Titel "Spiel Abgeschlossen" wird mit dem Befehl player.execute("title @p title Spiel Abgeschlossen") angezeigt.
__ Phyischer Bau__
Die Inneneinrichtung wurde von Hand, ohne jegliche Programme gebaut. Alle Treppen, Teppiche, Lampen und Brunnen wurden von Hand gebaut. Einige Türen sind mit Redstone verbunden und können durch Hebel geöffnet werden. Die Gegenstände in den Truhen wurden von uns selber so verteilt, ebenfalls die Schlüssel (Items) zu den verschiedenen Kammern wurden von uns auf der Map verteilt. Die Lifte sind ebenfalls von Hand gebaut, damit das Spielerlebnis ein bisschen verändert wird und man nicht nur Treppen laufen muss, sondern auch von selber zu einem anderen Stockwerk gelangen kann. Das Spielerlebnis ist somit besser, da wir uns Mühe für ein gutes Design gegeben haben.
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_11.57.58.png?400 |}}
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_11.58.56.png?400 |}}
{{ :group:gf:2d2:capture_d_ecran_2022-01-31_a_11.59.43.png?400 |}}
__ Schwierigkeiten__
Wir konnten zuerst keine Möglichtkeit finden, um die Zombie Pigmen zu zählen. Wir haben die Hilfe eines anderen Schülers benötigt. (**Laith Ben Yacoub**, 2BS).
----
**Fazit**
Die Umsetzung der Ideen war teilweise unmöglich, weil bestimmte Commands, die in der Java-Edition enthalten sind, nicht in der Education Edition vorhanden sind (z.B. /data). Dies trifft auch auf Python zu (Es fehlen z.B. Funktionen zur Verwaltung von Matrizen, was für das zweite Level notwendig ist). Ansonsten fanden wir es spannend, dieses Projekt zu verwirklichen und wir haben unsere Fähigkeit, methodisch zu denken, stark verbessern können. Wir fanden die Herausforderungen aufgrund der fehlenden Befehle anregend. Zudem fanden wir die Aufgabenstellung gut, weil wir einen grossen Raum an Self-Management und Kreativität einsetzen konnten.
----
**Anhang**
Link zur Welt:
https://eduetatfr-my.sharepoint.com/:f:/g/personal/olivier_gottrau_studentfr_ch/EqaaFU7s-jRBiH_t1dh3dAgBA6AumrBKUet_wzAKpTZxCg?e=fr5JYT
Vollständiger Code:
#setup 1: funktionen und variablen definieren
def levelAbgeschlossen():
player.execute("title @p title Level Abgeschlossen")
# für das erste minispiel
ziel1 = False
ziel2 = False
ziel3 = False
ziel4 = False
def ziel1True():
global ziel1
ziel1 = True
return ziel1
def ziel2True():
global ziel2
ziel2 = True
return ziel2
def ziel3True():
global ziel3
ziel3 = True
return ziel3
def ziel4True():
global ziel4
ziel4 = True
return ziel4
#für das zweite Minispiel
#Wir könenn keine Methode zur "routinisierung" der nachfolgenden Befehle. Es könnte unserem Wissen nach nur so gemacht werden, wenn alle Python-Funktionen zur Bearbeitung von Matrizen verhanden wären
player.execute("setblock 96 3 151 chiseled_polished_blackstone")
player.execute("setblock 97 3 151 chiseled_polished_blackstone")
player.execute("setblock 97 3 150 chiseled_polished_blackstone")
player.execute("setblock 96 3 150 chiseled_polished_blackstone")
player.execute("setblock 93 3 146 chiseled_polished_blackstone")
player.execute("setblock 90 3 143 chiseled_polished_blackstone")
player.execute("setblock 89 3 143 chiseled_polished_blackstone")
player.execute("setblock 89 3 142 chiseled_polished_blackstone")
player.execute("setblock 90 3 142 chiseled_polished_blackstone")
player.execute("setblock 93 3 138 chiseled_polished_blackstone")
player.execute("setblock 90 3 135 chiseled_polished_blackstone")
player.execute("setblock 89 3 135 chiseled_polished_blackstone")
player.execute("setblock 89 3 134 chiseled_polished_blackstone")
player.execute("setblock 90 3 134 chiseled_polished_blackstone")
player.execute("setblock 96 3 135 chiseled_polished_blackstone")
player.execute("setblock 97 3 135 chiseled_polished_blackstone")
player.execute("setblock 97 3 134 chiseled_polished_blackstone")
player.execute("setblock 96 3 134 chiseled_polished_blackstone")
player.execute("setblock 93 3 130 chiseled_polished_blackstone")
player.execute("setblock 89 3 127 chiseled_polished_blackstone")
player.execute("setblock 90 3 127 chiseled_polished_blackstone")
player.execute("setblock 90 3 126 chiseled_polished_blackstone")
player.execute("setblock 89 3 126 chiseled_polished_blackstone")
player.execute("setblock 93 3 122 chiseled_polished_blackstone")
player.execute("setblock 96 3 119 chiseled_polished_blackstone")
player.execute("setblock 97 3 119 chiseled_polished_blackstone")
player.execute("setblock 97 3 118 chiseled_polished_blackstone")
player.execute("setblock 96 3 118 chiseled_polished_blackstone")
player.execute("setblock 89 3 118 chiseled_polished_blackstone")
player.execute("setblock 90 3 118 chiseled_polished_blackstone")
player.execute("setblock 90 3 119 chiseled_polished_blackstone")
player.execute("setblock 89 3 119 chiseled_polished_blackstone")
# Für das letzte Minispiel
def tpLevel2():
player.execute("spawnpoint @p 18 5 -121")
player.execute("tp @p 18 5 -121")
return True #weil es sonst heisst: "solche funktionen müssen einen return-wert haben" (~)
found = False
def on_clock_interacted():
global found
player.say("Schlüssel für verborgene Kammer gefunden")
found = True
def on_compass_interacted():
global found
player.say("Schlüssel für Rüstungskammer gefunden")
found = True
def on_flint_and_steel_interacted():
global found
player.say("Schlüssel für Kammer gefunden")
found = True
def on_shears_interacted():
global found
player.say("Schlüssel für Treppenkammer gefunden")
found = True
def on_book_interacted():
global found
player.say("Schlüssel für kleine Kammer gefunden")
found = True
def on_travelled_walk():
x = player.position().get_value(Axis.X)
y = player.position().get_value(Axis.Y)
z = player.position().get_value(Axis.Z)
if (x == 19) and (y== 5) and (z==-99) and found==True:
blocks.fill(AIR, world(21, 5, -100), world(21, 7, -98))
if (x == 18) and (y== 13) and (z==-116) and found==True:
blocks.fill(AIR, world(17, 13, -118), world(19, 15, -118))
if (x == 13) and (y== 13) and (z==-119) and found==True:
blocks.fill(AIR, world(13, 13, -118), world(13, 14, -118))
if (x == 58) and (y == 59) and (z == -91) and found==True:
blocks.fill(AIR, world(57, 59, -93), world(59, 61, -93))
if (x == 46) and (y == 47) and (z == -98) and found==True:
blocks.fill(AIR, world(44, 47, -98), world(44, 49, -99))
if (x == 44) and (y == 38) and (z == -94) and found==True:
blocks.fill(AIR, world(45, 38,-94), world(45, 39, -94))
alive = 64
def addKill():
global alive
alive = alive -1
return alive
#setup 2: andere einstellungen und zu anfang teleportieren
player.execute("gamemode adventure")
player.execute("tp @p -1 5 20") #zu Anfang teleportieren
player.execute("spawnpoint @p -1 5 20")
#eigentlicher spielcode
#Level 1 ("jump n run")
while True:
player.on_tell_command("ziel1", ziel1True)
player.on_tell_command("ziel2", ziel2True)
player.on_tell_command("ziel3", ziel3True)
player.on_tell_command("ziel4", ziel4True)
if ziel1 and ziel2 and ziel3 and ziel4:
levelAbgeschlossen()
player.execute("tp @p 92 4 167")
player.execute("spawnpoint @p 92 4 167")
break
player.on_tell_command("level2", tpLevel2) # damit der Spieler nicht sofort ins dritte Level teleportiert wird; das zweite Level wurde nur mit Command Blocks programmiert
# Level 3 ("operation zombie pigmen")
player.execute("kill @e[type=zombie_pigman]") # "alte" Pigmen aufräumen (von Testdurchläufen etc.)
player.execute("gamerule mobGriefing false") # Damit die Pigmen keine Blöcke zerstören
#Setup, spawn der Gegner
for i in range(8):
player.execute("summon zombie_pigman 16 5 -92")
player.execute("summon zombie_pigman 15 13 -109")
player.execute("summon zombie_pigman 20 22 107")
player.execute("summon zombie_pigman 54 14 -98")
player.execute("summon zombie_pigman 75 28 -92")
player.execute("summon zombie_pigman 49 47 -89")
player.execute("summon zombie_pigman 50 59 -88")
player.execute("summon zombie_pigman 44 69 -87")
while True:
player.on_item_interacted(CLOCK, on_clock_interacted) #Die Geheimtüren und ihre Schlüssel
player.on_item_interacted(COMPASS, on_compass_interacted)
player.on_item_interacted(FLINT_AND_STEEL, on_flint_and_steel_interacted)
player.on_item_interacted(SHEARS, on_shears_interacted)
player.on_item_interacted(BOOK, on_book_interacted)
player.on_travelled(WALK, on_travelled_walk)
mobs.on_mob_killed(PIG_ZOMBIE, addKill) # wenn pigmen getötet 1 von "alive" subtrahieren
if alive == 0: #Wenn alle tot, das spiel beenden.
player.execute("title @p title Spiel Abgeschlossen")
break