Game loop

edited January 2014 in Development
Just working on my first ever Spectrum game, and am struggling to work out what the game-loop should look like.

I've got interrupts enabled (mode 2) - although I'm not doing anything in the interrupt routine at the moment.

From what I understand, interrupts occur when the TV beam hits the end of the bottom line, so I thought a basic game-loop could be this (pseudo code):

loop: halt ; wait for interrupt to occur
clear_sprites
update_stuff
draw_sprites
jr loop

Does this look right? I've decided to XOR my sprites onto the screen, so the first problem I've hit is the very first 'clear_sprites' routine actually draws them on the screen, and then the draw_sprites routine clears them off (unless they've moved a bit).

I could set a flag to say if first time in the loop don't clear them - but thats already getting a bit messy, and I wondered if there was a simpler / nicer approach.

I did think about padding the sprites with blanks around all edges, then I wouldn't actually need a clear_sprites step, but that seemed like a bit of a faff too.

When I was thinking about the halt instruction I then thought it might be better to do something like this:

loop: update_stuff
halt
clear_sprites (from old position)
draw_sprites (at new position)
jr loop

Which seems to give me a bit more time in the vblank, but adds the additional step of remembering everythings old position.

The game itself is going to be a space invaders clone (so a few sprites on screen, though not all necessarily moving every frame).

Any help / ideas are welcome.
Post edited by feynman on

Comments

  • edited January 2014
    feynman wrote: »
    From what I understand, interrupts occur when the TV beam hits the end of the bottom line

    It's when it hits the end of the bottom border (thus restarts at the top of the upper border).
    feynman wrote: »
    so I thought a basic game-loop could be this (pseudo code):

    loop: halt ; wait for interrupt to occur
    clear_sprites
    update_stuff
    draw_sprites
    jr loop

    Does this look right? I've decided to XOR my sprites onto the screen, so the first problem I've hit is the very first 'clear_sprites' routine actually draws them on the screen, and then the draw_sprites routine clears them off (unless they've moved a bit).

    I could set a flag to say if first time in the loop don't clear them - but thats already getting a bit messy, and I wondered if there was a simpler / nicer approach.

    loop:
    draw_sprites
    halt ; wait for interrupt to occur
    clear_sprites
    update_stuff
    jr loop
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited January 2014
    feynman wrote: »
    I've decided to XOR my sprites onto the screen, so the first problem I've hit is the very first 'clear_sprites' routine actually draws them on the screen, and then the draw_sprites routine clears them off (unless they've moved a bit).

    I could set a flag to say if first time in the loop don't clear them - but thats already getting a bit messy, and I wondered if there was a simpler / nicer approach.

    You will have to print the sprite once before (or in the moment) you initialise your routine. Later, each time you move your sprite, you XOR it in the old address (to clear it) and the XOR it in the new address to print it.

    As you are going to use a XOR routine, I'd go for this approach (so you don't have to worry for the screen refresh if you don't mind a little tearing):

    I use a XOR routine that gets both the old coordinates of the sprite and the new ones, so it does the clear/print thing line by line of the sprite instead of clearing the whole sprite and then printing it again in the new position. So when the electron gets you, it only affect one line of the sprite. If you use a similar aproach, you can do this:

    Print sprite
    Loop:
    Get movement
    Update Sprite (Clear/Print)
    Update stuff
    GOTO Loop
  • edited January 2014
    Thanks guys.

    Small change to my main loop, but obvious when I see it written down. (I'll go with Einar's approach, at least to begin with).

    What's a common use of the interrupt routine? I've got interrupts enabled mainly so I can use the Halt in the main loop. Should I be thinking of putting stuff in there? (and if so - what? sound?) (or is that question too general to answer...)
  • edited January 2014
    You've said sound already, so I'll go with keep track of time.

    frame interrupt occurs 1/50th of second. Count up 50 of them and you have a second, etc.
  • edited January 2014
    feynman wrote: »
    What's a common use of the interrupt routine? I've got interrupts enabled mainly so I can use the Halt in the main loop. Should I be thinking of putting stuff in there? (and if so - what? sound?) (or is that question too general to answer...)
    I found that for your first game there's no need to put anything inside the interrupt routine. If I remember correctly my last game didn't had much in it either.

    Sound might not be a good thing to put in interrupt routines because they usually need more than 50hz per second, but I know some games do have them. AY-music routine could also work.

    Also, remember that anything inside an interrupt routine will be executed just after your halt instruction in your main loop, i.e. immediately before your sprite routines. This might be a good or a bad thing.

    If you have no idea you could always put a counter inside the routine and increment it. You can then refer to it later in your main game code. But you could also add a counter just after the halt in your main loop instead.
  • edited January 2014
    OK thanks, makes sense.

    Still struggling with sound actually (48K so beeper only). Anybody know any good guides to using the speaker for producing sound effects, etc?
  • edited January 2014
    loop: halt ; wait for interrupt to occur
    clear_sprites
    update_stuff
    draw_sprites
    jr loop

    It all depends how much time each operation takes. You don't want to get caught in the screen while you cleared a sprite but didn't redraw it at new position - it would make sprite disappear. Usually when coder doesn't have control on such events sprites appear and disappear randomly which is called flicker and looks badly of course.

    You may organize your job so to start drawing when raster reaches a BOTTOM border, not top one. It will give you more time to do drawing.

    Then there are other techniques as buffers but as you wrote, you draw directly to screen. It's perfectly okay as long as it works ;)

    Personally I do:
    Loop
    - ClearSprites
    - DrawSprites
    - Do Stuff
    Jp Loop

    with HALT somewhere in between of these operations :)
  • edited January 2014
    Haha, but where in between ? :-)

    Still struggling to see how I can play a sound effect using the 48K beeper that spans across several frames of action.

    e.g. Player fires - I want a sound effect that lasts (say) half a second. I don't want to stop the game for half a second, so I need to split my sound out over this time. Do I just play a small bit of sound each frame? Wont this sound odd?
  • edited January 2014
    feynman wrote: »
    Haha, but where in between ? :-)

    It really depends on how you are drawing to the screen.

    One thing I think you are maybe not considering is how the TV display is drawn. After the ULA interrupts there are about 14k z80 cycles until the first pixel of the top left corner is drawn by the TV raster. The raster proceeds left to right, top to bottom until the entire display file is traversed by the TV raster.

    Why is this important? Because if you are busy erasing things while the TV raster crosses the area being erased, you will see flicker (sprites gone, then later reappearing). If the raster crosses the mid section of a sprite that is not erased, you may see it torn (the top half at an earlier position compared to the bottom half). I think flicker is probably a worse effect.

    If you want to erase and then redraw, you want to position your halt to give you the best chance to finish these operations before the raster catches up with you. There are a lot of different ways to do this. One that has source code in the public right now is antoniovillena's FASE sprite engine (which, btw, you could use instead of writing one from scratch).

    If you want to just XOR sprites on screen, it may be acceptable to totally ignore this issue because XOR suffers much less from flicker unless the sprites move far between updates.

    Other draw algorithms never suffer from flicker because they never erase anything. Instead they draw completed graphics to a buffer and then copy the finished result to screen. sp1 (my sig) is an example that draws completed graphics to screen one character square at a time.
    Still struggling to see how I can play a sound effect using the 48K beeper that spanscross several frames of action.

    Playing beeper sounds takes all the cpu's time because you need to control the tone periods precisely by counting the exact time the z80 takes between toggling the speaker state. In other words, if you are playing a beeper tune across a few frames, you are not moving sprites on the screen :). You can try to play a segment of a tone every 1/50s in the interrupt (remember that 14k cycle delay to the first pixel? well some draw strategies might want to wait 14k cycles before doing anything so you can fill that time with tasks like sound generation that take a known amount of time) but a lot of people may think that sounds too harsh for music (as opposed to sound effects) but you could try anyway. JetSetWilly is an example.

    Sound effects are brief and sporadic -- they can almost always fit into the "play a bit every 1/50s" model that work on interrupt.

    Alternatively you can use the AY to play music. That doesn't require much of the z80's attention.
    e.g. Player fires - I want a sound effect that lasts (say) half a second. I don't want to stop the game for half a second, so I need to split my sound out over this time. Do I just play a small bit of sound each frame? Wont this sound odd?

    That's what you do. Design your sound effects to sound ok.
  • edited January 2014
    feynman wrote: »
    Still struggling with sound actually (48K so beeper only). Anybody know any good guides to using the speaker for producing sound effects, etc?

    The basic principle is changing the state of bit 4 of port #FE in a loop. The type of sound you get is defined by how much time has passed between each of the state changes.

    So e.g. for a pure tone of a specific frequency you insert a fixed delay of some kind between the OUTs. If you use two different (but still fixed) delay durations, it is a constant frequency tone as well which however is going to sound more 'thin'; this is what it looks like.

    For noise, the delays should be random. Noise frequency is inversely proportional to the average delay duration.

    For multichannel sound, several methods exist, e.g.

    1) Using several tone channels with low duty cycle values so that they don't interfere with each other and actually sound like distinct channels ('pulse width modulation').
    2) Creating a 'middle signal state' of sorts - w/e it's called - by flipping the bit state really fast, and then adding/subtracting tonal pulses to that.

    It's sort of all building upon these really.
  • edited January 2014
    feynman wrote: »
    e.g. Player fires - I want a sound effect that lasts (say) half a second. I don't want to stop the game for half a second, so I need to split my sound out over this time. Do I just play a small bit of sound each frame? Wont this sound odd?

    In my (brief) experience, when I've been mucking about with short beeper-type sound FX, I've found it's best to keep the sounds quite short for commonly heard sounds that play "in their entirety" in a frame (e.g. a footstep or a shot).

    Half a second sounds like quite a long time for a sound to play in its entirety.

    If it's going something like "peeeoooowwwnnn....!" "peeeooooownnnn..!"* then I'd personally split it over multiple frames, to keep the action going.

    (*apologies for the use of technical jargon)
  • edited January 2014
    At the end of thread are. some examples of beeper routines including source code. And snapshot. http://www.worldofspectrum.org/forums/showthread.php?t=44743
    And you can find more threads about beeper here on wos, its quite popular topic in development forum.
  • edited January 2014
    Ah great - thanks for this.

    I didn't realise the forum defaulted to only showing posts from the last month (doh! thought it was a bit odd there were only 3 pages :confused:)
  • edited January 2014
    spud wrote: »
    You've said sound already, so I'll go with keep track of time.

    frame interrupt occurs 1/50th of second. Count up 50 of them and you have a second, etc.
    Very common thing is to set simple flag that interruption occured. In that way the interrupted code knows it should finish asap but is not killed immediately and has time for housekeeping.
    In your main loop you replace the halt by
    Do sound until interrupt sets flag
    The advantage here is you don't waste time by doing nothing. Cobra does it same way.
Sign In or Register to comment.