Main
Proposal
Milestone 1
Milestone 2
Postmortem
Postmortem

Overview

In CowChompers, you control one of several farmers just minding their own business, when suddenly, a hungry band of aliens show up to eat your cattle. Being fond of your farm, you must prevent the aliens from devouring it.

The adventure/intercept portion of the game looks like this:


The screen is split to allow each player to move independently and each player has their own third-person camera so that they can quickly see in whichever direction they want. The number of cows remaining is displayed in the top right corner of the screen. Aliens are teleported in by an alien spacecraft that flies around the game area. In the demo level, the wave of enemies consists of random groups being spawned approximately every 10 seconds for 2 minutes, as well as a mini-boss group and a main boss that are scripted to spawn in at specific times. Enemy groups will move toward your cows and begin to eat them once they get close enough. If you intend to win, you must intercept them as quickly as possible. Once a player gets close enough to an enemy group, the 4 way split screen is replaced by a single viewport for battle mode.

Here is a shot from battle mode:


In battle mode, the team of heroes attempts to defeat the team of enemies using the weapons available to them. In the demo, the farmers are equipped with a shotgun, some grenades, and a strange alien cannon. Combat occurs in 2 phases. First there is a selection phase, during which the players (and aliens) decide what they want to do. This is followed by an action phase, and this is when the characters actually fire their weapons and throw grenades. The 2 phases repeat until one (or both) of the teams are defeated. Players can move around on the battlefield, change their weapon, and prepare to fire or throw their weapon during the selection phase. When firing or throwing, the player first has to press the ‘A’ button to stop a quick-moving "reaction time" meter. The point at which the meter is stopped determines when the character will perform his action during the action phase. After that, the player will similarly determine the quality of their aim by stopping a quick-moving vertical bar overlaid on a target/crosshair. This is followed by stopping a horizontal bar on the same target. Once that bar is stopped, the player has locked-in their action for that turn. Each selection phase lasts a maximum of 15 seconds. If a player hasn't locked-in their action by the end of the 15 seconds, they forfeit their action for that turn. There is an audible warning for the final 3 seconds of the selection phase. If all active players lock-in their action before the 15 seconds are up, the game will immediately skip to the action phase. Grenades and cannon shots collide with each other, bounce off the terrain, and are deflected by explosions thanks to the physics engine.

The players win the level by defeating all of the enemies in the attack wave.
The players lose if all of the cows are lost and all players are defeated.

If a player is defeated, they can return to play by trading the nearest cow for full health recovery. They do this by mashing their buttons and spinning the D-pad/analog stick while outside of battle to fill up a recovery meter. Once the recovery meter is filled, the player's health will be completely restored, but the nearest cow will be "eaten".

There is a decent amount of strategy to winning battles without taking too much damage. Since explosions launch other explosive projectiles out of the way, your team will generally be more successful if players use different weapons or synchronize their timings with the same weapon. Also, the cannon has a large blast radius and digs out the terrain well. The grenade has a smaller range but it is more deadly if connects with its target, so one strategy that came out of having deformable terrain was that one player would shoot a cannon early in the fight to carve out a hole around the alien so that later grenades would fall into the hole. Also, the terrain comes into play in some battles because grenades will roll back down hills if you are fighting aliens at a higher position.

 

Game Features:

  • Runs on the Xbox, which is great because it ensures that the end-user will have a consistent experience
  • Controls: The control scheme is intended to be simple, encouraging more casual gamers to get into the action quickly.
    • D-Pad/Analog stick: Move character/navigate menus
    • Start button: Pause & display main menu
    • ‘A’ button: accept current menu selection
    • ‘B’ button: cancel menu selection
  • On the PC version, the players can remap input devices and buttons as they see fit, using the DirectInput GUI that is launched from the input menu (this menu is not part of the Xbox builds).
  • Multi-textured deformable terrain with scorch marks
  • Animated Half-life models with animation-to-animation interpolation and weapon attachment
  • Hardware-billboarded bitmaps
  • 2D overlays
  • Original soundtrack and sound effects
  • Hit-box collision detection
  • Saved Xbox configuration files
  • Tokamak physics engine for grenade and cannon shot collisions

 

Development Summary


Code:

Here is a listing of all the files in our development directory at the time of submission: Code

 

Content

  • Graphics:
    • Players and aliens: Half-Life models found on the web
    • Cows: obj model, textured by Dave
    • UFO: free turbosquid model, textured by Dave
    • Farmhouse: modeled by Dave
    • Terrain textures: www.i-tex.de
    • Skybox: www.scentednectar.com
  • Sound:
    • level victory, game over, cow moos, ufo beam, ambience: contributed by Dave’s friend Dave Berol
    • main menu navigation sounds: Steve
    • combat sounds: Half-Life
  • Music:
    • Title screen, Intro cut scene, and level music composed by Steve’s friend Dan Keller.

Significant Technical Features

  • Half-Life Characters:
    The Half-Life model rendering was based around the model viewer code from the Half-Life SDK. The original code was built to render models without saving states between renderings. This meant the code had to be overhauled to handle rendering a 4-player split screen so that the models would not have to be re-rendered each frame. Features absent from the model viewer that had to be gleamed from Half-life's rendering engine were quaternion interpolation for bones when switching animations at any point and weapon attachment. Weapon attachment is not well documented in the SDK and involves replacing bones in the weapon's model file with bones from the character.
  • Deformable Terrain:
    The terrain uses a 4-texture pixel shader to modulate two detail textures which are specific to several terrain "blocks" which allows the terrain to use more than just 2 detail textures for the whole thing. Lightmaps were generated using a simple horizon computation algorithm and a cloud texture was moved over the terrain. Each terrain vertex also uses an additional field for deforming the terrain's height and shading it to simulate scorching from explosions.
  • Well-organized Codebase Supporting DirectX 9 and Xbox DirectX 8:
    Another large technical challenge that we faced was keeping the codebase clean and flexible enough to maintain productivity. This is even more of an issue when the code is going to run on multiple platforms and needs to be fast. Since we’re not expert game designers, and we only had a short period of time to complete the entire project, it was important that our code was flexible enough to withstand significant changes in functionality. Having spent a large amount of time restructuring code multiple times on previous projects, Steve decided that it might be a good idea to spend less time fixing up code that already worked and more time implementing new gameplay features. Ideally, one would have a good enough idea of what kinds of requirements are in store when designing the various components, but of course, this is rarely the case. When switching to DirectX, we had to worry about reinitializing device objects after the app loses focus; something which wasn’t part of the logic when using OpenGL. This required some additional “plumbing,” since now we needed a way to notify all of the various buffers used by the entities in our world. We weren’t completely successful in this aspect; after most of the adventure and battle system code was in place, we decided that it would be better to have multiple teams of farmers, so that all the players could have a character in every battle, and not have to teleport into a battle on the other side of the farm. However, there was a large amount of logic already in place that made assumptions about each player only controlling an individual farmer. Since this would add a decent amount of complexity, we decided to leave it with only 4 farmers for now. Knowing how to balance code restructuring and actually implementing new features is certainly not an exact science; too much restructuring wastes time, but too little restructuring causes the project to get messy enough that it becomes hard to add new features and easy to introduce bugs. The way we approached this problem was to basically toss in lots of implementation early on, clean up a bit after milestone 2, and then try to work quickly again at the end. When it came to abstracting away differences in the platforms, we used abstract classes and virtual methods for objects that needed to have multiple instances. For things like the actual graphics manager that exist only as singletons, we just had common header files that declared all of the functions needed by the game logic and then had separate cpp implementation files for each of the platforms. Instead of actual member variables, we could just use globals within those implementation files. This might not have been as clean as using the factory pattern, interfaces, and virtual methods, but if every call to the input/graphics/etc managers needed to perform a few extra pointer dereferences, we figured it could buy us a little improvement in frame rate. While a lot of the abstractions were placed in separate directories, we also ended up using the quick fix:

    #if defined(PLATFORM_DX9)
    #elif defined(PLATFORM_XBOX)
    #else
    #endif


    in several places. Especially when you’re just trying something out to see how/if it works, this can save a decent amount of time. Once the limitations and requirements are better understood, the restructuring becomes much more useful, since you can be pretty confident that the code organization will be much more robust to change.

 

Reflections

Things that went badly:


  1. Half-Life Model Interpolation:
    There are several Half-Life renderers available with source on the internet. However all of them only handle rendering one model at a time. We had to separate out the model skinning calculations and the model rendering and allow multiple characters states to be preserved. Once this was all finished, there was still a large performance penalty associated with doing bone transforms on the meshes. The Half-Life models have too many bones and are organized into lots of small triangle strips and fans which each may need any number of bones which would not allow for hardware skinning on the Xbox. In the final version we still took a fairly large performance hit (about 8 fps) for every individual Half-Life model that was being rendered. Luckily even with the number of enemies in the game our frame rate would stay just above the 30 fps threshold in adventure mode.
  2. Swizzled Textures:
    The textures were showing up very garbled when being rendered on characters on the Xbox. We load the Half-Life model textures directly into memory from the .mdl files, and it turned out that the Xbox uses a swizzled texture format for performance reasons. Since we weren't using the texture loading APIs, we had to make sure that the textures were properly swizzled while loading them. Additionally, when loading the textures into memory in DirectX 8, we had to write a mip-map generation algorithm so the model textures could be drawn using mipmaps.
  3. Everything disappearing when going from debug to release on the Xbox:
    Uninitialzed variables can be a pain, and C++ makes it way too easy to accidentally use them. In debug builds, the Microsoft compiler does you a favor by filling uninitialzed variables with some constant value like 0xcdcdcdcd. But in release builds, this is not the case. So when you compute the aspect ratio by dividing two floats that are uninitialzed in debug mode, you get 1. If you’re us, you don’t even notice that anything is wrong in debug mode, since an aspect ratio of 1 isn’t that far off from 4/3. But then switch to a release build and things are not so pleasant. It turned out that the fields we had been using to determine the display area dimensions in Windows were unused on the Xbox.
  4. Music dropping out:
    We used DirectMusic for all of our audio. However, we were running into the problem that the music would stop playing when we asked our IDirectMusicPerformance8 interface to start a lot of sounds in a short period of time. After trying a bunch of different things like upping the number of audio channels to 48 and switching around primary segments and secondary segments, we ended up fixing the problem by just creating 2 performance instances, and DirectMusic is totally fine with that.


Things that went well:

  1. Split Screen:
    We were trying to decide between using a single screen for all players and using a 4-player split screen. There was a large cost with going to split screen, since the scene would have to render 4 times, decreasing our maximum graphics capabilities, and the Half-Life models had to be heavily modified to separate rendering from skinning because the skinning calculations took a long time. Ultimately the split screen allowed us to avoid camera problems, gave each player a good view of the battlefield, and let all players roam around independently. Also, the Xbox's rendering speed allowed us to keep the frame rate above 30 fps even with 4 viewports.
  2. Porting to Xbox:
    There were a number of issues with moving to the Xbox including resource paths, re-writing each DirectX call which changed between DirectX 8 and DirectX 9, working with swizzled texture resources, and working around some graphics features that are not present on the Xbox because of the DirectX version rollback. The benefits were a speed boost that allowed us to do 4-player viewport, easy playing for 4 people at a time on gamepads, and a standard hardware configuration so we wouldn't have to worry about hardware shader support for our advanced features.
  3. Prioritizing:
    By the Milestone 2 deadline, we had a pretty good idea of what we could finish by launch, and we did a good job of following our list of work remaining (reprinted here for reference):
    Work Remaining:
    - Finish adventure mode gameplay
    - Finish implementing battle physics
    - Sound & Music
    - Deformable terrain
    - Shadow maps
    - Billboarded textures, Particle System
    Would be nice, but probably won't get to:
    - Character growth
    - Save/load progress
    It was important to prioritize here; above all, we wanted the main gameplay to be fun and feel polished. A character growth system and progressing through a story in adventure mode were things that would have been really cool to have in there, but without the fundamental gameplay, they aren’t worth much. Overall, we did a pretty good job of not getting too distracted by some of the less important aspects.
  4. Playtesting:
    We were even able to play the game with 4 people several times to help balance it a bit, and as a result, our demo level is actually at a reasonable difficulty level for first-time players; a team of four will usually end up winning, but with very few (if any) cows left.