Drawing with loops
Calvin Robinson mods Minecraft Pi to work on Linux, building 3D pixel art and shapes with Python loops and hoops.
Calvin Robinson mods Minecraft Pi to work on Linux, then builds 3D pixel art and shapes with Python, loops and pyramids.
This issue we’ll take our Minecraft pixel art into the third dimension, by building Pyramids with Python loops. We previously built some pixel art in Minecraft with two dimensions, so now we’re going to take a look at building 3D pixel art. Technically, everything in Minecraft is 3D, but our previous pixel art was ‘flat’; this time we’re adding another axis to build upwards/outwards. We’re then going to hack our pixel art into another player’s game.
We’re going to start by building a pyramid using while loops and for loops in Python. The idea is that the loops will save us typing lots of similar lines of code, which will make life easier for us to create our prefabricated designs. Spawning in our coded creations is so much faster than placing each individual block manually in Minecraft’s Creative mode.
If you’re using Minecraft Pi edition on a Raspberry Pi, no additional software is necessary. We’ve also put together a number of tools to ensure this hack works on Linux, with a retail version of Minecraft. Therefore as a prerequisite, we assume you’ve installed Mcpifomo from our previous three issues. Mcpifomo includes MCPIPY by ‘fleap’ and ‘bluepillrabbit’ of Mcpipy.com and Raspberry Jam, developed by Alexander Pruss. Martin O’hanlon of www.stuffaboutcode.com has put together some prefabricated shape functions, which we’ll be using in this tutorial, available on Github.
All Python scripts should be saved in ~/home/ .Minecraft/mcpipy/, regardless of whether you’re running Minecraft Pi edition or retail Linux Minecraft. Be sure to run Minecraft with the Forge 1.8 profile that’s included in our Mcpifomo package.
Spawning variables
Git yourself a copy of Minecraft Stuff: git clone https://github.com/martinohanlon/ minecraft-stuff
You could also visit Github and manually download Minecraftstuff.py, as that’s all we’ll need for this tutorial. Just make sure you pop it in the ~/home/ .Minecraft directory along with Mcpifomo.
Another way of installing Minecraft Stuff would be to use Python’s package index tool with sudo pip install Minecraftstuff or sudo pip3 install Minecraftstuff . We’re going to want to create a new PY file in our favourite text editor/idle and import all the relevant
Minecraft-related Python modules. Save your Python file in ~/home/.minecraft/mcpipy/. import mcpi.minecraft as Minecraft from mcpi import minecraft import mcpi.block as block import server import time import minecraftstuff
We’ll want to connect to our Minecraft world: mc = minecraft.minecraft.create()
Here we’re creating an instance with the variable mc
that we can use later on to spawn shapes directly into our open world. We’ll want to use this variable when initiating an instance of Minecraftstuff: mcdrawing = Minecraftstuff.minecraftdrawing(mc) .
Now track our current location in-game and we’re good to go: playerpos = mc.player.gettilepos() . For our starter pyramid, we’re going to initiate a bunch of variables, collecting the player’s standing position pos = mc.player.gettilepos() , and breaking that down into x,y,z coordinates x = pos.x + 2, y = pos.y z = pos.z . x = pos.x + 2 y = pos.y z = pos.z
height = 29
count = 0 blockid = 24 blocktype = 1
We’ve also got variables for the height of our pyramid – we’ll get to that shortly. Below that we have blockid and blocktype , which will affect the blocks used to create our pyramid. Block ID 24:1 would be broken down to blockid 24, blocktype 1. We chose this as we felt Chiselled Sandstone looked quite ‘pyramid-y’ to begin with.
Let’s create our first loop to get the basic pyramid spawning, starting with a large base and then each row getting smaller and smaller until we reach the peak of the pyramid. while height - (2 * count) > 0: for block in range(height - (2 * count)): for row in range(height - (2 * count)): blockx = x + block + count blocky = y + count blockz = z + row + count mc.setblock(blockx, blocky, blockz, blockid, blocktype)
count += 1
We’ve already initialised variables for height and count. By changing the height, we can make our pyramid larger or smaller. count is used in the loop to make sure our pyramid stops on the number of rows we specified in height , starting at 0 and incrementing upward with each cycle of the loop.
If we wrap our setblock command in conditional if / else statements, we can alternate rows, using the
count variable to see if the row is an even number or an odd. We can place a different blockid on each row. Replace the current mc.setblock(blockx, blocky, blockz, blockid, blocktype line with:
if count % 2 == 0: mc.setblock(blockx, blocky, blockz, woolblockblack, woolblockblacktype) else: mc.setblock(blockx, blocky, blockz, woolblockgreen, woolblockgreentype)
Alternating colours
Rather than setting the colour based on odds/evens, we can get more creative and make each row a different colour. Alter the if statement to include elif s for each row that you’d like to colour, but don’t forget to initialise blockid / blocktype variables: woolblock = 35 woolblockwhitetype = 0 woolblockgreentype = 5 if count == 0: mc.setblock(blockx, blocky, blockz, woolblock, woolblockblacktype) elif count == 1:
mc.setblock(blockx, blocky, blockz, woolblock, woolblockyellowtype) elif count == 2:
mc.setblock(blockx, blocky, blockz, woolblock, woolblockbluetype) elif count == 3:
mc.setblock(blockx, blocky, blockz, woolblock, woolblockgreentype) else:
mc.setblock(blockx, blocky, blockz, woolblock, woolblockwhitetype)
Having a fancy if statement is great, but don’t forget the variables: woolblock = 35 woolblockgreentype = 5 woolblockblacktype = 15 woolblockyellowtype = 4 woolblockbluetype = 3 woolblockwhitetype = 0
We should now be in a position to alter the height of our pyramids and the types of block used for each individual row, or to alternate the colour of rows depending on what suits our needs. Now is the time to get really creative and put that together to produce something original. You could also try reversing the for
loops to create an inverted pyramid.
When everything is pieced together, we should end up with something that looks a little like the following: import mcpi.minecraft as Minecraft from mcpi import minecraft import mcpi.block as block import server import time import minecraftstuff mc = minecraft.minecraft.create() pos = mc.player.gettilepos() x = pos.x + 2 y = pos.y z = pos.z height = 30 count = 0 woolblock = 35 woolblockwhitetype = 15 woolblockgreentype = 13 woolblockbluetype = 4 woolblockyellowtype = 14 woolblockblacktype = 15
while height - (2 * count) > 0: for block in range(height - (2 * count)): for row in range(height - (2 * count)): blockx = x + block + count blocky = y + count blockz = z + row + count if count == 0: mc.setblock(blockx, blocky, blockz, woolblock, woolblockblacktype) elif count == 1: mc.setblock(blockx, blocky, blockz, woolblock, woolblockyellowtype) elif count == 2: mc.setblock(blockx, blocky, blockz, woolblock, woolblockbluetype) elif count == 3: mc.setblock(blockx, blocky, blockz, woolblock, woolblockgreentype) elif count == 4: mc.setblock(blockx, blocky, blockz, woolblock, woolblockyellowtype) elif count == 5: mc.setblock(blockx, blocky, blockz, woolblock, woolblockbluetype) elif count == 6: mc.setblock(blockx, blocky, blockz, woolblock, woolblockgreentype) elif count == 7: mc.setblock(blockx, blocky, blockz, woolblock, woolblockyellowtype) elif count == 8: mc.setblock(blockx, blocky, blockz, woolblock, woolblockbluetype) elif count == 9: mc.setblock(blockx, blocky, blockz, woolblock, woolblockgreentype) elif count == 10: mc.setblock(blockx, blocky, blockz, woolblock, woolblockyellowtype) elif count == 11: mc.setblock(blockx, blocky, blockz, woolblock, woolblockbluetype) elif count == 12: mc.setblock(blockx, blocky, blockz, woolblock, woolblockgreentype) else: mc.setblock(blockx, blocky, blockz, woolblock, woolblockwhitetype) count += 1
We’ve got our pyramids down, so let’s take a look at the prefabricated shapes we have available to us: drawline , drawsphere , drawcircle and frawface . We can spawn these shapes in our world with the
mcdrawing variable we set up a moment ago, but we’ll need to make sure we pass the coordinates to the function, as well as the type of blocks we’d like to use. For example:
mcdrawing.drawsphere(playerpos.x, playerpos.y, playerpos.z, 15, BLOCK.DIAMOND_BLOCK.ID)
The drawface function is a great tool for filling in surfaces. Think of it as a paint bucket from Photoshop. mcdrawing.drawline(x1,y1,z1,x2,y2,z2,blockid) mcdrawing.drawsphere(x,y,z,radius,blockid) mcdrawing.drawcircle(x,y,z,radius,blockid) mcdrawing.drawface(shapepoints,true,blockid)
The prefab shapes are great, but you might want to draw a custom shape of your own. We can do that by setting coordinates of each point and then filling in the blanks with drawface . shapepoints = [] shapepoints.append(minecraft.vec3(x1,y1,z1)) shapepoints.append(minecraft.vec3(x2,y2,z2)) shapepoints.append(minecraft.vec3(x3,y3,z3))
Now that we’ve set the coordinates of the points, drawface will connect up the dots and fill within the lines with your chosen blockid. mcdrawing.drawface(shapepoints,true,blockid)
However, if you merely want to draw the outline of the shape you can change the True to False and
drawface will create lines but not fill them in.
Shapes are brilliant, but sometimes you might want to literally just create lines in any given direction, with a specific type of block. We can do that with the
drawline function:
mcdrawing.drawline(playerpos.x, playerpos.y + 2, playerpos.z, playerpos.x + 2, playerpos.y + 2, playerpos.z, block. DIAMOND_BLOCK.ID)
Combined with drawface , this function provides us with some pretty powerful tools to get completely creative in our Minecraft worlds, without spending too much time placing blocks or calculating where they should go.
You may have noticed that we imported the ‘time’ module when we were setting up our Python scripts. That’s so we can slow things down (or indeed speed them up) when needed. When using the drawline or
drawface functions, it’s best to include a slight pause after each line, otherwise your game could have problems playing catch-up and the lag would result in a messy creation. Just insert a time.sleep(x) after each instance, where x is an integer. Any number between 1 and 5 usually suffices.
Keep it neat
As your Python script becomes more and more complex with the addition of different prefab and custom shapes, it’s good practice to comment the code, but it’s also a nice touch to update the current player with a status update. mc.posttochat(“spawning X_shape in the player world”) where X_shape is the name of whatever you’re spawning. We could create a different function for each shape and call them with commands: def circles():
mc.posttochat(“spawning circles in the player world”)
mcdrawing.drawsphere(x,y,z,radius,blockid) mcdrawing.drawcircle(x,y,z,radius,blockid)
Then run the circles() command in the code whenever you want to spawn that shape. You could of course have a different function for each shape or set of shapes, then set up user commands to call said functions.
So far we’ve mostly been making use of the Minecraftdrawing class, with useful functions for drawing shapes, but the Minecraft-stuffs module also comes complete with a class specifically designed for
creating shapes, Minecraftshape , taking the variable inputs (mc, position, shapeblocks=none, visible=true) . This is designed more for manipulating shapes; for example the function draw() will draw the shape in
Minecraft, then you can use move(x, y, z) to move that shape to another position and finally redraw() to clear all the blocks and redraw them. reset() puts the shape back to its original position.
Some other functions include moveby() which moves a shape in relative terms, as opposed to absolute coordinates. rotate and rotateby offer relative vs absolute degrees for turning a shape around. clear() removes the shape entirely. from minecraftstuff import Minecraftdrawing from mcpi.minecraft import Minecraft from mcpi import block mc = Minecraft.create() pos = mc.player.gettilepos() mcdrawing = Minecraftdrawing(mc) mcdrawing.drawcircle(pos.x, pos.y + 15, pos.z, 10, block. WOOD.ID)
Have fun with your building!