[comp.sys.amiga.programmer] Lemmings - a tutorial Part III

farren@well.sf.ca.us (Mike Farren) (03/24/91)

NOTICE - this entire posting is Copyright (c) 1991 by Michael J.
Farren.  The only reproduction rights granted under this copyright are
for electronic transmission between Usenet sites, and other sites
connected to Usenet sites.  Contact farren@well.sf.ca.us for reproduction
rights other than the above.

Level 2 - MASSA'S IN THE CODE, CODE GROUND

The second Big Excuse game developers use, this time to justify taking
over the machine, killing multitasking, or not using the Amiga ROM
Kernel routines, goes something like "but if we didn't, we couldn't do
real time stuff that ran smoothly".  Let's take a look at that one in
a little more detail, again (as always) using Lemmings as the example.
Also, this will continue the memory discussion from the earlier post,
since I also need to show that everything will fit into 80K of code
space.

First, let's plan out just what the code that runs Lemmings needs to
do.  Let's look at the series of actions that take place every time a
screen is changed.  I call the interval between screen changes a
"frame".  Not standard terminology, but I'm used to it.

At the beginning of each frame, one of the screen buffers is being
shown on the screen, and the other is sitting in the background,
waiting to be redrawn.  For each frame, the following things happen:

   - First, the appropriate image for each animated object (flames,
     traps, stuff like that) is drawn into the buffer which is
     currently non-visible.  This includes the exit box - and this
     explains how you can get a "buried" object, or an exit box which
     is hidden, as the next step will overwrite this imagery.  As each
     image is drawn (or after they've all been drawn), the counters
     which keep track of the current image for each object are
     updated.  In some games, all of this happens during the vertical
     blanking period.  This is not true for Lemmings, as I'll explain
     in a bit.

   - Next, the appropriate section of the background is copied into
     whichever buffer area is not visible currently.  This overwrites
     whatever was in that buffer before.  You can do some optimization
     here, to avoid copying over a bunch of stuff which is unchanged,
     but each bit of optimization carries a penalty in code size and
     speed, so you've got to be careful.

   - For every lemming, go through a loop which determines, first, if
     it is active or not.  If it's an active lemming, actually running
     around on the screen or whatever, then check to see where it will
     end up with after it moves.  This can get a bit complex, and
     depends a lot on just what kind of lemming you're dealing with.
     Blockers never move, for example, so they're easy, whereas miners
     are more complicated, since you have to check to ensure that
     they're actually going to dig something, check to see if what
     they're going to dig is metal, change them to walkers if they
     can't dig, or update their positions if they can.  Like I said,
     it gets a bit complex.

   - Once the lemmings' position has been calculated, draw it into the
     buffer, using the appropriate image to get the animation.  Have
     you noticed, by the way, that all of the lemmings seem to walk
     "in step"?  It would seem that instead of a counter for each
     individual lemming, they may have used one or two counters for
     all of the lemmings.  Just a theory.  Anyway, when drawing the
     lemmings, don't bother to draw the ones that won't be visible
     this frame - that would be a severe waste of time.  Only draw the
     ones whose positions place them within the buffer (which is a
     window onto the entire width of the background).  This drawing
     can be done either after each individual lemming's location has
     been calculated, or all at once after all the locations are
     known.

   - For each action which affects the background (diggers, builders,
     bombers exploding, or anything else which causes the background
     to permanently change), make the changes in the appropriate spots
     in the background buffer, so they'll show up from then on.

   - Update the map display in the lower right corner to reflect the
     new position of the lemmings and state of the background.

   - Change the copper list to cause the buffer that you've just drawn
     into will become the visible buffer after the next vertical
     blanking period.

There are some other tasks to take care of each time through, as well
- you need to check the mouse position, move the cursor, update all of
the displays to reflect that, update the score and "lemmings out"
display, update the square cursor on the lower right-hand map, count
down the time, update its display, take care of the music sequencing,
and probably a few more things I've forgotten.  A lot of this type of
thing is best done in a vertical blanking period routine, since it
doesn't take very long to do, and doing it every VBlank makes the
interface look and feel a lot smoother.  Whether it's in a VBlank or
not, though, that's all you need to do.  I'm not saying it's
necessarily all _easy_ to do, but the list of tasks isn't all that
long.

So, let's look at this from the two criteria we started out with.
First, the matter of code size.  I don't actually _know_ how big the
Lemmings code is, but I would be extremely surprised if all of the
above routines took up more than 48K or so.  That estimate comes from
the fact that I designed a game about seven years ago which was of a
similar level of complexity - it was a dungeon type game with 128
levels, twenty or thirty different monsters, each with its own
movement patterns, sixteen different spells, each of which had
different effects when activated, and a lot of other bits of magic,
weaponry, armor, doors, secret doors, traps, and on and on and on.
The memory price for this game, _including_ all of the graphic data
and tables, was 16K total.  All in 6502 assembler, of course, but
then, I would expect Lemmings to have been coded in assembler anyhow,
for the most part.  At least, that's the way I would have done it -
80K isn't hay, but it isn't a lot of memory for your average C
program, either.

Add in the extra code needed to take care of the between-levels
displays and the opening "one player, two player" selection screen,
and I still have a hard time imagining Lemmings taking more than 80K,
total, for code space.