Commodore‎ > ‎C128‎ > ‎Games‎ > ‎

Battle Blox

This page is about programming a video game in "high" machine language, or equivalantly "typical" assembly language.  In other words, it is for programming nerds.  If you don't care about programming the 6502 CPU then run away now... it only gets more technical!
 
This game is a typical character (font) based game.  It is "lame" in contrast to most 1-player games, but absoultely fantastic for 2-player mode!  I do not know of ANY other C128 game that is so much fun for two players... if you know of one then please contact me... thanks!

If you just want to play this cool Multi-Monitor game, get this download.  Only technical/nerd stuff follows!  P.S., you really should play the game a few times to understand what follows...
 
Anyway, I decided to explain the code in order to encourage developement for any/all other games for the C128.  My code works for either 40 or 80 column and either NTSC or PAL, so hopefully you can use/modify it to make a great game for any standard.  In case it isn't obvious, the VIC screen is limited to 40 characters horizontally (320 pixels in hi-res mode) while the VDC is limited to 80 characters horizontally (640 pixels, but ONLY hi-res mode).
 
For game timing, it is important to note that NTSC has a screen refresh of 60 Hz while PAL has a screen refresh of 50Hz.  Battle Blox uses an abstracted refresh of 300 Hz (simply 50 times 60) which ensures identical game play regardless of your country's video standard.  Well, that is the concept... actually it removes a factor of 4 so that it runs at a virtual 75 Hz for either sytem (i.e., 300/4).  NOTE if you wanted (speed not critical) you could abstract it further to 15 Hz (75/5), but speed is important for this game, so yeah, 75 Hz.
 
And in case it isn't obvious, I'll tell you explicitly that game timing is critically important!!  I don't know how many games that I've downloaded from the WWW that run at an "acceptable" pace in one mode (PAL or NTSC) but are virtually impossible if run in the "other" mode.  If you are using an emulator, such as VICE, this is just an inconvienence because you can easily change emulation between NTSC/PAL, but for real hardware it is a game killer (FAIL) when your software does not run smoothly on real hardware.
 
 
  K.I.S.S.  
 
Keep it simple, Simon!  Battle Blox basically does this:
  1. read input (player q)
  2. update (player q)
  3. switch player (q=1-q)
  4. repeat forever!
Okay, I must admit that is over-simplified.  For example, you must initialize player (q) and you need some way to detect game over, but REALLY that is the philisophy of the game design.  At start-up, there are no players ("preview mode"), but as soon as a player starts a game (by pressing FIRE on joystick as instructed by on-screen display) they are assigned as player 0 (q=0) or player 1 (q=1), depending if signal came from joystick $dc00 or joystick $dc01.
 
FYI, the game also allows Keyboard in addition to Joystick, but we'll ignore that as an "advanced" feature...
 
 
  Read Input  
This part is stupid simple!  Basically just read $dc00,x where X is either 0 or 1 (i.e. X = q).  Save the value for testing...
To be honest, there is ugly code needed for the case of time elapsed is greater than allocated time.  For shame, I need to document this!!
 
  Update Player  
I won't kid you...  this is the most complex part, and is explained below.  But the principle is simple... 
 
  Switch Player  
Very easy, q = 1-q.  Only detail is if single-player game.  Then you need to test if "computer" is the active player and invoke AI.  Nothing else to do if 2-player.  In other words, for one-player you need to test for NOT human and invoke AI...
 
  Repeat Forever  
If either player (human or computer) is "swamped" (all blocks filled) then game is over.  Otherwise, jump back to step 1 (Read Input).
 
...
 
So now the only important thing remaining is "update player".  This is basically a SWITCH statement based on current state:
  • New Level = call 'iLevel'
  • found match = call 'shatter'
  • shatter = call 'collapse'
  • otherwise = call 'fall step'
Well that is hard to interpret without looking at the source code, so let me explain:
 
When the game first starts (or after a level is completed), "iLevel" is called.  This basically just clears the play field and generates a new "fall group"
 
The "shatter" routine just flags all blocks that match (if any).
 
If blocks match (per "shatter") then "collapse" is called.  This routine eliminates the matching blocks, updates the play field, and does a GOTO the shatter routine.  Or for those who like structured programming:
 WHILE shatter() {
  collapse()
 }
Note that a score is calculated during the "collapse".  If nothing collapses, there is no score change and play field remains unchanged.
 
If there were no matches (per "shatter"), drop the "fall group" by one row... otherwise (something shattered), generate a new "fall group" and place it at top row.
 
Oh, by the way, a "fall group" is just a set of random blocks that are generated when "update player" is called (with no prior match/collapse)... for example, at the start of a new level (or when the "fall group" hits the bottom and no matches were found).
 
So that is it!!!  Everything else is details (like input processing, sound effects, AI routines, and video display).  See the source code for details, or contact me if you have questions.
 
  Interesting Techniques
 
Still reading?  Here is some more info that you might find interesting.

In order to conserve memory (this game was for a 4K competition), I called routines in BASIC ROM dealing with PLAY, instead of writing my own music engine.  The interesting thing (IMHO) is that the PLAY string is updated during IRQ routine... this allows the main game loop to run non-stop.  This is different from BASIC behavior where PLAY will stop the program until the last note in a PLAY string starts playing.  This "play in background" is really cool, and a shame that BASIC does not have this feature.  Study the source code to learn more!  On the other hand, there are times when the program will wait for a piece of "music" (most are really just sound effects) to finish before it continues.  So, the program uses both methods (play-non-stop, and play-and-wait)!

Although the blocks in a "fall group" (two blocks per group) are generated randomly, they are saved in a buffer so that both players get the same sequence of blocks.  This makes the game as "fair" as possible.  Imagine the opposite... each player gets a different set of random blocks... hopefully you agree that if that happened, one player could gain an un-fair advantage by getting a "lucky" set of blocks.  Another way to think about it: both players get the same list of blocks to play... the only variation is how fast the player sets his/her blocks into position, and how clever (efficient) he/she is at eliminating blocks.  (Of course the list of blocks is random so that games are not identical.)


© H2Obsession 2011, 2014, 2015
Comments