torsdag 23 maj 2013

Me and my c64(s) - why did I want to make a c64 game in 2013


I got my (first) c64 for my tenth birthday in 1987. It was magic; receiving that computer actually changed my life in many ways - it was the one specific thing that got me interested in computers and without it I might have studied political science instead of computer science and followed a completely different career path. In the days of the c64, writing small BASIC programs was part of the home computer experience: and I wrote them too. I dreamt about programming a really cool game, or a new and super cool demo effect, but as a ten year old it was hard to do.

So, suddenly I'm 35 and felt like I should get around to making that c64 game :)

I work at a big company making a high profile iOS app and making this c64 game in my spare time has been a nice counterweight to working in a big team, on a large code base.

The Game


I wanted to make a c64 game that was actually a bit new, at least in some aspect, and hopefully fun to play.

Ever since I did my master's thesis, which centered around a self learning agent teaching itself to play a version of the classic arcade game Pong, I've had an idea about a game, somewhat similar to Pong but mashed up with Shufflepuck and with some play-with-gravity components. When I was considering what to implement as my first complete c64 game, that stood out as the most logic and appealing option. This is a video of how the final game looks and plays.

If you are interested in the source code of the game and you want to build it yourself, you should visit my Github page, which has everything you should need to do so. You can also download the d64 image for the game here.



Design and Execution


It was clear from the start that I needed to use some sort of fixed point math to be able to implement the game. For those who don't know the c64 architecture, it has a 1 MHz 8-bit processor (called 6510, which is very similar to its predecessor 6502) and no hardware support for floating point values. Considering the limited amount of time I had to spend on this project and the fact that it was going to be more complex than any other project I had tried out on the c64 before, I chose to use c as my development language and to do the development on a modern day PC instead of directly on a c64. I already knew of cc65, a very nice 6502 development package, including a C-compiler, which I had used for other small projects before, so choosing that was a given.


The development setup


One of the goals of the project was to do as much as possible myself and to invent my own tools as development progressed - which made me not wanting to use an IDE.  

This is the toolset I ended up using to begin with
- cc65 (www.cc65.org)
- VICE - the emulator (http://www.viceteam.org)
- Gnu Emacs - good old beast of an editor (http://www.gnu.org/software/emacs/)
- Gnu Make, for building (http://www.gnu.org/software/make/)

I have developed mostly on Ubuntu (on a VM on either Windows or OSX, but at the later stages of the project I switched to using Windows directly, but compiling through Cygwin). 
At the end of development I also used Eclipse and AppCode for some tasks (like renaming something used in the whole project).


Design


I've been programming in objective oriented languages for a very long time. I have worked with other things such as microcode and c programming, but for the last 7 years I have (professionally) only been using object oriented paradigms to model my code. So, doing it all in c felt somewhat awkward to begin with. I got some inspiration on how to use function pointers to manage the different states of the game from 

iPhone Game Development: Developing 2D & 3D games in Objective-C 

and basically it boils down to the following game loop:

game loop {
     while(true)
          if(inTheSpecialInGameState) {
               doSpecialRunLoop()
          } else {
                globalGameState->handleEventFPtr();
                globalGameState->updateFPtr();
                globalGameState->renderFPtr();
                newState = globalGameState->changeState(&gPngGame);
          
                if(currentGPngState != newState) {
                    changeState(newState);
                } 
          }
}

and the special game run loop does some more things:

  scoreChange = updatePongGame(&gPngGame);
       if(scoreChange == PLAYER_ONE_GOAL || scoreChange == PLAYER_TWO_GOAL) {
          flashScreen();
          currentGPngState = gameChangeState(&gPngGame);
          if(currentGPngState == MENU_STATE) {
              setIntroNeedsRedraw(1);
              introDisplayInit();
              return;
          } else {         


         resetPongGame(&gPngGame);

         printScores(&gPngGame);
          }
    }
    handlePlayerInput(&gPngGame, 1);
    handlePlayerInput(&gPngGame, 0);
    gameRenderGame(&gPngGame);

the states the application can be in are the following

    MENU_STATE - the menu
    GAME_STATE_SINGLE - single player game
    GAME_STATE_DOUBLE - two player game
    TITLE_STATE - init screen
    HOW_TO_PLAY_STATE - instructions screen

  } GPngState;

Changing state then means switching the function pointers of the global game state, as in:

     globalGameState->initFPtr = introDisplayInit;
     globalGameState->handleEventFPtr = introHandleEvent;
     globalGameState->updateFPtr = introUpdate;
     globalGameState->renderFPtr = introRender;
     globalGameState->clean = introClean;
     globalGameState->changeState = introChangeState;
     globalGameState->initFPtr();

which will change the game in to the intro state.

Implementing the game loop this way makes changing states, and adding more states really easy. It also keeps the contract of the functionality each state has to handle, explicit and clear. Unfortunately I never found the time to merge the loop such that there would be no need for an if-else in there (for the actual game playing states), so I will leave that for a later version. The way the text displaying states work can also be improved, having the data specify the details instead of the code.

Some simple physics on an 8 bit computer


The 6502 processor has no floating point unit. It has an accumulator which supports addition, subtraction, shifting and rolling. It also has two other registers, x and y. That's it. Sort of. The cc65 compiler deliberately doesn't support floating point arithmetic (it would be too slow anyway), so the obvious way to go to get some physics going was to use fixed point arithmetic. I looked around the internet for some inspiration and found it here http://www.flipcode.com/archives/3D_Graphics_on_Mobile_Devices-Part_2_Fixed_Point_Math.shtml

The primitive types I had to choose from were 8 or 16 bit, where 8 is clearly too small (the screen has a resolution of 320x200) and 16 bit is slightly overkill due to the fact that the game is a bit too slow to really be able to leverage the extra precision in the fractional part. In  the end I chose to use int values (which have 16 bits) and use 4 bits to represent the fractional part and 12 bits representing the integer part. This is somewhat wasteful since 2 to the power of 12 is 4096 and we only really use 320, so another optimization for the next version would be to use more bits for the fractional part. I think I ended up using this setup because I at a point had the code running on my PC as well, and wanted to use something that worked on both machines (I was using a much bigger game window than 320x200 on my PC). This leads me in to the next topic.

Initial cross platform ambitions


To begin with I had some ideas about porting the game to some other interesting platforms, such as the Megadrive and to have a reference version on PC. That is why there are still some obvious cross platform constructs in the code. At some point during development I realized that I probably want to do something completely different after finishing this project and that it would just make this project less fun if I made it over complicated.

Collision and physics - implementation


To be able to implement all the concepts of the game I needed to have collision between 
the ball and the court
the ball and the paddles, with 4 different types of collision: top, bottom, front and back
paddle and the court

This is mostly quite straight forward and the real challenges were caused to the fact that the game has to allow for maximum velocities of 2.0 for the ball and 1.0 for the paddles (that means 2 and 1 whole pixels per game loop iteration) - otherwise it comes off as too slow and sluggish. On a faster machine this would of course not be a problem. But allowing for such high maximum velocities means that the worst cast scenario for a ball-paddle collision is that, at the time of collision detection, the ball might be 3 in to the paddle already and that makes the determination of top and bottom of paddles a bit tricky. I solved this problem by adding some heuristics to the collision detection checking the directions of the colliding objects etc, which made it work well enough. 

As for the physics of the game, there is not so much needed either. I needed the ball to give some part of its velocity to the paddles (on collision) and vice versa; same thing for the paddles and ball colliding with the court. The constants used to determine how much is transferred on between objects on collision, were set by trying different ones and seeing what is the most "fun". As for gravity, one of the game mechanics is to have the direction of gravity change between 8 different directions (N, NE, E, SE, S, SW, W, NW); and the actual acceleration of the game objects is done by adding or subtracting a small constant to the game object's x  and y velocities, depending on the current direction of gravity, every n game loops.

Difficulties - some things are harder than other


Debugging

For me, not having a proper debugger was probably the most challenging part of the entire development process. After years of using different IDEs, all with integrated graphical debugging facilities, I was quite used to always being able to step the code and look at what a specific value is. With the setup I had when developing gPng that was not quite possible. Surely I could insert a brk assembler instruction and step through the code with the VICE monitor, but it was always a less viable option than simply adding a lot of print statements and look at them instead. This was due to a few different reasons: 1) I used 16 bit values to represent my fixed point fractional numbers - which makes them hard to read when you look at 8 bit values in the monitor. 2) the code is compiled c-code which makes the compiled code even harder to read. So, since I realized this early on, I spent an hour or two to add some print functions to be able to print all game objects in a nicely formatted way.

No IRQ

What one would normally do if developing this game in assembler, would be to hook up the game loop to the raster interrupt which happens 60 times per second. This provides consistent timing for things like input handling and music. Developing the game in c makes this option much more difficult than it would otherwise be (in assembler); there seems to be ways to do it, but it was not in scope of this first iteration of the game. However, this is one of the major things that I would somehow change if I would do another iteration on this project, either by re-writing it all in assembler or figuring out how to add only the interrupt handler in assembler into the c-project.

Improvements


If I ever get the time and energy to revisit this project I would:
1) re-write in assembler (thereby getting the interrupt handling for free… well sort of)
2) optimize (all parts but for instance the usage of fixed point would be low hanging fruit)
3) add more graphics
4) add music and sound effects

What I learned


I now know much more about how to implement a game in c - even if this is a very small one. I also know more about writing a program in c in general.

I also have obtained a good gut feel for how much computing power you can cram out of the c64. When I started out programming I had no idea how fast or slow it was going to be in the end. I have also gotten a much better understanding of how, working with the "wrong" type of problem (such as 16 bit fixed point calculations on an 8-bit system) for a particular architecture can affect performance.

Why can I recommend this type of project to others


For me this project has been quite the counterweight to my normal day job programming. I think many of us now a days work on big projects building on layer upon layer of other people's code and sometimes, it can be nice to get a dose of that original creative satisfaction one had when starting out with programming. Writing all code yourself for once, not depending on anyone else's work can be quite satisfying. Sometimes it's been almost zen like :) I feel like this type of code has the potential to be much 'tighter' than code you normally write, using objective-c or java or whatever high level language you use, and it is actually possible to understand exactly what the code does and how it interacts with the hardware it's running on.

Another thing is that this type of programming really encourages being creative by making your own tools. During development I for instance made a small command line tool in python to help me rotate the gravitation arrow sprite such that I wouldn't have to draw it several times (look out for that in on my Github page in the near future :) ). 

Finally, programming on this level has been a good reminder for me on how a compiler, linker and the processor architecture it builds for actually work. I find that building on modern day frameworks often abstracts the overview you get regarding this.

If you enjoyed this blog post


I think you will find this one interesting as well. It is a similar project, but with a different approach.

Inga kommentarer: