Pixel-Art Structures: Houses, Halls and Interiors on iPhone

This post has not been translated yet, so you are reading the English original. Read it at the English URL

A Pokémon Center is four tiles by four outside in every 2D generation, and Emerald draws its ordinary house from a kit of three columns by four rows, a left cap, a repeatable middle and a right cap over roof top, roof bottom, upper wall and wall with door, so one set of art makes Petalburg’s four-wide houses and Wally’s five-wide one.12 The top row of that roof is walkable and drawn on the background layer above the player, which is how a sprite walks behind a roof on a handheld with no depth buffer.3 The room behind the door is a separate map about eight times the footprint, and the door opens as three 16-by-32 frames of about 84 milliseconds each before the screen fades.14 Those facts cover most of what a building is in a top-down pixel game: a projection, a kit with rules, a layer order, and a warp. Our town’s houses were single 16-pixel tiles with nothing drawn above a walker, so we measured the canon from the decompilations, read Stardew’s building records and Emerald’s elevation rules, and rebuilt: a kit of 58 pieces drawn by rule, seven buildings composed from it, a layer the engine sorts by depth, doors that open, windows that light at dusk, and interiors the forge draws with a stair between floors. This is the guide, with the numbers, the RealityKit recipe and what shipped.

TL;DR

  • Footprints barely moved in fifteen years. The Center and the Mart are 4 by 4 cells in Red, Crystal and Emerald; a Gym is six wide in all three; the player’s house grew from 4 by 3 to 5 by 5, which I read as the Game Boy Advance’s fifteen cells across, against the Game Boy’s ten, giving the art the room. Height is half roof or more in Emerald: two or three rows of it over two rows of wall every time; the primary kit’s houses take two whether four or five wide, Littleroot’s 5-by-5 houses and the 7-by-5 lab three.156
  • A building is a kit plus rules, not a picture. Emerald’s house is three columns by four rows of metatiles; Crystal keeps five roof shapes and recolours them per map group, with a day pair and a night pair for its 26 map groups; RPG Maker gives roofs and wall faces sixteen shapes where its floor table has 48 (the 47-tile blob plus the pattern the palette shows), because a wall only has to know its four cardinal neighbours.278
  • Walking behind a wall is a priority bit, a layer, or a depth. The SNES gives every tile a priority bit so one background holds both floor and foreground; the GBA routes each metatile’s two halves to three layers by type; Stardew puts the roof’s edge on Front and the overhang on AlwaysFront; in RealityKit, one depth rule for walkers and objects and cut-out alpha let the depth buffer decide.931011
  • A door is a cell, a destination and a ceremony. Of Emerald’s 1,306 warps, 512 sit on the two-cell exit mats inside buildings and 195 on animated exterior doors; the door plays closed plus three frames, each held five updates (the table says four; the counter starts at zero), about 84 milliseconds, the player is walked one forced step in, the door closes, then the fade.12413
  • Interiors are bigger than their outsides, on purpose. Emerald’s 4-by-4 Center opens onto a 14-by-9 room; Sprout Tower’s 4-by-5 shell holds three floors of 20 by 16; Stardew’s 7-by-3 Shed is 11 by 9 inside. Every one has two impassable wall rows along the top, and floors are separate maps joined by stairs that land you on the matching stair.161415
  • Height is three different things. A tall building outside is art, stacked storeys of two rows each. A floor is a map. Ground height is a per-cell number with two special values, transition and multi-level, that Emerald uses for stairs and bridges; FF VI uses an upper or lower z-level per party; CrossCode stores pixel heights per level and jumps for you at edges.161718
  • What shipped. A 58-piece kit (three roof materials in ten colours, three wall materials, a three-frame door, windows and shop windows with lit variants, chimney, sign, awning, window box, sconce), seven buildings composed from it (five houses, a seven-wide show hall, a two-storey shop) whose 195 cells dedupe to 131 atlas tiles, 188 blocked cells and seven warps in the letter map, doors that swing open at 70 milliseconds a frame, nineteen windows and the hall’s two sconces that light when the lamps come on, and three forge-drawn interiors of 14 by 11 with a two-row wall band and a stair between the house’s floors. 178 unit tests, 14 on the server, and the walks on an iPhone 18 Pro Max simulator; TestFlight build 22, and build 23 the same day with the sconces lit.1920

1. What a building is in three-quarter top-down

A top-down pixel building is a lie told consistently. Raymond Schlitter’s definition of the view, in the first of his projection lessons, is that “it’s as if you are viewing the house from 45 degrees above,” so that “you see about 3/4 of both the roof and facade.”21 The foreshortening lands on the façade only: “the front of the house is shortened but the angles of the roof are not affected by the implied tilt,” and he keeps it that way because true perspective would flatten the roof and he prefers “the look of the clean angles.”21 The Liberated Pixel Cup’s style guide says the same thing in degrees: “The camera angle is top-down, roughly 60 degrees. Rendering should be orthographic.”22 Between those two numbers, 45 and 60, sits every town in this post.

What that view makes of a house is a roof slab tilted toward you over a front wall, and nothing else. There is no side wall; the gable ends, where a game draws them at all, are a single column of diagonal shading on each end. The roof takes the upper half or more of the footprint, the ridge is the top edge, and the eaves overhang the wall by a few pixels. I measured that from my own renders of the pret decompilations, not from a tutorial, because the tutorials agree on the view and then skip the numbers: none of the eight Schlitter lessons I read gives a wall height, a roof depth, an eave or a door size in pixels or tiles.23 The numbers in this guide come from Emerald, in section 2.

The tutorials do give the order of work, and it is the same order the forge follows now. Pedro Medeiros’s “Top Down Houses” is seven instructional stages in a GIF: “START WITH A BOX,” “MAKE THE ROOF SLIGHTLY BIGGER,” bend lines “TO ADD A TINY BIT OF FAKE PERSPECTIVE,” block out “DOORS, WINDOWS AND DETAILS,” “ADD MORE DEPTH AND DETAILS,” “ADD BASE LIGHT/SHADOW,” then “RENDER TEXTURES”; and, for shape, “TRY OTHER SHAPES, LIKE L AND T SHAPES,” “STACK BLOCKS,” and “ADD FOOTERS THAT ‘STRAIGHTEN UP’ THE SILHOUETTE.”24 Schlitter’s city-builder lesson sizes buildings in “16x16px grid units” so they sit on the terrain, lets the top of a sprite extend “beyond this foundation a few pixels” to overlap what stands behind it, and forbids the opposite: “do not break the foundation along the bottom or sides,” because that is where the layer order goes wrong. His process “starts with the roof and works down,” then windows and doors, then shadows “under eaves, in recessed areas,” and his outlines are “a slightly darker color than the adjacent pixels,” never ink.25 On the barn in his farm lesson the rule is “Keep it simple and repeat consistent clusters for the texture to read well.”26 Depth, in the projection lesson, “comes from lighting. Small ledges and outcroppings that catch different light angles can improve depth.”21

Two warnings in those lessons are worth more than the rest. The first is “don’t mix viewpoints”: a building drawn from 45 degrees next to a tree drawn from 60 looks wrong in a way nobody can name.21 The second is that the roof’s colour is a choice, not a fact: “In reality, most roofing materials are dull browns, and grays,” and the saturated roofs of the genre are “colorful simplifications.”25 Crystal, as section 2 shows, built a whole system on that choice.

The kit the forge draws, every piece at four times with its name: three roofs (clay, shingle, slate) as ridge, body and eave rows with left, middle and right ends; three walls (plaster, brick, stone) as upper and lower courses with ends; the door's three frames; the window and shop window, each with a lit variant; chimney, sign, awning, window box, and the sconce lit and unlit.

Our first collector stood on a town whose houses were a single 16-pixel tile each, drawn whole by one function, with the canopy of every tree baked into the ground pixels of the cell above its trunk. That worked for exactly as long as nothing had to pass behind anything, and it is the diagnosis section 7 starts from.

2. Pokémon’s buildings, measured

Everything in this section was read from the pret decompilations rather than from screenshots. To get numbers I wrote three small renderers, one per generation (Gen 1 blocksets, Gen 2 Johto metatiles, Gen 3’s 4-bit tiles plus palettes plus metatiles.bin plus map.bin, the last also dumping each cell’s collision, elevation, behaviour and layer type), drew a 16-pixel grid over the result, read the footprints off it, and checked every door against the map’s warp list. Footprints are in 16-by-16 walk cells, one Gen 3 metatile or a quarter of a Gen 1 or 2 block, and mean the drawn building including its roof.27

Footprints, generation by generation

Building Red/Blue Crystal Emerald
Player’s house 4 by 3 4 by 4 5 by 5
Small house 4 by 2 4 by 2 and 4 by 3 4 by 4, 5 by 4
Professor’s lab 6 by 4 6 by 4 7 by 5
Pokémon Center 4 by 4 4 by 4 4 by 4
Poké Mart 4 by 4 4 by 4 4 by 4
Gym 6 by 4 6 by 4 6 by 5
Landmark – Sprout Tower 4 by 5 Devon Corp 10 by 9

Three things fall out of the table. The service buildings never grew: a Center and a Mart are 64 by 64 pixels in all three games, and a Gym is six cells wide in all three. The player’s house grew a cell wider and two taller between Red and Emerald, 4 by 3 to 5 by 5; my reading is that the Game Boy Advance’s 240-by-160 screen, fifteen cells by ten against the Game Boy’s ten by nine, gave the art the room. And height is at least half roof in Emerald: a Gen 1 or 2 house is one cell of roof over one or two of wall; an Emerald house is two or three rows of roof over two rows of wall. In Littleroot the family house’s rows 0 to 2 are roof and rows 3 and 4 are wall; in Oldale’s 4-by-4 house, rows 0 and 1 are roof, 2 and 3 wall; and Wally’s five-wide house in Petalburg, built from the primary kit, has two roof rows as well, because the kit is four rows deep at any width. Roof depth follows the tileset, not the width. A tower is a stack of roofs: Sprout Tower’s exterior is four cells wide and five tall and draws three roof tiers for a building with three floors of 20 by 16 inside.27561

The kit

Dumping the cells under Petalburg’s and Littleroot’s buildings shows three ways Emerald builds. The ordinary house is a modular kit in the primary General tileset: metatiles 0x08 to 0x0A, 0x10 to 0x12, 0x18 to 0x1A and 0x20 to 0x22, three columns by four rows, a left cap, a repeatable middle and a right cap over roof top, roof bottom, upper wall and ground wall with the door, plus 0x0B (upper wall with window) and 0x13 (ground wall with window). Petalburg’s four-wide house is 08 09 09 0A / 10 11 11 12 / 18 19 0B 1A / 20 21 13 22; Wally’s five-wide house repeats the middle column three times. Width is a parameter; the art is one kit.227 The franchise buildings are fixed stamps: the Mart is 0x28 to 0x43 and the Center 0x48 to 0x63 in the same primary tileset, sixteen unique metatiles each laid four by four, which is why every town in Hoenn has the identical Center, down to the pixel; Rustboro’s Gym swaps only its roof row from the town’s own secondary set. And the one-off buildings, the Littleroot houses, Birch’s lab, Devon Corp, live in a secondary tileset per town or district: Littleroot, Oldale and Petalburg share one, Rustboro has its own.227

Littleroot’s two family houses are 5 by 5, three roof rows over two wall rows, nineteen distinct metatiles for 25 cells each; the pair are mirror-composed, the door on the right of one and the left of the other, and share thirteen metatiles, 25 distinct between them. The roof’s middle column repeats three times across, which is the nine-slice in its plainest form. Birch’s 7-by-5 lab uses 22 distinct metatiles for 35 cells; its roof body is one metatile repeated eight times. Oldale’s 4-by-4 houses use fourteen for sixteen.27 Those ratios, about nineteen of 25, are the budget our composer was measured against in section 7.

The composer's street render of its five sample buildings, by day and after dark: two 5-by-5 plaster houses under clay roofs, red and green; a 4-by-4 brick house under brown shingle with two roof rows; a two-storey brick shop under violet slate with an awning and a hanging sign; and a seven-wide stone hall under blue slate with a sconce either side of the door. At night the windows and the sconces are drawn lit and the whole render takes a flat dusk tint, panes included; in the app the lit cells are a separate untinted mesh, so they keep their gold.

Walking behind the roof

The collision on those stamps is the trick that matters most. In Littleroot and Petalburg the top roof row is walkable (collision 0, elevation 3) and drawn with layer type Normal, so the player can step behind the ridge and the top background layer covers their head; every row below is blocked. A 5-by-5 house therefore has a 5-by-4 collision footprint. The Gym’s two bottom corners are walkable because its façade is chamfered. Where a house backs onto a hedge, as Wally’s does, the top roof row is blocked instead.327

The mechanism is in the map format. Each cell of map.bin is sixteen bits: bits 0 to 9 the metatile, 10 and 11 collision, 12 to 15 elevation; each metatile’s attribute word carries a behaviour byte and a layer type. DrawMetatile routes the two 2-by-2-tile halves of a metatile by that type: Normal sends the bottom half to the middle background and the top half to the layer above sprites; Covered sends both below sprites; Split sends the bottom to the lowest layer and the top above sprites. Roof ridges, tree canopies and the tops of door frames are Normal, so the player passes beneath them.328

Elevation decides who may step where and who draws over whom. A walker may only move between cells of equal elevation unless one side is 0, which is “transition” and used on stairs and ramps; elevation 15 is “multi-level” and, in the editor manual’s words, “remembers the player’s previous elevation and maintains that elevation,” which is how one bridge cell on Route 119 lets a player walk east–west over a surfer going north–south. Draw priority comes from a sixteen-entry table, {2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 0, 0, 2}: even elevations from 4 to 12 lift a sprite above the middle layer, 13 and 14 higher still, and the rest sit below it, so a player on a bridge deck draws over its rails and a surfer at elevation 1 draws beneath them. Multi-level cells are rare and concentrated: thirteen of 441 layouts use any, and Route 110’s Cycling Road alone has 185.291627

Roofs, per town and per hour

Crystal’s answer to variety is the one this post ends up copying. The game keeps five roof shapes, nine 8-by-8 tiles each (New Bark, Violet, Azalea, Olivine, Goldenrod), and LoadMapGroupRoof copies the right nine into video memory for the map group you are in: Mahogany, Blackthorn and Lake of Rage take Azalea’s roof, Ecruteak takes Violet’s, Cianwood Olivine’s.30 The colour is a separate table: roofs.pal holds two RGB555 colours per map group with a morning-and-day pair and a night pair for each of Johto’s 26 map groups (27 entries, the first unused), written into colours 1 and 2 of background palette 6, and only when the environment is a town or a route. Olivine’s day pair is 14,17,31 and 07,11,15, its night pair 09,09,17 and 05,07,13; Mahogany is a green by day and a near-black green by night.7 Five shapes, 26 colour pairs, two times of day: that is how one tileset makes every town in Johto look like its own place. Emerald instead gives towns and districts secondary tilesets of their own (Littleroot, Oldale and Petalburg share one; Rustboro and Mauville have theirs), and with each one seven of the thirteen background palettes, so roofs differ by art as well as colour from district to district while the primary-set Center and Mart stay identical everywhere.31

Doors, mats and warps

A warp in Red is a cell on the town map that names a warp index in the destination (warp_event 5, 5, REDS_HOUSE_1F, 1); each tileset lists its door tiles, and arriving on one makes PlayerStepOutFromDoor simulate a single press of Down. There is no door animation.32 Crystal names the roles in its collision constants: DOOR, LADDER, and four WARP_CARPET directions.33 Emerald separates the door, which is a metatile behaviour, from the warp, which is an event in the map’s JSON with x, y, elevation, dest_map, dest_warp_id. Littleroot has three; Brendan’s door is {"x": 5, "y": 8, "elevation": 0} into BRENDANS_HOUSE_1F warp 1.34

Across all 518 Emerald maps there are 1,306 warp events whose cell lies inside the layout. Looked up by the behaviour of the metatile under each, 512 are MB_SOUTH_ARROW_WARP, the exit mats inside buildings; 217 are MB_NON_ANIMATED_DOOR, mostly staircases and plain doorways; 195 are MB_ANIMATED_DOOR, the exterior doors; 100 are ladders; 94 are plain cells with scripted or edge warps; 34 are escalators, 17 up and 17 down; 57 are the gym-puzzle warps of Mossdeep and Lavaridge; and the remaining 97 are the Aqua Hideout’s and the Trick House’s own warp kinds, cave mouths and Mt. Pyre’s holes, east and north arrows, water doors and a handful of one-offs. Every ground-floor interior I dumped has its exit as two adjacent south-arrow cells with two warps to the same outside door, Oldale’s Center at (6,8) and (7,8) for instance, so a player can leave from either side of a two-wide doorway; outside, the cell below the door is a decorative mat with no behaviour at all.1235

The door animation is the part most games copy without knowing it. field_door.c holds 53 door entries, one of them unused: 25 hinged with a normal sound, 27 sliding (Centers, Marts, Gyms, the department store, the Contest Hall) and the Battle Arena’s with a sound of its own, each with a size: 52 are size 1, which animates a one-by-two-metatile area, 16 by 32 pixels, the door cell and the one above it, and one is size 2. The frames are {4, -1}, {4, 0}, {4, 0x100}, {4, 0x200}, closed plus three open frames; each entry’s time of 4 is a counter that starts at zero, so a frame holds for five updates, about 84 milliseconds at the GBA’s 59.7 hertz, played backwards to close. The graphics are mostly strips of 16 by 96 pixels, three 16-by-32 frames each (48 of the 53 files in the folder), copied into eight tiles reserved at the top of background VRAM, which is why only one door animates at a time.4 The entry sequence, Task_DoDoorWarp, is five states with no input accepted: freeze the NPCs, play the door’s sound and open it above the player, force one WALK_NORMAL_UP step into the doorway, hide the player and close the door, fade the music and the screen, load the interior.13

Interiors are bigger than outsides, and floors are maps

Interior Red/Blue Crystal Emerald (layout incl. walls)
Player’s house 1F 8 by 8 10 by 8 11 by 9
Player’s house 2F 8 by 8 8 by 6 9 by 8
Generic house 8 by 8 8 by 8 10 by 9, 11 by 8, 8 by 6
Lab 10 by 12 10 by 12 13 by 13
Pokémon Center 1F 14 by 8 10 by 8 14 by 9
Poké Mart 8 by 8 12 by 8 11 by 8
Gym 10 by 14 (Pewter) 10 by 16 (Violet) 11 by 20 (Rustboro)
Dept Store floor 20 by 8 16 by 8 18 by 8

Sizes come from the map constants in the Gen 1 and 2 projects (there in 32-pixel blocks, doubled here) and from layouts.json in Emerald.561 An Emerald Center is sixteen cells outside and 126 inside, a factor of about eight; Sprout Tower is twenty outside for three floors of 320, a factor of 48. Nobody notices, because the fade breaks the spatial comparison. Layouts are shared: one PokemonCenter_1F_Layout serves fifteen Emerald maps, Mart_Layout thirteen, House2_Layout twelve, because the map (events, people, warps, scripts) is a separate file from the layout (the cells), so one room drawing becomes many rooms.127

Every interior I rendered has the same anatomy: the top two cell rows are back wall, with windows, posters, shelves and the PC or television standing against it; the floor fills the rest; where a floor has a door to the outside, its exit mat is two cells wide along the bottom wall (Brendan’s at (8, 8) and (9, 8) in an eleven-wide room, right of centre); the stairs sit in a top corner, and an upper floor like Brendan’s has no mat at all, only the stair warp. Brendan’s ground floor is 62 walkable cells with the stair warp at (8,2) drawn as an opening in the wall band; the floor above has 54, with its return at (7,1). The Center’s counter is drawn six cells wide, and the one cell the nurse stands behind, (7, 3), carries MB_COUNTER so she is talked to across it; the Mart has MB_SHOP_SHELF cells; the house has MB_TELEVISION.271 Interiors also switch the outdoors off: of 307 indoor maps in Emerald, 287 forbid both running and cycling and 304 have no weather, and the door sequence stops the rain sound on the way in.2713

Multi-floor buildings are one map per floor, linked by stair warps whose destination index is the matching staircase on the next floor, in the same column or at the opposite end, so floors read as stacked though each is independent. Lilycove’s department store is 1F to 5F plus a rooftop and an elevator, seven maps; its 1F stairs at (16,1) land on 2F’s warp 0 at (16,1), and 2F’s up-stairs are at (13,1); the 18-by-12 rooftop is bigger than the 18-by-8 floors below it.15 Silph Co. is eleven floors plus an elevator, its floors shrinking from 30 wide to 16 as it rises; Tin Tower is nine floors and a roof with as many as eight warps on one floor; Olivine’s lighthouse drops you from paired warps on an outer ledge to the balcony of the floor below, the clearest handheld example of a building with a walkable outside.5636 The elevators are a menu, a shake and a dynamic warp: Red’s ShakeElevator scrolls the screen a pixel up and down a hundred times, two frames apart; Emerald shakes the camera every three frames for a count that grows with floors travelled, cycles the wall lights in the direction of travel, and the elevator map’s exit warps are WARP_ID_DYNAMIC, the floor you chose.3738

Diamond and Pearl, briefly

The DS games keep the 16-pixel walking grid but build the world in 3D: a tile is sixteen units, the world is 32-by-32-tile chunks on a matrix, buildings are props with a position, rotation and scale, and walkable height comes from plates with a plane equation, flat or tilted for stairs, with overlapping plates making the bridges you walk under.3940 Interiors stay separate maps entered through a door and a fade, exactly as before, and the Center keeps its fixed, recognisable exterior.

3. The SNES answer: a priority bit and a z-level

Every Super Nintendo town sits on the same piece of hardware. Mode 1 gives two sixteen-colour background layers and one four-colour layer, and every tile entry on every layer carries a priority bit beside its flips and palette; sprites slot between the high- and low-priority halves of each layer. Because each tile can be high or low, a single background contains both the floor and the things in front of you, and a character at priority 2 falls between them. A roof edge, a balcony rail or the top of a doorway is simply a high-priority tile.9 That bit, plus one or two extra layers, is how every game below draws walking behind and under, and Emerald’s three-layer routing in section 2 is the same idea rebuilt on the GBA.

Final Fantasy VI’s engine, read from the field notes of its disassembly, does upper floors as a rule rather than a layer. The party carries a z-level, upper or lower; a tile’s properties say it is passable on the lower level, on the upper, or on both, which makes it a crossing point, and a “bridge tile” is walked over on the upper level and under on the lower. Two more flags decide whether the top and bottom halves of a character draw above the high-priority background, so someone can stand with their legs behind a railing and their head in front of it, and objects can override it (“upper sprite above bg, lower sprite below bg”). Stairs are tile flags that bend movement diagonally; ladders force the sprite to face up; doors have a flag of their own.17 EarthBound keeps the same half-body idea in its surface flags, OBSCURE_LOWER_BODY and OBSCURE_UPPER_BODY, on a world that is one map of 256 by 320 blocks with its interiors drawn as indoor regions at the same scale as the street, and whose stairs exist only on the four diagonals because the art is oblique.4142

Keizou Kokubo, who drew Final Fantasy V’s scenes, named the problem in a 1992 interview: “For the maps with multiple grades/elevation levels, making the characters who walk on the upper levels look natural was a big programming challenge.”43 The towns that answered it are the ones people remember. Zozo is built up rather than out, its structures “like modern skyscrapers,” climbed by “a mixture of interior and exterior stairwells” to a room at the top; Narshe’s houses sit on cliffs joined by staircases; Figaro Castle’s walkways and towers retract when the castle submerges.444546 Chrono Trigger’s answer to a town that changes was to redraw it per era, so Truce gains buildings by 1000 AD and Leene Square stands where Truce Canyon was: the same plot of land is a different map per period.47 Terranigma made the replacement the player’s doing: Loire, Freedom and Nirlake each have a base map and two expansions, Litz and Suncoast a base and one, triggered by errands, and the VGMaps rips measure the growth, Freedom from 512 to 768 pixels wide. Director Tomoyoshi Miyazaki: “For the big levels, there are 3 different stages of resurrection, but honestly it depends on how you play.”4849 Super Mario RPG went isometric and, as Chihiro Fujioka told it, “we solved that by allowing Mario to move in all 8 directions, as opposed to the usual diagonal-only movement of isometric games.”50

What the generation established: interiors are separate, larger maps at the same scale (all but EarthBound); upper floors are a flag plus per-tile passability; walking behind is a priority bit refined to half-body flags; stairs are tile flags or warps; rooftops are walkable upper-level tiles with scripted leaps; and change over time is a whole map swapped on a flag. Tomoe Inazawa, on FF VI’s field graphics: “we tried to give the maps a sense of realism, like people actually lived there and inhabited these spaces.”51

4. The modern masters: buildings as records

Stardew Valley

Stardew is the clearest statement of a building as data, because the modders’ documentation is the game’s own format. A building has a Size, “the building’s width and height when constructed, measured in tiles”; a HumanDoor, “the position of the door that can be clicked to warp into the building interior … measured in tiles relative to the top-left corner tile”; a CollisionMap, “an ASCII text block … each character can be X (blocked) or O (passable),” defaulting to all blocked; a SortTileOffset, “a Y tile offset applied when figuring out render layering”; DrawShadow for an automatic shadow along the bottom edge; Skins for alternate appearances; and an IndoorMap, with the arrival rule, in the wiki’s note on animal doors, that “the player’s entry position after entering the building will be 1 tile North of the first warp in the location’s warp list.”52 The vanilla footprints are small and tidy, Coop 6 by 3, Barn 7 by 4, Shed 7 by 3, Silo 3 by 3, Fish Pond 5 by 5, Cabin 5 by 3, and the footprint is the ground rectangle: the sprite rises above it.53 The farmhouse is “a 9×5 rectangle” outside and “a 10×7 rectangle” inside before any upgrade; the 7-by-3 Shed holds an 11-by-9 room and the Big Shed 17 by 12.5414

Its maps are layered back to front as Back (terrain), Buildings (walls: any tiles placed on this layer “will act like a wall unless the tile property has a ‘Passable’ ‘T’,” the inner quotation marks the wiki’s own), Paths, Front (“drawn on top of things behind them, like most trees”) and AlwaysFront (“always drawn on top of other layers as well as the player”). A roof’s edge sits on Front so you walk behind it; the wall sits on Buildings. Night is a tile swap: NightTiles “changes the tile after 7pm … mostly used for lamps,” with DayTiles to swap back at six, while WindowLight is a light source that is “only lit during the day when it is not raining (like the light coming through a window),” which is to say exterior windows at night are tiles and interior window light is daylight coming in.10 Doors are timed independently of their counters: Pierre’s shop building is open 9 to 9 but the counter 9 to 5, closed Wednesdays until the Community Center is restored.55

The upgrades are a loop of money, materials and days: 10,000g and 450 wood for the kitchen, 65,000g and 100 hardwood for two more rooms, 100,000g for the cellar, three days each, 175,000g in all; after the second upgrade Robin offers renovations, nine of them in 1.6 (four new in that version), and “Renovations are completed instantly.”54 Eric Barone, on why the town works: the villagers “all have fully fleshed-out lives and schedules that they go about every day.”56

Animal Crossing: New Horizons

Animal Crossing grows a house from a 4-by-4 tent through seven loans, 98,000 Bells for the house itself (a 6-by-6 room) to 2,498,000 for the basement (10 by 6), 5,696,000 Bells in all by my arithmetic, interest-free and without a deadline; “a maximum of 150 items can be placed in each room”; and, unlike the earlier games, “only the main room can be expanded in size, as all other rooms have a fixed size.”57 The exterior is a reward ladder: the mailbox and roof unlock with the right room, the door with the left, full style changes only after the second floor, each change for 5,000 Bells until every loan is paid.57 Furniture sits on a grid from 1 by 0.5 to 3 by 3, pushed in half-tile steps, with a separate grid for the walls and surfaces that hold other items.58 The weekly Happy Home Academy letter scores the room, and a first S ranking earns a one-time 1,000-mile achievement; the bonuses reward series, complete sets and colour unity.59 Aya Kyogoku, its director: “I’m always about paying back the loans, so I want to try to make as many bells as possible.”60

Terraria, Habbo, Mistria, Moonlighter

Terraria defines validity rather than appearance: a house must be enclosed by a frame of blocks, platforms or doors, backed by player-placed walls, hold a light, a flat surface and a comfort item, have an entrance, and fall between minimum and maximum sizes; the housing menu names the failing rule, and “NPC happiness is not affected by a house’s appearance or decoration.”61 Habbo’s room is a floor plan plus a height grid: the open-source Nitro client’s constants put the plan at up to 64 tiles an axis with a 27-step height scheme (x0123456789abcdefghijklmnopq), and the wiki counts 29 room layouts.6263 Fields of Mistria upgrades the house four times, from a 13-by-8 room to 17 by 12, then rooms to the left and right, then “an entire second story” gated by a story quest, instantly.64 Moonlighter’s shop “outside appearance remains the same at all expansions”; inside, the sales tables go from four to sixteen and the showcases from none to four.65

CrossCode, Sea of Stars, Eastward

CrossCode is the modern game that took height seriously in a 2D engine. Every entity has a z, maps declare discrete levels with pixel heights (32 pixels a step by default), each level has its own collision layer, and the map editor draws each level’s wall face as height divided by sixteen tile rows under every platform edge. There is no jump button: “you just move toward an edge to fall down or jump to another platform, or you move toward a wall to jump up,” and the game “jumps at the last possible moment in front of the gap.” Radical Fish’s own summary is the line to keep: “height is definitely an important part of CrossCode, jumping is not,” and free jumping was rejected because “it’s often hard to see the actual position of entities.”186667 Sea of Stars went the other way, to seamless traversal: Thierry Boulanger, “if it looks like you can touch something, you can actually go there. There are no invisible walls that don’t make sense,” with dynamic lighting that “gave volume to the environment” and leads the eye to doors.68 Eastward grew from one building, a sketch of “a weird monster dormitory building resembling Hong Kong’s Kowloon Walled City,” and reads height from tall façades, walkways over the player and light rather than from any elevation system its makers have described.69

What they agree on

Interiors are separate maps larger than the building that holds them. Walking behind is a layer above the player, named Front, AlwaysFront, BG1 or a star in a tile flag. A door is a cell with a destination, with a ceremony in front of the fade. Upgrades cost money and materials; they change the outside in Stardew and Animal Crossing and only the inside in Moonlighter, and they take days in Stardew and a night in Animal Crossing, while Moonlighter gates its later ones on clearing a dungeon and Mistria makes them instant. And variety comes from a few shapes recoloured, L and T plans, and one or two building types that break the rhythm, which is Slynyrd’s cityscape rule as well: “duplicate some buildings and make small variations, rather than pixel each building from the ground up.”70

5. The craft, as rules with numbers

The tutorials give the order of work and the handhelds give the proportions; the forge now follows both. These are the rules it draws by, each with where it came from. The figure they are sized for is the 26-pixel collector of the previous post, on 16-pixel tiles; Emerald’s are for a 16-by-32 player, so the ratios are kept rather than the pixels.71

Element Rule Source
Storey two tiles, 32 pixels of wall per storey every Emerald house, Center and Mart has two wall rows
Roof two rows for a building four or fewer tiles wide, three from five; the top row is the ridge the forge’s choice, after Littleroot’s 5-by-5 houses and the 7-by-5 lab (three) and Oldale’s 4-by-4 (two); Emerald’s primary kit keeps two at five wide
Footprint height roof rows plus two per storey a 5-by-5 house is 3 + 2; a two-storey shop on a five-wide plan is 3 + 4 = 7 rows
Eaves the roof overhangs the wall by two pixels on each side; a three-pixel dark band under its bottom edge on the wall Medeiros, “make the roof slightly bigger”; Slynyrd, shadows “under eaves”
Door one tile wide, 16 by 32 pixels over both ground-storey rows, three frames Emerald’s size-1 door and its 16-by-96 strip
Windows one per bay on each storey’s upper course, never where the door is; a lit variant for night Stardew’s NightTiles; Emerald’s 0x0B upper-wall-with-window
Outline a darker shade of the adjacent colour, not ink, and only where the silhouette meets the ground Slynyrd, “a slightly darker color than the adjacent pixels”
Ground shadow at most a tile, to the right and a pixel or two below, and never on the building: it belongs to the ground layer (not drawn yet; the lawn has no shadow layer) Slynyrd, “No matter how tall the rock wall all shadows are the same length”
Bricks lengths of 15, 7, 3 or 1 pixels with one-pixel grout on a 16-pixel tile; other lengths break the repeat Slynyrd, bricks lesson
Variety few roof shapes, many roof colours by palette swap; most houses one size, one civic building that differs in shape and colour where the paths meet Crystal’s five shapes and 27 colour pairs; Littleroot’s two 5-by-5 houses against one 7-by-5 lab

Sources for the table: Emerald’s measurements in section 2;27 Medeiros;24 Slynyrd’s city builder, bricks and tiles lessons;257273 Stardew’s map properties;10 Crystal’s roof tables.307

Two of those rules deserve a sentence each. The outline rule is the one that separates buildings from characters in this world: the collector keeps an ink outline because a person has to read at a glance against anything, but a building drawn in ink reads as a cartoon stuck onto the lawn, so its edge is each ramp’s own darker step, and only where the forge’s finishing pass finds the silhouette meeting empty ground. And the shadow rule is about layers rather than drawing: a shadow cast on the lawn by a house is a property of the lawn, so the composer never paints one on the building; the eave’s shadow, which falls on the building’s own wall, is the building’s.

Materials are 16-by-16 tiles that repeat both ways, drawn from the forge’s existing hue-shifted ramps with three or four stops: plaster (a flat light fill with a one-pixel course line every eight pixels and a few two-pixel chips, the eave band doing the work), brick (seven-pixel bricks with one-pixel grout in four-pixel courses, staggered by half a brick), stone (eight-by-eight blocks in eight-pixel courses, seven of face and one of seam, with a lit top-left edge, for the plinth and for civic buildings), clay tile (four-pixel rows of four-pixel scallops with a highlight on each scallop’s top left, Littleroot’s roofs), shingle (staggered four-pixel rows, each row’s bottom pixel a shade darker, the ridge lighter) and slate (flat bands with a one-pixel light line, for the hall and anything official). Roof colours are palette swaps on the drawn shape, Crystal’s way: clay in red, green, blue and violet; shingle in brown, grey and green; slate in violet, blue and grey.19

Trims are stamps placed by rule, never tiles and never over an opening: a chimney on the ridge, a hanging sign on a bracket beside a shop’s door, a striped awning over a shop’s upper course, a window box under a window, a sconce either side of the hall’s door that lights at night. Each is anchored to a bay in the building’s record (chimney@3, box@0, awning@2), so a town’s list of buildings is text.

Why sixteen shapes are enough for a roof or a wall, when a floor needs the 47-tile blob (48 in RPG Maker’s table, with the representative pattern the palette shows): a wall face only has to know whether its left, right, top and bottom neighbours are the same wall, four bits, whereas a floor’s corners need the corner-aware blob. RPG Maker encodes exactly this split. Its Tilemap.js draws on a 48-pixel tile from 24-pixel quarters, with a FLOOR_AUTOTILE_TABLE of 48 shapes read from a block two tiles by three and a WALL_AUTOTILE_TABLE of sixteen read from two by two; the A3 sheet, exterior roofs and wall sides, uses only the wall table, in alternating rows of eight roofs over eight wall sides.8 Boris the Brave’s roundup notes that the quarter-tile method can fill in for the blob “with only 20 subtiles (plus the empty tile, unchanged)” and is “used by the RPG Maker VX community.”74 Our composer goes one step plainer still: a rectangle on a 16-pixel grid needs only a nine-slice, ridge, body and eave rows by left, middle and right ends, and the kit’s roof is exactly that, nine pieces per material.

Interiors have conventions of their own, read from the Emerald rooms and from Slynyrd’s interiors lesson. The wall band is two tiles tall in every Emerald interior I rendered, a cornice course above and a face below where the windows, shelf backs and the stair opening go.27 Rooms are ten to fourteen wide, which a phone shows whole at a useful size (an iPhone 18 Pro Max gives thirteen tiles six device pixels per texel and our fourteen five), and taller than the shell outside. Furniture is “designed within a 16x16px tile map, so they mesh with the environment”; stairs are “often … simplified to a single tile in top down games of this style … for the flexibility of level design,” and “the railing tiles will need to go on a separate layer above the stair tiles.”23 The floor is boards across or stone, with one rug as a nine-slice (Stardew’s rugs run from 2 by 1 to 3 by 2); back-wall pieces stand on the first floor row and rise into the band; the exit is two cells wide at the bottom, as the Emerald ground floors I rendered have it (the Battle Frontier has a three-wide one, and Brendan’s sits right of centre).7527

6. The Apple way: a depth, a mesh, a door and a floor

The first post in this series set out the engine: a RealityKit scene used as a 2D renderer, an orthographic camera, the ground as one mesh of textured quads at z 0, the woods beyond the edge at −1, each walker a quad at a depth that rises as it walks down the screen, every material an UnlitMaterial with opacityThreshold set so that pixels are either drawn or discarded.76 Buildings needed three things that engine did not have: a layer above walkers, a way to sort a thing that is taller than its footprint, and warps as data rather than letters. None of them needed a new technique; all of them needed one rule.

One depth rule for walkers and objects

The rule is that anything standing in the world is drawn at the depth of the row its feet are on, and lower on the map is nearer the camera:

/// How far forward anything standing at a world height is drawn:
/// lower on the map is nearer the camera. Walkers and objects share
/// it, so an object drawn at its base row's depth, less a hair, is in
/// front of a walker one row up and behind a walker on its own row.
func depth(atWorldY y: Float) -> Float {
    1 + (1 - y / (Float(height) * Self.tileSize)) * 0.5
}

/// The depth an object's cells are drawn at: its base row's, less a hair.
func depth(ofObjectWithBase base: Int) -> Float {
    depth(atWorldY: centre([0, base]).y) - 0.001
}

Walkers already used the first function. The second gives a building a single depth, that of its bottom row less a thousandth, so a collector standing on the door’s row (the base row) is in front of the wall, and one standing a row up, behind the ridge, is hidden by it. Rows on a 30-row map are about 0.017 apart in z, so a hair of 0.001 never crosses one. This is Stardew’s SortTileOffset with the offset made explicit and Emerald’s walkable top roof row without the walkable row: our whole footprint is blocked except the door, which section 7 lists under what this build does not do yet.523

Every object, every building, tree canopy and the kiosk’s top, goes into one mesh, each cell’s quad at its object’s depth, and the depth buffer decides who is in front:

/// Everything that stands up, as one mesh: each object's cells at its
/// own depth, so the depth buffer, not draw order, decides who is in
/// front. Nil when the drawing has nothing standing.
@MainActor
func objectsMesh() throws -> MeshResource? {
    guard let objects = drawing?.objects, !objects.isEmpty else { return nil }
    var cells: [(x: Int, y: Int, index: Int, z: Float)] = []
    for object in objects {
        let z = depth(ofObjectWithBase: object.base)
        for cell in object.cells {
            cells.append((cell.x, cell.y, cell.index, z))
        }
    }
    return try mesh(cells: cells, name: "objects")
}

mesh(cells:name:) is the ground’s mesh builder generalised by one parameter, a z per cell, writing a MeshDescriptor of positions, normals and texture coordinates into the town’s atlas. The ground is built through the same function at z 0. That is the whole layer system: 27 objects, 215 cells, one draw call, no sort groups. It works because every material is cut-out: opacityThreshold makes RealityKit discard pixels below the threshold and render the rest fully opaque, which means every drawn pixel writes depth, and depth, not draw order, decides occlusion.11 ModelSortGroupComponent, which lets you order models “even if that order contradicts their relative positions to each other in the scene,” is the tool for translucent things and is not needed here; Apple’s own discussion of it is the one to read if a fading roof ever is.77

Windows that do not darken at dusk

The rig tints every material by the hour’s light, which is right for a wall and wrong for a lit window: a lit pane must not take the dusk tint. So the nineteen windows and the hall’s two sconces are their own mesh, each cell at its building’s depth plus half a hair, with a white (untinted) material, and the entity is disabled until the lamps come on. The forge exports for each of those cells only the pixels that change when it is lit, the pane and the flame, so the wall around a window keeps the hour’s tint and no lighter square shows through:

// The windows lit, over the buildings, in the art's own colours
// whatever the hour: off until the lamps come on.
if let mesh = try map.windowsMesh() {
    let windows = ModelEntity(mesh: mesh, materials: [tiles.material(light: .white)])
    windows.isEnabled = false
    root.addChild(windows)
    self.windows = windows
}

and in the light pass, windows?.isEnabled = lampsOn. That is Stardew’s NightTiles done as an entity rather than a tile swap, and it is why the forge exports the lit pixels of every window and sconce in a windows list beside objects.10 The capture below is the walk that keeps time, at night.

The town at night on an iPhone 18 Pro Max, an unscaled capture: the hall's two windows and two sconces lit in the art's own gold over the dusk-tinted stone, two houses' windows lit at the bottom of the screen, the fountain in the square between them, one collector on the square and one on the path.

Doors

A door is a 48-by-32 sprite sheet of three frames, closed, half and open, drawn by the forge beside the kit. When the walker’s step lands on a warp cell with a door, the rig puts a 16-by-32 quad over the door cell and the one above it, at the door row’s depth less half a hair (half a hair in front of the building’s cells, behind a collector on that row), shows the half frame, swaps to the open frame after 70 milliseconds, and removes the quad 1.4 seconds later; the warp fires 220 milliseconds after the door starts to open, so the door is seen swinging open under the collector before the room appears. Emerald opens the door first and then walks the player in, and holds each frame about 84 milliseconds; ours opens on arrival, at 70, which section 7 lists among the gaps.478 The frame swap is a material swap on the quad, the way the water ripples and the walkers animate: textureCoordinateTransform picks a column of the sheet.79

Warps as data, and floors swapped in place

The letter map still says where feet may go, and the server reads nothing else: building cells are X, blocked, and door cells keep their trigger letters so an older server would still treat them as doors. But what a door leads to is a warp record in the export, {"at": [5, 5], "to": "room:me", "door": "door-wood"}, and the stage resolves to when the walker arrives: room:me is your house, room:npc-moss is Moss’s, when Moss is in the room, and hall is the show. Inside, the interiors carry their own warps, leave on the two door cells and floor:room-up on the stair opening in the back wall.78

Floors are separate maps, Emerald’s way, but a staircase is not “back,” so a stair does not push a new screen. The house view holds its floors and a @State naming the current one; a stair warp swaps the map in place, PlazaStage(map: floor.map, arrival: arrival …).id(floor.name), so SwiftUI tears down one RealityView and builds the next, arriving on the new floor’s stairs spot. Each interior’s JSON names its spots (arrival, stairs, shelf, showcase, desk, the wall board, pedestals), so the furniture the app dresses from the collection, binders on the shelf, three favourites in the case, ribbons and stamps and pins on the board, is placed by the map rather than by Swift literals, and a redrawn room cannot leave a shelf hanging in the air.78

What it costs

The town atlas grew from 107 tiles in a 256-by-112 image after the variety pass (56 in a 256-by-64 image before it), four water frames each, to a 16-by-15 sheet of 233 tiles, 256 by 240, with its four frames: the seven buildings’ 195 cells dedupe to 131 distinct tiles (215 and 133 with the tree canopies and the kiosk’s top), near Emerald’s nineteen distinct of 25 for a house and 22 of 35 for the lab; the 21 lit cells dedupe to two, a window’s glow and a sconce’s. The room’s atlas is 256 by 32, the hall’s 256 by 16, the door sheet 48 by 32. The objects are one mesh and the lights another, with no sorting pass on the CPU; what the scene holds is the woods, the ground, the objects, the lights, a walker and its shadow each, a halo per lamp, and a door while it opens.19 The composer’s timings in the previous post are that post’s measurements, and nothing here touches the walkers; what the two new meshes cost the GPU on a phone is not profiled yet.71

7. The case study: from one tile to a town with buildings

The diagnosis

The town the first post ended with was 40 by 30 tiles with seven house fronts on it, and each front was one 16-by-16 cell drawn whole by a function called entrance(): five pixels of roof, nine of wall, a door, done. The show hall was two cells. Nothing in the world was drawn above a walker; the canopy of every tree and the top of the kiosk were ground pixels, safe only because their cells were not walkable. A house taller than one tile broke that the moment anyone walked behind it, and the measured canon says that is exactly where people walk: Emerald’s roofs have a walkable top row, Stardew’s roof edges sit on Front. The interiors were Swift literals, a 12-by-10 room and a 14-by-10 hall with one-tile walls, dressed by coordinates typed into the view.7678

So the order of work was the one the engine dossier set: a depth rule shared by walkers and objects; an objects layer proved first with the art that already existed (the forge stopped baking canopies and the kiosk’s top into the ground and exported them as objects, which changed nothing anyone could see and everything underneath); then the kit, the composer and the buildings; then warps; then doors; then interiors from the forge; then night; then the stair.

The kit

kit.py draws 58 pieces on the palette’s ramps: the roof and wall tiles by rule, loops that lay courses, seams and lit edges, the awning the same way, and the door, the windows and the other trims as text grids, the way the tiles and the people are drawn, so a stamp is a few lines of letters and a key. Roofs are a nine-slice per material, ridge, body and eave rows by left, middle and right ends, in clay, shingle and slate, with a salt so a long roof’s middle tiles are not identical. Walls are two courses per storey, upper and lower, with ends, in plaster, brick and stone, the eave’s two-pixel overhang inset into the upper course. The door is 16 by 32 in three frames. The window and the shop window each have a lit variant. The trims are a chimney, a hanging sign, an awning two tiles wide, a window box and a sconce, lit and unlit. Roof colours are palette swaps, Sprite.replaced(mapping) on the material’s ramp: clay in red, green, blue and violet, shingle in brown, grey and green, slate in violet, blue and grey. The contact sheet in section 1 is the kit’s own check, every piece at four times with its name, and it is where a wrong light or a missing end shows first.19

The composer

buildings.py takes a spec, a width in tiles, a storey count, a wall, a roof and a colour, the door’s bay, the window bays and the trims, and returns one picture and one record. The rules are the ones in section 5: roof rows two up to four wide and three from five (Littleroot’s depth, chosen over the primary kit’s two); the façade two courses a storey, ends at the ends; the door stamped over both ground-storey rows after the walls, windows on each storey’s upper course and never where the door is, trims last and never over an opening. Finishing runs once over the whole picture: the eave’s shadow band on the wall under the roof, three rows, and an outline in each ramp’s darker shade where the silhouette meets nothing, two things that only work on a composed building and not on tiles. Then the picture is cut into 16-by-16 cells for the town’s atlas, which dedupes them. The record says what the map needs: the cells and the atlas index each takes, the base row it sorts by, the door cell, the blocked cells, and the windows for the lights.19

A street render of five sample buildings by day and by night is the composer’s own check and the figure in section 2. The night half is drawn with the lit window and sconce variants in place under a flat dusk tint over the whole picture; the app does the lighting better, by enabling an untinted mesh of the lit pixels over the tinted buildings.

The town

town.py was rewritten around a list. Seven buildings, each a line of text: your house at [3, 1], five wide, plaster under red clay, a chimney and a window box; Moss’s beside it under green clay; the hall at [19, 1], seven wide, stone under blue slate, a sconce either side of the door; Prism’s at [29, 1], brick under brown shingle; Comet’s shop at [4, 14], two storeys of brick under violet slate with an awning and a sign; Dawn’s at [13, 16] under violet clay; and a spare house at [22, 16] under blue clay that leads to your room too. The town’s letters are then drawn by rule onto a lawn around them: the fence, the loop path with a spoke to every door, the inner loop round the stone square with its fountain, the park with the pond, the furniture. The export is version 2 of the town’s JSON: beside the letters and the ground, objects (id, base row, [x, y, index] cells), warps (at, to, door) and windows ([x, y, litIndex]). Building cells become X in the letters, 188 of them, and the seven doors keep their trigger letters, E for a house and H for the hall. The server ships the same letters and reads nothing else: a building is blocked because X is not in its walkable set, with no change to its code.1980

The town the forge exports, with the seven buildings in place: three houses and the hall across the top, the hall where the paths meet, two houses and the two-storey shop below the square, the stone square with its fountain in the middle, the loop path with a spoke to every door, lamps, benches, the signpost, the kiosk at the right, the pond and the park below.

The forge checks what it draws before it ships: every door reachable from the arrival tile over walkable letters; no building outside the map or overlapping another building or a standing prop; every door’s destination one the app resolves (the hall, your room, or a town collector’s). town.py --ship refuses otherwise.

The town on an iPhone 18 Pro Max by day, an unscaled capture from the town walk with a scripted peer in the plaza (the status line counts six): the hall's stone front and slate roof at the top with the collector on the square below it, two houses at the bottom with their doors on the path's spokes, and Dawn, in headphones, walking the path between them.

In the engine

TileMap.Tile gained building (X, blocked) and stairs (U, walkable, a trigger by warp); the previous rule that an unknown letter is walkable grass was already gone, replaced by a fence, after the first post. WorldMap decodes the three new keys as optionals, so an export without them still decodes. TileMap.depth(atWorldY:) is the one rule from section 6; objectsMesh() and windowsMesh() build the two new meshes through mesh(cells:name:), and the ground is built through it too. PlazaRig adds the objects entity, the windows entity disabled until lampsOn, and openDoor(at:sheet:); PlazaStage checks for a warp on every step and, when the warp has a door, opens it and fires onWarp 220 milliseconds later. PlazaView resolves room:me, room:npc-… and hall; RoomView holds the floors and swaps them in place on floor: warps; HallView carries the room’s cards in with it, because pushing the hall fires the plaza’s onDisappear, which leaves the room and empties the list the hall used to read. That last one was a bug the hall walk found on the day: the show came up empty with a scripted peer in the plaza, and the probe that found it was a line appended to the hall’s status text, since nothing else the simulator logs is readable from a UI test.7820

Three interiors the forge draws, its previews at two times: your ground floor (boards, the shelf and the glass case against the back wall, the rug, the lamp, the desk, the stair opening in the back wall at the right, the two-cell door mat at the bottom), the floor upstairs (a bed at the top, the rug, the lamp, the desk at the right, the stair down at the top right, and the plain stretch of back wall at the left where the app hangs the board), and the hall (stone, eight pedestals in two rows of four, two windows in the face, the mat).

Interiors

interiors.py draws the room, the floor above and the hall as letter maps, each 14 by 11, with a two-row wall band (C the cornice, A the face, N a window in the face), boards or stone, a rug as a nine-slice by its neighbours, a shelf, a glass case, a desk, a lamp, pedestals, a bed in two cells, the place the wall board hangs, a stair opening in the face (U up, u down) and the door with its mat. Each map exports with its own atlas, its spots and its warps, and the Swift side loads them by name (Interior("room")), so Room.ground, Room.upstairs and Hall.interior are data. Upstairs is reached from the stair at [11, 1] in the back wall, the way Brendan’s house puts its stair at (8, 2); the arrival on the floor above is the cell in front of the same stair, [11, 2], so the collector comes up where they went down. Beds and pedestals are exported as objects, so a collector walks behind a pedestal and in front of it. The rooms are bigger than the shells that hold them, 14 by 11 inside a 5 by 5 or 7 by 5, as everything in section 2 is.191

Your house on an iPhone 18 Pro Max, an unscaled capture from the room walk on its fixture collection: one binder on the shelf and the glass case still empty along the back wall, the rug, the lamp, the desk, the stair opening at the top right, and the collector just in from the door.

The hall on an iPhone 18 Pro Max from the hall walk, with a scripted peer in the plaza: five cards on show on the pedestals, the town's four collectors' and the peer's, under the week's theme, the collector just in through the door at the bottom.

What was tested

178 unit tests pass in the app’s suite, among them: the town decodes whole and every door is on the lawn and leads in; the kiosk, a roof cell and the doors are where the forge put them; an object one row south of a walker sorts in front and one north behind; the room and the hall load from JSON with their spots and warps and lie within the masonry margin; the hall’s card list is taken at the door. Fourteen pass on the server, with the map’s kiosk, a roof cell and the doors asserted from plaza.json. The simulator walks on an iPhone 18 Pro Max: the plaza opens and walks; the room has its binders; the town counts five on arrival and one of its own says a line; the plaza keeps time and the windows light at night; with a scripted peer in the plaza, the company walk counts six and the hall shows five cards. TestFlight build 22 carries the buildings, with the server deployed to Railway on the same map; build 23, uploaded the same day, carries the lit sconces and the glow-only light cells of commit 0c39158, which the night capture above shows; a later commit, ba8f239, keeps a sign from covering a window and adds the forge’s prop-overlap and destination checks; the only pixels it changes are the 48 in the shop’s sign cell where the window used to show through the sign’s gaps.20

What this build does not do yet

The honest list. The top roof row is blocked, not walkable as Emerald’s is, so a collector cannot step behind the ridge; the layer would draw it correctly, the letters forbid it. There is no ground elevation: no terrace, ledge or bridge, and so none of Emerald’s transition and multi-level rules in the walk, though the engine dossier has the (cell, height) search written for when a map wants one. Tall buildings are art; the shop’s upper floor has no room behind it, and the shop has no shop interior at all, so Comet’s door leads to a house. There is no cutaway roof, and whether OpacityComponent fades a cut-out material is still untested. No building casts a shadow on the lawn; the rule for one is in section 5 and the ground has no layer for it yet. Rooms have no wallpaper or floor choice and no placeable furniture; the house is dressed from the collection and nothing else. The interiors are not networked, so a visit to Moss’s room is a visit to a map, not to Moss. The hall carries the room’s cards in with it and does not update while you are inside. No door is locked or timed, and a door opens when the collector arrives on it rather than ahead of the last step, as Emerald’s does. And nothing here has been timed on a phone.

Key Takeaways

If you draw the art

  • Fix the projection and never mix it: 45 to 60 degrees above, orthographic, a roof slab over a front wall and no side wall. The roof takes the top half or more; the eaves overhang by two pixels; the outline is each ramp’s darker step, not ink.212225
  • Use the handhelds’ proportions: two rows of wall per storey, two or three rows of roof (the forge takes three from five tiles wide, as Littleroot does; Emerald’s kit keeps two), a 16-by-32 door in three frames, windows on the upper course.274
  • Draw a kit, not buildings. A nine-slice roof, two wall courses with ends, openings as stamps, trims anchored to bays; then recolour the roof per district the way Crystal recolours it per map group.27
  • Interiors: a two-row wall band, ten to fourteen tiles wide, bigger than the shell, a two-cell mat at the bottom centre, the stair as an opening in the back wall.2723

If you build the engine

  • One depth rule for walkers and objects, each object at its base row’s depth less a hair, every material cut-out: the depth buffer sorts the town and one mesh draws it.11
  • Keep the letter map for feet and ship the warps as data: a cell, a destination, a door sheet. The server need not learn what a building is.52
  • Lit windows are a second mesh in untinted colour, enabled when the lamps come on; a door is a quad with three frames and a timer in front of the warp.104
  • Floors are maps; a stair swaps the map in place behind the same view rather than pushing a screen, because a staircase is not “back.”
  • When a walk fails and nothing the simulator logs reaches you, put the probe in the status text the test prints.

If you design the game

  • Interiors can be eight times the footprint and nobody minds, because the fade breaks the comparison.1
  • Height is three things: storeys outside are art, floors are maps, ground height is a per-cell number with “transition” and “multi-level” values. Add the third only when a map asks for it.1618
  • The town is a list of buildings as text, and the forge is the editor. Author in data; let the tooling draw.52
  • A home feels owned through a few choices with immediate feedback and a room that visibly fills; the loans, the ranking letter and the exterior unlocks are what the social games add on top.5759

FAQ

How big should a top-down pixel-art house be?

On 16-pixel tiles, four to five tiles wide and four to five tall, two rows of wall under two or three rows of roof. Pokémon Emerald’s family houses are 5 by 5, its Center and Mart 4 by 4, its lab 7 by 5; Stardew’s farmhouse is 9 by 5 on the ground with the sprite rising above. Civic buildings are wider, not taller.154

Why are game interiors bigger than the buildings they are in?

Because they are separate maps, and the fade between them breaks the spatial comparison. An Emerald Pokémon Center is sixteen cells outside and 126 inside; Stardew’s 7-by-3 Shed holds an 11-by-9 room. The interior is sized for the screen and the furniture, the exterior for the street.114

How does a character walk behind a building in a 2D engine?

With a layer drawn above the player, or a depth per object. The SNES used a priority bit per tile; the GBA routes each metatile’s two halves to three background layers by type, and Emerald makes the roof’s top row walkable so the player steps under it; Stardew puts the roof’s edge on its Front layer. In RealityKit, give every object the depth of its base row and use cut-out materials, and the depth buffer sorts it.9310

How does a door animate in Pokémon?

Emerald holds 53 door entries (one unused), most of them a 16-by-96 strip of three 16-by-32 frames, and plays closed plus three frames, each held five updates, about 84 milliseconds, over the door cell and the one above; then the player is walked one forced step in, the door closes, and the screen fades.413

Should the floors of a building be separate maps?

Yes, as Pokémon and Stardew do it: one map per floor, joined by stair warps that land on the matching staircase. The art is cheap, the rules are simple, and nothing has to be drawn over anything else. Reserve per-cell elevation for terraces, ledges and bridges on the ground, where Emerald uses a transition value for stairs and a multi-level value for a bridge cell.1516

How do you light windows at night in a tile game?

Stardew swaps tiles after 7 p.m. with NightTiles and keeps WindowLight for daylight coming in; Crystal recolours whole roofs with a night palette. In our engine the lit windows are a second mesh in untinted colour, enabled when the lamps come on, so they ignore the dusk tint the rest of the world takes.107

Related on this site: Pixel-Art Worlds on iPhone is the first guide in this series, with the tile systems, the palette rules and the RealityKit recipe the buildings stand on; Pixel-Art People: Characters and a Creator on iPhone is the second, the 26-pixel collector these doors are sized for; iPhone Duo for Developers derives the inner display’s point size that the interiors’ six-pixels-per-texel floor is measured against; Prepare Your App for iPhone Duo covers the fold the world keeps clear; RealityKit’s Spatial Mental Model explains the entity and component system the rig is built on; Metal 4 Essentials is where the depth buffer that sorts the town is explained from the GPU’s side.

Sources


  1. pret, pokeemerald/data/layouts/layouts.json (PokemonCenter_1F_Layout 14 by 9, Mart_Layout 11 by 8, LittlerootTown_BrendansHouse_1F_Layout 11 by 9 and _2F 9 by 8, LittlerootTown_ProfessorBirchsLab_Layout 13 by 13) and the town layouts’ map.bin files, accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/data/layouts/layouts.json. Exterior footprints measured by the author as described in note 27. ↩↩↩↩↩↩↩↩↩↩↩↩

  2. pret, pokeemerald/data/tilesets/primary/general/ (metatiles.bin, metatile_attributes.bin, tiles.png), the metatile ids read from data/layouts/PetalburgCity/map.bin (the four-wide houses at rows 16 and 21, the five-wide one at row 2), accessed October 3, 2026, https://github.com/pret/pokeemerald/tree/master/data/tilesets/primary/general. ↩↩↩↩↩

  3. pret, pokeemerald/include/global.fieldmap.h (MAPGRID_METATILE_ID_MASK 0x03FF, MAPGRID_COLLISION_MASK 0x0C00, MAPGRID_ELEVATION_MASK 0xF000, the METATILE_LAYER_TYPE_NORMAL/COVERED/SPLIT attributes), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/include/global.fieldmap.h. The walkable top roof row (collision 0, elevation 3, layer type Normal) was read from the Littleroot, Oldale and Petalburg map.bin files by the author, October 3, 2026. ↩↩↩↩↩↩

  4. pret, pokeemerald/src/field_door.c (sDoorAnimGraphicsTable, the frame lists {4, -1}, {4, 0}, {4, 0x100}, {4, 0x200}, NUM_TILES_TOTAL - 8) and pokeemerald/graphics/door_anims/ (53 PNG files, 48 of them 16 by 96 pixels), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/src/field_door.c and https://github.com/pret/pokeemerald/tree/master/graphics/door_anims. The counts (53 table entries, one of them the unused Battle Frontier door: 25 DOOR_SOUND_NORMAL, 27 DOOR_SOUND_SLIDING, 1 DOOR_SOUND_ARENA; 52 of size 1 and one of size 2) are the author’s tally of the table, October 3, 2026. The 84-millisecond frame is AnimateDoorFrame: it draws at counter 0, advances when the counter equals the entry’s time of 4, so each frame spans five updates at 59.7 hertz. ↩↩↩↩↩↩↩

  5. pret, pokered/constants/map_constants.asm (sizes in 32-pixel blocks, doubled in the text), pokered/maps/PalletTown.blk, pokered/maps/ViridianCity.blk and pokered/data/maps/objects/PalletTown.asm (warp_event 5, 5, REDS_HOUSE_1F, 1), accessed October 3, 2026, https://github.com/pret/pokered/blob/master/constants/map_constants.asm. ↩↩↩↩

  6. pret, pokecrystal/constants/map_constants.asm (Sprout Tower floors 20 by 16, Tin Tower, Olivine Lighthouse, Goldenrod Dept Store), pokecrystal/maps/NewBarkTown.blk and VioletCity.blk, accessed October 3, 2026, https://github.com/pret/pokecrystal/blob/master/constants/map_constants.asm. ↩↩↩↩↩

  7. pret, pokecrystal/gfx/tilesets/roofs.pal (two RGB555 colours per map group, “morn/day” and “nite” rows, 27 entries for NUM_MAP_GROUPS 26 with group 0 marked unused; Olivine RGB 14,17,31, 07,11,15 and RGB 09,09,17, 05,07,13) and pokecrystal/engine/gfx/color.asm (PAL_BG_ROOF, written for TOWN and ROUTE environments), accessed October 3, 2026, https://github.com/pret/pokecrystal/blob/master/gfx/tilesets/roofs.pal and https://github.com/pret/pokecrystal/blob/master/engine/gfx/color.asm. ↩↩↩↩↩

  8. RPG Maker MV core script, rpg_core/Tilemap.js (this._tileWidth = 48, FLOOR_AUTOTILE_TABLE of 48 entries, WALL_AUTOTILE_TABLE of 16, isRoofTile and isWallSideTile on A3), accessed October 3, 2026, https://github.com/rpgtkoolmv/corescript/blob/master/js/rpg_core/Tilemap.js. ↩↩

  9. SNESdev Wiki, “Tilemaps” (the 16-bit tile entry with its priority bit) and “Backgrounds” (Mode 1’s two 4bpp layers and one 2bpp layer), accessed October 3, 2026, https://snes.nesdev.org/wiki/Tilemaps and https://snes.nesdev.org/wiki/Backgrounds. ↩↩↩

  10. Stardew Valley Wiki, “Modding:Maps” (the Back, Buildings, Paths, Front and AlwaysFront layers; NightTiles, DayTiles, WindowLight, Light, AmbientLight; TouchAction Warp, Action Door, Doors), accessed October 3, 2026, https://stardewvalleywiki.com/Modding:Maps. ↩↩↩↩↩↩↩

  11. Apple Developer Documentation, “UnlitMaterial.opacityThreshold” (“A threshold below which RealityKit ignores opacity”; pixels with opacity below it are discarded and the rest rendered fully opaque), accessed October 3, 2026, https://developer.apple.com/documentation/realitykit/unlitmaterial/opacitythreshold. ↩↩↩

  12. Author’s count, October 3, 2026, over every data/maps/*/map.json in pret’s pokeemerald at commit 731ad5b: 1,306 warp events whose cell lies inside the layout (518 map.json files), each looked up in its layout’s map.bin and then in the primary or secondary metatile_attributes.bin for the behaviour byte: MB_SOUTH_ARROW_WARP 512, MB_NON_ANIMATED_DOOR 217, MB_ANIMATED_DOOR 195, MB_LADDER 100, MB_NORMAL 94, MB_AQUA_HIDEOUT_WARP 28, MB_MOSSDEEP_GYM_WARP 22, MB_LAVARIDGE_GYM_1F_WARP 20, MB_LAVARIDGE_GYM_B1F_WARP 15, MB_UP_ESCALATOR 17, MB_DOWN_ESCALATOR 17, MB_EAST_ARROW_WARP 9, MB_CAVE 8, MB_MT_PYRE_HOLE 8, MB_WATER_SOUTH_ARROW_WARP 8, MB_TRICK_HOUSE_PUZZLE_DOOR 8, MB_NORTH_ARROW_WARP 6, MB_WATER_DOOR 6, and sixteen more across ten behaviours (MB_DEEP_WATER 3, MB_MOUNTAIN_TOP 3, MB_DEEP_SOUTH_WARP 2, MB_INDOOR_ENCOUNTER 2, and one each of MB_SKY_PILLAR_CLOSED_DOOR, MB_SHOAL_CAVE_ENTRANCE, MB_BRIDGE_OVER_OCEAN, MB_STAIRS_OUTSIDE_ABANDONED_SHIP, MB_ICE and MB_OCEAN_WATER), 1,306 in all. The behaviour constants are in include/constants/metatile_behaviors.h, https://github.com/pret/pokeemerald/blob/master/include/constants/metatile_behaviors.h. ↩↩

  13. pret, pokeemerald/src/field_screen_effect.c (Task_DoDoorWarp’s states and PlayRainStoppingSoundEffect), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/src/field_screen_effect.c. ↩↩↩↩

  14. Stardew Valley Wiki, “Shed” (7 by 3 outside; 11 by 9 inside, 17 by 12 as the Big Shed), accessed October 3, 2026, https://stardewvalleywiki.com/Shed. ↩↩↩

  15. pret, pokeemerald/data/maps/LilycoveCity_DepartmentStore_1F/map.json and the 2F to Rooftop maps (stair warps at (16,1) and (13,1); LilycoveCity_DepartmentStoreRooftop_Layout 18 by 12 in layouts.json), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/LilycoveCity_DepartmentStore_1F/map.json. ↩↩↩

  16. Porymap manual, “Editing Map Collisions” (“Elevation is how the game determines whether or not an object is on the same level as something else”; the Transition and Multi-Level types, the latter “remembers the player’s previous elevation and maintains that elevation”; Route 119’s bridge), accessed October 3, 2026, https://huderlem.github.io/porymap/manual/editing-map-collisions.html. ↩↩↩↩

  17. everything8215, ff6/notes/field-ram.txt (the party’s z-level; “Passable on lower z-level,” “Passable on upper z-level,” “Bridge tile”; the upper- and lower-sprite priority flags; “Tile uses up/left movement,” “Always Face Up,” “Door Tile”), accessed October 3, 2026, https://github.com/everything8215/ff6/blob/main/notes/field-ram.txt. ↩↩

  18. Radical Fish Games, “CrossQuestion: Why do we use auto jump in CrossCode?,” CrossCode development blog (the quotations), accessed October 3, 2026, https://www.radicalfishgames.com/?p=1168. ↩↩↩

  19. Author’s forge, Kiradex repository at commit ba8f239 (October 3, 2026): scripts/forge/kit.py (58 pieces by pieces()), scripts/forge/buildings.py (compose, finish, ROOF_COLOURS), scripts/forge/town.py (BUILDINGS, the version-2 export), scripts/forge/interiors.py (ROOM, ROOM_UP, HALL, their spots and warps). Counts in the text (27 objects of 215 cells and 133 distinct atlas tiles, of which the seven buildings are 195 cells and 131 tiles; 188 X cells; 7 warps; 21 lit cells, 19 windows and 2 sconces, deduping to 2 tiles; a 16-by-15 atlas of 233 tiles, 256 by 240, in four frames; room and hall atlases of 256 by 32 and 256 by 16; a 48-by-32 door sheet) were read from the exported Kiradex/Resources/World/town.json, room.json, room-up.json, hall.json and their PNGs on October 3, 2026. The earlier atlases are read from the same file at commits 4f7ff35 (16 by 7, 107 tiles) and 2455b44 (56 tiles). Private repository; the dossiers behind this post are docs/research/structures/01-structures-in-the-canon.md and 02-structures-craft-and-engine.md. ↩↩↩↩↩↩↩

  20. Author’s test runs, October 3, 2026, on an iPhone 18 Pro Max simulator (iOS 27.2 runtime, Xcode 27.1 beta): xcodebuild test reporting “Test run with 178 tests in 17 suites passed” (logs build/unit22final.log and build/sconce22.log); the room and town walks (log build/ui22.log), the plaza and night walks (build/all22.log, the night walk again in build/sconce22.log after the sconces were lit); the company and hall walks with scripts/plaza_peer.py in the live plaza (logs build/walks22b.log and build/hall22f.log, the hall after its fix in commit 805f89b); the server’s suite, 14 passing, run from server/ with pytest. The captures in this post are the walks’ own screenshots. ↩↩↩

  21. Raymond Schlitter, “Pixelblog 3: Graphical Projection Part 1,” Slynyrd, March 14, 2018, accessed October 3, 2026, https://www.slynyrd.com/blog/2018/3/14/pixelblog-3-graphical-projections-1. ↩↩↩↩↩

  22. Liberated Pixel Cup, “LPC Style Guide” (“The camera angle is top-down, roughly 60 degrees. Rendering should be orthographic”), accessed October 3, 2026, https://lpc.opengameart.org/static/LPC-Style-Guide/build/styleguide.html. ↩↩

  23. Raymond Schlitter, “Pixelblog 35: Top Down Interiors,” Slynyrd, November 30, 2021, accessed October 3, 2026, https://www.slynyrd.com/blog/2021/11/30/pixelblog-35-top-down-interiors. The eight lessons read for this post are Pixelblogs 3, 14, 21, 34, 35, 43, 45 and 51. ↩↩↩

  24. Pedro Medeiros, “Top Down Houses,” pixel-art tutorial GIF, 2018, read frame by frame by the author (the capitalised captions are the GIF’s own text), accessed October 3, 2026, https://saint11.art/img/pixel-tutorials/Top-Down-Houses-Compressed.gif, listed at https://lospec.com/pixel-art-tutorials/author/pedro-medeiros. ↩↩

  25. Raymond Schlitter, “Pixelblog 51: City Builder,” Slynyrd, July 25, 2024, accessed October 3, 2026, https://www.slynyrd.com/blog/2024/7/25/pixelblog-51-city-builder. ↩↩↩↩

  26. Raymond Schlitter, “Pixelblog 34: On the Farm,” Slynyrd, October 24, 2021, accessed October 3, 2026, https://www.slynyrd.com/blog/2021/10/24/pixelblog-34-on-the-farm. ↩

  27. Author’s renders and measurements, October 3, 2026, from shallow clones of pret’s decompilations (pokered at d2704a6, pokecrystal at 5beda23, pokeemerald at 731ad5b, pokefirered at 037335f, late September 2026 masters): three scripts that draw Gen 1 blocksets plus .blk maps, Gen 2 Johto metatiles, and Gen 3 4bpp tiles with JASC palettes, metatiles.bin and map.bin, the last dumping each cell’s metatile id, collision, elevation, behaviour and layer type; a 16-pixel grid overlaid; footprints read off it and every door cross-checked against the map’s warp list. Rendered: Pallet, Viridian, New Bark, Violet, Littleroot, Oldale, Petalburg, Rustboro, and the Emerald Center, Mart, Brendan’s house and Lilycove department store interiors. Counts of layouts, indoor maps, running and weather flags and multi-level cells are scripted over every map.json and map.bin. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  28. pret, pokeemerald/src/field_camera.c (DrawMetatile’s routing of a metatile’s two layers to the three backgrounds by layer type), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/src/field_camera.c. ↩

  29. pret, pokeemerald/src/event_object_movement.c (sElevationToPriority[] = {2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 0, 0, 2}, IsElevationMismatchAt, SetObjectSubpriorityByElevation), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/src/event_object_movement.c. ↩

  30. pret, pokecrystal/engine/tilesets/mapgroup_roofs.asm (LoadMapGroupRoof, ROOF_LENGTH EQU 9, vTiles2 tile $0a), pokecrystal/data/maps/roofs.asm (the map-group-to-roof table) and pokecrystal/gfx/tilesets/roofs/ (five 24-by-24 PNGs), accessed October 3, 2026, https://github.com/pret/pokecrystal/blob/master/engine/tilesets/mapgroup_roofs.asm and https://github.com/pret/pokecrystal/blob/master/data/maps/roofs.asm. ↩↩

  31. pret, pokeemerald/include/fieldmap.h (NUM_PALS_IN_PRIMARY 6, NUM_PALS_TOTAL 13), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/include/fieldmap.h. ↩

  32. pret, pokered/engine/overworld/auto_movement.asm (PlayerStepOutFromDoor) and pokered/data/tilesets/door_tile_ids.asm, accessed October 3, 2026, https://github.com/pret/pokered/blob/master/engine/overworld/auto_movement.asm and https://github.com/pret/pokered/blob/master/data/tilesets/door_tile_ids.asm. ↩

  33. pret, pokecrystal/constants/collision_constants.asm (DOOR, LADDER, WARP_CARPET_DOWN/UP/LEFT/RIGHT), accessed October 3, 2026, https://github.com/pret/pokecrystal/blob/master/constants/collision_constants.asm. ↩

  34. pret, pokeemerald/data/maps/LittlerootTown/map.json (warp_events: {"x": 5, "y": 8, "elevation": 0, "dest_map": "MAP_LITTLEROOT_TOWN_BRENDANS_HOUSE_1F", "dest_warp_id": "1"} among three), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/LittlerootTown/map.json. ↩

  35. pret, pokeemerald/data/maps/OldaleTown_PokemonCenter_1F/map.json (two warps at (6,8) and (7,8) to Oldale’s warp 2), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/data/maps/OldaleTown_PokemonCenter_1F/map.json. ↩

  36. pret, pokecrystal/maps/OlivineLighthouse2F.asm (the paired ledge warps at 16,13 and 17,13), accessed October 3, 2026, https://github.com/pret/pokecrystal/blob/master/maps/OlivineLighthouse2F.asm. ↩

  37. pret, pokered/engine/overworld/elevator.asm (ShakeElevator, 100 scrolls of one pixel, two frames apart) and pokered/scripts/CeladonMartElevator.asm, accessed October 3, 2026, https://github.com/pret/pokered/blob/master/engine/overworld/elevator.asm. ↩

  38. pret, pokeemerald/src/field_specials.c (sElevatorTripLength[] = { 8, 16, 24, 32, 38, 46, 52, 56, 57 }, the wall lights, SE_ELEVATOR) and data/maps/LilycoveCity_DepartmentStoreElevator/map.json (whose warps’ dest_warp_id is WARP_ID_DYNAMIC, defined in include/constants/maps.h), accessed October 3, 2026, https://github.com/pret/pokeemerald/blob/master/src/field_specials.c and https://github.com/pret/pokeemerald/blob/master/data/maps/LilycoveCity_DepartmentStoreElevator/map.json. ↩

  39. pret, pokeplatinum/include/constants/field/map.h (MAP_OBJECT_TILE_SIZE, the 32-by-32-tile chunks and the map matrix) and pokeplatinum/include/overlay005/map_prop.h, accessed October 3, 2026, https://github.com/pret/pokeplatinum/blob/main/include/constants/field/map.h. ↩

  40. pret, pokeplatinum/docs/maps/bdhc.md (height plates with a plane equation; overlapping plates for bridges), accessed October 3, 2026, https://github.com/pret/pokeplatinum/blob/main/docs/maps/bdhc.md. ↩

  41. Herringway, ebsrc/include/enums.asm (the surface flags OBSCURE_LOWER_BODY, OBSCURE_UPPER_BODY, LADDER_OR_STAIRS, SOLID), accessed October 3, 2026, https://github.com/Herringway/ebsrc/blob/main/include/enums.asm. EarthBound’s 256-by-320-block world map is from CoilSnake’s MapModule.py, https://github.com/pk-hack/CoilSnake/blob/master/coilsnake/modules/eb/MapModule.py. ↩

  42. pk-hack, CoilSnake/coilsnake/model/eb/doors.py (seven door kinds; stairs on the NW, NE, SW and SE diagonals), accessed October 3, 2026, https://github.com/pk-hack/CoilSnake/blob/master/coilsnake/model/eb/doors.py. ↩

  43. shmuplations, “Final Fantasy V – 1992 Developer Interview” (English translation of a Japanese magazine interview; Keizou Kokubo’s quotation), accessed October 3, 2026, https://shmuplations.com/ffv/. ↩

  44. Final Fantasy Wiki (Fandom), “Zozo,” accessed October 3, 2026, https://finalfantasy.fandom.com/wiki/Zozo. Fan wiki; the quoted phrases describe the map, which the author checked against the VGMaps rip. ↩

  45. Final Fantasy Wiki (Fandom), “Narshe,” accessed October 3, 2026, https://finalfantasy.fandom.com/wiki/Narshe. ↩

  46. Final Fantasy Wiki (Fandom), “Figaro Castle,” accessed October 3, 2026, https://finalfantasy.fandom.com/wiki/Figaro_Castle. ↩

  47. Chrono Wiki (Fandom), “Truce” and “Leene Square,” accessed October 3, 2026, https://chrono.fandom.com/wiki/Truce and https://chrono.fandom.com/wiki/Leene_Square. ↩

  48. RPGClassics Terranigma shrine, “Town Expansion” (the three states of Loire, Freedom and Nirlake and two of Litz and Suncoast), accessed October 3, 2026, https://shrines.rpgclassics.com/snes/terranigma/expansion.shtml. The rip widths (Freedom 512 to 768 pixels) are read from the PNG headers of the VGMaps SNES atlas, https://www.vgmaps.com/Atlas/SuperNES/index.htm, by the author, October 3, 2026. ↩

  49. shmuplations, “Terranigma – 1995 Developer Interview” (Tomoyoshi Miyazaki’s quotation), accessed October 3, 2026, https://shmuplations.com/terranigma/. ↩

  50. shmuplations, “Super Mario RPG – 1995 Developer Interviews” (Chihiro Fujioka’s quotation), accessed October 3, 2026, https://shmuplations.com/supermariorpg/. ↩

  51. shmuplations, “Final Fantasy VI – 1994 Developer Interview” (Tomoe Inazawa’s quotation), accessed October 3, 2026, https://shmuplations.com/ff6/. ↩

  52. Stardew Valley Wiki, “Modding:Buildings” (Size, HumanDoor, AnimalDoor, CollisionMap, SortTileOffset, DrawShadow, Skins, IndoorMap, the arrival rule), accessed October 3, 2026, https://stardewvalleywiki.com/Modding:Buildings. ↩↩↩↩

  53. Stardew Valley Wiki, “Carpenter’s Shop” (building footprints, costs and build days), accessed October 3, 2026, https://stardewvalleywiki.com/Carpenter%27s_Shop. ↩

  54. Stardew Valley Wiki, “Farmhouse” (“The outer footprint of the farmhouse is a 9×5 rectangle. The inner footprint of the farmhouse without upgrades is a 10×7 rectangle”; the three upgrades and the 1.6 renovations), accessed October 3, 2026, https://stardewvalleywiki.com/Farmhouse. ↩↩↩

  55. Stardew Valley Wiki, “Pierre’s General Store” (hours; closed Wednesdays until the Community Center is complete), accessed October 3, 2026, https://stardewvalleywiki.com/Pierre%27s_General_Store. ↩

  56. Vulture, “How a First-time Developer Created Stardew Valley, 2016’s Best Game to Date,” March 2016 (Wayback Machine capture of November 30, 2017), accessed October 3, 2026, https://web.archive.org/web/20171130125426/http://www.vulture.com/2016/03/first-time-developer-made-stardew-valley.html. ↩

  57. Nookipedia, “Player house” (the New Horizons loan and room-size table; “A maximum of 150 items can be placed in each room”; “Only the main room can be expanded in size”; the exterior unlocks), accessed October 3, 2026, https://nookipedia.com/wiki/Player_house. The 5,696,000-Bell total is the author’s sum of the seven loans. ↩↩↩

  58. Nookipedia, “Furniture” (item sizes and half-tile placement in New Horizons), accessed October 3, 2026, https://nookipedia.com/wiki/Furniture. ↩

  59. Nookipedia, “Happy Home Academy,” accessed October 3, 2026, https://nookipedia.com/wiki/Happy_Home_Academy. ↩↩

  60. Andrew Webster, “Animal Crossing: New Horizons’ deserted island was designed to shake up the series,” The Verge, March 20, 2020, accessed October 3, 2026, https://www.theverge.com/2020/3/20/21188006/animal-crossing-new-horizons-design-interview-aya-kyogoku-hisashi-nogami. ↩

  61. Terraria Wiki (wiki.gg), “House” (the frame, wall, furniture and size rules; “NPC happiness is not affected by a house’s appearance or decoration”), accessed October 3, 2026, https://terraria.wiki.gg/wiki/House. ↩

  62. billsonnn, nitro-react/src/components/floorplan-editor/common/Constants.ts (MAX_NUM_TILE_PER_AXIS = 64, HEIGHT_SCHEME = 'x0123456789abcdefghijklmnopq'), a third-party open-source Habbo client, accessed October 3, 2026, https://github.com/billsonnn/nitro-react/blob/main/src/components/floorplan-editor/common/Constants.ts. ↩

  63. Habbo Wiki (Fandom), “Guest room” (“There are 29 total room layouts”), read through the wiki’s api.php parse endpoint, accessed October 3, 2026, https://habbo.fandom.com/wiki/Guest_room. ↩

  64. Fields of Mistria Wiki (wiki.gg), “House” (four upgrades; “An entire second story is added,” gated by the story quest “Upgrade the Carpenter’s Shop”), accessed October 3, 2026, https://fieldsofmistria.wiki.gg/wiki/House. ↩

  65. Moonlighter Wiki (Fandom), “The Moonlighter” (“outside appearance remains the same at all expansions”; the table and showcase counts), read through the wiki’s api.php parse endpoint, accessed October 3, 2026, https://moonlighter.fandom.com/wiki/The_Moonlighter. ↩

  66. Radical Fish Games, “Path Finding in CrossCode,” CrossCode development blog (the per-level navigation nodes, jump-up and jump-down edges, and the “you just move toward an edge” sentence), accessed October 3, 2026, https://www.radicalfishgames.com/?p=498. ↩

  67. CCDirectLink, crosscode-map-editor/webapp/src/app/services/height-map/height-map.service.ts (wall faces generated as level.height / 16 tile rows), accessed October 3, 2026, https://github.com/CCDirectLink/crosscode-map-editor/blob/master/webapp/src/app/services/height-map/height-map.service.ts. ↩

  68. Ben Brosofsky, “Sea of Stars Thierry Boulanger Interview: Aiming At ‘Holding Up Amidst All These Classics’,” Screen Rant, August 29, 2023 (the two quotations), accessed October 3, 2026, https://screenrant.com/sea-stars-interview-thierry-boulanger/. ↩

  69. Game Developer, “Road to the IGF: Pixpil’s Eastward” (Feng Ye’s quotation), accessed October 3, 2026, https://www.gamedeveloper.com/disciplines/road-to-the-igf-pixpil-s-i-eastward-i-. ↩

  70. Raymond Schlitter, “Pixelblog 14: Cityscapes,” Slynyrd, February 23, 2019, accessed October 3, 2026, https://www.slynyrd.com/blog/2019/2/23/pixelblog-14-cityscapes. ↩

  71. Blake Crosley, “Pixel-Art People: Characters and a Creator on iPhone,” blakecrosley.com, October 3, 2026, https://blakecrosley.com/blog/pixel-art-characters-on-iphone. ↩↩

  72. Raymond Schlitter, “Pixelblog 45: Bricks, Walls, Doors, and More,” Slynyrd, July 21, 2023, accessed October 3, 2026, https://www.slynyrd.com/blog/2023/7/21/pixelblog-45-bricks-walls-doors-and-more. ↩

  73. Raymond Schlitter, “Pixelblog 43: Top Down Tiles Part 2,” Slynyrd, March 26, 2023, accessed October 3, 2026, https://www.slynyrd.com/blog/2023/3/26/pixelblog-43-top-down-tiles-part-2. ↩

  74. Boris the Brave, “Tileset Roundup,” July 14, 2013 (“only 20 subtiles (plus the empty tile)”; “used by the RPG Maker VX community”), accessed October 3, 2026, https://www.boristhebrave.com/2013/07/14/tileset-roundup/. ↩

  75. Stardew Valley Wiki, “Furniture” (rug sizes; placement preview), accessed October 3, 2026, https://stardewvalleywiki.com/Furniture. ↩

  76. Blake Crosley, “Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew,” blakecrosley.com, October 3, 2026, https://blakecrosley.com/blog/pixel-art-world-on-iphone. ↩↩

  77. Apple Developer Documentation, “ModelSortGroupComponent” (“You can configure the group to render models in a specific order, even if that order contradicts their relative positions to each other in the scene”), accessed October 3, 2026, https://developer.apple.com/documentation/realitykit/modelsortgroupcomponent. ↩

  78. Author’s app, Kiradex repository at commit ba8f239: Kiradex/World/TileMap.swift (depth(atWorldY:), depth(ofObjectWithBase:), objectsMesh(), windowsMesh(), mesh(cells:name:), Tile.building and Tile.stairs), WorldMap.swift (the optional objects, warps and windows keys; Interior), PlazaRig.swift (the objects and windows entities, openDoor(at:sheet:) with its 70-millisecond and 1.4-second timers), PlazaStage.swift (the warp lookup on each step and the 220-millisecond delay; PlazaView’s room:/hall resolution; RoomView’s floors), HallView.swift (Entry, cards(in:)). Private repository. ↩↩↩↩↩

  79. Apple Developer Documentation, “UnlitMaterial.textureCoordinateTransform,” accessed October 3, 2026, https://developer.apple.com/documentation/realitykit/unlitmaterial/texturecoordinatetransform-swift.property. ↩

  80. Author’s server, server/app/rooms.py (WALKABLE = set("gtpTBFsEH")) and server/app/plaza.json, Kiradex repository at commit 805f89b. Private repository. ↩

相关文章

Pixel-Art People: Characters and a Creator on iPhone

How the best pixel-art games build and dress their people, measured, and how we rebuilt Kiradex's collector, its wardrob…

63 分钟阅读

Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew

How Final Fantasy VI, Pokémon and today's best pixel-art games were made, the rules that follow, and a tested recipe for…

61 分钟阅读

Designing for iPhone Duo: What Moves, Splits, and Stays

Apple's iPhone Duo design guide and three Tech Talks, read as rules: two size classes instead of poses, displacement at …

23 分钟阅读