Before you get to the game screens and the game logic of the racing game itself, take a quick look at the original concept of the game so you can understand why certain implementations were made and why other parts are missing from the game. Some parts of the game were added later after most features from the concept were implemented. One example of this would be the shadow mapping, which is always a complex part of a game engine because you can spend a lot of time fine-tuning it until it looks right. Other things had to be skipped or were intentionally left out because they were just too complex to implement in such a short time frame. The best example here would be the obstacles on the road (for example, columns) into which the player could crash if he did not drive carefully (see Figure 14-2).
The main problem with this idea was to set the obstacle columns effectively. There is no level or track editor for the game as you learned in Chapter 12. All tracks are generated from splines and it would be hard to even set those obstacles in 3D Studio Max. It would cause even more problems game testing these objects and updating the collision checking system for these obstacles. In an earlier test version I used the road columns that the road stays on (see Figure 14-3) to test out this obstacle idea, but it did not look very convincing and driving on the road was already hard enough. A much better solution was to add destructible objects like road signs, traffic lights, trash cans, and so on, but as you already saw in the last chapter the physics engine is not capable of handling many different objects. It just checks for any collision between the car and the guard rails and does a couple of physics calculations, and that’s it.
After a couple of unit tests I decided to drop the idea and concentrate on the rest of the game. I also added tunnels and an easy way to set palms, signs, lanterns, and so on near the road and added a nice technique to automatically add landscape objects to each level near the roads.
The following text is directly from the initial game concept that was made in August 2006 way before the Racing Game was developed later in 2006. Please be aware that it was written before the first XNA beta came out and I did not know many details about XNA in general.
The Racing Game is a simple 3D racing game. The player will see his car in the front and can directly control it with an Xbox 360 Controller (Xbox) or the mouse and keyboard (PC). The main goal in the game is to drive as fast as possible to the finish line circumventing all obstacles in the way. Crashes do not hurt the player, but the car will lose all its speed and the total time for completing the track will be much higher. The player can’t lose a game; he only can take a long time to complete a track. There are 3 tracks with different difficulty settings and a highscore list for each of the tracks. (Note: Neither multiple tracks, nor the high-scores or trophies are implemented in this racing game version for this book; please check out the XNA Framework Racing Game Starter Kit for more details and additional features of the game plus more mods.)
The graphics for this game (see Figure 14-4) are oriented on games likes Trackmania, Grand Tourismo, Need for Speed, and similar games. Obviously these games took a very long time to develop and have highly polished graphics, which is not possible in this short timeframe, so the main focus for this game is to create a simple and easy to learn racing game with just the basic features. The main graphics theme is located in the city (showing streets, some simple buildings, and some trees). The underground is done with the help of a simple 2D graphic, the tracks are auto-generated from a 2D image (using points in certain intervals), and for additional difficulty a height-map is used to create hills and make the track more difficult. (Please note that this idea was abandoned and instead real 3D data is now imported into the game.)
In the game there are no other cars around (like in Trackmania), which keeps the code simple and only requires collision checking with objects in the level. The game consists in the basic version just of a main menu, three simple single-player tracks, and a highscore list for each of the tracks (time dependent).
More graphics sets (snow, desert, grassland, and so on), more tracks, and maybe even mods or multiplayer game code could be made at a later point (for example, by the XNA community).
Due to the short development time everything is cut down to the basic idea of controlling the car and driving through the tracks. A simple physics system is also implemented and collision with static objects in the scene is checked each frame. All other ideas are left out of the basic game. This simplifies not only the game play, but also the understanding of the techniques and the code.
The game is viewed from behind, showing the car in the front, the track in the middle, and some more objects and the sky far away. The sky is implemented with a sky cube map.
The concept goes on and talks about the development time, all the features, and which project files are used, how shaders are going to work, and which effects and sounds are used. Then it gets a little bit more interesting again when the concept discusses the technology to be used.
The XNA Racing Game Starter Kit will be programmed in C# using .NET 2.0 and XNA using XNA Game Studio Express (similar to Visual C# Express with DirectX). A big plus is using the XNA API and managed code in general, which helps to write very easy to read code, which executes powerful functionality like loading textures, using shaders, or displaying models with just one line of code.
The graphics are all .dds files. All models (Car, Trees, Effects, and so on) are modeled with 3D Studio Max 8 and exported into Microsoft DirectX .X files. Models will not use animations for easier understanding of the underlying code (Note: And as I later found out XNA does not even support animated models out of the box).
The game will not use any existing engine or additional complicated framework. Non-game programmers would not be familiar with it and this would only confuse anyone wanting to take a look at the source code. The game consists basically of these parts:
Helper classes for texture loading, model loading, game screen handling, managing XNA, handling controls, and so on.
Shader class to support rendering models (mainly the car) and the landscape with shader effects. (Note: I quickly found out that I underestimated the amount of landscape objects required to make the road and the landscape look at least somewhat decent.)
Pre- and post-screen shader class to help you with rendering the sky and adding effects like glow, motion blur, and color correction.
Main menu class for starting a game, checking the highscores, and for quitting the game.
Highscore game screen for showing the highscores and maybe even submitting them to an online server. Highscores are also saved locally, which can be viewed with this class as well. (Note: Because XNA does not support any networking and I wanted to use the same code for the PC and the Xbox 360 version I left out the sending the highscores to an online server.)
Mission class to play the game.
That was almost the entire concept and I started coding as soon as I had the first XNA beta in my hands. Of course there were many more problems during the development of the project, but thanks to unit testing and updating the concept from time to time it was not hard to keep track of the general game idea. The hardest parts were definitely getting the landscape and track code working (see Chapter 12) and the physics from Chapter 13. The shadow mapping is probably also not an easy thing for most inexperienced programmers, but I just implemented a shadow mapping engine into another engine a few months back, and by looking at the unfinished racing game I was sure that it would never look very good without implementing some kind of shadows.
If you take a look at the class overview of the whole racing game (see Figure 14-5), it looks quite complex, especially if you think about the short development period. The biggest help was the Rocket Commander project, which allowed me to quickly test out ideas, models, and rendering techniques with the help of its unit tests and code I added early in the development process. Later when the XNA engine was standing on its own feet I could move code over and continue testing in the new engine. For example, I found out early that my idea to generate the tracks from 2D images was not very clever and caused more problems than it solved. Implementing the basic tracks was not very hard, but when I needed access to 3D vertices I was happy to have some Managed DirectX code from the Rocket Commander game that allowed me to load .x files and use their vertices to generate an early track. Sadly XNA does not support accessing or loading the vertices of imported 3D models; you could write you own content importer, but that was too much work since at the time I just wanted to test some tracks. I later changed the way tracks are imported and added many features to it (tunnels, road widths, road objects, palms, and so on). Tracks are now directly exported in 3D Studio Max into the Collada format and then imported in the game engine into a binary format (for quicker loading).
The following features were imported from previous projects (mostly from Rocket Commander):
Helper classes like the Log class for debugging, the versatile StringHelper class or other important classes like the RandomHelper, Vector3Helper, or ColorHelper classes.
The graphics engine was written from the ground up, but the basic ideas for handling textures, materials, models, fonts, and lines through special classes, which extend the functionality of XNA classes, was taken from Rocket Commander. Most classes in XNA are much simpler and did not do much at the beginning like the Model class, which just loaded an XNA model and displayed it with a couple lines of code. But later the model rendering was improved a lot for higher performance (which is discussed later in this chapter) and the internal logic for the model class changed completely. Models were no longer rendered directly; instead, the shaders and meshes for each used 3D model were collected and rendered at the end of the frame together with all other meshes that use the same shader, material, and mesh data. This way the rendering performance was improved by 200%–300% on the Xbox 360. And thanks to the abstraction to the special model class none of the code that used models had to be changed, it just got faster!
The game screen logic was stolen 100% from Rocket Commander. I really like the idea behind it and it is very simple to implement.
Shaders and other graphical classes like the ParallaxMapping, PostScreenGlow, PreScreenSkyCubeMapping, and the LensFlare class were imported from Rocket Commander too, but after a while only the LensFlare and Sky shader were left because all the other shaders changed too much. Instead of writing a new shader for every new material, a more general ShaderEffect class was written and all the shaders are now derived from it. This simplified some shaders and made classes like NormalMapping or ParallaxMapping obsolete because they work now directly from the ShaderEffect class, which can handle all the used shader parameters. The PostScreenGlow shader was derived from PostScreenMenu, which is a new shader just for the menu.
For unit testing a similar approach was taken, but both NUnit and TestDriven.NET were left out to simplify the development on the Xbox 360 and because TestDriven.NET does not work with the Express editions of Visual Studio anyway.
There are of course several new features in the game, which are not even in the game concept like the shadow mapping, which uses the following classes:
RenderToTexture: While this class was implemented for the post-screen shaders anyway, it had to be improved to support rendering shadow maps, which require different surface formats for better precision. This means that for normal render targets you can just use the same format the back buffer uses. This is most likely R8G8B8X8, which means that 8 bits are used for all the color channels and 8 bits are reserved because alpha does not make sense for the back buffer, and 32 bit in total is better than 24 bit, which makes it much harder to read pixel data on 32-bit platforms.
Anyway, for shadow maps you need more precision per pixel and you don’t really need colors. Each pixel of a shadow map should be just a depth value between 0 and 1 and have as much precision as possible. Good formats are R32F or R16F if the first one is not available. But these formats have to be initialized differently and the RenderToTexture class handles this for you. It also decides based on the options the user selected if the shadow map is 2048×2048 in size, or 1024×1024 or just 512×512 for slower computers. It also supports fallback to a 32-bit color format if both R32F and R16F are not available.
ShadowMapBlur: This class helps you to blur the shadow map. The technique used here is similar to the glow technique for the PostScreenGlow shader. It takes the input data and blurs it in two steps. The blur not only makes the shadows much softer and appear more realistic, but it also fixes certain shadow mapping artifacts, which are just blurred away.
This trick was not used in the XNA Shooter game because it will not look good if you have a lot of smaller objects in the background and a lot of overlapping shadow areas and non-shadow areas. In this racing game it looks much better; the shadows on the car especially have fewer artifacts and appear much smoother. All the other landscape objects are mostly distant and their shadows do not overlap with other objects. This allows you to use even nicer blur values.
ShadowMapShader: This is the main class for shadows. It initializes all the required render targets, used textures, the shadow mapping shader, and it handles the ShadowMapBlur class. More about the shadow mapping technique is discussed later in this chapter.
Other game features like loopings and the landscape rendering engine you saw already in Chapter 12 were also not in the game concept. They just evolved from the early unit tests of the TrackLine and Track classes and they made the game more fun.
I do not really remember why the game concept does not talk about the game screens. It just says that the main menu should allow starting a mission, viewing the highscores, and quitting the game. The game ended up with a highly complex game screen namespace (see Figure 14-5), which supports many more game screens than planned. Please note that some of the game screens are not implemented in the code for this chapter (for example, the track or car selection screens); they are only used in the full Racing Game Starter Kit for the XNA Framework. The main reason here is that you only have one track and one car and it would not make much sense to implement those screens if there is nothing to select anyway. You can find a more complex version of the racing game and more information about the game in general at http://www.xnaracinggame.com, the official website for the game.