In fact, now might be a very good time to start journaling this project. I tend to most often work within a prototype style of development, and I just got done with my first prototype. I think now is a good time to determine what things went well, and what needs improvement.
This for all practical purposes my first real attempt at a game of any significant size. I'm trying very carefully to put a lot of effort into design this time, since I've had bad experiences (at work and on my own) with projects getting very difficult to maintain or expand upon due to a lack of design after a certain point.
Early on in the brainstorming stage of starting this project, I read Jeff Plummer's Masters Thesis, A Flexible and Expandable Architecture for Computer Games. I then attempted to expand on this and produce a design that would meet the criteria that he described. In the end, I discarded this work, because it was starting to look like a highly fancified way of organizing globals, and many of the problems that globals introduce seemed to be leaking in around the edges. Nonetheless, many of the concepts from the paper and from my experiment still remained on my mind.
I decided to cut back on the demands I was placing on myself, and just try to get a prototype up and running, with a few of the ideas in place, without requiring that it be the "perfect" design. One of the elements I wanted to keep was a concept of a list of components, the primary purpose of each is to provide a Process() function that the game engine can call to make it do its work. Each frame, Process() should be called on each component in turn, and they do whatever it is they should do.
In addition, I decided also to use a system of state objects. The game engine is at any one time aware of the active state, and tells that state to do its thing. To support many of the ways that states tend to work in games, I decided to provide a PreProcess() function and a PostProcess() function on states. The PreProcess() function primarily adds a list of components to a master list that should be processed this frame. Within the PreProcess() function, a state call call PreProcess() on states that are considered secondary. (For example, if in the middle of a game the user goes to the main menu, the normal gameplay state might still be semi-active in the background. In this case, the main menu state stores a reference to the gameplay state, and tells the gameplay state to PreProcess() and PostProcess() as necessary. This way, the gameplay state still gets to add to the list of components that need processing, and also doesn't need to get destroyed/recreated as the user enters/exits the main menu.) Within the PostProcess() function, the state determines what things need to change based on the results of the various components' processing. Additionally, it returns a reference to the state that should become active. In most cases, this is a reference to itself, but can be a reference to a newly created state (such as starting a new game, and transitioning from the main menu to the menu where the user chooses the new game properties), or a reference to an internally stored state (such as the choosing "Resume Game" from the main menu).
The final purpose of this prototype was to simply get different components working together. I have done very very little 3D work up until now, and this was to be a 3D game. (I have worked a lot in Direct3D, but it was doing 2D stuff almost exclusively, orthographic projections, axis aligned cameras, and all that.) External libraries I decided to use were Direct Input, Ogre, CEGUI, and Ageia PhysX. I wanted to see all of these working together. I also ended up learning a bit of Blender in order to make models.
Of course, so far I haven't mentioned anything about the game itself, only architecture. The game is a space sim. I wish to eventually have a rather sweet ship builder with real physics being influenced by the design (location of thrusters, distribution of mass, et cetera), along with the necessary pseudo-physics stuff necessary to make it playable (inertial dampeners or what-have-you). Try to add a true sense of ownership to the ship and parts that a player can create during the course of the game. Further, less developed ideas include a randomly generated universe with some form of an actual simulation of governments, economics, wars, and so forth to really provide a decent sense of immersion and believable/meaningful missions, rather than either the fixed storyline or the sets of dull missions that are practically all the same. But I'll worry about that later. I'm trying to do this in stages, so as not to give up like I have on so many projects in the past.
So what I've accomplished with this first prototype is to indeed get all the components working together. My first work was with input. I managed to wrap up Direct Input all nice to my liking, as well as make it fit the component idea that I am trying to create. Calling Process() on the input component will check the buffered events and add those to a list that can later be examined by other parts of the program, as well as update the unbuffered state of any device that is currently active. I added the ability to set virtual ranges for Joystick data, as well as deadzones, and intend to eventually also add modifiers to buttons (any type of button) to allow reading the button in an auto-repeat mode as well as the normal raw mode, whichever is appropriate at the moment.
I then moved on to graphics. I went straight for getting both Ogre and CEGUI working at once, not bothering to first get Ogre working alone. That turned out well enough, and I then began making states to represent the various menus. For now, each menu only has buttons, which is definitely incomplete. The options menu, for example, has Okay, Apply, and Cancel buttons, but no actual options. It simply returns to the main menu if you choose Okay or Cancel, and does nothing if you choose Apply. Regardless, it felt good being able to navigate through the menus, and get to the actual gameplay state (which at this point was merely a black screen that waited for the Escape key to be pressed, at which point it went back to the main menu).
So next I struggled through the process of getting an actual 3D model displayed in the gameplay state. This was complicated, mostly due to the process of getting familiar with a new library, though my limited experience with 3D caused some problems as well. I have a lot of book-ish knowledge on 3D applications, but first-hand experience does wonders for ones ability to know which settings or function calls may have been overlooked or used inappropriately. I finally got a poorly modeled ship actually drawn. Then a skybox (after I realized that my far clip distance was too short to allow the skybox to be visible, arg!) and finally some ogre heads, so I would be able to see how I was moving, and not just how I was turning.
Then I finally began on physics. I first of all managed to model my ship as a sphere in physical terms, and set gravity to the standard -9.8y. Excitingly, this worked just as I wanted. Then I proceeded to tie joystick input to forces applied at different locations of the ship, and after many complications with confused orientations and such, got standard Newtonian physics to work. (Navigating in a space ship with such physics is a bitch!) One of my biggest problems with the orientations was that Ogre expects positions and optionally orientations, relative to parent nodes, whereas PhysX provides positions and orientations according to absolute space. Figuring out how to make those two work together properly (while also trying to compensate for Blender's different axis orientation, without writing an exporter or something similar myself) caused some problems, but I finally got it working. The last step was to provide some sense of better control over the ship. I turned on a bit of angular dampening to stop the crazy rolling. I also applied two forces of equal magnitude to the center of gravity of the ship: The first force was in the exact opposite direction of the ship's velocity, and the second force was in the direction that the ship was facing. The magnitude of the force was proportional to the magnitude of the velocity. This causes the ship to generally want to go in the direction in which the ship is facing, cancelling out velocity when appropriate, but doing nothing if the ship is facing the exact same direction in which it is moving.
So that's what I have now, and I think it is a good time to move on, having learned quite a bit already. There are some things I definitely woudl like to rework (there were some things that I purposely didn't design very thoroughly, since I simply wanted to get something working, and didn't have the appropriate experience to know how to design it well anyway), and some things that I'm quite pleased with.
First, things I'm pleased with. There have been some discussions I've seen saying that using state objects aren't really necessary or very useful, but I'm liking them quite a bit thus far. They haven't gotten in my way any, and have allowed me to very easily work on one part at a time. They seem to be a very effective way of separating responsibilities in an appropriate manner. Maybe that'll change as things get larger and more complicated, though. We'll see.
I also like my wrapping up of Direct Input. I should be able to quite easily swap out Direct Input for some other input library if need be, and everything is objectified in a way that I find useful and intuitive. However, in the process of trying to encapsulate and hide implementation details, I've found that providing access to containers is a little annoying. I don't really want the outside to know if my list of events is a vector or a deque or a list or whatnot, for example. So I wrote a container wrapper that simply provides a begin(), end(), and bidirectional iterators, and uses some virtual functions and templates internally to hide all the details of the specific container used. But in the end, I think this has complicated things more than anything, and I might decide to simply make publicly visible the container that I choose to use for each collection of items.
Another problem with encapsulation was that I had initially organized all my files to assume that graphics, GUI, and physics would all also have interfaces that were library-agnostic. In the end, though, in order to get the prototype doing stuff, rather than mucking about with designing good interfaces and wrapping up the libraries to fit the interfaces, I decided to just expose the library objects directly. I will probably end up ditching the interface entirely, other than to wrap the library up so that it works with the component model's Process() concept. If experience leads to an idea of a good library-agnostic design for some particular component, I might follow that lead later on, but not initially. The input is already designed pretty well, but the others can simply maintain the design decided on by the respective library writers.
This next time through I am also going to need to write some model converters. I would like the z-up axis system in Blender to be converted to a y-up system in the process. Additionally, I'd like to be able to model the collision objects, and physics objects in Blender, along with the model, and then convert them from graphical data (associated with materials and such) to a purely physical form. Both objects need to be convex, for reducing computation and playing nice with PhysX. The collision objects need to be as simple as possible, since they're relevant at runtime, but the physics objects need to modeled so that they more accurately represent the ship, since these objects will end up being reduced down into precomputed mass, center of mass, and inertia tensor. They also need to be arranged so that I can effectively assign physical materials to them in a way that makes sense (making certain parts of a ship heavier than others, stronger, and so forth). I also need to create a system of attaching ship components to each other, and attaching functionality to some of them (engines are things that can apply forces, guns fire stuff, et cetera). Right now I merely hard-coded the locations and strengths of forces.
By the end of this next stage of prototyping, I hope to be able to choose a scenario (flying around, shooting stationary objects, shooting moving objects, facing hostile ships that fire back, and so on), choose a predefined ship that is made from components that have their graphical and physical properties specified in files, and then play through those scenarios. There won't be any winning conditions, this is still just a testbed for software architecture, as well as the the gameplay style of using different ships that have real physics. I will also want to include at least a few options in the options menu (resolution, fullscreen/windowed, perhaps a few others). At this stage, if all is going well, then I will probably begin to more seriously think about the overall gameplay, not just the flying around shooting part.
I'll provide some screenshots of my current progress later, as basic and boring as they may be, just for historical purposes or something. [smile]