Jump to content
  • entries
    40
  • comments
    0
  • views
    1,107

About this blog

Behind the scenes of "Oh Shoot!" 

Entries in this blog

Assembler

I used defines with IFCONST to optionally include code into the ROM.  I added a DEFINE_DEMO which results in reduced features and a different title screen in the output ROM.  If I comment out DEFINE_DEMO the output ROM is the full game.  This is very convenient for debugging and being able to easily enable and disable game features. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Define constants A ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Regions

I created PAL and SECAM builds of Oh Shoot for thos regions.   I couldn't change the number of lines per row to try to better fill the PAL screen so the result is the screen is a bit scrunched with a thick border on the bottom.  It plays well though.  Here you can see NTSC vs PAL:   Here are some SECAM screen shots:   I wonder how bad these would look on actual SECAM hardware and CRT... I guess I'll never know.   Here's the sources I used for t

Select Screen

One part of my game that folks don't seem to like too much or find confusing is the select screen.  I'll just go through the history of the Oh Shoot select screen.   On almost all of the original 2600 games in the select screen all you get is a number and you have to look this number up in the instruction manual's "game select matrix" to figure out what game settings correspond to the game select number.  This kinda sucks!  But it took very little rom space to implement.   Th

Cut Features

Let's look back on things that were cut or never implemented or abandoned for various reasons. -matching ships mode -blinking playfields -striped playfields and backgrounds -multiple ripple maps -complex controls of the Destroyer shots -variable range in helicopter shots -numerous screens including giant galactic toilet paper -kamikaze ship type -invisible ships unless firing -bomb drops when above enemy -Easter egg where human falls o

Helpful Tools

Here's my primary tools for this project: Visual Studio/C#, 8bitworkshop, Visual Studio Code + Atari Dev Studio, Stella, MSPaint, PaintShop Pro 7, Winmerge   I'm curious what tools Atari programmers had to help them develop their games in the old days.  I had Visual Studio and C# which let me crunch out any helpful tools fast.  Here's the tools I made with a brief description.   6502Branching- help me generate assembly to handle <, >, <=, and >= logic.  I was

Loading Frame

I use one frame between matches to load up a screen, process it, initialize player and match settings, and initialize FX or AI variables.   I didn't want a frame of perceptible flicker during this so I had to make sure I had the correct screen colors set before I start the load screen and I draw the score and top barrier so those wont flicker.   I have a a debug option to change the background color after various loading steps so I could ensure I wasn't running out of time du

128 Bytes RAM

128 bytes is a crumb of RAM!   I made a RAM label visualizer to help me see what's going on.  The first blue section is the p0 data, the red section after that is p1 data.  The dark grey section is my screen in RAM.  The very dark section at the end is my function call stack.   I don't want to go off the deep end on what every bit is for.  But there's some interesting points.   The game has two basic modes - FX mode and AI mode.  I made the FX first (all the s

AI: Ranger

It was easy to design the original Ranger before there was any AI.  Things got complicated with the Ranger and MANUAL flight AI though.  The problem was the Ranger didn't know its enemy was out of firing range.  I had a big chunk of AI code that was on the chopping block.  This code was for an attack mode I thought I didn't need.  In this mode the attacker randomly picks one of 3 directions that will move the attacker towards the enemy.  In testing I found this mode annoying as it always lead to

AI: Missiles

A ship with missile dodge assistance has a top and bottom radar to detect missiles.  If an enemy missile is detected by the top radar the ship automatically moves down.  If a missile is detected by the bottom radar the ship will automatically move up.   Here you can see a missile dodge sequence.  Top radar detects a missile.  Ships avoids by dodging downwards.   With harder computer players their radars increase in size making them able to respond to missiles sooner.  T

AI: Optimization

Despite all the optimizations I already mentioned I needed MORE optimizations!   The AI ships don't need to perform a front scan every frame.  So I have two AI modes - "low power" and "high power" and the ships alternate between those every frame with only one ship in high power mode and the other in low power mode.  A ship must be in high power mode to perform the following tasks - a front scan, missile scan and missile dodge initiation, changing to a new state (attack vs meander).  A

AI: Avoidance

Let's talk about corridor escape implementation.  Here is a typical screen showing corridors in green.  Note that there are empty rows that are too small vertically for your ship to safely fit in and hence are not considered corridors and are not green in this image.   It is actually somewhat complicated to determine if a row is in fact part of a corridor because of the requirement that a corridor be at least two rows tall.  So I do the slower more time consuming corridor assessm

Rumble Effect

Let's get ready to rumble!     Rumble is similar to ripple but it's only applied to the top and bottom boundary rows.  The rumble data is a sin table with values that are dampened further into the table.   Rumble sort of breaks AI... but in a good way.  With rumble the playfield is drawn out of place vertically from where the AI playfield detection thinks it should be causing some surprise deaths.

Scrolling Effects 2

How do I get the chunk index from a screen row?   For all the scrolling functions I need to retrieve the playfield bytes of a specific row.  The screens are compressed on the ROM and are stored as indexes to chunks.  So to retrieve a row's playfield data I need to know which chunk it's in.  The problem I needed to solve is given a row number for a screen what chunk is the row in?   This may sound like a trivial problem, but I need to do it very fast since I need this during g

Screen Tool

Now lets get into how these screens are created!   Let's focus on one subbank or one screen set of 64 screens.   Step 1.  draw 64 screens.   I only had to draw half of each screen of course since the end result is mirrored on the Atari.  There are restrictions of course so I can't just draw whatever I want.  Here's the restrictions: -every screen is composed of chunks... every chunk must be stored in the chunk palette but can be reused many times -reusing chunk

Screen Compression

I want to go into more detail on my screen encoding (done in a C# app) and loading (done in the game).  Today I'll discuss screen loading.   This image is half of the 32KB ROM.  This is 4 banks of data containing the 1024 screens in the game and the loading code is duplicated in each bank.   Each bank is divided into 4 subbanks.  The loading code is in the first subbank cutting into the available screen data a bit.   An individual subbank holds 64 screen

Title Screen

I want to talk about my title screen next!   I had a code sample that draws a large object using the 6 character kernel.  I did some brainstorming on how I could use this in my title screen.  I came up with having large detailed (and animated!) versions of the ships menacing each other.   Here's the concept drawing vs the end result.   I developed the title screen as a standalone "tech demo" using a full 4KB!  Half the title screen bank is the graphics!  Most

More Banks

Most of my favorite 2600 games were 4KB - Adventure, Pitfall!, Space Invaders, Frostbite, and Frogger to name a few.  So in my mind a 4KB game is a "standard" 2600 game and that's what I wanted to make and I did.  I thought my final 4KB game was pretty good - I was able to get 64 screens into the game!  I read up on bank switching in my Steven Hugg 2600 programming book.  I had to try it out.  I was always curious how bank switching worked.  I'm glad I did because I could do WAY more with the ex

Main Kernel

Lets talk about the kernel (screen drawing code).  In this image you can see the relative size of the kernel code (yellow) compared to the total ROM.  This is the code that draws the score and the play area.   Although this is only a sliver of code... this is the only code that's executing 72% of the time during gameplay (according to my calculations).   Oh Shoot uses a 2 line kernel.  That means it takes the game 2 lines to update all the graphical elements of the game

Ram Screen Into RAM

For my compressed screens to work I needed to store an uncompressed copy of a screen in RAM.  I made an image showing the relative sizes of total RAM, available RAM, and how much RAM I needed to store the screen.   This was when I learned about Atari memory management and making every bit count.

Sound

Early in this project I was interested in sound.  I didn't have a clue how things worked in Atari land and decided to make a feature rich sound engine.  The sound engine could load and play sounds in either channel (basic stuff), but I also allowed the sound frequency to in crease or decrease over time... I also allowed the volume of the sound to increase or decrease over time.  I even made "compound" sounds which would play back a list of sounds.  I think I was using 12 bytes of ram per sound c

ROM Visualizer

When developing Oh Shoot I got frustrated with running out of space all the time.  I created a "ROM visualizer" to keep tabs on how much space all my chunks of code and graphics were taking up visually.  I wrote some scripts to assemble every single code backup I made (I made a LOT of backups since it's easy to screw things up and introduce weird bugs that can be hard to track down).  I made another script to create a ROM visualization of each WIP ROM.  Then I made an animated gif out of it!  Th

Screen Compression Evolution

I got a bunch of questions about how I made 1024 screens for Oh Shoot!  I hope this answers those questions.   There were several iterations to my level storage solutions.   1.  Naïve level storage -store PF0, PF1, and PF2 for each of the 22 rows of a screen (66 bytes per screen - no compression) -screen drawn directly from ROM -I think this only allowed me 4 screens in my 4KB game?  Don't remember.   2.  Run length encoding -used 4 unused bits in PF0 to sto

"Oh Shoot!" Overview

Here's some basic info about my 2600 game "Oh Shoot!": -32K F4 bank switched 1v1 "Combat Killer!" -uses stock 2600 hardware - no extra RAM or arm processor -1, 2, or 0 player modes -multiple flight modes and speed options -4 ship types -1024 screens -flashy title screen -large flashy animated explosions -full screen scrolling, warping, and rumble effects -fully couch compliant with match tapout -user friendly game select screen -AI controlled players and "AI assistance" f

Development Overview

My first console was a Coleco Gemini!  I thought it was better than an Atari... not sure if that's true but it was really good!  I had a trading network so I could try lots of games without having to shell out a ton of cash.  My head is still full of memories of whose house I saw which games for the first time.  I was always very curious about how these games were made.  Over the years I heard strange things about the 2600 in magazines like it only had 128 bytes of RAM, 4KB games, ~1Mhz processo
×
×
  • Create New...