Indie game developer in the high mountains of Colorado!
So for those of you that have been following this blog on the remake of my 90's retro arcade space shooter, Ex Nihilo Studios has officially added DECIMATION Reloaded! as one of our games. Further updates on the ongoing development of Decimation can be found under News on the game page. We look forward to more exciting updates coming soon!
Since working at Realtime Associates, Inc. many years ago, I have written at least half a dozen 2D game engines based upon some of the methodologies I learned in the professional game development business. Each new engine typically incorporated techniques and improvements I had encountered along the way, and eventually I incorporated into the engine a graphical editor for managing game assets like backgrounds, sprites, tiles, and paths.
My latest game engine is called EXnGINE and while it is the most full-featured and powerful engine I have built, I have decided to almost completely start from scratch with Decimation, scaling back a bit and taking a different approach to some systems within the engine framework. Taking this path for the development of Decimation will also allow you, the reader, to see more of the foundational development first-hand, as we build each component, line-by-line. So let's begin!
In my opinion, defining the data your game engine will be working with is a critical first step. If your data is poorly designed, no amount of code optimization will improve the performance of your engine to the degree that starting with a good data structure will give you a head start towards a more efficiently running engine. So, we first need to define some custom Types that will serve our basic data needs in our game engine.
Let's start with a simple vector Type. We want to be able to represent an object's location in world and screen space with a high degree of accuracy, so we will use floating point numbers to define an object's x, y, and z location. Even though our engine is only a 2D engine, I find it is sometimes useful to allow for tracking an object's z location, so we will define our vector in 3 dimensions instead of 2.
Type tVectorField x:Float Field y:Float Field z:Float End Type
Next we will define an RGB color Type, useful for representing any of the possible 16.7 million colors we may want to display. Since each color value can be from 0 to 255, an unsigned byte is perfect for this data type.
Type tVectorField red:Byte Field green:Byte Field blue:Byte End Type
And now for some heavy lifting, the game object Type which will represent all our Actors and Particles in the game engine. You may notice it uses both of the data types we have already defined.
Type tGameObjectField link:TLink Field objType:Int Field state:Byte Field aiByte:Byte Field flags:Int Field varInt:Int Field varFloat:Int Field rotation:Float Field rotationSpeed:Float Field lifeTimer:Int Field location:tVector = New tVector Field velocity:tVector = New tVector Field sortDepth:Int Field color:tRGB Field size:tVector = New tVector Field halfSize:tVector = New tVector Field gfxLink:TLink Field frame:Float Field hitPoints:Int Field damage:Int Field mass:Float Field parent:TLink Field collidedWith:TLink Field collisionMask:Int field resolveCollision:Byte Field update(obj:tGameObject) Field draw(obj:tGameObject) Method Compare(obj:Object) If tGameObject(obj).sortDepth > sortDepth then Return 1 Else Return -1 End Method End Type
Since we are developing our game using the BlitzMax programming language (I covered this in more detail in a previous blog post here), we are using several of the built-in types that are predefined in BlitzMax. One of these is the TLink data type.
Linked lists are a foundational data structure and programming methodology that we will be using in our game engine and BlitzMax (BM) makes using them quite easy. The TLink data type is part of BM's built-in linked list handling functions and it represents the internal link to a given object within a linked list. Storing these links allows efficient and quick access to other tGameObjects we may want to access during the game.
You may also notice that within our tGameObject Type definition, we have a Method called Compare() which takes as a parameter a tGameObject. This method overrides the default Compare() functionality with our tGameObject specific Compare(). Sorting a list of tGameObjects will now sort them based on their sortDepth field.
We have a few more custom data Types to define before we are finished.
We need an efficient way to manage each game object's graphic images. Some may have one image, while others may have many images. Many game objects will share the same graphic images, so we don't necessarily want to store a game object's graphics in the game object itself. Doing so would lead to excessive and unnecessary use of memory to store the same image over and over. What we need is a library of graphic images that any and all game objects can reference and use, where every image is stored only once.
To solve the above problems, I use a Graphics Repository; nothing more fancy than a linked list of a custom graphics object Type. In code I call it the gGfxRepository and the custom graphics objects are tGameObjectGfx. Let's see how simple this really is...
Type tGameObjectGfx Field objType:Int Field link:TLink Field primary:TImage Field secondary:TImage End Type
The objType field is used to match a given tGameObjectGfx to a corresponding tGameObject, so many tGameObjects can use one tGameObjectGfx if they share the same objType value. The link field is a self-reference to this tGameObjectGfx that can be passed to game objects so they can quickly reference this graphics object. The primary and secondary fields use the BM built-in TImage type, a friendly Type that allows storing static and animated graphic images. There are 16 secondary TImage slots available for storing additional graphics images for a game object.
One last Type definition is required to get our engine's data requirements off to a good start - an FPS data type to manage and track our game's frame rate.
Type FPS Global Counter:Int Global Time:Int Global TFPS:Int Function Calc:Int() Counter :+ 1 If Time < Millisecs() TFPS = Counter Time = Millisecs() + 1000 Counter = 0 End If Return FPS End Function End Type
This last Type incorporates a few Global variables and a Function Calc() that will return the elapsed frames per second. We will use this to provide some engine performance feedback and diagnostics when we get our main loop coded up.
That's it for this post. In Part 2 we will get into coding the main loop and the engine functions that will be using our data types we have defined here.
As I began to write this first blog post on coding the engine for Decimation, I considered the possibility that for many of the visitors here, this may be their very first foray into the world of video game development. Quite possibly some of you have never programmed an application of any kind, let alone a real-time simulation for entertainment purposes. There are some concepts and methodologies used in game programming that are not necessarily used in any other kinds of programming, and most are not obvious to the non-programmer. With that in mind, I decided to cover a few basic video game coding concepts...call it Video Game Development 101. For my fellow game developers, I beg a bit of your patience.
From my very first program in BASIC way back in 1982, I learned that a computer program was expressed as a series of sequential lines of code, usually numbered for reference, that had a definite start and end. Commands like GOTO and GOSUB permitted the programmer to deviate from this sequential execution of his code and branch to different lines for various reasons, almost always to return back to the primary flow of code where the program would eventually end.
After awhile I learned that to make a game, even the most rudimentary, a programmer must employ some kind of loop that repeats a particular set of functions, over and over, until something instructs the program to end - usually a certain key press or a certain selection from a menu. This is generally called a main loop. In the main loop, the following simplified functionality would generally be repeated...
1. Update the player with input from various devices, ie. keyboard, mouse, joystick.
2. Update the enemies or other game entities that interact with the player.
3. Update the game environment or world that the player is in.
4. Clear the screen
5. Draw the game environment to the screen.
6. Draw the enemies to the screen.
7. Draw the player to the screen.
There are a lot of details that fall under one or another of the 7 steps listed above, which I will cover in another post, but the above list gives a generalized breakdown of the functionality that must occur repeatedly, usually between 30 and 60 times per second! You read that correctly, all of those steps will be repeated 30-60 times every second, because that is what creates the illusion of movement on screen, very similar to a movie being played back on your DVD or Blu-Ray player...still image after still image being displayed in rapid succession, each slightly different from the last. So, at it's core, a video game is running a pretty tight loop of code usually referred to as the 'Main Loop'.
Another interesting aspect of video game programming is how the video display is updated. Video displays, as fast as they refresh today, are still not fast enough to fool the human eye if one attempts to draw lots of objects to the screen directly. And what video game does NOT have lots of objects being drawn all over the place?!? To overcome this limitation, game programmers have employed a technique called 'doublebuffering', which builds on the idea of a 'framebuffer', which is a section of memory on a video card that holds what is being shown on screen.
A doublebuffered video display is a system where two copies of the game's display are maintained. One framebuffer holds the currently rendering (drawing) frame and is kept in video or system memory that is not visible to the video display system. The second framebuffer is visible to the video display and it holds the last completed frame of gameplay - basically it's what you are always looking at on your monitor. When all the drawing is completed in the first framebuffer, it is fast-copied to the second framebuffer, which updates the video display to the new frame that was just drawn. The first framebuffer is wiped clean to get it ready for the next frame to be drawn. This process is repeated around 30-60 times per second, depending on the game's frame rate. In this way, two copies of the display are switched between to produce smooth movement and animation.
Practically every video game will have to manage a host of objects, whether they are cards and chips for a poker game, or aliens, missiles, and saucers for a Space Invaders clone. We will need to manage all sorts of information about each of these game objects, like the object's state - is it alive, hit, dying, or dead? It's position and velocity, hitpoints and damage, etc. Does the object have a parent or sibling object it is associated with? Has the game object received mail from another object? Did the object collide with another object? After awhile, these game objects can seem almost real, with so many human-like characteristics it seems only natural to apply some anthropomorphic metaphor to them - and so many game programmers do just that!
So I have borrowed and use the 'Actor' metaphor in the game engines I have built. Like actors on a stage, all game objects have a script that tells them when to appear, what to do, and when to leave the stage. All actors ultimately take their direction from a super-actor, called the Director, who manages the different stages of the game and makes sure that all the actors are in their proper place.
Almost all game objects in my engine are Actors with the exception of Particles, which get treated a little differently in some of the game's internal functions, like in the collision system. An Actor, almost without exception, has set of graphic images associated with it, so it can be visually represented on the screen. Each Actor has three custom functions, or blocks of computer code, that are registered with the Actor management system for callback during gameplay - an Init() function, an Update() function, and a Draw() function.
The Init() function is called for every actor at the game's initialization phase to load and register that actor's graphics with the global graphics repository. Additionally, sound effects particular to that actor are loaded and cued for playback in the engine's audio sub-system.
The Update() function is the heart of every actor's artificial intelligence, or AI. This body of code controls each actor's behavior in every circumstance that we, the game developers, can imagine. The Update() function handles the actor's progression from the game states of BIRTH, ALIVE, HIT, DYING, and finally DEAD when it is removed from the actor management system. For example, if we are dealing with the 'Hero', or primary player actor, if its state is ALIVE, any input from the keyboard or controllers is polled and responded to which allows the player to control the Hero throughout the game. For computer controlled actors, like enemies or NPCs, this is where they assess their environment, their relationship to the Hero, and respond accordingly. This is also where actors respond to notifications from the collision sub-system telling them that they have collided with another actor.
The Draw() function does exactly what it's name implies, it draws the actor to the backbuffer. This allows each actor to draw itself in whatever way the game developer desires, often incorporating effects and additional 'support' graphics based upon flags and variables set within the Update() function. For instance, a spaceship might be the primary Hero graphic that is drawn but if the player is pressing the 'forward thrust' button, an additional 'thrust' graphic might be drawn as well to visually indicate that the ship is moving forward.
These three functions comprise the essential elements of each actor's AI to allow independent and efficient management and control of every actor within the game. For us, the actor metaphor helps make sense of some of the complexities of handling hundreds of game objects during the course of the development and maintenance of a video game.
When I was writing Decimation over two decades ago, I found that the Turbo C runtime image drawing routines were not quite as fast as I needed. So I embarked on the tedious process of writing my own custom image blitting routines in 80x86 assembly code, and in the process, creating my own 'optimized' image file format to aid in quicker drawing times. I called this image file format .TES, for Transparency Encoded Sprite.
Basically I would encode any length run of black pixels (my transparent color) in two bytes; 0x0 (used as a transparency run indicator) followed by the length of the run. For any colored pixels, I would simply encode the palette number (a byte) for each pixel. Remember, this was mode 13h where only 256 colors can be used onscreen at a time. Since a byte can hold a value from 0 to 255, using a byte to index into a 256 entry LUT (look up table), you have quick access to 256 colors. That was the gist of my .TES file format and now I just needed to open one of these files in Frhed and see if I could figure out how I encoded all this in the .TES file itself.
Since I had a screenshot of Decimation showing the player's spaceship that I could use as a reference, I decided to open SPCSHP02.TES in Frhed and attempt to make some sense of it. Below is what I saw...
I knew that the spaceship was 22 pixels wide by 17 pixels high from closely examining my reference screenshot, so the first two bytes were likely the width and height of the .TES file in pixels. Sure enough, 16 hex is 22 in decimal and 11 hex is 17 in decimal - so far, so good. The next byte was something of a mystery, 06 hex, which is just the number 6, so I skipped it for now and moved to the next byte, zero.
Since I knew that I had used zero (0) as a transparent run indicator, the next byte must be the number of pixels to skip. 0a in hex is 10 in decimal, so the first scanline of the image must start with a 10 pixel run of transparent pixels. Carefully counting ten pixels from the leftmost edge of my reference image of the spaceship brought me to the first colored pixel in the image, and there were two of them, and then another run of ten blank pixels. So the first run of 10 transparent pixels matched the .TES data!
The next two bytes in the file were the same, 2b, which is 43 in decimal. Following that was another zero indicating another transparency run, followed by 0a, or 10 again. It was looking like I was on the right track. The first scanline of the .TES file was decoded, except for that first mystery 06 byte. Thinking back over the first scanline I recounted the bytes,
1. 0 - transparent run indicator
2. 10 - length of the run
3. 43 - color byte
4. 43 - color byte
5. 0 - transparent run indicator
6. 10 - length of the run
That totaled 6 bytes for the scanline, and my mystery byte at the beginning of the scanline was 6! I think I had solved it - the first byte indicated the number of bytes in the .TES file for that scanline. I quickly decoded the next sequence of bytes to determine if my conclusions were correct. YES! It fit for the next scanline as well.
Below is the spaceship graphic I was working with and the raw pixel data and .TES encoded pixel data for the first four scanlines. The numbers in red are the .TES data length indicator for that scanline. The numbers in blue are the transparent run length and the green is just raw pixel data (actually indexes into the mode 13h palette of 256 unique color values).
Confident I had successfully decoded the .TES file, I began coding up a small program in BlitxMax to parse the file and draw the results to the screen. I had previously concluded that I would scale up the original images by a factor of two since these graphics were originally designed to be displayed on a screen 320 pixels wide by 200 pixels tall and we were going to remake Decimation at a resolution of 1024 x 768, so I made that adjustment in my program. Below is the finished program.
When ran, the program opened and parsed a .COL palette file to build the correct color palette for the .TES files. Then it opened a specified directory and decoded every .TES file in the directory, drawing it to the screen and then saving the image out as a.PNG file into a different directory. After decoding BOBS.PKG,which held all of Decimation's .TES sprite files, I had 493 .PNG sprite files ready for use in our remake. We now had our art assets fully realized. This was excellent progress!
Next up...coding the game engine!
So the first order of business is to see if we can crack the .PKG files that contain the static game images and the sprites used in Decimation. It was over two decades ago when I wrote the original version of the file concatenating tool used on our data files. It would take a whole directory of files and write them, end-to-end, into a single .PKG file with a small header that could be string searched for the desired filename,offset, and size.
Since that tool has long been lost along with all the file specs for my proprietary .PKG file format, we're going to have to get our hands dirty using a hex editor to inspect the actual binary files and see if we can determine how to extract the files out of our two game data files, GFX.PKG and BOBS.PKG. A quick search of the net led me to Frhed, Raihan Kibria's Free Hex Editor. This useful tool can be found at the following URL: Frhed.sourceforge.net
Firing up Frhed.exe and opening Decimation's GFX.PKG file in the editor window yielded the following results...
For those of you unfamiliar with hex editors, the leftmost pane is the byte offset of that row in hexadecimal. The middle pane is the actual file contents in hex, a byte at a time. The rightmost pane is the file's contents converted to ASCII which is really helpful if some of the byte data is actually readable text, which is the case in our particular file. A hex editor will allow you to edit the file, one byte at a time, along with a host of other useful functions. For our purposes here, all we will be using Frhed for is to examine the data and try to make some sense of it!
By counting the number of bytes (characters) in the rightmost pane from one readable filename to the next, I was able to quickly determine that the header was comprised of 17 byte chunks, with the first 12 bytes containing an 8 byte filename, a period, and a 3 byte extenstion. This left 5 bytes of numerical data that must contain the offset and/or size, but since an integer is usually stored as a 4 byte word, I wondered if the 5th byte was some form of padding or spacer. It was time to code up a quick program implementing what I had surmised thus far.
I opened a new program file in the MaxIDE of BlitzMax, my go-to programming language and compiler for 2D graphics applications on the PC, Mac, and Linux platforms. If you are a programmer and like messing with 2D graphics and have not used BlitzMax, I encourage you to check it out at: Blitzbasic.com. BlitzMax is an easy-to-learn, object-oriented, compiled BASIC-like language that borrows heavily from C/C++ concepts and is geared towards rapid 2D graphics application development.
So, back to my code.
Implementing the 17 byte 'directory entry' concept seemed to be heading in the right direction. I was able to open one of the .PKG files and read a 12 byte 'filename', followed by a single byte spacer (which I discarded), and finally a 4 byte numerical 'offset'. I read this 'header' in a loop until my offset value came up zero, at which point I closed the file, having reached the end of the header. By using the offset value of the current header entry and subtracting it from the offset value of the subsequent header entry, I could determine a file size.
Now I just needed to take the offset and file size values I was calculating and see if they matched the actual location and size of the files within the .PKG file. My first offset for the BLITFNT.PCX file was coming up as 10217 with a file size of 5717 bytes. Checking back in Frhed, I scrolled down to offset 10217 (0x27E9 in hex) and found the hex value 0a. This seemed familiar, so I Googled PCX file format and was confirmed when I found that 0a or 10 in decimal is the starting byte indicator of a PCX file! So it looked like my offset calculation to the first byte of the BLITFNT.PCX file contained within the GFX.PKG file was correct! I was pretty confident I was on the right track with these calculations.
At this point, I was just displaying the filename with the offset and file size values next to it for easy reference and proof-checking. Now that I was confident that my algorithm was correct, it was time to actually read the data at the offsets and write it out as a separate file using the filename found in the directory / header. Here is the code I wrote to do this:
Success! I had a working .PKG unpacker tool! I could now feed it the name of a .PKG file and it would unpack all the files contained within it into the directory I specified. The first thing I did was to unpack GFX.PKG. For the first time in over two decades, I was able the view the original .PCX files I had used as my background and overlay static images for my game! You can view them all in my Gallery in the UNPACKED FILES group.
When I unpacked the BOBS.PKG, however, I was greeted with 493 .TES files! These were my proprietary Transparency-Encoded-Sprite files that I had created that contained compressed sprite image data that my 80x86 assembly code image drawing routines would uncompress and draw! There was no paint program on the planet that was going to know how to display these files. I would have to reverse-engineer these files and re-display them myself to restore them for use in our remake of Decimation.
Well, that was going to have to wait for another all night coding session!
Welcome to Ex Nihilo Studio's first development blog!
We are quite excited to be sharing the behind-the-scenes development of our latest video game...Decimation. We hope that you will follow us as we attempt to rebuild the game that started me down the path of commercial interactive entertainment development many years ago back in sunny California!
Decimation started out in the early 90's as a fun project to teach myself C and 80x86 Assembly, two languages I'd been wanting to learn for some time in my quest to break into the professional game development scene. My 'fun project' soon grew into a full-blown arcade space-shooter and within a few months, I had attracted the interest of several other talented individuals who began collaborating with me to provide better graphics and music.
I wrote Decimation using Borland's Turbo C/C++ compiler which included MASM for my inline assembly graphics blitting routines. I used Autodesk's Animator ( a 2D paint and animation program) to create most of the static graphics and my 3D artist used Autodesk's 3D Studio (pre-cursor to today's 3DS MAX) to build and animate the models that were rendered down to our animating sprites.
Written in the popular PC graphics mode 13h, the screen resolution was 320x200 with 256 colors, tiny by today's gaming standards, but this was the early 90's folks, so low-res 256 color games were still quite common and we were thrilled with the results!
After completing about half the game and polishing and tuning the heck out of it, I took a chance and answered an ad in the newspaper for a game programmer at the L.A. based independent game developer, Realtime Associates Inc., using my half-finished game as a resume. I remember sitting in an office with the president, Dave Warhol, and a handful of his producers as they asked me questions about my experience writing games.
I was one nervous 25 year-old surrounded by industry veterans with dozens of titles under their belts, yet somehow I managed not to pass out. Instead, I apparently impressed them with what I said and with what I showed them...Decimation. The following day I was contacted by Realtime and offered a position as a game programmer at their El Segundo offices...one of my childhood dreams had just come true!
After landing my game programming job with Realtime, I was far too busy to finish Decimation and so it sat dormant for many months. Eventually I revved up my Iomega Zip Drive where I had saved all the source files for Decimation and began finishing the game. Within a few months I had completed the game to my satisfaction when I was contacted by a shareware distributor that wanted to include it in a space shooter CD bundle they were preparing. They asked me to make a few modifications and then send the finished game to them. I was thrilled with the idea of having my first game published and agreed to modify my game to their specifications. Then tragedy struck! My Iomega Zip Disk crashed hard!
I spent weeks trying to recover all the source files using every recovery scheme imaginable and affordable to me at the time - but nothing worked. If my memory serves me I even sent my Zip Disk away to a company that claimed they could recover anything off of any media and they came back with a quote around $500 to 'attempt' a recovery, but no guarantee as to the outcome. I was resigned to the fact that I had lost every source file to my game.
All the original artwork, all the original source code files, all the original sound files, everything was on that dead Zip Disk. I was crushed. Needless to say, Decimation never made it onto that shareware CD and although still playable on a x86 PC running Windows '98, it soon became a distant, though fond, memory.
A little over a month ago, I was working with my 21 year-old son, Nicholas, (who was born shortly before I began working on Decimation) when he suggested we make a game together. I agreed.
This was not the first game my son and I would code together. I had introduced him to programming years ago and he had even been a student of mine in a local private school where I was able to teach an Introduction to Computer Programming class. In that class we built three games - a Breakout clone, a Ms. Pac-Man clone, and a fun Space Invaders ripoff.
After much brainstorming and several game design sessions with Nick and other members of the Ex Nihilo team, we decided that there was probably no better game to officially start off our new studio with than to resurrect Decimation in all of her pixel-art, 256-color, 16-bit sound glory! We realized we had some challenges ahead, not the least of which was to reverse-engineer my original game data files that survived but were created over two decades ago using some compression / packing scheme I have long since forgotten. If we could crack those files and unpack their contents, then we would have access to all the original game-ready backgrounds and sprites, which is certainly a huge part of making a finished video game.
If that hurdle could be cleared, then we would need to code, from scratch, a new game engine upon which we could then rebuild Decimation. Not a trivial task for sure, but one made easier by the fact that I have several versions of a 2D sprite-based game engine that I wrote for several of my Game Programming classes a few years ago and have continued to update from time to time. This was beginning to sound like some serious fun!
And so it begins! I hope your appetite is whet for what lies ahead. We will do our best to keep you updated every step of the way as we create a video game from the ground up. We welcome your input and feedback, suggestions and comments, and certainly your encouragement. If this is your first time following the development of a game, then we are certain you will find it interesting and educational, and if you are already an experienced game developer yourself, then hopefully this will be a bit of a nostalgic walk down memory lane of your own development adventures in the past!
No blogs were found matching the criteria specified. We suggest you try the blog list with no filter applied, to browse all available. Join now to share your own content, we welcome creators and consumers alike and look forward to your comments.