Here is an example of the code to expose the properties of our food item (note the comments serve as a kind of metadata to Midden to add extra flare to the GUI):
AddFloat( "HealthBoost", &mHealthBoost ); // min:0 max:1000
AddInt( "FoodType", &mFoodType ); // enum:eFoodType
AddInt( "AnimationChooserToUse", &mAnimationChooserToUse );
AddInt( "FoodEffect", &mFoodEffect ); // enum:eFoodEffectType AddFloat( "OuncesOfPureAlcohol", &mOuncesOfPureAlcohol );
AddBool( "IsSpoiled", &mIsSpoiled );
From the beginning we chose to store all of our data in plain text format, even thought it would have been more efficient to store things in a binary format. First, plain text is human readable, and in a pinch editable. Doing a "diff" is far easier between revisions (very useful to find out when things broke).
It is also far easier to grep (find in files), merge text files when conflicts arise and to write scripts to do bulk editing of objects (i.e. add 10% damage to all weapons). Lastly, if it showed up on the profiler or we needed the memory, adding a step in the pipeline to convert things to binary is far easier than reading binary in the first place.
Using Midden to create a palette of objects, it was now time to place them and populate the world. Having the generic AFO system allows placement information to be just another AFO with a location, rotation information.
The designer spawns items in Midden then fine tunes the objects location/rotation using the GUI while watching it take place in the game in real time. When they are happy with their placement save their work and submit it to the source control. No baking required.
While we had the standard property types (int, float, string, bool, enum, etc), the ability to define custom widgets in Midden really paid off when it came time to define item rotations. All angles in Dead Rising 2 were specified in quaternions; in other words, not for human consumption. A widget was built to allow the user to enter the rotation in a number of different ways:
Midden soon turned into its namesake, with so much data it was hard to find the thing you were looking for. Pretty quickly, content creators created a standard as to where in the hierarchy things should be placed. A number of tab views were created to separate item definition from item placement information.
A fully functional Ctrl+F was paramount; it needed to search not just the name of the AFO but also through all of the key/value pairs. Bulk placement was also a problem, adding banks of slot machines or attaching doors exactly to the frame was a very time consuming operation. Though, in the end, quantity was our biggest challenge. It is easy to gloss over the numbers, but imagine if it took only one minute to tweak an item placement. For Dead Rising 2, that is 18,000 minutes -- or almost 8 person-weeks. Yikes!
With all the items in the world we needed a better way to visualize them. We often had questions like: How far apart are combo weapon regents from combo rooms? Where are all swords? Where are all the Zombrex posters? These questions gave birth to another tool called Chuck's Stash. The tool would read all of the data files then display the results in 3D. This is what the 18,000 objects look like in Dead Rising 2 look like from space (the rose colored polygons are individual stores in the game).
Next came a vast array of filtering options (type, name, time, location, regex, etc.) With this, a content creator could now easily find where and how many items existed, more importantly export the co-ordinates and then teleport to that location in game. This greatly helped in testing, Verifying the Improper Behaviour[vi] achievement now took 30 minutes (alternatively, one could run around the world and try to find all of the Zombrex posters.)
We've talked about the plug and playability of our toolsets. It should be no surprise that the next step for Chuck's Stash was to read data from various sources. Data could now be visualized from play logs, our database, generated through other tools, or even live from the game.
This now opened a whole new set of questions that could be asked both in tuning the game and in fixing bugs during final. There were the standard questions: Where does Chuck die? Where do the zombies die? How do the zombies die? What stores did the focus group enter? These are a little dry to talk about, so here are a few of the more interesting questions we've asked:
Falling Through the World. While our automated Chuck traveled the world while we slept, the tool kept track of where it fell through the world (logging it all to the database). Visualizing this data in Chuck's Stash gave this picture to the art team (the concentrated red blotches are bad). Collision geometry was fixed up and we could run the test again.
The Frame Rate. With a large open world, populated with thousands of zombies and weapons, it is impossible to test every combination to ensure a quality user experience. Each time the frame rate dipped during a valid play session, it would be logged to the db. Then we could visualize it, go to the location in game, and figure out what the combination was that caused the frame rate drop. Rinse, Lather, Repeat!
Content creators are often at the mercy of programmers to get their content into the game. Enabling them to iterate and create content on their own is a huge step forward in increasing quality. In summary here are a few of the things I think we did very well when making Dead Rising 2:
[vi] This achievement was to spray paint all of the Zombrex posters in the game.
[vii] "If you always do what you've always done, you'll always get what you've always got" - NLP adage