Sunday, 7 February 2016

Let Python be with you... Always...

What do you call acquiring  a new Atari 2600?  Well, how about an excuse to recreate a classic console Star Wars game in Python - both riding on the current Star Wars hype, as well as getting that retro-nerd fix at the same time.

The force was strong in 1982...

Back in the 80's, Parker Bros. decided to enter the home console market (being traditionally a board game company) and released a range of licensed Star Wars games for the Atari 2600.  I fondly recall playing one in particular, The Empire Strikes Back , as a kid while in an appliance store with my parents.

It was cool (because Star Wars was also pretty recent back then), and was a fairly simple game - you fly your snow speeder and take out the ever advancing ATAT walkers before they reach Echo base.
Its this simplicity that was fun - and of course that made it one of those "I bet I could recreate that in Python pretty easily" challenges...

Plus having just bought an old Atari 2600, I was full of retro-stimulated adrenaline (ok, perhaps that's a tad over the top)...

So - I decided to just do it - for the challenge of it, and because its always fun to code.  This blog article is a look at my initial take on the game.  Its still got a few small bugs I will need to iron out, and it was a bit of a hack to write (there's always room to clean up, optimise and improve the code) but it worked and it can be a lot of fun to play.

However, this post isn't about the code (you can download that and read it for yourself), but a very brief overview of some of the logic and approaches I used to build the game.  Hopefully they may be of interest to those who may want to code games of their own.

Play it for yourself

I've put the game code, graphics and sounds up online for you to download and rip apart (feel free to bug fix as well!).

Note that this is a .7z file, so if you are unfamiliar with this compression format, then go and grab the free opensource 7Zip tool online.  Its a great compression tool, and supports all the standard formats as well..

How do I run it?

I'm sure if you're reading this post, you're possibly already familiar with all of this and thinking this is a pointless thing to ask - However for those who aren't (and are curious about the game project rather then the programming), I figure I should point this out...

You also (obviously) need Python 2.7 and pygame installed.  I do most of my Python scripting using Notepad++, and the awesome PyNPP plugin (which you'll find from Notepad++'s plugin manager) that lets me run the scripts from Notepad++ directly.  However there are plenty of great IDE's and editors, as well as being able to just run the program from the commandline.

Breaking the game down...

Lets take a peek at the game itself. The overall logic of the game is fairly simple - 5 ATAT walkers travel a scrolling game field from left to right.  The player flies the Snow speeder left and right, shooting at these ATAT walkers,  The aim is to prevent the walkers from reaching the far right of the play field, which is where Echo base is.

Original Atari 2600 - screen captured running in Stella

A radar at the base of the screen lets us see where the ATAT's are in relation both our player and the game field itself. The game ends when either the player dies, or the ATAT's reach Echo Base and the Shield Generator.

The player must hit an ATAT multiple times (48 according to the Atari version) to slow them down and destroy them.  All ATAT's advance as fast as the furthermost  right lead ATAT - even if hit, ATAT's further back still travel as fast as the leader.

As each ATAT is hit, their colour will slowly change to indicate their damage (every 8 hits).  Once they reach yellow, they are close to being destroyed.  When an ATAT is destroyed, a new ATAT will appear on the far left, continuing the assault on your base.

As the game progresses, every 3 ATAT's destroyed gradually increases the speed of the ATAT's as well as the frequency of their shooting (this is something I set, no idea what the games rate was).  In terms of the player being hit by the ATAT's fire, its worth noting that they only damage the players ship.  However the ship can only sustain four hits before its destroyed.

A good strategy to slow down the ongoing attack is to shoot the lead ATAT to decrease its strength, which in turn will slow down the advance of the rest of them.

Fighting back

As far as player advantages go - As the player gets hit, they get damaged.  As mentioned, after four hits, the player dies and loses one of their 3 lives.  However, the player can land on the ground and repair their damage - though only in the valleys (lowest parts) of the lower terrain..

At random intervals, a target locator will appear on an ATAT's legs, allowing the player to hit this to seriously damage the ATAT and drop its strength substantially - when in this state, they require just 4 hits to destroy.

In the original Atari game, the ATAT had a 'smart bomb' port - a flashing dot on the body...  When that was hit, the ATAT was destroyed instantly.  However ATAT walkers never really had Smart bombs in the movies...  They could however tie a tow cable around the legs and then blast the walker...  I figured just mixing the two ideas there made a lot more sense... And meant I could put a little personal twist on the game.

Some other differences

There are a few details I've not implemented in this version of the game.  ATAT's could also fire a smart bomb, which would follow the player on screen.

If the player can survive on screen for 2 minutes, they get 'the force' which enables them to have 30 seconds of invulnerability in which they can shoot the ATAT's without fear of being destroyed.  This is a relatively simple feature (a couple of timers to trigger and enable the effect) however I've not coded this in... I figured the game is challenging, but perhaps a little easy to warrant this - mind you, that could be a sign that I need to improve the difficulty...

Screen scale

I wanted to base this loosely on the same resolution of the original game.  The Atari 2600 had a fairly low resolution by todays standards, and I figured I'd go for a classic 160 x 94 pixel mode.  To get the graphics authentic to this resolution, I measured them up loosely in Photoshop.

Because this resolution is so tiny, I set a scale mode in the code and used pygame's transform() function to scale up all of the sprites, etc.

The logic - or how does the game work...

This article, I'm just going to explain the logic behind how each feature in the game works at a fairly layman's level.  At some stage in the future I may decide to write up something about the code behind it, but for now lets just take a peek at how I approached various things.

The scrolling attack

The first thing I thought about with the game was how I would create this simple parallax scrolling game field.  It also raised the question about how I would keep track of where the ATAT's and player was, and how I would translate that into a radar display as well.

The solution was extremely simple - rather then scroll, we just move the player and ATAT's using their coordinates, and then draw the graphics based on whether they sit anywhere within an area around where the player was (given the player is always on screen).

By checking for coordinates within +/- 32 pixels outside the viewable area, it made the appearance of the ATAT's appear to smoothly come in from outside the screen rather then pop into existance if we were to check for the location being exactly inside the screen bounds.

The Radar

Was very simple.  The game field was 1280 wide (as in X coordinates within the game, not a pixel value).  The radar is a rectangle that is 128 pixels wide on screen - so, (X location / 10) = the pixel.  Easy! :)

Speeding up and down

One observation in the game was that the players ship would appear to accelerate and decelerate (rather then just move at a constant rate on screen).  Creating this was very simple - two delta values that increment and decrement by one as the player holds the keys down.

This value was used to update the players actual X and Y values, and obviously if the delta is small, the change would be small.  As the delta increases, the amount that is added or subtracted is also larger.

The players ship sits at a fixed screen location on the right of the screen, and to create the effect of acceleration, a small amount of shift from its location helped a lot (again as observed in the real game).  This was also easy - draw the players ship +/- the speed value from the fixed screen location.

Parallax mountains

As for the parallax terrain (two simple 80 x 8 pixel high graphics) it was a case of doing something similiar.  By creating a tiled strip of each (240 pixels wide) and using an X coordinate range of -80 to 0, simply moving these strips on X and resetting their positions based on when the edges of this graphic were at the edges of the screen, there was no need to create tiles or bother about actual 'scrolling' over a large terrain map.

To achieve the parallax effect, the bottom area would move at the same speed as the player, and the top part would move at the same value, but divided by 4 to slow it down and create that effect of distance.  This type of effect was common in older games to give games some depth - the classic Moon Patrol is a prime example of parallax terrain (though in 3 layers rather then just these two).

The rest of the game screen was constructed of flat blocks, so no need to process anything else to create our scrolling game.

Collision detection

The nice thing about the simplicity of these types of games is that collision can be tested by simply comparing the X/Y coordinates of items.  Its how I checked for bullets hitting bullets, bullets hitting ankle targets and of course ATAT's.

For example - the ATAT's only move horizontally.  To determine if we managed to shoot the ATAT body and head, and allow shots to completely bypass the legs, checking if the player's bullets Y coordinate sat between the screen area the ATAT's body was aligned to was very fast and took care of dealing with every ATAT (since they all move at the same height).  We only needed to check the X coordinates of both to see if the bullet hit inside the same area of the ATAT...  Easy-peasy.

How do we land on a scrolling terrain?

The same went for the repair (as the player is hit, they lose strength and this lets them recharge)

To determine where the terrain was low enough to land.  Given we know where the player is on screen (they can only move around a set location on the center-right of screen) and that our terrain moved via an X coordinate, it was a simple case of comparing the values to see if the players screen location was positioned around the correct terrain X value where the tiled terrain was at its flattest.

If so, we could stop the players craft and use a counter to count down the repair time during the game loop.  Very simple stuff as you can see.

Timers, timers and more timers

Finally, its worth noting that a majority of the game relies on timers.  Because the game runs in a loop, each time a loop is processed, a timer (or timers) are decremented.  These timers are then tested in most of the logic to ensure that things have completed before actions can be taken.

They're used to control how long a delay to wait between ATAT fire occuring, the animation cycle of a dying ATAT and how long a delay to take before all the ATAT's move.

For now...

Those are probably the key features of the game that were worth talking about.  Obviously, the code itself should hopefully explain more (I've left loads of comments in the code).  So take a peek and see if you can understand how the process works.

Thanks again for reading.  Check out the game and feel free to post comments or ask questions below...


I'd also like to note that this project was done for personal and educational purposes.  Obviously I can't claim rights to anything related to this game, apart from my interpretation of the game, a couple of graphics and the python code itself.  The graphics (other then the score font, lives symbol and 'Echo base' sprite that I created), game concept and themes belong to their respective owners - Parker Bros. and of course Disney who own the Lucasfilm/Star Wars franchise.  No disrespect or breach of copyright was intended here.


Post a Comment