Salty Scientist Studios

Wizard School · November 2026

Moving to 3D

Every GIF in the earlier posts is flat: pixel art seen from straight above, the way RimWorld is. That view is on its way out. Wizard School is now drawn in three dimensions from a corner, the way The Sims was in 2000.

a kitchen and dining hall at supper, seen from each of its four corners in turn
a kitchen and dining hall at supper, seen from each of its four corners in turn

Why

The game had drifted. It started out as a colony sim, and over the autumn it turned into more of a dollhouse. You play the headmaster and a small household, you click where they should walk, you queue up things for them to do. It was playing more and more like The Sims while still looking like RimWorld, so I wanted to see it with different graphics and everything else left alone.

That came to one new module. The world keeps its coordinates and gets a height. A camera with no perspective looks down at thirty degrees from a corner. The ground is the same tile pictures laid flat, the walls are thin and cut away in front of rooms, and a plumbob hangs over whoever you're playing. Nothing in the simulation had to change. A click used to be a point on a flat map and is now a ray down to the ground, and the menus and the queue never noticed.

It looked like The Sims the first time it ran. Most of that look turns out to be the camera angle, the cut walls and the plumbob.

the same supper in the old flat view and in the new one
the same supper in the old flat view and in the new one

Not porting everything

The flat view has months of effects in it: smoke, casting circles, snow cover, the marks on rock to be dug. I didn't want to redo them one by one. So a single system takes whatever sprite the flat view still draws and puts it into the 3D world, either laid on the ground or stood upright, going by its old drawing depth. The snow day scene worked the first time it ran, snowballs included.

It gets some things wrong in a way I quite like. Smoke used to rise up the screen. Up the screen is now north, so for a while all smoke drifted north.

Items are staying flat for good, as pictures on little cards. They're cute that way.

Four tries at a goat

The first models were balls, rods and boxes read from a table. There are 150 kinds of creature in the game and I wasn't going to model them, so one builder reads the data that already says what each one is (a bird, a hoofed beast, has wings, has horns) and stretches a few shapes to fit. On its first run every cat and goat came out as a bald person. A four-legged body in this game is a person's body whose arms walk, and nothing had told the builder so.

Then I did it properly in Blender: a skeleton of points with one skin grown over it. Everything a skin touches goes round and fat. The first golem made that way was a tooth with ears. It was also too smooth, a modern look and not a 2000 one, so the next pass cut each creature down to a hundred or two flat faces.

And then I threw most of that away. A Minecraft pig is a few boxes with a picture on them and nobody minds. A goat is now a body, a head, a snout, four legs and two horns, with a 128-pixel coat painted on: pale belly, dark hooves, patches. That took an afternoon and looks better than the two days of skins did.

Boxes don't suit people, though. What worked there was Mario in 1996: a few round shapes, shaded smooth, with the detail painted on. So a person is a profile turned on a lathe with a picture wrapped round it like the label on a tin. A face used to be thirteen separate shapes (eyes, glints, lids, brows, tears, cheeks, a mouth) and is now one picture with a cell for each feeling.

Two bugs from all this that I'm fond of. Every model is written out with its outline, which is the same shape a little bigger and turned inside out. A tidy-up step that turns faces the right way out did it to the outlines as well, and every animal in the game wore its outline as a skin and went black. And the golems had been lying on the ground since the very first 3D picture, because a figure lies down when it's asleep, cut down or not alive, and a golem isn't alive. Nobody noticed. They looked like crates.

Keeping the old night

The lighting nearly sent me back to 2D. The first 3D night had a dim blue sun standing in for the moon and lamps that shone straight through walls. The flat view had something much better, a map of how much light each tile gets, flooded out from every source and stopped by walls, and drawn as bands of dithered dark.

So the 3D night is the flat view's night. The world is drawn as if by day, and a pass over the finished picture reads that same light map and darkens it. The one new thing it has to work out is where on the map a pixel is, which needs the pixel's depth. It's about thirty lines of shader. Since the pass was there anyway it also makes the picture chunky and cuts it to fifteen-bit colour, PlayStation style, and the low-poly models suddenly look deliberate.

wolves come out of the dark at students round a campfire
wolves come out of the dark at students round a campfire

The flat view cleared a little oval of night around each person so you could see them. Carried into 3D that became a disc of bright grass at everybody's feet, like a spotlight. It's measured from their eyes now, so the head and body show and the ground mostly doesn't. Firelight comes in eight steps with a hard dithered edge between them. Each step needs roughly twice the light of the one below, which makes the rings get wider the further they are from the fire. And a tree next to a bonfire has a lit side, without there being any real lights in the scene: the shader works out which way a surface faces from the depth around it, and where the fire is from which way the light map gets brighter.

The colours needed fixing as well. Next to the flat view the 3D one looked washed out, with lime grass and pastel robes. The engine lights a scene well past white and then squeezes it back with a film curve, which is right for a photograph and wrong for a palette that was picked a pixel at a time. With the curve off and the lights sized by measurement, sunlit grass went from (147, 174, 91) to (102, 160, 65). The flat view's is (98, 169, 64).

Fire, water and wind

a teacher conjures a light, throws a fire lance and calls lightning down in a stone yard at night
a teacher conjures a light, throws a fire lance and calls lightning down in a stone yard at night

A fire was a little picture of a flame on a card. A burning storeroom looked like a shelf of birthday candles. The flame is arithmetic now: for each chunky pixel of the card, how far it is from the middle of one of three tongues, how high up it is, and what a field of noise sliding upward says at that spot, all cut into five flat colours. My first version was three stiff orange spires. The noise has to win near the top, because a tongue of flame is mostly the gaps torn in it. Once a card could be a flame it could be a bolt, a ring or shards of ice, and spells got the same treatment.

tufts of grass lean in the wind and ripples run over a pond
tufts of grass lean in the wind and ripples run over a pond

Water was a blue sheet. Now the ground itself is sunk where the water is, deeper under deep water, with a flat surface over it that hides more the more water there is underneath. A pond gets a beach for free, since that's just where the bed comes up through the surface.

The weather has had a wind for months. It chills people and fans fires, and until now nothing on screen showed it. It leans the tufts of grass and pushes the ripples on the ponds, the same wind for both.

Two views of one school

Keeping the flat view around turned out to be a good test. I photographed twenty set pieces in both views and went through them in pairs, which is how I found that a burning field looked like a field of traffic cones and that a school planned ahead of its builders was a wall-high block of blue glass with the builders somewhere inside.

The better test was running one seed under both views and comparing the dice. The demo school matched for twenty-seven thousand ticks. A burning shed didn't. At tick 1152 the two views rolled a different number of dice and never agreed again. The system that exposes every body to the weather was walking them in whatever order the engine happened to hold them, and that order depended on what the view had attached to them. There were eight loops like it. My determinism tests run without any graphics, so they could never have caught this.

What it costs

A frame takes 3.6 ms in 3D against 2.2 in the flat view. The scarier number was a crowd of three hundred on a field: 6 ms a frame but 22 ms of processor time, three cores busy drawing a picture one could have drawn. Most of that wasn't work. The game has hundreds of small systems and the engine was waking a thread for each. Run one after another on one thread it's 13 ms, and the frame is no slower.

golems put up a hall, a walled garden and a cottage over two days
golems put up a hall, a walled garden and a cottage over two days