I already wrote a post-mortem, so I guess this is a post-post-mortem? Panning for gold may not be the most accurate metaphor, but it's a prettier picture than digging through my crap hoping to find something reusable.
Now that all the submissions are in and my brain has had a few days to relax, I'm ready to get back to some game development. To ease myself back into the swing of things, I wanted to sift through the hacky mess that past-Eck left for me to clean up... AGAIN. I can blame a lot of my problems on past-Eck. I really wish he would eat better and exercise more... Anyway, here's what I found in my own stuff that I plan to reuse. (after some MAJOR clean-ups and refactors of course).
Game State Management
Before the competition, I hadn't really finished many complete games. As such, there was really only one state for my half-finished projects: You run the app, you're in the game. I knew I'd need a title screen, and probably a win/loss screen, and somewhere for game logic to run. But Laurie (graphics/creative team member) wanted to do an Introduction scene, and a boss-fight cut scene which complicated matters.
Before the competition, I had a general game state manager, that mapped strings to an empty GameObject (my basic top level game entity). This top level game object was dumb and only held a list of child game objects that it drew and updated as necessary. This was not nearly sophisticated enough for what Laurie wanted to do so I had to add some features.
Now I have a top-level GameStateObject that's a little smarter.
- has associated music (specified in settings files)
- can auto change states after a specified time
- can fade out to the next state (or not)
- can specify a backgrund image.
- can specify the next state to go to (for things like the cut scenes)
- have an Activate method called when changing to this state
We used the improved GameStateManager for the cutscenes which were just image files strung together almost like a slide-show. Show this slide for 4 seconds, fade out, then move to this slide. For popping up the speech bubbles for the same scene, I just killed the fade. You can view the slides individually in .\Data\Textures.
At first there was going to be a toy-box for the off-screen parent and child speech bubbles to show up on. I wired up the speech bubbles by themselves as place holders, but we liked how the fading speech bubbles looked "in the dark" without the toy box background, so we went with that instead. As a side note, we specifically didn't specify the gender of the child.
Game Logic API & AI
My game logic is probably too hacked together to reuse effectively. The states are strung together in a "holy crap I need to get this done NOW"-fashion. But one thing that was a little cool was making sure the AI could interact with the game. I've read some articles and cruised the forums,
but I've never written any AI before. As such I'd call myself ignorant with whatever best practices there are. I still managed to get it working though.
I made sure that however I intended the human player to do his job, the AI went down the same code path. The only difference is, the human starts from mouse input where the AI starts from the game logic telling the AI it needs to do something. This might be obvious to veteran game developers, but I thought it was really cool that the AI was effectively just hitting the same buttons. The AI is pretty simple, but I'm pleased with my first attempt.
Timed Action & AI
One problem with an AI player (especially for very simple AI) is the speed in which it has completed its evaluation. "Move to the nearest Hope" only takes a split second. Deploying random units caused a cacophony of sound effects to play as ALL the units confirmed their placement near simultaneously. I created a TimedAction for "Think Timers" containing delegates (function pointers) to execute the appropriate action.
As the game grew in complexity, this got pretty hard for me to keep track of, but I managed to keep the house of cards up for the delivery. I think there is likely a better way to do something like this, but I think there might be something here. Further investigation is required.
Turn-based Combat System
I had something similar to this for a space game I'm working on, but it had too much game specific logic with things like engines, power plants, weapon slots, mass, thrust, shields etc,. that I couldn't reuse it for the contest. So I rewrote a simpler system that took 2 days ( Days three and four I think?) putting in some long hours.
Turn-based combat compared to say, an arcade shooter is different the way the results of a combat action are calculated. In the arcade shooter, you're running a basic simulation. You fire a bullet, and if it hits something, you handle it. For the turn-based combat, there wasn't any time factor. When ordering an attack, it queued the sound effects, applied the damage, and was completed instantly.
At first, I tried to string the TimedActions together and technically it worked, but it looked like GARBAGE and the timing was completely off. On Day 6, I was thinking I'd have to rewrite the combat state management system so I ran that idea by my wife. She's not a programmer, but understood what a rewrite would mean time-wise. She suggested I use what I already had, particles, game objects, hand-wavium, whatever. Explore ideas for the next 30 minutes and get back to her. I'm happy she talked me out of it!
Instead of trying to get the timing right external to the game world, treat it like a real-time game! Again, this is probably obvious to veterans, but I was THRILLED with the revelation in my sleep deprived mind. I created a Bullet GameObject that had data about the attack (in addition to visual effects). I spawned it with a specified target and the game update took care of the rest. Once it arrived at its destination, it told the target to apply the damage it carried along with it, which in turn triggered the Hit or Death sound depending on the damage applied. It was like a little deadly info packet. *THUNK* "Message for you sir!"
Once that was done, the game looked SO much smoother. I ripped out all the timing stuff except for the "aim" timer because the robot sound effect has a charge-up sound built-in to the wav file. So instead of the bullet firing immediately, it waits a configured amount of time before launching the projectile and it looks pretty slick.
Ultimately, I hope to make a turn-based combat system similar to Master of Orion II, and I think this will be a good start.
Data Driven Game/Settings Files
This isn't something I coded for the jam, but this was its first "test of battle" and I loved the results. When you drive your game (or business app) with data files, it gives you so many cool benefits. Once you get a decent system for this in place, it usually saves you more time than it costs to move your hard coded data over to the files.
Sometimes it's hard to see what benefits you might get from this, so here's a few concrete examples.
Gamestates:[code=xml:0]PreTitleScreen 3.75 ParentChildSpeech0 1.25
Being able to specify the various game states let me do a couple of things. Configuring the StartingGameState was useful so I could just start at whatever game state I was working on. Being forced to sit through the intro cut scene and fight through the first battle just to see the boss cut-scene takes too damn long. And changing that first line in my settings file seems cleaner than hacking in some temp-code to force the game to that state. It's also easy to fix if you get it wrong.
The "PreTitleScreen" gives an example of the slide-show like cut-scenes. show the slide for 3.75 seconds, then fade out for 1.25 seconds before advancing to the "ParentSpeech0" game state. Laurie was even able to wire up the second cutscene on her own so I didn't have to get involved.
The "TitleScreen" and "MainGame" states don't do much but specify what music to play. User interaction is required to move from these game states. And having the music specified in my settings files meant we could turn off the music when XNA crashed on a judges computer for not having Windows Media Player installed. *WHEW*
Unit stats:[code=xml:0]
Putting some unit stats into files may be pretty obvious, but try to put ALL your unit data in data files. Which team is the unit on? What's it look like when idle, moving, or dying? What's it sound like? etc. When you do that, you open up your game more to the modding community which can greatly increase the life of your game. In Toys Guardians of Innocence, you can add your own units, or make all units available to both sides.
Level Data[code=xml:0]
The LevelData structure defines the dimensions of the playing field and deployment area. It also dictates whether each side is controlled by an AI or Human player. I didn't have time to wire these up to a UI screen, but it would have been cool. I also specify the point values for each Stage. I figured I'd keep the game short and sweet with 2 stages, but after playing some of the competition, I probably should have added in a few more stages here.
You'll notice that I only specify VictoryScreen in the stage data. Losing currently takes you to a hard coded LossScreen, and I think that's a mistake. If I configure a LoseScreen for each stage, that could open the game up to a campaign with multiple paths to ultimate victory. Sure you might lose a battle, but you could still win the war.
Other Benefits of Data Driving
Non programmers can wire stuff up for testing without bothering the developers.
You can setup a debug button to reload the data files and change game behavior while the game is still running.
Your players can add things to your game and make it fun for a longer period of time.
Anyway, if you were wondering what cool code might exist in Toys: Guardians of Innocence, that's what I found. What cool technology did you cram into your game for the contest?
We used the improved GameStateManager for the cutscenes which were just image files strung together almost like a slide-show. Show this slide for 4 seconds, fade out, then move to this slide. For popping up the speech bubbles for the same scene, I just killed the fade. You can view the slides individually in .\Data\Textures.
At first there was going to be a toy-box for the off-screen parent and child speech bubbles to show up on. I wired up the speech bubbles by themselves as place holders, but we liked how the fading speech bubbles looked "in the dark" without the toy box background, so we went with that instead. As a side note, we specifically didn't specify the gender of the child.