Inheritance is not just about money!
I have no claims that this will be as interesting as Joe’s Adventures in Texture Land, but I think that developers who are new to programming or new to Object Oriented Programming (OOP) may find this useful. I suppose that it’s a true sign of my growing geekiness that I’m enthusing over OOP.
I'm not actually including code samples or instructions on how to code inheritance; you can find that in various books and websites. I do give examples of where I've used inheritance, which can be more important than actually knowing the syntax.
We've used inheritance a lot in Gal Civ, and I’m appreciating it a lot as I work on Gal Civ 2. I’ve been doing a lot of work on our engine to update it, and I’ve been using inheritance heavily. It saves me a lot of work and pain, even though everything in our engine is currently geared towards 2D graphics. For the most part, our engine was designed to be easy to maintain and update. Don’t get me wrong—there are parts of it that make us want to scream—but generally it’s been easier to update code than start from scratch. So I’ll give some examples of how inheritance has made our jobs easier.
In Gal Civ, both Starships and Stars both share a common parent, GCObject, and Starbases are children of the StarShip class.
Cool thing #1: Object Inheritance means that you don’t have to re-write as much code, which means that it’s easier to make changes.
Most of the objects in the game, including the tilemaps, inherit from a linked list class that has been around since Entrepreneur. If you design your classes for re-usability without making them so generic as to be a pain to use, you can re-use them for years-- particularly if it’s something that you always use, like a linked list class.
Another place where we used inheritance was in our customizeable listbox class. We had a listbox class that has been around about as long as the linked list class. It worked, but it was rather limited. You could have a listbox whose entries were either images or text, but not both at the same time. If you wanted a listbox entry with both images and text, you had to create two listboxes and link them together with one scrollbar. As you can imagine, that was a pain. The entries were also painted onto the screen by the listbox class, so they didn’t have their own hit detection.
So I started to design a listbox that was flexible enough to have any kind of entry, whose entries were actually members of our “window” class. (I use the term window loosely because Gal Civ is really just one window, with everything that appears to be a sub-window having to be drawn.) The basic entry class automatically had a default definition of a background image, a small image, and a formatted text box, all of them with highlight states.
The research list, the planet list, and the ship list are all examples of this listbox class in action. They all use the same listbox class, with unique entries derived from the base entry class. An even cooler example is the Starbase Upgrade window, which can also open in a viewing mode if you click on a starbase. When the window opens in view mode, it has a list of all the different types of bonuses already built on the starbase, with the total amount for that type of bonus. When you click on one of those entries, the list expands to show the individual bonuses of that type.
Cool thing #2: If all your objects are descended from one parent, they can all be in one list. You wouldn’t want to do that with the stars and starships, but it’s pretty cool for the listbox.
I mentioned earlier that the child object need to be able to update and paint themselves. If the data that they contain is not static, or you’re going to need to call the paint function from a pointer that has been typecast to the parent class, you need to be able to call the functions of the child rather than those of the parent.
Cool thing #3: Use the virtual keyword. When you define a function in a parent class as virtual, it will automatically use the child function even if the pointer pointing to the child is typecast to the parent’s type. You can also define a pure virtual function, which means that you don’t have to write a body for the function in the parent class, but you will have to define a body for the function in all child classes of that parent. I don’t find those as useful. If you don’t make the parent function a pure virtual function, then you can put debug messages in the parent function so that if something goes wrong and it gets called instead of the child function, you’ll have a better idea of where things went wrong.
That’s how I found a bug in Gal Civ where multiple windows had the same ID, so the correct ones weren’t being updated. Our “window” class, which is called MWin is the base class for all the window and control types in the game. Even scrollbars have MWin as the base class; this makes it easier to pass messages between the various windows and controls, because they’re all in the same list. So you can see why having repeat IDs is a problem: whichever one is first in the list with that ID gets returned when you try to find the window by ID. (Maybe one of these days I’ll write code that will generate unique IDs so that we don’t have to worry as much.) Anyway, not all of MWin’s children need text, but the SetFont function is defined virtually in MWin. If it gets called by a MWin or a class whose parent is an MWin that doesn’t have it’s own SetFont, MWin::SetFont will print out a debug message. Once I saw that message, I just had to put a breakpoint in that function and wait for it to break. As soon as the function was called, the debugger stopped the program and I was able to determine the culprit windows.
Earlier this week, we got an e-mail from the people who are doing the German version of Gal Civ. They had run into a problem where the wrong strings were displaying because the table where they were defined had grown larger than 64k. That sounded suspiciously like a memory overrun. Luckily, they had sent the file that they were modifying, so I had something to use for testing, and I didn’t have to waste anytime modifying our file so that I could reproduce the bug. First I checked all the variables that were holding the strings, and they were all large enough to handle the string lengths, and BoundsChecker wasn’t reporting any memory overruns or memory leaks. That meant that the problem was happening when the file was being parsed. So I had to start stepping through the parsing function. At first, everything looked fine. Then I noticed that the offsets that mark the beginning of the strings in the string tables were being typecast to unsigned short integers. Well, there was the problem. Why were the offsets being typecast to unsigned shorts when they were unsigned longs?
Well, to my dismay, I found that the reason was because the tags used by the data manager class (that has probably been around longer than the linked list class) only used unsigned shorts for their offsets, and the string table was inherited from the data manager. Unfortunately, so were the sprites, palettes, and a bunch of other systems. If I wanted to change the data manager to use unsigned longs, I’d have to write functions to convert the old formats so that we wouldn’t have to re-encode all of our sprites and stuff.
Cool thing #4: Inheritance makes it easy to change stuff.
Since the string table was just inheriting from the data manager, all I had to do to fix the problem was to create a new data manager class, which I called CDataMan32 to distinguish it from the older one, and make it the parent of the string table. So, I only had to worry about making the string table class compatible with the new data manager, and since it doesn’t save its data blocks like the sprite manager does (we have to read in the string tables each time), it only took me a matter of hours instead of days to fix. Now I can convert each system that inherits from the data manager one at a time, as I have the time.
Today I am going to use inheritance to convert the FOW manager from Gal Civ 1 to use Direct3D so that we can have hardware accelerated alpha blending. Yay!