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:
- 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.
- 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.
- 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.
- 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:
- 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.
- 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.
- 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.
- 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.
|