[stella] Qb: Post-Mortem

Subject: [stella] Qb: Post-Mortem
From: "Andrew Davie" <adavie@xxxxxxxxxxxxx>
Date: Thu, 15 Mar 2001 01:41:39 +1100
I thought I'd jot down some of my thoughts on Qb, now that I truly believe I
am in the last week of development on the game.  Ruffin requested a
post-mortem, so here it is.  I'm pretty tired - this is typical of a project
for me.... getting it out is the programmer's equivalent of childbirth.  You
really have to push hard, or it won't happen.  I tend to work long hours,
into the wee hours of the morning.

I'm very happy with what I've ended up with - quite a playable game, and
essentially the same game as my original on the Atari 800.   It's turned out
pretty much exactly as I would have planned it, had I planned it exactly :)
But I didn't plan it exactly... each step seemed to be a natural one, and I
just did the next thing that needed doing.  It helped a lot, knowing exactly
what I wanted before I started.  As I wrote to the list earlier, I'd spent
some time thinking about if it was possible.

I'm not really sure if/when I knew for sure that I could do it.  Obviously,
looking at how tight things are now, there were major problems compressing
the amount of code/graphics into the ROM, and let's not forget the RAM usage
(multiple overlays, etc).  It is amazingly functional for 4K.  If I'd
planned out all of that, how much code/ram needed... I'm not sure I would
have started.

A large part of time, during my early programming years, was spent playing
around trying this and that... just putting in anything and everything...
where whim took me, without any gameplan, so to speak.  That is where the
majority of time goes.  If you know what you want, and stay focused, then
you can get things done quickly.  I think Qb was done pretty quickly... well
under 50 days from first keystroke to last.

The thing that sticks most in my mind is the optimisation cycle.  It
constantly amazed me how, whenever I really needed to squeeze some space out
of the code (which was just about every day for the last three weeks), I
could find almost as much space as I needed.  I recall being jam-packed,
then saving 70 bytes, then 'spending' it, then saving another 190, then
spending that, then finding another 100... etc.   I guess each iteration the
code was becoming more and more esoteric and less structured.

I started thinking about doing some playing on the 2600 (again) sometime in
January.  I couldn't stand the thought of programming for some months before
that (I tend to burn out, and was at that stage, then).  However, I found
the supercharger, and got it set up and found my (very old) PUSH demo.  The
push demo was an aborted attempt at a sokoban clone - in particular, it
demonstrated a 4-colour playfield by multiplexing playfields of different
colours, in time.  This was not well-received by the list (such is life!),
but one thing that stuck in my mind was somebody's comment (about a
fast-moving square on the screen)... "that's playfield graphics animating?
cool!"  or something like that.

I hadn't (and still haven't) seen a lot of 2600 games (and played even
fewer).  But there didn't seem to me to be a lot of games which
animated/used the playfield.  Mostly the playfield was used for
walls/pictures... but nothing interactive.  Yeah, I'm sure there are some
games that do it... my point is that this is what I was thinking about back
then... and what was on my mind when I picked up the Supercharger again.

As I said, the Sokoban-clone (push) didn't make it... but it was my first
experiment with animating playfield.  The blocks were all playfield.  So,
this was the first bit of code I picked up... just to see what I'd done.  I
started to think about my Atari 800 game 'Qb' and thought that the blocks in
Push were pretty similar to blocks in Qb.  So I thought I'd set up the dev
environment again, and see just how many blocks I could draw.  This required
me to set up a playfield, of course... but the essential code was already
there.  I wrote it all very general-purpose - using equates for the sizes of
the blocks, calcualtion of RAM usage, etc.... however as I settled on an
actual size (4x4) things kind of got more and more hardwired (mainly for
efficiency), and the generality suffered.  That wasn't a problem, though...
just a natural progression from a testbed to an actual game.  Generality was
compromised for functionality.  Hold off on the compromise as long as
possible :)

So, it took me a night to experiment and I had 7 blocks shifting around
fairly randomly (using an eor-draw).  I posted this to the list on 30
January.  At that stage I clearly had in mind Qb, and was thinking about how
to implement the game on the 2600.  I was quite happy with the kernel, as it
had a fair bit of processing time left, though I was rather concerned about
RAM.  Kind of gung-ho that I'd find the RAM if I needed it :)

It only took me a few days to mock-up a target display, as well as a main
playing grid.   Really, these sorts of things were rather straightforward,
as I didn't have to think much about resources... I had a whole 4K.  Mostly
you could call the first couple of weeks an experimental stage... I was just
playing, and prepared to drop the project at any time.  In fact, the
feedback for the first few weeks was (to me) most disappointing.  Nobody
seemed really interested.  Oh well.

One problem was that I had balky emulators running on my sytem, which didn't
lock to the video card frame-rate.  So, what looked normal speed for me...
was in fact about 700x normal speed :)   So the code posted to the list
animated/crawled like a slow snail.  It took me quite a while to realise
this.  Once I fixed up the speed, people started paying a bit more
attention.  I guess I was seeing something different to everyone else.

At this early stage, people were starting to suggest code optimisations to
me.  I'm in two minds on this one.  The chances are high that you will need
to rewrite your systems anyway, so I prefer to get it working, first... and
then optimise it if you must.  A typical example of poor optimisation was my
use of a 256-byte table for the sprite-draw routine.  I figured
(incorrectly) that it would be quicker to have a ROM-table, so spent some
time writing code to use the table (and to avoid branching in my kernel).  I
was pretty proud of it!  Only, it was crap - as Thomas rightly pointed out.
The moral is, don't be too proud to toss your brilliant work away :)

About a week after starting, I had a rudimentary demo which let you move
blocks around, to their correct positions, even... and a rudimentary target
display, and was working on sprites.  This was pretty rapid, but there was
very little 'guts' in there.  The gameplay and polish is what really takes
all the time.

At about this stage, I was really beginning to learn new things about the
2600.  In particular, tight timing of kernels, and how sprites worked,
tv-frames were generated, etc..   Here the list truly became useful - many
many times people like Eckhard, Thomas, Manuel, etc.... helped me out with
suggestions and comments and answers.  It would have been much more
difficult without this help, and so the [stella] community truly deserves
part-credit for the game :)  Thanks!

I was freely sharing my source code, pretty much from the start of the
project.  I guess this was one incentive to keep the comments and structure
fairly good... but fundamentally I'm a programmer who just needs to have
good coding style, anyway.  Things get too complex for me to remember, and
every comment helps :)  I also feel that it is the nature of this [stella]
community that we give what we can, and get what help we need.  Sharing my
code wasn't so much me saying 'hey, look how well I comment', but more...
here's what I've done and how its done.  The benefits from sharing my code
(optimisation, suggestions) were enormous, and I would recommend it to
anyone.  Don't be shy :)

By a week into development, I only had 6 bytes of RAM (!!) and 1300 bytes of
ROM left.  I was concerned about the RAM, but ROM seemed plentiful.  In the
end, it would swing the other way around - as I developed a neat overlay
system (which you should all use!!) - which essentially removed my RAM
troubles for most of the rest of the project.  ROM, however, became an
increasing problem - especially towards the final days.

By 8th of February, I had a version which let you play through the screens.
Enemies were yet to come, as were things like scoring, jumps, etc., etc.
But at the time it seemed like a significant step.  I could see my game
coming along, and about this stage I really decided that I was going to 'go
for it'.  I started spending long hours (4-5 a day) programming.  I gave
myself till the end of the month to get something done.

About this time, Eckhard was pointing out the timing problems - which
remained until very very late in the development process.  I definitely
appreciated the assistance, but figured fixing timing at this stage was a
waste of time because things would probably (and did, several times)
significantly change.  One change was the kernel.  Initially, I recall, I
had something like 4 kernels in there... all doing similar, but not the
same, things.  They were all rather massive, and I started to run seriously
out of ROM space.  So, over a few days I generalised the functionality of
the kernels and managed to come up with a single kernel which draws the
target area, the playfield area, and the sliding cube coming on to the
screen.  It's quite a neat working kernel... looks quite simple, but it is
doing a lot.  I spent a lot of time (and heartache) trying to get it
working.   I was absolutely delighted when I finally managed to get it all
going... this was a major achievement of the whole project - for,
compressing the multiple kernels into a single one meant that I 'd saved
many hundreds of much-needed ROM bytes.

I started to think about scoring, and dug up the 6-digit score routine from
the list archives.  As my original had large digits, I wanted to do the same
thing - so it was a simple matter (and fun) to develop a double-height
6-digit variant.  I don't think that had been done before, so I posted it to
the list.  About this time, Thomas became prominent with his wonderful
optimisation suggestions (and I gave in to illegal opcodes - much as I'd
earlier disliked them, they gave wonderful functionality).  I've always been
most impressed with Thomas's code/suggestions...

The scoring routine is a good example of being in a rut.  I spent
considerable time writing a pretty-neato binary-to-decimal conversion
routine - because I figured I'd need a fast one to do it in the avaialble
time.  It was pointed out to me that I'd not need that if I used BCD
(something I'd completely forgotten about) - and so, the neato routine was
promptly junked.  A shame, as I liked it :)  But, BCD was clearly the way to
go.  We spent some time playing around with colour-shading the digits, but
you will note that this is not present in the final version (removed to save
ROM space).  Nobody but nobody mentioned that this was missing (something I
use to judge if features are important or not)....  if somebody notices it
and mentions it, then its probably something worth reconsidering.

An example of the above is the scores when you eat fruits.  Several times
these were installed and then removed.  Nearly every time, somebody
commented that they liked them, or missed them.  They were major
space-wasters for me, but clearly a wanted addition to the game - and the
final version does retain the scores (even though they're costly).

On about the 11th, I was working on sprite positioning and ran into the
weirdo method that the 2600 uses for positioning sprites horizontally.
Basically, a divide by 15 was required.  I asked for a quick version, but no
response came (I was working quickly anyway)... so I sat down and analysed
the difference between divide-by-16 and divide-by-15.  I came up with a ...
ahem.... stunninlgy beautiful routine, which performed admirably.  Proudly,
I posted it to the list, only to find that I'd reinvented the wheel :)))
somebody had done essentially the same thing before me (in fact, the code
was strikingly similar).  This was a moment of relevation for me... at this
time I realised that many many hundreds of programmers had spent years on
this machine - and just about anything you could think of had already been
done by somebody else - and better!  So, I'm constantly impressed when we
see new tricks show up on the list.  Of course, we have the benefit of a
huge knowledge base, and great tools....

On the 12th, I started numbering versions.  I was in serious-mode.

My version of the 16th was just about the first version where I started
wasting time actually playing the thing.  There wasn't much gameplay, but
I've always found it kind of fun just sliding pattern after pattern into
place.  I was pretty happy with things at this stage.  This was about the
time that the versions of Crazy Valet were posted.  I kind of went through a
little slump.... it didn't feel like I was doing anything worthwhile... I
didn't seriously consider dropping it, though I did feel that what I was
doing wasn't up to scratch.  I recall being determined to finish... that's
probably one of the main factors I kept going.   Looking back on it, I'm not
sure why I felt this way... but this is a post-mortem, and I was just so
impressed with the other guys' efforts... it hardly seemed worthwhile going
on.

On the 18th, I was complaining about lack of space.  I had about 400 bytes,
with no creatures or AI installed :)  I changed to the new sprite-display
system that Thomas had suggested.... and gained myself heaps of ROM (mixed
with other changes/optimisations).  Even though you think you have a lot of
space, you don't.  Just a few lines of code chews up ROM .  In hindsight,
I'd recommend saving 500 bytes JUST for the finishing touches that everyone
requests.  Clearly, I was deluding myself thinking that I'd be able to pack
it all in and only tweak a few things here and there to finish it off.

However, deluded, I carried on ;)

I added colour to the player on the 19th, and was very dubious that I would
be able to get a SINGLE opponent into the game (!).  things were obviously
extremely tight once again.  This constant see-saw/fight between features,
progress, and available space is something that clearly sticks in my mind.
I am still amazed how I managed to find space (with help, of course!).

I added fruit sometime around this point.  For the longest time, you
couldn't do anything... it just sat there, sliding up and down.   The
original game introduced creatures by having them slide up out of cubes.  I
implemented this on the 2600 version, too... and a lot of hard work THAT
was!   Basically I had to introduce ten zero-bytes at the beginning of every
frame... and adjusted the sprite pointer up/down so that the creature
appeared to slide in/out of cubes.  It worked, but it was very expensive.  I
spent quite some time worrying about how to solve this.  Clearly, I couldn't
afford the ROM space (10 extra bytes/frame).  Equally clearly, the creatures
couldn't just pop on and off (well, maybe clearly in hindsight).  For a
while, I let the creatures do just that - and, though there were no
complaints - it just seemed 'odd' to me.  Then I thought of eggs... and
tried that... and it worked nicely (even added some gameplay element - jump
on the egg before it hatches)... so I'm very happy with how that turned out.
Just 4 frames of egg animation... and I drop all the sliding up/down code
and data.  Good thing, too.

Manuel kept badgering me to let him eat the fruit.  And, for me, this was a
turning point in the coding.  Pretty much up until now, I'd been
concentrating on systems.  I'm a systems guy - I like designing systems and
architecture... I'm good at it.  What I am *not* good at is finishing
things, and hacky-type coding.  By that, I mean code suited for a
single-purpose.  I love general-purpose code which doesn't have any little
fiddly bits for specific circumstances.  So, I'd been concentrating on the
sytems and now it all worked.

But now, I was at the crossroads.  If I was going to make the fruit edible,
it really meant that I was going to start tackling gameplay and creatures.
I kind of dreaded this - this is the stage I always get to where I spend
ages procrastinating, optimising and improving generality... when I really
need to start making the game... the game.

So, Manuel's request came at the right time.  I figured I'd start with the
fruit.  This meant collision detection, and getting creatures to react to
things.  I've done this sort of thing many times before, of course, so it
was pretty straightforward.  I opted for a simple state-machine (which is an
excellent way to do this sort of thing).  Each creature has a table of
pointers to routines.  If a creature can react to a message, it has an entry
in the table for that message.  That way, you don't know anything about a
creature, you just send it a message and let IT worry about what to do about
it.

OK, so I got the fruit working, collisions... and then the weapon, and
started to concentrate on the gameplay.  I had been thinking about what to
do for creatures, of course, for quite a while.  I'd planned to have one
creature opponent, and an invisible opponent which did nasty things.  The
first nasty thing I came up with was to shift cubes randomly.  I posted a
version which did this - moved the cubes - and it worked OK, but feeback was
rather ambivalent.  I wasn't happy with it, anyway, and soon opted to have
an actual creature doing the shifting.

One thing about the Atari 800 version was that I had two creatures on the
screen at the same time (enemies).  I thought this would be a major problem
on the 2600 version (in the end, it wasn't).  But I was trying to figure out
how to make things more difficult - and one creature seemed a major
limitation.  It was a limitation I couldn't get around, though, so it wasn't
much point worrying too much about it.  The game would have to survive with
one creature (that is, visible sprite).

But, as I optimised, and added more frames and AI, and optimised... I found
that I had room for 2, even 3 creatures (and later, even 4...5)...  I had to
find things for them to do.   So, I settled on one creature which shifts
cubes away from their correct place (the Octopus), one which shifts them
towards their correct place (the Rabbit), and one which is an assassin (the
fireball).  Why these shapes?  Well, they looked OK, basically.  It was
exceptionally difficult for me to do reasonable graphics, as I was just
doing character-bit editing in a text editor - no graphics tools.

One feature that I implemented early on was colour-multiplexing of sprites.
You will have noticed that the fruit, for example, is 3-colours (red, green,
yellow) and they flicker (like crazy, on an emulator).  I toyed briefly with
monochrome sprites - but, IMHO, they looked crap.  I traded flicker for
colours.  I'm quite happy with the results, and I'm not sure if it has been
used elsewhere.  I got some nice comments from people about it.   It was
very tricky, though, designing sprites which multiplexed nicely (and, to
tell the truth, I have *still* not seen it on a real TV).

I released alpha #1 on 27/2.  This is basically a version which has all the
features (is essentially complete), but which needs extensive
testing/revision/fixing.  An alpha is a demonstrable game, which can be
played.  I hope my alpha meets that criteria... I haven't gone back and
checked it :))  I got quite a lot of feedback on the alpha... all of it was
much appreciated.

Throughout the process, i have tried to listen to and respond to
feedback/requests.  I hope I've succeeded in that respect.  Many of the
suggestions that I've taken up have significantly improved the game - right
up to the last version!  So, thanks once again for all those who complained
;)

OK, here we are into march.  I'm at the end of the time I allocated myself
for completion... but obviously I'm pretty close.  An alpha is a long way
away from completion, but it was really an unannounced, self-imposed
deadline.  Basically, if I have a time to work towards, I can judge my
progress... if I just did things when I felt like it... it would never
happen.  Now I've released an alpha, things are starting to move.   I'm
getting interest in cartridges, people are starting to enjoy playing, and
I'm getting more and more feedback.  Good!

I'm not sure when, exactly, but in the newsgroups one of the organisers of
the PhillyClassic convention posted a short note about the convention.  I
figured that this would be great - to release a cartridge at the show - and
so, wrote to them and asked what they thought of the idea.  They were pretty
keen on the idea, too, and so I now had a target date (April 20th) by which
I'd not only need to have finished, but by which date cartridges would need
to have been made AND delivered.  But, the timing was good.  Finally, I'd
have done something from start-to-finish.

It was my plan to do the whole shebang (disassemble cartridges, desolder,
burn ROMs, solder, swear, throw away and start again).  I soon realised that
this was impractical, and decided that I'd get the carts made externally.
Randy of Hozer offered a pretty-good price for the finished thing, AND he
was going to the show... so I went with him.  Chris also has boards
available, and I'm still leaving the option open to use his boards for the
later private release.

So, I've got release organised... and I'm in early beta testing....

and disaster strikes!!

Chris, I think it was, pointed out that the game worked OK up until about
screen 4, and then started flickering like crazy.  It was a black few days
for me, actually.  I found out that I was simply using too much time to do
things.  And I really really needed to do those things.  For a while I
thought I'd have to cut back to just 3 cubes (ridiculous!) or ... well, I
was in limbo.  I tried playing with my code, but didn't really see much
hope.  The code was heavily optimised (for speed) but it was still too
long...  I wrote to the list with my options.

I wasn't terribly happy, really.  I guess I thought I'd fix it somehow...
but the fight against the available space vs. the functionality I needed...
was a tough one.

Forgot to mention that I'd spent some time working on a nifty title screen.
I'm sure many of you will remember that.  It was the wonder of the modern
age ;)  I fought long and hard to get back the memory to allow the playfield
title to be installed, and finally managed to fit it in.  And then, Chris's
problem struck.

I blame you for this Chris, I really do!!  Whenever I think of the pain and
difficulty caused by the flickering of >4 cubes...I think of "Chris's
problem".  ;)

At this point I was so short of space that a byte here and there were major
miracles.  And I found that the only solution to the flickering problem was
to de-optimise some code (split it into several subsections and execute the
subsections at different times).  I figured about ... I'm not sure... about
50 bytes, something like that.   Clearly, I wasn't going to find that space
in the already heavily optimised code.

So, the title screen got junked.  It's still there (in the code)... just
disabled.  I have visions of one day restoring it.  But in reality, losing
it was a GREAT thing... it allowed me to add extra animations to the player,
and gave me space for the tweaks, polish, and optimisations that I really
wouldn't have been otherwise able to afford.   Sure, it would be a bit more
'professional' looking with the tile... but it plays a LOT better without
it.

Well, it took a great suggestion from Manuel (not one of my options posted
to the list) to resolve the flickering problem.  I split the culprit routine
into two and executed the second part in the vertical blank, and the first
part in the overscan.  It's all to do with available time.  I still found
that I was using too much time (specifically in the vertical blank), but I
managed to multiplex the routine usage in time (one, one frame, another,
another frame, etc)... and that finally solved the over-usage of time.  That
was the last really thorny issue I had.

Over the last day, I've just finished installing a few suggestions (RESET,
SELECT).  These probably came a bit late in the day - I should probably have
thought this bit out a bit more thoroughly.  It seemed to me that it was OK
to require the player to turn the machine off/on to restart.... but that's
what a reset button is.  Anyway, after a very grumpy and frustrating
night... I got the ahem... frigging code... to work... and I've just posted
my final version.

Yes, I know... maybe not final.  But... closer than anything that's come
before.

The one failing of the game is the lack of decent sound.  The two reasons
for this are that I am obviously not the right person to do sound (I don't
hear so well, and I know nothing about sound/music), and I would obviously
have to trade off some other feature to allow sound support.  The game works
OK with the minmal sound there is...  but it is something I am still a bit
uncomfortable about.   Still, that's really the only missing thing that I
regret.  Everything else looks pretty good to me, and I'm quite happy with
the results.

Personally, I'm a wreck.  Burnt out, not sleeping well... and most
definitely not relaxed.  In other words, a typical games programmer at the
end of a project.

So, there you go Ruffin... that's your post-mortem.  I hope it doesn't sound
too self-centered.

Cheers
A
--
 _  _  _| _ _        _| _    * _                               _  ,
(_|| )(_|( (/_\/\/  (_|(_|\/(_(/_                           ,~' L_|\
                                                         ,-'        \
see my Museum of Soviet Calculators at                  (            \
http://www.taswegian.com/MOSCOW/soviet.html              \    __     /
                                                          L,~'  "\__/
                                                              @--> v



-
Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://www.biglist.com/lists/stella/

Current Thread