A wall of falling code that costs nothing until you walk up to it
TERMINAL DESTINY's world is an island floating in the void. The terrain ends at a clean rim and drops as a cliff into darkness, and the server clamps every body to the map, so you can't fall off. You just stop. That's correct and it's also silent: you walk to the edge, you stop, and nothing tells you why.
The game is called Terminal. The edge of the world admitting it's a terminal felt like the right thing to say.
The budget comes first
Every new thing in this world has to survive a phone, and the rule I use is that cost must be paid only where the thing is visible. A wall around the whole map would be four planes the size of the world, alpha-blended, on screen whenever you look toward a horizon. That breaks the rule before it starts.
So the wall doesn't exist until you're near it:
- Every mesh is disabled until the camera's target is within 22 units of a rim. Everywhere else in the world it costs a few comparisons per frame.
- Near an edge, it's one plane per layer, 90 units wide, that slides along the rim with you. It isn't a wall the length of the world; it's a window onto one.
- There are two layers for depth, the back one dimmer and slower. At a corner, both axes can show at once: four draw calls, worst case.
- The glyphs are drawn once, at load, into a 256×512 tiling texture. The rain is that texture scrolling, which is one number written per frame. There is never a canvas redraw.
- It's kept out of the bloom layer. A large emissive sheet blooms into a white wash; I've lost an afternoon to that before.
Measured at a corner on both the desktop and the phone tier: 60 fps, and four extra draws in a scene of several hundred.
The code has to stay where it is
Because the plane follows you along the rim, the texture would follow you too, and the columns of code would glide along beside you like a screensaver. They should behave like something standing in the world: you walk past them.
The fix is to wind the texture back by exactly as far as the plane moved:
L.mat.emissiveTexture.uOffset = (along / TILE_W) % 1;
along is your position along the rim. The plane moves, the glyphs don't, and a column stays put as you walk past it.
Attempt one: upside down
The first screenshots looked almost right. Some of the characters looked mirrored, a few looked like nothing at all, and the bright "head" of each falling column was at the top of its trail instead of the bottom.
They were upside down. A canvas drawn into a DynamicTexture comes out flipped vertically on this kind of surface. I knew that, because there's a comment about it on the shooting stars, and I forgot it anyway. A negative vertical scale puts the canvas the right way up.
Attempt two: the code fell upward
Upright now, but comparing two frames a second apart, the bright heads had moved up. Once the texture is flipped, the scroll direction flips with it. I almost shipped that, because two still frames a second apart don't make the direction obvious.
So I stopped trusting my eyes and pinned the offset: one frame at a known value, one at that value plus a little, everything else frozen. If the glyphs move up between those two frames, the sign is wrong. They did, so I flipped it.
Attempt three: the fade climbed the wall
The wall fades out toward its top so it never shows a hard edge against the sky. The first version painted that fade into the canvas: opaque at the bottom, transparent at the top.
That fade was painted into the texture that scrolls. So it scrolled. As the rain fell, the fade crawled up the wall with it, and because the texture wraps, it eventually came back round. The seam was visible.
The fix moves the fade out of the texture and into the wall's own geometry. The curtain is a ribbon with three rows of vertices (the foot, a shoulder just above halfway, the top), and the transparency is written on the vertices:
const rows = [[0, 1], [0.55, 0.9], [1, 0]]; // [fraction of height, alpha]
Now the texture is only glyphs and can wrap forever, and the fade belongs to the wall. One subtlety: the ribbon maps its texture the opposite way round from the flat plane in attempt one, so the vertical flip had to be applied again here.
The two places it must not appear
The spawn cave. It sits in a corner of the world, cut into a mountain, and every new player starts there. It's within 22 units of two edges, and the last thing anyone needs on their first second in the game is a wall of code behind the scenery. That corner is excluded by hand.
Between you and your own hero. If you stand at the rim facing inward, the camera swings out over the void, and the curtain ends up between the eye and the body. Painting code over your own character is wrong, so when the camera is outside the rim the wall thins to a ghost of itself.
What I'd do differently
Attempts one and two were the same mistake twice: reading a direction off two still frames. Screenshots are good at what is on screen and bad at which way it's moving. If I'd pinned the scroll from the start I'd have saved a round trip.
And the fade crawling up the wall is a rule I'll keep: anything that animates by scrolling must not carry anything that shouldn't scroll. Fades, masks and vignettes belong to the surface, not to the pattern sliding across it.