SCROLLING

As mentioned in my notional "Resolutions 2024", I'm working on porting an isometric arcade adventure game to Amiga. The past month or so has had me round the houses, and round the bend a little, adding scrolling and large rooms to the engine.

The initial plan was to use three screen buffers worth of memory to support double buffered y-scrolling without a coppersplit (I didn't want to deal with a coppersplit). The buffers would chase each other down the memory space following the y-scroll position, sharing the allocated memory but never quite touching.

However, like any great plan, and most terrible ones, it did not survive contact with the enemy (reality). After a few hopeful "IT WORKS!" moments, I discovered there were a lot of edge (ha!) cases where, depending on scroll direction and which buffer was updated first, the background buffer updates would overwrite memory currently belonging to the displayed buffer causing single frame visual glitches. I went deep in to hunting these down and finding solutions, but eventually came to the conclusion it was always going to be too complex and brittle for my liking. There was potentially a hacky fix of blanking out some pixels around the borders of the displayed area to hide the glitches ... but eww.

I switched to two entirely separate buffers using four screens worth of memory. That works out at about 200Kb, so this is going to be an ECS game requiring 1Mb of chipram, and probably 1Mb of other RAM as well. It's hefty for an Amiga game, but this isn't the 90's (can you believe that was ten years ago now!) and not many active Amiga users will have trouble meeting that specifciation today.

The other challenge was making the edge update rendering fast enough. I'm aiming at 25fps but the initial prototype wasn't even hitting this.

The challenge was efficiently finding which isometric blocks project in to the vertical or horizontal strip that needs drawing. I store the screenspace bounds of each block anyway, so they can quickly be AABB tested against the strip, but the test room needed ~750 such checks and larger rooms would need thousands so it was too slow overall. The checks would be x2 if a frame needed both vertical and horizontal edge updates. Oof.

Rendering even a lightweight edge strip could take half a frameRendering even a lightweight edge strip could take half a frame

I landed on keeping two lists of the room's blocks, sorted by the X and Y screen coordinates of the blocks respectively, with an index to allow rapidly - O(log2(N)) - jumping to the first block with a given X or Y coordinate. So for any strip, the first possible overlapping block can be found quickly, as well as the first subsequent non-overlapping block (after which it is it guaranteed no more overlapping blocks are possible). Ballpark, this reduced the AABB checks for most strips from 750 down to around 25-50, and the checks themselves had a comparison shaved off as well. So a good improvement overall.

There are some small CPU optimsations that could be done, but I suspect edge rendering is blitter-bound now as heavy strips need clearing, copying and 50+ assorted clipped cookie-cut blits to fully rasterise. There is a possiblity it could be converted to use regular 16x16 unmasked tile rendering, thus reducing it to a previously solved problem, by finding all the unique 16x16 pixel blocks produced by rendering out every isometric room. However, this would require an unknown (but probably large) amount of chipram for the tiles, and the game won't be 50fps anyway so I'm not sure there's really a need. It's something to keep in reserve though, just in case.

Many isometric blocks can project on to any given edge stripMany isometric blocks can project on to any given edge strip

I don't know how other games implement this. There weren't that many I could find that actually implement isometric scrolling, in fact. I disassembled a little of Spindizzy Worlds and found it's using similar ideas for block masks, and a softscroll, but I didn't look in to exactly how the edges are updated. I suspect my arbitrary height isometric blocks have complicated matters a lot.

In comparison, converting from a fixed 16x16x16 block room shape to a more flexible scheme was straightforward! I now also have 8x8x64 and 16x128x2 shaped test rooms, which also served to highlight some operations that run too slowly with such large rooms. Those are next, before I start on object rendering in the isometric world.

The YouTube link below has some footage of the three test rooms scrolling around. Tall room is tall. Long room is long. Enjoy!

Related Links