Maze Mania - How did they do it?

edited May 2009 in Development
Hi,

I was looking at the game "Maze Mania" by Hewson Consultants, written in 1989. The game can be downloaded from here http://www.worldofspectrum.org/infoseekid.cgi?id=0003092

I was looking to write a large map based game, similar to "Fred", or "Magic Pockets" (PC - Bitmap Brothers) and wanted to develop a method of drawing the blocks quickly and transferring them with no flicker etc.

The main screen is 28 chars by 16 chars in size, moving 1 char in all directions.

Do you think each frame they

1. Drew the blocks to a back buffer
2. Drew the sprites in their correct positions
3. Copied the buffer to the main screen

It seems so fast on Maze mania. I think some of it is an illusion, in that the sprites move a few pixels at a time, while the main character moves 1 char at a time, giving quite a nice smooth movement effect, even though the map is moving 8 pixels at a time.

It seems like it is moving about 25 FPS but it could be less.

I would be interested in your ideas about the best way to implement this.

In the "diagram" below, I am trying to show how I think this could work.

1 = Backbuffer 32x24 chars. Each block in (4x4) and are drawn on 8 columns x 5 rows offset depending on position
2 = The centre 28 x 16 chars are copied
3 = screen

buffer.png

I am wondering if there is a better way by calculating offsets for the blocks/sprites and to draw directly to the screen etc
Post edited by robpearmain on
«1

Comments

  • edited May 2009
    i dont know HOW they did it but my experience says that redrawing whole screen from tiles can be slower than other methods.

    let me explain:
    galactic gunners (which is very different kind of game) has backbuffer with terrain and scroll it by one pixel.
    the scrolled terrain is then copied to the screen (area 24x16 chars) and copying is done just behind beam generating screen (floating bus check). when it is finished the beam is somehere in the bottom border. it leaves at least 15000 T for drawing sprites directly into screen (probably much more if sprites are sorted somehow)
    i think Terra Cresta uses something similar (but we have local expert on Terra Cresta so ask him)

    on other side, i believe that Probe's char scrollers like Dan Dare III, Savage really constructs screen on char basis for every frame.

    another idea is put the sprite to the backbuffer and copy backbuffer to screen then lift sprite (means replace it by original background), scroll background a and draw tiles on edges.

    i would recommend to count frames in Mazemania and you will see (i know that U.N. SQUADRON runs on 12.5 fps, i believe CHRONOS has same framerate. both are rather decent games)

    this is interesting thread but i have to be brief as i have no time. may be later.
  • edited May 2009
    Thanks for your Reply FIKEE. I have checked out some of those titles, and your right 12.5 works well.

    Looking at the Maze Mania code in more detail, I think they do a backbuffer copy, but it is pretty smart.

    I think they have a table for each of the lines (18 x 8 = 144 lines) containing the source address and the destination screen address. The SP is pointed to this table, and the values are "popped" into HL and DE. Then 28 x LDI's are done to copy from the source to the destination.

    I have the pseudo code here (After halt) based on what I can see in Maze Mania
    HALT      ; Wait for Top Of Scanline, in reality would set up IM2 or something
    DI          ; Disable Interrupts
    LD (spstore),SP ; Store Stack Pointer
    LD SP,table_of_source_and_dest_addresses ; Point SP to our table of source and destination (display) addresses
     
    ; Wait for scan line 
    LD BC,$0320 ; 800
    loop:
    DEC BC
    LD A,B
    OR C
    JR NZ,loop
     
    LD A,$90 ; 144 (18 Rows of 8 bytes)
    line_loop:
    POP DE   ; Get DESTINATION (Display) from our table
    POP HL   ; Get SOURCE address from our table
    LDI        ; Copy Contents of (HL) to (DE) 28 times (28 columns)
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
    LDI
     
    DEC A    ; Move to next row
    JR NZ,line_loop ; repeat until all done
     
    LD SP,(spstore) ; restore Stack Pointer
    EI ; enable interrupts
    

    An example of the "table_of_source_and_dest_addresses" then looks like this:
    table_of_source_and_dest_addresses:
    ; Destination (Display), Source
    defw $4000,$E500,%4100,$E600..... for all 144 lines (18 Rows)
    

    Where the first 2 bytes are the screen destination, the next 2 are the backbuffer (in this case starting at $E500)

    This takes about 79,000 T-Cycles, so I reckon with some optimised Tile Writing and Sprite Code I could get 50/3 = 17 Frames per second, or at least thats my goal.

    So, I think I will see how I get on with the following...

    1. Copy Tiles to BackBuffer
    2. Copy Masked Sprites to BackBuffer
    3. Copy BackBuffer To Screen
  • edited May 2009
    Sorry to burst your expectations, but that is nowhere near 25fps.

    The scrolling moves in characters, and it takes about 3s to scroll the width of an entire screen. That's 7 blocks, or (7x4=) 28 characters, in (3x50=) 150 display frames. That means it's taking about 4 or 5 frames of the TV display to refresh, around 10-12.5 frames per second. That's more than enough time to redraw the entire screen from character blocks. And given that it's only displaying a small windowed screen (28x16 characters) that's enough time to redraw the background, copy the sprites onto it, then copy the whole lot to the display.

    In fact, they're probably not re-drawing it from blocks. Look at the screen border graphics; 2 characters wasted on each side, and four at the top and bottom. So if you had a back screen that'd give you enough room to draw new 4x4 blocks at the edges of it, then copy off a window from that.

    This is not the most efficient screen refresh around. It's fairly typical for even the better Spectrum games to take 4 or 5 TV frames to refresh.

    As for 'chasing the raster' with copying the screen, that's complete hogwash. It's just not fast enough. If you could copy the entire screen, 6K of data, in one TV frame then you could do it on the attributes too, and we'd all be using full-screen rainbow generators in every game.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Thanks for your reply joefish.
    joefish wrote: »
    As for 'chasing the raster' with copying the screen, that's complete hogwash. It's just not fast enough. If you could copy the entire screen, 6K of data, in one TV frame then you could do it on the attributes too, and we'd all be using full-screen rainbow generators in every game.

    I wonder why they wait before copying? (The code is dissassembled from the Maze game).

    Also, you are right it is more like 2 x 79,000 (160,000) T-Cycles :-o

    Right, time to be more realistic then :D

    10-12.5 frames it is then :-)

    However, I suppose if I did lots of POP's and PUSHES's and had a line for each of the 14 bytes...
    LD SP,0E500h
    POP AF
    POP DE
    POP BC
    POP HL
    EXX
    POP DE
    POP BC
    POP HL
    LD SP,04000h+14
    PUSH HL
    PUSH BC
    PUSH DE
    EXX
    PUSH HL
    PUSH BC
    PUSH DE
    PUSH AF
    

    The above takes about 250 T-Cycles

    So we would have 144 Lines x 2 = 288 of the above

    Thius would be 288 * 250 = 72000 to copy the 28 Columns by 144 lines (Interestingly 128 lines, or 16 rows) would take about 64000.

    The trade off of course if that the above would be about 28bytes x 2 per row = 56 bytes * 144 = 8064 bytes for the code.

    So you would need a backbuffer (6144 bytes)
    The code to copy super fast (8064 bytes)

    Doesn't leave much for a game :-)

    So the trade off is to use the earlier code and aim for 10 - 12.5 frames per second
  • edited May 2009
    An easy way to check this is to run in SPIN and have it take a snap-shot of every frame, and then examine each image. It is indeed taking 4 frames between each movement.

    As the display is only 28*16 it's only redrawing about 58% of the screen each frame, which helps, and could be using a number of methods - you could certainly draw the whole thing to the back-buffer, and then blit in that time, or to the front buffer directly, or just scroll the old data in the required direction, and then fill-in the blanks from the new data. One speed-up to be had when drawing the whole thing is to make any tiles which have no graphic use the same paper & ink colours - as you then don't need to blit the graphic at all as it won't be seen.

    (Coincidently I'm currently doing some scrolling code in a snake-type game - see the "New Game Ideas" forum)
  • edited May 2009
    Hi bobs,

    Cool, so basically SPIN has confirmed that the Maze Mania games is updating at 12.5 frames per second.

    I will check out the scrolling game you are working on too, thanks.
    bobs wrote: »
    An easy way to check this is to run in SPIN and have it take a snap-shot of every frame, and then examine each image. It is indeed taking 4 frames between each movement.

    (Coincidently I'm currently doing some scrolling code in a snake-type game - see the "New Game Ideas" forum)
  • edited May 2009
    The most impressive scroller I've ever seen is the one in R-Type. That pixel-scrolls the scenery in place in the screen memory, but only where there are actual scenery blocks present (I presume it tracks the attributes in a separate buffer). Extra characters are drawn in to a black strip down the right-hand side, then scrolled onto the screen. If you poke some coloured attributes into the right-hand edge using an emulator you can see the new characters popping in every 8 refreshes.

    You can also poke bytes onto the screen - if they change the scenery, the changes will scroll along with the scenery. If you poke into an empty area of screen, they won't.

    All the game sprites are then drawn from character blocks - a huge batch of UDGs - but are only copied to the screen where there isn't any scenery, so they never over-write the foreground or get trapped in the scroll.

    Pretty clever all round, and highly optimised.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    I have a full screen scroller that updates the entire display at 50 FPS :p
  • edited May 2009
    :D A demo would be nice!
    Woody wrote: »
    I have a full screen scroller that updates the entire display at 50 FPS :p
  • edited May 2009
    It wouldn't happen to be entirely attribute-based, by any chance?
    Because that's just being lazy. If the screen is going to be blocky attributes, you should at least be flashing OUT 254 to extend the blocky graphics into the top and bottom border.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Actually it doesn't touch the attributes, only the 6K display memory. Funny that you mention about extending into the border as it does that too :)
  • edited May 2009
    joefish wrote: »
    As for 'chasing the raster' with copying the screen, that's complete hogwash. It's just not fast enough. If you could copy the entire screen, 6K of data, in one TV frame then you could do it on the attributes too, and we'd all be using full-screen rainbow generators in every game.

    This is misunderstanding. SURE, you can't copy 6k in one refresh but you can rewrite whole screen without flickering.

    When you start chase raster the beam is ahead. You can fill about half of screen memory before beam reach the bottom of screen.
    Then beam is going to build upper border (14500T) and the upper half of pixel area (224*12*8 = 21504 T) and you continue in filling bottom part of memory. You just have to reach bottom border before beam.
    It gives you one complete frame (70000 T) + frame without upper and bottom border (cca 43000T) = 113000 T.
    Rainbow generator is something completely different because you have to be REALLY synchronized with beam.
  • edited May 2009
    Fikee wrote: »
    This is misunderstanding. SURE, you can't copy 6k in one refresh but you can rewrite whole screen without flickering.

    When you start chase raster the beam is ahead. You can fill about half of screen memory before beam reach the bottom of screen.
    Then beam is going to build upper border (14500T) and the upper half of pixel area (224*12*8 = 21504 T) and you continue in filling bottom part of memory. You just have to reach bottom border before beam.
    It gives you one complete frame (70000 T) + frame without upper and bottom border (cca 43000T) = 113000 T.
    Rainbow generator is something completely different because you have to be REALLY synchronized with beam.

    correct: whole screen is probably imposible, just about 5000 bytes
  • edited May 2009
    OK, so without contention you can copy maybe 15 bytes of data per line of the screen. And because one whole screen is 312.5-ish lines (half of 625) that means you've got 312.5+192=504.5 lines before the beam catches you up again. So, OK, it's possible to redraw the pixels without flicker, but it takes most of two 50Hz frames to do it. You'd have to be very careful to copy up the attributes in stages as you go though, or they'll be out of step with the pixels.

    I was assuming it was impossible because you can't do it in a single frame, but that's flawed logic. The rainbow generator is limited because it has to copy each line at the same rate that the beam is drawing the lines.

    I can see Woody is going to keep making claims but not put up anything concrete or useful to this case. And paging screen RAM in and out doesn't count as a refresh.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Rob got the code long ago now ;)
  • edited May 2009
    Now shall we start getting really clever and start scrolling screen areas around by simply using the I register and a few choice opcodes or maybe just leave it there for now. Yeah, let's leave it cos I really can't be arsed :D
  • edited May 2009
    +3_Demo != 48K_Game
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Just as well as this is a 48K/128K trick only ;)

    Edit: Can't find the thread where this came up ages ago atm. Will look again later.

    Edit 2: http://www.worldofspectrum.org/forums/showpost.php?p=140386&postcount=166 :)
  • edited May 2009
    I still can't quite escape the feeling that this, innovative as it is, may be more trouble than its worth, as far as a game is concerned...
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    joefish wrote: »
    I still can't quite escape the feeling that this, innovative as it is, may be more trouble than its worth, as far as a game is concerned...

    /me doesn't recall the effect being part of a game being one of the rules.

    D.
  • edited May 2009
    So a thread that begins "I was looking to write a large map based game" should be taken as more of a guideline, rather than a rule?
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Thanks for all the feedback and advice.

    I shall post demos as my game progresses

    Thanks again
  • edited May 2009
    joefish wrote: »
    So a thread that begins "I was looking to write a large map based game" should be taken as more of a guideline, rather than a rule?

    Rob only asked for a demo. Which, if Woody still has it, is quite fun to look at. I recorded an anim .gif in ZXSpin with it a while ago now, too. We found it while probing more of the 48k machine's secrets, quite by accident as I recall. Was pretty astonishing to watch - hardware scrolling at 50fps :-)

    D.
  • edited May 2009
    I'd still like to see it working, and it's great if an emulator can be so accurate as to emulate it. It's just my experience of doing those things on the ST is that they're highly impractical to turn into a controllable, go-anywhere, multi-directional scroller. If you manipulate the screen redraw, you then have to chase the effect around by writing fill-in data at odd places in the screen memory, which can be more trouble than doing the scroller in software. As for the admission that the effect is dependent on the temperature of the ULA at the time - seriously, how are you supposed to control that?

    But if someone is going to try and tease or wind people up with cryptic postings when someone's actually asking for help and advice, then you'll have to forgive me for exhibiting zero sympathy.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    Where as I have zero sympathy for people who post things like
    joefish wrote: »
    I can see Woody is going to keep making claims but not put up anything concrete or useful to this case.

    about people who have done much, more for the community than you ever have. You made your bed, now lie in it.
  • edited May 2009
    Look, I can quote too!
    Woody wrote: »
    ...let's leave it cos I really can't be arsed :D
    That's a concrete contribution is it?

    Clever as what he's achieved is, I still fail to see how anything he's put here has helped Rob even slightly. If it had, I wouldn't have been critical. But, I called him on his deliberate attempt at a wind-up of someone who's trying to achieve something (or, from then onwards, me).

    Frankly I had a miserable experience trying to get some ideas for coding myself, so if some old guard teasing or setting upon anyone new is the best you think this 'community' has to offer then nighty-night indeed.

    But somehow I suspect that if Woody actually gave a flying f**k about anything I put then he'd have said so, instead of continuing the wind-up.
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    joefish wrote: »
    Clever as what he's achieved is, I still fail to see how anything he's put here has helped Rob even slightly.

    I suggest you read posts #16 and #23 in this thread very carefully as you've obviously the missed the subtle subtext of "Rob's got the code" and Rob saying "thanks".
    Frankly I had a shitty experience at the hands of some people trying to get some ideas for coding myself

    This thread? I wonder why that was?
  • edited May 2009
    Seems I did - well then, so he was just winding me up then, not Rob. Fair game.

    As for 'that thread', I'm not the one who went through and deleted a load of comments from it. In fact, since that 'episode' no-one can any more. Although maybe that should count as a great contribution to the community too in some way...
    Joefish
    - IONIAN-GAMES.com -
  • edited May 2009
    joefish wrote: »
    Clever as what he's achieved is, I still fail to see how anything he's put here has helped Rob even slightly. If it had, I wouldn't have been critical. But, I called him on his deliberate attempt at a wind-up of someone who's trying to achieve something (or, from then onwards, me).

    Wrong. I supplied the code and source to Rob. Which, by the way, was unrelated to this "scrolling" trick. That was thrown in only as a potential point of interest for some, and nothing more than that.
    But somehow I suspect that if Woody actually gave a flying f**k about anything I put then he'd have said so, instead of continuing the wind-up.

    Oh look, wrong again. You showed a little bit of interest and I spent my time searching this forum to try to locate a link to the description of how I'd found it works on the real hardware and supplied you with that very link. Excuse me for doing that. My bad. Won't happen again...
  • edited May 2009
    Oh come off it. That wasn't "thrown in only as a potential point of interest" - that was a deliberately vague wind-up, and you know it, and you kept it going just as much as I did. It is interesting stuff, and thanks for the link.

    But do you really need anyone being grossly offended on your behalf and leaping to your defence with a personal attack on me? What is this, the letters page of the Daily Mail?
    Joefish
    - IONIAN-GAMES.com -
Sign In or Register to comment.