This is a port of Minecraft running on a TI 84. No really, you can place blocks, move around,
save and load worlds, all in real-time on a device about a thousand times less powerful
than a modern computer. But full disclosure, this isn’t the first time the game Minecraft
has been brought to a graphing calculator. In fact, there’s a proud tradition of calculator
Minecraft ports going back to at least 2011 from what I can tell. A common trend you’ll
find between all of these is that they use a 2D side view of the game rather than 3D.
One of the better ports I found was “Minecraft 2D” for the TI nSpire by Jen’s Kolbinger. Depite
being 2D, it’s a pretty extensive port of the game, with different game modes, mobs,
crafting, furnaces ansd more. Obviously it’s not going to beat the PC version in terms of content,
but for a port of the game flattened down into 2D, it’s got enough to be compelling.
The other notable port I found was Crafti by Vogtinator. This one is probably the most
impressive port there is. It’s able to pack in full 3D graphics, chunk loading, and even redstone
onto a calculator. I’d say that “Program of the Year” award from ticalc.org was well earned.
That being said, there are some things about these two that I don’t particularly care for. For one,
crafti uses affine texture mapping instead of perspective, which causes this really
ugly warping to happen whenever you get too close to a block. But more importantly, both of these
ports are only available on the TI nSpire, which as someone who didn’t HAVE an nSpire while going
through high school, makes me just a little salty. So here’s the goal. Get a 3D version of Minecraft
running on the TI84. And that’s already a pretty tall order. Even though they look similar on the
outside, under the hood the TI 84 and nSpire couldn’t be more different in terms of power.
The nSpire has 64MB of RAM, which doesn’t sound like much, until you realize the Ti 84 has 256KB.
The nSpire runs at nearly 400 MHz in the latest model, the Ti 84 runs at 48MHz. And
let's be clear here, that doesn’t mean the nSpire is “just” 8 times faster. The nSpire
is using a 32 bit ARM processor whereas the 84 has a kinda-8 kinda-24-bit eZ80.
The ARM has way more registers and more powerful instructions which basically means that cycle for
cycle, the ARM chip still does more work than the eZ80. About the only thing even remotely similar
between these two calculators is that they both have the same 320x240 display hardware.
With that in mind, perspective 3D is basically out of the question. It involves doing a bunch
of projections and texture warpings to draw each face of a block, and for a world with as
many polygons as Minecraft, a TI84 would absolutely crawl. But thankfully, there
is another way to do 3D that’s been used in games as far back as the mid 80s: isometric projection.
You can think of it like having the three 3D axes oriented like this. Each of the dimensions has a
unique direction, so you can still perceive 3D structure even as we project down to 2D.
The big difference here from perspective 3D is that things don’t change in size as they
get closer or further away from the viewer, and this ends up being a big help for games, since we
don’t need to warp or scale an object to get it to look like it’s moving around in 3D. All it takes
is sliding it around the screen, something tilemap and sprite hardware can do really fast. That’s why
so many 3D retro games, from Marble Madness, to Sonic 3D Blast, to Sim City 2000 all use isometric
graphics. You just need to draw each object from one fixed perspective, and you’re good!
So let's make some isometric sprites to use. Creating these 3D blocks out of 2D textures ends
up being a pretty simple task. You just take the 2D face of a block, and shift all the pixels down
from left to right. It’s similar for the right face and you do a slightly more complicated,
but still affine transformation for the top. With that, we now have some 2D sprites of 3D blocks to
play around with. It’s actually really simple to place them on the screen and get something
that looks 3D. Even moving them around in GIMP, you can see how nicely they all tile together.
The only real problem is that drawing the world like this on the calculator is slow.
Take a look at this scene. Since almost every block is covered on one side, we need to start
from the back and draw all the way to the front, otherwise the blocks will draw over each other
in the wrong order like this. For a typical scene, it takes up to 10 seconds to render in,
which is just unacceptable. The problem is, rendering this way wastes a lot of work drawing
in pixels only to draw over them again later. You can speed this up a little bit by avoiding
drawing faces covered directly by their neighbors, though that won’t help for blocks further apart.
What we need is a fast way to be able to tell at each pixel, what block is the one on top and only
draw that one. So here’s the trick I worked out. If you stare at the grid long enough, you might
begin to notice that each block can be divided into six equal sized triangular slices, two for
each face. More importantly, when projecting the blocks from the 3D grid, they should always line
up on these triangular boundaries. In fact, we can actually represent the entire Minecraft world
here as a triangular grid like this. Every time we place a block, we fill in the triangles around it,
and now we have a map to look up the closest block to the camera for any position on the screen.
Now this representation alone can already make things a lot faster,
since it’s cheaper to waste time writing 6 bytes to memory updating the grid compared to the 768
bytes it takes to write pixels to the screen. And we can add more data to this grid to help
keep track of things as we’re drawing. In this case, for each triangle we can track the distance
of the blocks on the grid to the camera. Normally, when we’re drawing in the world,
we need to start with the blocks all the way in the back and work our way forward so that any
block that gets covered up is drawn before we draw the block that covers it, but with a depth map,
we can just check and see if the block we’re adding should be in front of or behind each tile.
If the depth of the block we’re adding is greater than what’s already there,
we skip it, and if it’s closer to the camera, we draw over. This lets us update the triangle
grid in any order, and will be pretty helpful later on when we get around to placing blocks.
Now this triangle grid here already helps speed up the graphics significantly, going
from 10 seconds to draw the full screen to under one second. It’s a pretty serious improvement,
but still not enough to have animations like scrolling look very smooth. So,
time to dive into the technical stuff yet again. The Ti84 doesn’t have much by way of graphics
hardware. No sprites, no tilemaps, about the only thing it does have is double buffering. Basically,
the VRAM is split into two sections, so that one is shown on the screen and the other is invisible.
The idea is that watching graphics get drawn onto the screen would make things look flickery, so
instead, we draw our graphics to the back buffer, then when we’re done, the screen makes a quick
flip and starts showing that buffer, while we start drawing the next frame in on the old buffer.
One thing you might have noticed is that it's pretty wasteful to redraw the whole screen
when scrolling, since thanks to the isometric perspective most of the image stays completely
the same besides being shifted over a bit. And that’s a good observation, because on the ez80,
it’s much cheaper to copy pixels around than it is to draw in new ones. In fact there’s
a hardware instruction that makes copying large chunks of memory super fast. So when the program
is scrolling the screen, what it really does is copy most of the previous frame over to the
other buffer, then fills in the empty bits around the edges. Altogether this saves a ton of time
drawing the scene, and manages to get us up to frame rates of about 10 FPS. It’s probably the
first time I’ve ever been happy to see Minecraft running at 10 frames-per-second, but compared to
the 10 seconds-per-frame we were dealing with earlier, I’ll take this speed in a heartbeat.
At this point, things were starting to come together. There’s a 3D world you can look around,
a couple different types of blocks, I even added a function to create trees just to make things feel
a little more alive. But one thing I wanted to try to add to really make the graphics pop, was water.
Water is a pretty tricky thing to implement in the current system I have here. I mean,
the cheap way is just to make a solid blue block and call it a day. But that doesn’t look very
good since you can’t actually see what’s under the water. The real Minecraft has transparent water,
and I simply refuse to be outdone. Problem is, transparency on the Ti84 isn’t really an option.
In a traditional graphics pipeline, transparency is pulled off by taking the current RGB color on
the screen, blending each of the components with another color, and then replacing the
original with the blended color, but here I'm using palletized (or indexed) color, which
means that I chose 256 colors to draw my graphics beforehand, and then each pixel I write is just an
index into that palette like a paint-by-numbers. The only way to get a specific RGB color is to
find the closest one in the palette, which would take too long to do for every pixel.
Here’s the thing though, all the graphics you’ve been seeing so far were made to only use a quarter
of the total palette space. That’s right, I was sneakily planning ahead. I figured the easiest way
to find the color you want is to have it already in your palette, so for each of the 64 colors
used to draw this Minecraft world, there are three other copies blended with blue for water,
black for shading, and both for, well, both. Lay them out in the right order,
and adding shading or water color to a pixel is as easy as adding one bit to get the same color
from a different palette. Now that it’s so easy to tint pixels with different colors, I threw in
some water blocks, and shaded the right faces of each block to give everything a bit more depth.
Since I figured I had the graphics down pretty well, I started moving my focus
toward making this an actually playable game. I added a little cursor sprite for the player,
put in a block select GUI which takes advantage of my prior palette trickery to dim the background,
and finally added the ability to place and remove blocks into the world. It wasn’t quite
as easy as I’m making it sound here, but on the bright side, the game now feels a lot more like
Minecraft. You can go around and build the obligatory dirt house, or a pyramid of gold,
or even a shameless attempt at self promotion. The only real issue I saw at this point was that,
moving around the world, it’s not always easy to tell where things are in space. That’s the
one big drawback to isometric projections like this. Since things don’t shrink away with depth,
there’s an ambiguity between 3D positions. Like if I place a block here, then move south and up,
and place a block here, they end up in the same spot on the screen. You can actually use this
ambiguity to make some neat geometry like these impossible stairs, but in terms of usability,
it makes it really hard to make sure you’re placing blocks where you want to.
One quick fix I did was incorporating the depth map into the player sprite,
so now if the cursor is behind a block, it shows up as occluded. It’s not perfect,
but it does let you line up with something familiar like the ground or a wall.
I did have one other idea though, and that was to add shadows. Originally,
I was just testing around with some graphics in GIMP, taking screenshots I had generated in-game
and hand-drawing shadows on top to see what they would look like, and after a little bit I started
to notice a pattern with regards to the triangle grid. For each face of a block, there are only
four states the shadows can be in. Totally empty, totally shadowed, or half shadowed on each side,
and so rendering shadows on a block really just boils down to picking which shadow pattern to use,
and then combining a pre-drawn shadow mask to the block texture, using the same palette trick I used
earlier for the water. In other words, we can pick a block, pick the effects we want to render on it,
and then combine all these textures together when drawing to get the sprite we want.
The only tricky part now is figuring out which patterns we want to use. The sunlight shines in
from the top-left side, so for each block, if there’s something in between it and the sun,
the block should be in shadow. In other words, only the top-left-most blocks should be in the
sun. But how do we tell which blocks those are? Well, if you’re starting to think that this sounds
like the visibility problem I had all the way back at the start, you’re completely right. Turns out,
it’s the same exact question, but rotated 90 degrees. So I added another depth map, but this
one measures the distance of the closest block to the sun for each triangle. When adding in a block,
all you have to do is check if it’s at the top of the depth map, and if not, draw it in shadow.
I think the shadows do a lot to make these graphics look aesthetically pleasing. On one
hand they’re functional, since they give some cues to help you resolve depth, but they’re also good
at adding a little bit of variety in worlds with lots of repeated textures. Plus I dunno, in some
places they give off a bit of a moody vibe. Or at least, as moody as the bright colors of Minecraft
can be. Anyways, it’s something the original Minecraft doesn’t have, so checkmate Mojang!
And now it’s time for the “rest of the owl” part of the video, where I add in a bunch of less
interesting parts to finish this thing out. Stuff like adding save support or a nicer cursor sprite,
putting in a main menu, and also some more natural looking worldgen. Plus,
lots, and lots, of little bug fixes to cover the myriad ways water and shadows can interact.
Here’s what I ended up with. A realistic world where you can scroll around and build things.
Everything feels pretty quick and responsive, and the graphics, for a calculator, look pretty good!
In fact I actually think isometric Minecraft looks best on a tiny screen like this. Being able to see
the whole world makes everything seem small, but zoomed in like this? It fits just about right.
Now obviously, this Minecraft port is missing a lot, even compared to other calculator ports.
There’s no redstone, no mobs, basically nothing dynamic like falling sand or flowing water,
but I think there’s a pleasant simplicity to it regardless. At least, that’s the excuse I’m going
with. It kinda evokes a Minecraft Classic vibe back when the whole point of the game was just
to build things. In fact in some sense it’s a lot like Classic. Try as I might to cram in
as many textures as I can, there are only 24 different blocks to play with here. That 256k
of memory fills up fast, you know. Same thing with the world size. I played around with a few
different dimensions before settling on 48x16x48 as a reasonable space to play in. It’s just big
enough for a device this small, but it isn’t infinite. I actually did think a bit about
adding a chunk loading system like in real Minecraft, but the more I thought about it,
the more complex it sounded. Plus, by the time I was debugging graphical glitches I was getting
pretty worn down on this project, and as the saying goes “perfect is the enemy of good enough.”
And hopefully this is good enough for everyone out there. I mean, it is free after all. You get
what you pay for. But still, it’s a version of Minecraft you can play right on a TI 84
calculator. It certainly met my expectations for this project, and I gotta say, it is pretty fun
to build stuff in. My apologies in advance to all the high school math teachers who are bound
to catch students playing this instead of paying attention. Save this game for after class guys,
ok? But, anyways, there you have it, 3D Minecraft,
finally available for the first time ever, on the TI84, download link in the description.
Except uh, funny thing, that. Turns out just about a month after I started working on my version of
Minecraft, Michael2_3B of the cemetech forums released his own isometric Minecraft port for
the TI84. I unfortunately wasn’t able to get it running locally to try out,
but going by the screenshots he posted, it looks pretty nice. It has some features I didn’t
implement in mine like redstone and gravity, and the lighting system is pretty impressive.
No other way to put it, that’s a massive L for me, so I’ll have to be content instead
with making the second 3D port of Minecraft for the TI84, and you know what? I’ll take it.