A. Tipps und Informationen zum automatisierten Bauen
1. Blöcke direkt platzieren (ohne den Agenten)
Bisher haben wir Dinge mit dem Agenten gebaut, das heisst wir haben ihm jeweils ein Material in sein Inventar gelegt, ihn an eine bestimmte Position verschoben und haben ihn dann bauen lassen. Dies ist jedoch recht aufwändig, wenn man komplexere Strukturen bauen will. Zudem braucht diese Bauweise viel viel mehr Zeit.
Will man Blöcke automatisch erstellen lassen, kann man dies auch direkt machen, d.h. ohne den Agenten. Dazu verwendet man hauptsächlich die Befehle: blocks.place
und blocks.fill
. Mit ihnen kann man einen Block (blocks.place
) oder einen ganzen Quader (blocks.fill
) auf einmal erstellen. Dadurch sind jedoch die Koordinaten der Welt wieder wichtiger als zuvor!
Eine Linie von Blöcken erstellen, wobei jeder zweite Block aus Gold bzw. aus blauer Wolle besteht: (das %-Zeichen ist die Modulo-Rechnung vgl. Stunde)
for i in range(3,20,1): if (i%2)==0: blocks.place(GOLD_BLOCK, pos(i, 0, 0)) else: blocks.place(BLUE_WOOL, pos(i, 0, 0))
Einen grossen Goldblock erstellen und mit Luft aushöhlen:
blocks.fill(GOLD_BLOCK, pos(1, 1, 1), pos(10, 10, 10)) blocks.fill(AIR, pos(2,2,2), pos(9, 9, 9))
Will man einen ausgehölten Block erstellen, kann man auch andere „FillOperation“ verwenden.
blocks.fill(GOLD_BLOCK, pos(1, 1, 1), pos(10, 10, 10),FillOperation.HOLLOW)Andere Fülloptionen sind
REPLACE
, OUTLINE
, KEEP
, DESTROY
und wie bereits genannt HOLLOW
. Falls keine Fülloptionen genannt werden, wird der Block ausgefüllt.
2. Blöcke drehen
Es gibt Blöcke, die nicht symmetrisch sind (z.B. Treppenstufen, Türen etc.). Diese kann man mithilfe des Befehls blocks.block_with_data
drehen:
blocks.place(blocks.block_with_data(JUNGLE_WOOD_STAIRS, 3), pos(0, 0, 0))
3. Deny, Allow und Border-Blocks
- Wenn man möchte, dass sich die Spieler nur in einem bestimmten Bereich aufhalten können, kann man sogenannte Border-Blocks verwenden.
- Wenn man verhindern will, dass man auf einer Fläche bauen kann, verwendet man Deny-Blocks.
- Wenn die ganze Welt unveränderbar ist (man kann nirgends bauen), kann man mit Allow-Blocks Bereiche definieren, wo doch gebaut werden kann.
Um diese drei speziellen Blöcke von Hand setzen zu können, benötigt man Worldbuilder-Rechte. Diese kann man sich geben mit /worldbuilder
.
Zudem findet man die Blöcke nicht im Inventar, man muss sich diese mit /give @s allow <Anzahl>
, /give @s deny <Anzahl>
, /give @s border_block <Anzahl>
geben.
Um die Welt unveränderbar zu machen, verwendet man \immutableworld
Will man diese allow-, deny, oder border-Blöcke mit Code erstellen (z.B. über blocks.fill
), dann muss man statt des Namens die ID verwenden (210 = Allow-Block, 211=Deny-Block, 212 = Border-Block)
In diesem Video (3 Minuten, engl.) wird das Grundprinzip kurz erklärt.
4. Komplexere Strukturen effizient bauen
Angenommen, wir wollen eine etwas komplexere Struktur bauen, die wir nicht einfach mit einer for-Schleife oder einem einfachen Programm erstellen können. Natürlich könnten wir alle Blöcke einzeln mit blocks.place()
setzen, aber unser Programm wäre dann recht lang und redundant.
Man könnte die zu bauende Figur in ein Format codieren, die der Computer versteht, und sie dann automatisch bauen lassen. Betrachten wir einmal das unten abgebildete Beispiel. Der Smiley besteht aus einem 5×5-Raster, in welches 2 Arten von Blöcken gebaut werden (schwarze und gelbe). Diese Figur können wir sehr ähnlich codieren, wie ihr es im ersten Jahr bei der Darstellung von Pixelbildern gesehen habt. Es handelt sich je im Prinzip um ein Bild mit 5×5=25 „Pixeln“, wobei jeder Pixel für einen Block steht.
Wie codiert man nun diesen Smiley?
Wir könnten die 25 „Punkte“ in einer Liste mit 25 Elementen speichern, wobei wir z.B. für „Luft“ eine 0, für „gelb“ eine 1 und für „schwarz“ eine 2 speichern. Wenn man links oben beginnt und dann nach rechts und unten alle Pixel durchgeht, hätte man dann die Liste:
Smiley = [0,1,1,1,0,1,2,1,2,1,1,1,1,1,1,1,2,1,2,1,0,1,2,1,0]Die ersten fünf Zahlen wären die erste Zeile, die nächsten fünf die zweite etc. Doch diese Darstellung ist nicht sehr übersichtlich. Es ist angenehmer, wenn man die Codierung so macht, dass man eine Liste erstellt, in welche man alle Zeilen speichert - diese Zeilen sind ihrerseits wieder Listen:
Smiley2 = [[0,1,1,1,0],[1,2,1,2,1],[1,1,1,1,1],[1,2,1,2,1],[0,1,2,1,0]]
Wie kann man auf die Elemente zugreifen?
Wenn man nun z.B. Smiley2[0]
aufruft, so erhält man das erste Element der Liste 'Smiley2'. Dies ist aber wieder eine Liste (die erste Zeile). D.h. Smiley2[0]
ist [0,1,1,1,0]
.
Nimmt man nun von dieser Liste z.B. das dritte Element: Smiley2[0][2]
so erhält man die 1. Dies ist der Pixel in der Mitte der ersten Zeile.
Die Elemente einer solchen Tabelle (d.h. einer „zweidimensionalen Liste“) kann man aufrufen, indem man zwei Eckige Klammern verwendet und quasi die x- und die y-Komponente angibt. Also beispielsweise Smiley2[3][4]
für den fünften Pixel (ganz rechts) in der vierten Zeile.
Wie kann man den Smiley automatisch bauen lassen?
Wenn der Smiley mit der Liste der Zeilen codiert wurde, kann ich mit dem Befehl for i in…
alle Pixel durchgehen und dann entsprechend einen gelben oder schwarzen Block bauen. Ich muss also Smiley2[0][0]
bis Smiley2[4][4]
abklappern. Dies kann ich wie unten gezeigt machen. Dazu benötige ich zwei for-Schleifen: eine Schleife (for i in range(5)
) wählt die Zeile (von 0 bis 4) und die zweite (for j in range(5)
) geht dann diese Zeile durch (wieder von 0 bis 4):
Smiley2 = [[0,1,1,1,0],[1,2,1,2,1],[1,1,1,1,1],[1,2,1,2,1],[0,1,2,1,0]] for i in range(5): for j in range(5): zahl = Smiley2[i][j] # die Zahl des Pixels holen in der Liste Smiley2 (0,1,2) if zahl == 1: # wenn die Zahl 1 ist: gelber Block bauen blocks.place(GOLD_BLOCK, world(i, 4, j)) # Die Koordinaten sind world(i,4,j) if zahl == 2: # wenn die Zahl 2 ist: schwarzen Block bauen blocks.place(COAL_BLOCK, world(i, 4, j)) # wenn die Zahl 0 ist wird nichts gebaut (Air)Es ist nicht ganz einfach, das Programm von oben zu verstehen, doch mit diesem Prinzip kann man recht elegant und effizient komplexe Strukturen bauen. Die Koordinaten muss man an die eigenen Bedürfnisse anpassen. im Beispiel von oben geht man davon aus, dass die Figur auf der „Höhe“ 4 gebaut wird und bei den x- und z-Koordinaten 0 beginnt.
5. Einen Block "bewegen" lassen
Will man, dass sich ein Block „bewegt“, so muss man ihn durch Luft ersetzen und dann einen Block weiter wieder bauen. Dadurch entsteht der Eindruck, der Block habe sich um ein Feld bewegt. Spiele mit Bewegung laufen häufig in einem Game-Loop ab. Dies ist eine Schleife, die x Mal pro Sekunde ausgeführt wird (FPS=Frames pro Sekunde). Bei jedem Frame wird die Welt gelöscht und neu gezeichnet. Dadurch entsteht der Eindruck einer Bewegung.
running = True # solange dies auf True ist, läuft der Game-Loop def stopGame(): # wenn "stop" in den Chat geschrieben wird, running auf False setzen global running running = False player.on_chat("stop", stopGame) x = -60 frame = 0 while running: frame += 1 if (frame % 50 == 0): # immer nach 50 Frames blocks.place(DIAMOND_BLOCK, world(x+1,-60,0)) # Block an neuer Position bauen blocks.place(AIR, world(x, -60, 0)) # Block löschen x = x+1 # Position anpassen
6. Dinge in das Inventar eines Spielers/Agenten/Chest legen
Manchmal möchte man Items oder Blöcke in das Inventar eines anderen Spielers, des Agenten oder einer Kiste legen. Dazu kann man den Chat-Befehl /replaceitem entity @c slot.inventory 0 barrier
verwenden. Denentsprechend kann man im Pythoncode player.execute(„replaceitem entity @c slot.inventory 0 barrier“)
verwenden, um z.B. dem Agenten Barrier-Blöcke oder auch Items zu geben. Verwendet man statt entity
das Wort block
, kann man auch in das Inventar von Blöcken (Chest) Dinge legen.