Jump to content
IGNORED

New Atari 2600 Homebrew: Oh Shoot!


Recommended Posts

Posted (edited)

Let's talk about the scrolling effects.

 

So I had a huge amount of screens for my game, but I didn't want to stop there.  I wanted some sort of animation or movement to my screens.  It would have been nice if I could store multiple frames of animation for the various screens, but it wasn't possible.  I came up with the idea of scrolling the screens in and out both horizontally and vertically!  Most scrolling on Atari games is just cycling the playfield bits in RAM of each row being scrolled.  I wanted to scroll something off screen into view.  Requiring loading a few bits or bytes from another bank each frame.

 

Horizontal scrolling and vertical scrolling on the 2600 are two very different problems.

 

Horizontal scrolling

HScroll.gif.383efd6be3b71d9224a52173e35cb696.gif

The basic states of horizontal scrolling are:  scroll, reverse direction, sleep

 

The scrolling itself is done two rows at a time.  There is an optional delay each time the screen has been scrolled over a full tile to control the scroll speed.  Scrolling the screen out (of existence) is pretty simple and I already described horizontal scroll in.  Scrolling in is not very efficient.  I have to read an entire playfield row and extract a bit for each step of horizontal scroll in.

 

One thing that irritated me was scrolling in screens like the one below with a lot of empty space on the sides of the screen.  The game would be scrolling in the screen for a long time but its scrolling in nothingness for a long time.  My solution to this problem was to figure out the maximum that I could scroll the screen in before some of the screen becomes visible and that would be the initial scrolled out amount.

6-0-59.thumb.png.60ce3f39086abb95ff73013b23d878b6.png

 

Vertical scrolling

VScroll.gif.6451eb7b7e8d5deec92fcc17c9096ebe.gif

The basic states of vertical scrolling are:  scroll top in/out one row, scroll bottom in/out one row, delay briefly, loop until screen is scrolled in/out, reverse in/out, sleep

 

Note that the top and bottom parts of the screen scroll in or out in alternate frames to reduce processing per frame.  Rows of playfield are loaded in from another bank as needed.

 

The starting point is the top and bottom are scrolled out the minimum amount possible such that none of the screen is visible.

 

There is one major step missing with the vertical scrolling.  How do I decide where to partition the screen into top and bottom?

 

Consider these screens.  Where should the screens be divided into top and bottom for vertical scrolling purposes?

6-2-8.thumb.png.b152282916be72fc0c8cc570a09f54be.png6-3-11.thumb.png.3350afa6d065228a6cb83e4897840157.png6-2-25.thumb.png.54aff4c28cd366179794886cc0561e30.png

The game stores no metadata about the screens.  So I don't store where the screen should be partitioned.  I had to devise some algorithm to divide the screen in two pieces.  I wanted to maximize variety in the game so if possible I wanted a screen to be partitioned in different ways.  When partitioning I wanted single row open spaces to be considered as solid.  I can't divide anything considered solid.

 

So this is the basic algorithm I came up with to find a reasonable screen partition.  Pick a random starting point in the screen that isn't too close to the edges.  If initial spot is not a solid row... then start row is our partition.  Otherwise loop upwards if we find a non solid row above us... set current row to partition and done.  Otherwise loop downward until we find a non solid row below us... if we do set the current row to the partition.

 

This will get us a dividing line somewhere that isn't in the middle of a solid object.

 

Here's a few examples of partitioned screens:

6-2-8-Copy-Copy(2).thumb.png.b408d32139f587732ac154090904bb9a.png6-2-8-Copy-Copy(3).thumb.png.3b490ee8d06ea4def373be23dfd9c82d.png6-3-11-Copy-Copy-Copy.thumb.png.b8ba031df04588e4a4a6a110fdbbd2d0.png6-3-11-Copy-Copy.thumb.png.894c780f10edab91e7f9161e682a6093.png

 

You can see how even with the same screen the vertical scrolling can be quite different.

 

There is one "detail" that I haven't discussed.  For the scrolling functions I need to load in playfield data from a specific line.  The problem is the screens are compressed into chunks... so how does that work?  Next time I'll tell you.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

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 gameplay - not during screen load.

 

One option is to loop through the chunks of a screen and count up the chunk sizes until I pass the desired row number.... this is a terrible idea - too slow!

 

The solution I came up with is having a bit per row indicating if the current row is in a new chunk with a 1, or 0 if the current row is the same chunk as the previous row above it.  These bits are stored in 3 bytes called NewChunkIndicies0, NewChunkIndicies1, and NewChunkIndicies2.  Then I have to count all the bits down to and including the desired row.

chunkychange - Copy.png

 

Let's look at some examples.

chunkychange.png

 

The red line!  How do we count the bits down to and including the red line?  The red line is on the 7th bit of the first NewChunkIndicies byte.  Lets just mask out the bits of the first byte we're not interested in.  So let's do this:

Temp = NewChunkIndicies0 AND #%01111111.  Now we need to count the number of bits in Temp.

 

That brings us to another problem.  How do we count the number of bits in a byte?  The solution I came up with is having a 256 byte table with a bit count of any possible configuration of 8 bits.  That's a BIG table for the 2600!  I came up with an optimization.  I just store all possible bit counts of a nibble.  This is only a 16 byte table.  I use this table to add up the bits in both nibbles of a byte.  It's a tad slower but worth the speed tradeoff.  Let's put this in a "CountBits" function.

 

Going back to the red line problem I just put Temp into CountBits and get chunk #3.

 

Now what chunk is the orange line in?  The orange line is on the 10th row.  So to find the corresponding chunk I need to add up the first 10 bits of NewChunkIndicies.

 

I do CountBits on the entire first byte of NewChunkIndicies, and CountBits on the first 2 bits of NewChunkIndicies1.  So with some masking , counting and adding I get chunk #4.

 

For the lime green colored line I do the following:  CountBits(NewChunkIndicies0) + CountBits(NewChunkIndicies1) + CountBits(NewChunkIndicies2 AND %00111111).  This comes out to chunk #11.

 

Sorry if this was kinda boring, but game programming isn't all fun and games.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

Let's get ready to rumble!

 

Rumble.gif.169f63ec7dfc41daa070502a3639a1e4.gif

 

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.

Edited by Piledriver
  • Like 3
Link to comment
Share on other sites

Story time!

 

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.

RAM3.thumb.png.23dcc0bba779a36137e12942cd9ee009.png

 

This was when I learned about Atari memory management and making every bit count.

  • Like 2
Link to comment
Share on other sites

Posted (edited)

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.

bloop.thumb.png.66f5225a14b8e43a8aa481b42c4261bc.png

 

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 - players, missiles, and playfield.

 

Kernel line 1

-draw missile 1

-draw missile 2

-prepare sprite 0

-prepare sprite 1

 

Kernel line 2

-draw both sprites

-draw line of playfield

-decrement PF row remaining line count

-if row remaining line count is 0

    -increment playfield row pointer

    -increment ripple table index

    -set row remaining line count based on the net value in the ripple table

-decrement x

-if x is 0 exit kernel

 

If the missiles and sprites are updated on alternating rows shouldn't they be offset by one pixel?  They are.

MissileOffset.png.dac32e7a574d6fafaddaa60f8305b34e.png

 

While the "nose" of the helicopter looks like one square pixel, it's actually made up of two rectangular pixels.

 

My kernel code is very simple!  It does do the ripple effect, so that makes it a tad more complex than the Combat kernel.

Edited by Piledriver
  • Like 3
Link to comment
Share on other sites

Posted (edited)

Lets talk bank switching.

 

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 extra ROM!

 

At 4KB I was completely desperate for any spare bytes.  More bytes equals more game.  When I switched to 32KB ROM I had more ROM than I knew what do with (at first).  Initially I had this idea to have a few frames of my animated face as an Easter egg... and it would use up half the ROM.  My new profile photo is a mockup of my face in playfield graphics style from that time.  I'm glad I didn't implement that dumb idea; however, that dumb idea might inspire a future game - no promises.  Bank switching allowed my favorite aspects of this game to be possible - the title screen, the AI, big explosions, tons of screens, select screen, and many more subtle refinements to the game.  While my game is no longer "standard" it is still something that could have theoretically been made back in the day.  Fatal Run on the 2600 was a 32KB game that came out in 1990 while the 2600 was still officially supported.

 

20240511-Bank0Aligns.png.a9c211afe58f21ee8d762eed66926526.png

 

I know there are many different types of bank switching.  I didn't want to get fancy with this so I stuck with "standard" F4 bank switching.

 

Now lets get into some drawbacks of bank switching.  It's a tad slow - I calculated 27 cycles per bank switch.  My bank0 was full so early on I moved code from my VBlank and Overscan into Bank1 and then I jump into Bank1 to run those code chunks and then jump back to Bank0 as needed.  I have bank switches to and from the title screen, to and from the banks of screens, to and from the select screen, and to and from the AI code.  That's a lot of jumping around even though I tried to keep it a minimum.  I thought it was kind of weird how I did a bank switch during the drawing of the select screen as I mentioned earlier.  This allowed me to draw the ship sprite on the select screen without duplicating code to do so.

 

One thing that really annoyed me with banks is that any label defined in one bank is visible from any other bank.  This would cause me a lot of frustration when I would mistakenly called a function in another bank... the address of that function was not correct in the bank I was in so I would jump to some "random" location in the current bank.  To reduce my confusion I would tack "_BANK0" labels on any subroutine or table in Bank0... and of course I did this for all the other banks as well.  I wanted it to be crystal clear to me when I scrolled into another bank so I would have comments like this in my code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;*BANK2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;                                                     ;;;;;;

;;;;;;  ###   ##  #   # #  #     ##                        ;;;;;;

;;;;;;  #  # #  # ##  # # #     #  #                       ;;;;;;

;;;;;;  ###  #### # # # ##        #                        ;;;;;;

;;;;;;  #  # #  # #  ## # #      #                         ;;;;;;

;;;;;;  ###  #  # #   # #  #    ####                       ;;;;;;

;;;;;;                                                     ;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

 

My next annoyance is related.  When debugging code in Stella all labels from all banks were applied and would often clobber each other.  This made debugging very frustrating because the labels were all messed up and I had no idea what code I was looking at!  This was such a big problem for me I was tempted to quite this project a few times.  In my opinion all these assemblers and debuggers should disregard labels defined in other banks.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

I made some perler bead designs from my title screen graphics.  I haven't gotten around to making any of these yet.  Hopefully around Christmas.

ranger0.thumb.png.827840397cff3990766435ce65341525.pngbanshee0.thumb.png.f9d8fb600a7bc6914174bd5925d250e7.pngspectre0.thumb.png.a8e68013b05603ff485df0f7dbcc57b1.pngdestroyer0.thumb.png.ac6e6b7a47b59ce1e489000387c744c4.png

 

Here's some perler art I made long ago.

Dsc09144.thumb.jpg.7535199d2fc6e3185c59818c26a1852b.jpg

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Lets talk about the loading screen... or should I say 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 during the loading phase.  The loading can take varying amounts of time based on the screen complexity (number of chunks), the index of the screen (how close is it to one of the 8th screens, and whether the game is in FX mode or AI mode.  The yellow space is how long it takes to load and process the screen.  The next bar below that is how long it takes to setup the FX or AI.  The grey space is idle time.

FX.png.a183c5d17745922d147b42495e95878e.png     AI.png.4ebbe81d4d4d77669a9178b479ce9d6c.png

 

For the FX setup I had to randomly setup if there was vertical or horizontal scrolling, and what the scroll speeds would be, whether there is ripple or not.  If there was vertical scrolling what is the screen partition.  I had to setup the NewChunkIndicies values.  Initial scroll in state was another setting.

 

For the AI setup I needed to setup the CorridorMap values.

  • Like 2
Link to comment
Share on other sites

Posted (edited)

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 making so many errors typing in BCC or BCS or whatever... my brain only understands the crocodile head facing the greater amount.
CodeAnalyzer- generates list of macros and subroutines in my assembly code
CodeFormatter- eliminates tabs and automates indenting
CodeSubroutineCallAnalyzer- creates a report of how many places a subroutine is called.  This helped me find abandoned subroutines that were wasting precious ROM space.
HACKAssembleALLROMS- automated assembling all code files in a folder - step 1 for my animated code gif
Make48PixelBitmapCode- takes in a black and white 48 pixel wide image and converts it to a data table for the large sprites in my game.
MakeExplosion- takes multiple cells of explosion animation and converts it into data tables - one for each frame.  This allowed me as an artist to easily create the explosion animations.
MakeRAMBitmap- created the RAM image with labels
PlayfieldsGenerator- converts level pngs into compressed level data and outputs reports on all screens and data chunks, and a screen collage
RumbleSinData- output sin values for the screen rumble
SinData- output the sin values for the ships in the title screen to float up and down
WaveyGenerator- this helped me generate ripple maps
WindowsFormsApp- terrible terrible app name!  This creates the visualizations or ROM "x-rays" which helped me locate wasted ROM space
 

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

I created PAL and SECAM builds of Oh Shoot as well.

 

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:

OhShoot.txt.thumb.png.82a1f6621a44a7ff2ef87f2a2b4d8b74.png

 

Here are some SECAM screen shots:

OhShoot.txt_2.thumb.png.db76327622efe442309941cc53b30ec3.pngOhShoot.txt_3.thumb.png.f31e8d80ca101f2a15ba4ce77f7a698a.png

 

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 the conversions:

https://www.youtube.com/watch?v=o9wz3pdFU6o&t=163s
https://forums.atariage.com/topic/282616-ntsc-color-constants-and-instant-pal-60-conversion/

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

Lets talk about debugging.  I got frustrated debugging on 8bitworkshop.  With more complex bank switched code things would mess up and the program counter would just go to weird random places.  I switched to VS Code and Atari Dev Studio for this reason.

 

Despite the issue I had with the bad labels with bank switched code Stella is an amazing emulator and debugger!  You can step ahead by an instruction, a line, or a full screen.  You can even jump back in time, so if something weird happens you can rewind and not miss it!  Having AI players also helped with debugging since weird stuff would always reoccur identically after a rewind which might not be the case with human input.

 

Let's look at how Stella helped me fix two bugs.

bug 1.  inconsistent scanline count - especially during a screen transition.

Run this "breakIf _scanEnd!=#276".  So if any frame doesn't match my target frame rate the emulator will halt.  I hate glitchy screen jumps in 2600 games.  With this its very easy to track down the problem... and also identify the problem as it can be easy to not notice.  Fixing the problem is another matter though.  😀

 

bug 2.  At the moment a player dies the AI opponent can crash into a wall!

This occurred pretty rarely but really annoyed me and was in the game for a long long time before I finally tracked it down.  With Stella I could have 4 (more if I wanted) instances of the game running with computer players with the emulation speed cranked way up and a breakpoint for a player crashing into a wall.  This let me track the issue down pretty easily!  What happened was there was too much processing for the one player dying so it disabled the wall collision check for the other player allowing it to crash.  The fix was to disable the other players movement for that one frame.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

Lets 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

image.gif.105602f33d7c9f9bf99bc0757df98106.gif

-kamikaze ship type

-invisible ships unless firing

-bomb drops when above enemy

-Easter egg where human falls out of explosion, crashes into wall and causes another explosion

-Easter egg using half the ROM to display my animated face

-Easter egg that displays the full color palette

-giant explosions

-screen flash on player death

-special explosion effect at game over

-32 color palettes got cut to 16

-ships on title screen moving up and down and also forwards and backwards

-bricks filling in the screen on manual matches that take too long

-crashing at edge of screen in auto flight mode

-explosion clipping

-sometimes ships are damaged but not destroyed

-different engine sounds for different ship types

-sound engine was greatly simplified

-more strategic firing for AI instead of continuous auto fire

-different sound pitch for p0/p1 shots and explosions

-inverted controls for vertical movement

-some alternatives for a 1 player mode where you had to fly through a series of obstacle courses with rippling screens and scrolling going on

-the same color palette would be used for 4 screens in a row

-mismatched shot and ship speeds

-hover in place AI state

-AI far scan

-smooth vertical scrolling

-vertical scrolling and ripple effect at the same time

-a user friendly select screen  ;)

-complex flight path of ship in select screen

-mirrored ship doppelganger in select screen

-chunk of playfield on select screen demonstrating FX functionality

-horizontal and vertical scrolling in the same round

 

In the end I think I made the right choices with my cuts.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Posted (edited)

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 a collision.  I did however find a use for it to move a Ranger closer to its target so the target is in firing range.  If the Ranger picked this state every time it was out of range or even half the time when it was out of range it would tend to get stuck on obstacles trying to get to the other player.  So in the end when the Ranger isn't in range and its time to pick a new state there is only a 25% chance it will go into attack mode to move closer.  This is not perfect but it seems to work ok.  I think it was the best I could do since path finding is out of the question.  Special case code like this is a real pain on the 2600.

 

OhShoot.txt_4.png.e9a9144099b7cdd5cfdef4e15d11618a.png

 

Also none of the ships know how to avoid shooting into playfield obstacles, so you will see some dumb computer behavior there when playfield blocks shots.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

I just love exhibition mode!  Not only was it extremely helpful for debugging the game, but it just looks cool.  It adds to the ambiance of a retro gaming room.  Sometimes I'm doing something on my computer and I just like to see the Oh Shootians out of the corner of my eye.  By the way exhibition mode is in the demo.    :)

Dsc02988.thumb.jpg.9c9a24815dd178ab1d8610fd5aa22075.jpg

 

  • Like 2
Link to comment
Share on other sites

Sprite positioning on the 2600 is a lot weirder than you'd expect.  For some reason my missiles were always off by a pixel.

Incorrect.png.f48c18e1dd961fe7ca3aaec096403015.png

 

I hacked in some missile offset code to compensate and make things look good.

Correct.png.a11076a9ee3c67bdc7d0fcf8a2e0bfe1.png

 

The sprite positioning code in my book and other online sources all pretty much had the same issue.  Then I found this:

https://bumbershootsoft.wordpress.com/2018/08/30/an-arbitrary-sprite-positioning-routine-for-the-atari-2600/

 

When I put in this sprite positioning code I had to undo my hack to make things look right.

  • Like 2
Link to comment
Share on other sites

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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DEFINE_TITLEFIRST
DEFINE_PLAYAUDIO
DEFINE_SCREENFLIPS
DEFINE_RUMBLE
DEFINE_WAVES
DEFINE_COLLISIONDETECTION
DEFINE_SHIPCOLLISIONS
;===========================
DEFINE_DEMO
;DEFINE_BLACKSELECTSCREEN
;DEFINE_DISPLAYMETRICS
;DEFINE_MAXMISSILEDODGE

 

I recommend not just learning 6502 assembly, but also reading the dasm manual.  That's how I learned about using aliases with SET.  This allowed me to set aliases for my variables that are only in effect for a subroutine.  Some of the algorithms could get a bit complex and with variables like TEMP, TEMP2, TEMP3, and TEMP4 it can be very difficult to understand and debug.  Appropriate variable names makes things much easier to understand.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Partition Screen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;This function needs some cleanup
VerticalPartitionScreen_BANK0 subroutine
.StartRow SET Temp
.TempRow SET Temp2
.TopRow SET Temp3
.BottomRow SET Temp4
  • Like 2
Link to comment
Share on other sites

Posted (edited)

I wanted to highlight some refinements to polish the game up.  I had plenty of brainstorming sessions on how I could improve the game.

 

1.  Random initial weapon cooldown value at start of round for computer players.

Without this the ships would be shooting perfectly in synch and would often die perfectly in synch... I didn't like that.

 

2.  Half width front scanner for moving human player.

I mentioned this already, but worth mentioning again.  This makes it much easier for a human to navigate a complex screen while still benefitting from playfield dodge assistance.  You would definitely notice if I hadn't implemented this!

 

3.  Sticky edges for AUTO flight games.

I was crashing immediately at the start of a round too often in AUTO flight rounds.  By keeping the player stuck to the edge of the screen for about a half second at the start of the round you have a bit of time to react and don't have too many cheap deaths right at the start of the round.

 

4.  Disabled couch compliance at end of a match until screen saver starts.

This prevents the player from jumping to the select screen immediately after a match and gives them time to read the score.

 

5.  Random initial delay before scroll out.

This delay can be fairly long so the screen may start moving when you don't expect it.

 

6.  Flipping screen when it's scrolled out.

Spices the game up a bit - creates unexpected surprises on occasion.

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

Thanks for sharing some of the insights into your program!  I know when I develop Atari 2600 games I've gotten a lot of inspiration and helpful hints scouring the AtariAge archives to see what others have done.

  • Like 2
Link to comment
Share on other sites

Posted (edited)
4 hours ago, littaum said:

Thanks for sharing some of the insights into your program!  I know when I develop Atari 2600 games I've gotten a lot of inspiration and helpful hints scouring the AtariAge archives to see what others have done.

You're the Electroball guy!  I'm not normally big on sports games, but you made AI on the 2600!  I'll check it out!  We're both Gustavo Pezzi and Steven Hugg students.   :)

 

This forum is pretty cool.  I can get all the behind the scenes info on games I craved back in the day.  And yeah maybe my game dissection will inspire another game or help improve another game.

Edited by Piledriver
  • Like 3
Link to comment
Share on other sites

Let's talk color fade in and out.

 

This was possible and actually fairly easy due to the 2600 color palette.

OhShoot.txt_1.thumb.png.d46f367693408a318241d9d78e16bfd0.png

 

I kinda did a cheesy version of color fade in.  All I did was clamp the luminosity or brightness of each color to a "max brightness" each frame.  The max brightness becomes larger over a few frames up to full brightness ($15) during fade in and likewise max brightness drops to zero over a few frames during fade out.

OhShoot.txt_4.thumb.png.a7ab7804ccc44c114ad936ac68aee7be.png   OhShoot.txt_6.thumb.png.4ea16f0abaa3c16f545f777e45513731.png

 

I chose not to do this between rounds... I wonder if that would have been annoying or an enhancement?  Oh well its fine the way it is.

  • Like 2
Link to comment
Share on other sites

Posted (edited)

Story time!

 

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 processor, and no frame buffer!  I remember how lots of the graphics were blocky, but sometimes things were super detailed - ET title screen, Space Invaders shields.  Sometimes the screen would be filled with color, but usually not so much.

 

When I realized there was a book on programming the 2600 (by Steven Hugg) I had to give it a go!  I didn't want to just monkey around with some code I wanted to make an actual game that didn't suck.  I was aware that programming the 2600 is extremely difficult so attempting something like Pitfall 3 would not be a good idea.  I figured I'd make a simple game.  The 2600 was designed to play Combat so I figured I should make a game similar to that.  I wanted my game to be faster and easier to control.  I wanted to keep things fresh with random ship assignments and quickly moving to new arenas instead of staying in the same one for 2 minutes and 16 seconds.  I had a rough idea of my 4 ship types right from the start.  I was always looking for ways to spice up the game so I came up with the two different flight modes - manual and auto flight.  I initially planned to have 4 arenas, but with run length encoding I was able to make 16 simple screens.  At this point I thought I had a decent game that could compete with Combat and I was done.  I even made cartridge cover art and had 2 cartridges made from AtariAge.

OSCartridge.thumb.JPG.247aecf83d2b1b062717b15bded62047.JPG

 

I wanted to do a making of "Oh Shoot!" YouTube video.  For the video I wanted a bitmap of the 4KB ROM showing every bit in the game.  When I made this bitmap I couldn't stop staring at it.  I just knew there had to be a way to pack more screens into the game.  I spent hours staring at a bitmap of bits n bytes.  Eventually I came up with the chunk palette and screen in RAM solution.  I was able to make 64 screens but they were extremely simple.  I was desperate for bytes.  I reviewed all my code and found some waste and things that could be simplified.  The sound engine was completely gutted, and I found all kinds of waste sprinkled throughout my code.  I was able to get a reasonable amount of detail in my 64 screens while still in a 4KB ROM.  This was pretty exciting stuff!  I once again thought I was done the game.  I didn't make any cartridges this time.   :)

 

I decided to play around with bank switching.  I figured 16KB would be plenty.  I first used one bank just for screen data.  Suddenly I had 256 screens.  I added another bank for screens.  Now I had 512 screens.  I was very pleased with what I had at this point but I figured the game needed a title screen.  I spent about as much time on the title screen as Atari did on the ET cartridge.  Although the title screen was based on very simple drawings that looked like kid's drawings the end result looked pretty cool.  I had large animated ships moving around on the screen!  Since Atari programmers in the past couldn't get credit in their games I figured I'd put my animated face in the game.  I didn't stick with this idea for very long, but I did want more screens!  I wanted to double my screens to 1024!  This sounded like a mind bogglingly large number of screens for an Atari game to me and I figured it would sound impressive to others as well.  So I switched to 32KB and crunched out more screens.  Making these screens was fun and low stress compared to 2600 programming.

 

I pretty much did all this in isolation.  I saw it as a challenge to make the best game I could using mostly what's between my ears.  I wasn't too interested in all the fancy modern 2600 stuff like arm chips, expanded RAM etc.  All that stuff is cool mind you, but it's not part of the Atari I grew up with.

 

Even though I had a lot of screens I figured people would get bored pretty quick and I still had a lot of RAM left and 2 empty banks.  I went and designed my scrolling effects.  I loved those effects!  The funny thing is all this time I was making a 2 player game and I had no one I could play this with.

 

I was thinking a lot about making an AI player but thought it was impossible.  I was completely out of RAM, but I still had a full bank of ROM that needed filling.  I figured maybe I could shut off the scrolling effects and reuse that RAM for AI game modes.  This was some of the most challenging programming I've ever done but I got it working to my satisfaction!

 

Doggie.thumb.png.751d9712abf9d566ec303d067e6a10ac.png

The dark side of all this is that it could be extremely frustrating at times!  Sometimes a few weeks would go by and I had made zero progress.  Often working on this game felt more like a nightmare than a fun project.  I often had this feeling of dread that I would not be able to get my latest feature working because of the constraints and would have to fall back to a less interesting version of the game.  I didn't see many Atari games that feature what I was trying to do.  I definitely did not choose the path of least resistance here.  Hopefully the end result of all this is people will see my game as something that stands out and not call it a Combat hack.   :)

 

OhShootExplosion.thumb.png.e0fb1e7070615795f4cff139c5a6731d.png

What is an Atari Video Computer game?  A delightful carnival of bytes ... and a miserable little pile of hacks!  Enough talk!  Have at you!

Edited by Piledriver
  • Like 2
Link to comment
Share on other sites

  • 3 weeks later...

Story time!

 

I sort of hijacked this local community board game night tonight with my Atari setup.  I figured I could get some kids' reactions.  The boys appeared to be having a good time and some girls too.  These kids ranged from grade 2 to 6 I think.

Dsc03225.thumb.jpg.06f3991cc44fc3140ef637208e000f80.jpg

 

Some popular girls with long hair and cell phones checked the game out for 10 seconds.  One of them said "this makes my eyes hurt.  I can't watch anymore."  I lolled.

 

Nobody even looked at the instructions I wrote up.

Dsc03227.thumb.jpg.a04c7fff0d3a0c649f0cf899c27c4530.jpg

Link to comment
Share on other sites

OMG!  I just heard that Nolan Bushnell will be at PRGE 2024!  That means there's a good chance he might play my game!

 

@Albert could you do me a favour?  Please try to get a video (landscape orientation) of Nolan saying my game is "neat" assuming he feels it's worthy of such praise.   :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...