Audio input and output formats

We've had some questions about what audio formats the XNA content pipeline can take as input, and what formats they are converted to. Now that the 3.0 beta has shipped, I figure it's a good time to get some of that information out there. So, to be clear, this information is all true for the 3.0 beta, and will likely be true for the final release of 3.0 as well.

In v3.0, there are three main ways to have your game make some noise. Two of these are new to v3.0. First up is XACT, and the AudioEngine, WaveBank, SoundBank, and Cue types.

XACT:

The version of the XACT used by XNA Game Studio only accepts .WAV files containing PCM data as input. By default, the XACT build process will keep your data in PCM format as it built. This means your data does not go through any kind of re-encoding.

If you want, you can enable compression in XACT, which will change your assets to XMA data when built for Xbox 360, and ADPCM when built for Windows. Mitch has more information here.

MediaPlayer:

The MediaPlayer & Song API, which is new for v3.0, is designed for music playback in your game. To use it, drag a WMA or MP3 file into solution explorer, then use Content.Load to load it as a Song. Once you have a Song, you can use MediaPlayer.Play to play your music.

WMA, MP3, and WAV files can all be built as Songs. This is the default setting for WMA and MP3 files, but not WAVs: if you want to load a WAV as a Song, you have to change its processor to SongProcessor. Regardless of the input format, the SongProcessor will convert your audio assets to WMA files for use at runtime, so your music will go through some re-encoding here. The bitrate of the WMA file depends on the Conversion Quality you select, as you can see below:

Conversion Quality: Bitrate:
Best (default) 192k
Medium 128k
Low 96k

SoundEffect:

The third way to play audio in v3.0 of the framework is via the SoundEffect and SoundEffectInstance types, which are (duh) designed for sound effect playback. These are the counterpart of the Media/Song API. To use it, drag a WAV file into solution explorer, and then use Content.Load to load the file as a SoundEffect. Once the SoundEffect has been loaded, use SoundEffect.Play to play it.

WMA, MP3, and WAV files can all be built as SoundEffects. This is the default setting for WAV files, but not MP3 and WMA files. (Remember, their default setting is to be built as Songs.) If you want an MP3 or WMA file to be built as a SoundEffect, just change its processor from SongProcessor to SoundEffectProcessor.

The SoundEffectProcessor’s output format depends on two things: the target platform and the quality you select. It breaks down like this:

ConversionQuality: Windows Xbox 360 Zune
Best (default) PCM XMA 60 PCM
Medium ADPCM XMA 40 PCM 3/4*
Low ADPCM 1/2* XMA 20 PCM 1/2*

* The audio asset is down-sampled to either 3/4 or 1/2 of the original sample rate, with a lower limit of 8khz.

XMA is an internally developed sound format which has better compression ratios and sound quality than ADPCM. The Xbox 360 can decode XMA data in hardware, making XMA a great choice for a sound effect data format. I’m not privy to the details of the XMA encoding algorithm, so I can’t tell you what the quality ratings of 60, 40, and 20 mean, exactly. They’re not bitrates; they’re more of a hint to the XMA encoder as how aggressively it should compress. In practice, almost no one needs a quality setting higher than 60, so we’ve set that as our upper bound. You should definitely experiment with the quality settings on all platforms, to see how much compression you can get away with before the artifacts start to get too objectionable.

You are hearing me talk.

The powers that be have just posted slides and WMA audio recordings from GameFest! Here’s some links.

SoundEffect APIs in the CTP

We've just released the Community Technology Preview (CTP) of Game Studio Version 3.0. If you haven't read the post on our team blog yet, check it out here

One of the coolest new features (I'm a bit biased smile_regular) in version 3.0 is the SoundEffect API, which allows you to drag and drop wav files into your game project, and then use Content.Load<SoundEffect> to load SoundEffects.

A few people have asked about what this means for XACT. Well, the XACT APIs aren't going anywhere, they are still supported and we expect people to still use them! XACT is useful if you're looking for a more content-driven solution, where the majority of the sound design is done in the XACT tool. What the SoundEffect APIs offer is a more self-contained, code driven approach. Hope that clears things up a bit.

Posted 07 May 08 08:55 by etayrien | 0 Comments   
Content Importers

If you've thumbed through the links in my last post on this subject, hopefully you'll have a bit more of an understanding of what problems the content pipeline is trying to solve, and what constraints it has. I'm going to start off by writing a bit about content importers, since (as we'll see) the importer is the first stage of the pipeline. The beginning is a good a place to start as any, I suppose.

Note that throughout these articles I'm going to refer to "assets". It sounds kind of fancy, but it's just a generic catch all for any game resource that isn't code. 3D models, textures, level data - all assets.

OK, so first, the big picture: as you build an asset, it passes through the content pipeline in a series of stages:

  1. Import the asset from disk, and load it into memory
  2. Do some additional processing on that in-memory asset
  3. save the processed asset to disk

At each stage, the content pipeline farms the work out to a helper object. The helper objects in charge of each of those steps are the ContentImporter, ContentProcessor, and ContentTypeWriter, respectively.

The content importer's job is to load a file from disk and into memory. For every file that the content pipeline wants to build, it will call the Import function on a content importer, and then wait for the importer to return the object.

For example, Game Studio ships with the FbxImporter, which loads Autodesk .fbx files. These files contain models and meshes in a 3D scene. Most 3D modeling tools support the .fbx format. The FbxImporter will read and parse the content in an .FBX file and change it into a NodeContent, which represents a 3d scene. (More about NodeContent objects and 3d scenes here.)

Different importers read different file formats, and convert the data into different types. This help page has more information about all of the importers that Game Studio ships with, the file formats they import, and what kind of objects they are imported to.

While the asset is being imported, the importer should also do it's best to get rid of any file-format specific nuances that are a result of the file format, and "normalize" the asset as much as possible. After the importer is finished, the rest of the stages of the pipeline shouldn't have to worry about whether the asset was originally .FBX or .X, or whether it was .TGA or .JPG. This will make it much easier for the next stage, the content processor, to do its work. I'll talk more about this when I write about content processors.

The content pipeline was designed with extensibility in mind, and so it's pretty easy to move beyond the standard importers and plug your importer (or someone elses!) into the content build and use it to bring in other formats. This help page shows how to create a new importer, and this one tells how to tell the content pipeline to use your the assembly containing new content importer. For a sample of how to actually implement an importer, I strongly recommend you check out the Custom Model Importer sample, which'll import models from the .OBJ file format.

So, that's it for importers. Feel free to comment with any questions and I'll do my best to answer them.

Useful Content Pipeline Links

As I wrote the other day, I'd like to help out people that are having trouble understanding the content pipeline, and I'm planning on writing some blog posts about it. I don't have a lot of time right now to dedicate to this, but I was digging around the other day trying to see what information was already out there, and I found several links that might be useful.

Michael Klucher wrote a high level overview of the content pipeline for our team blog back when we shipped v1. (memories....) Shawn also wrote several useful blog posts explaining various concepts about the content pipeline, including:

  • an example of the way most people will use the content pipeline
  • some of the design assumptions we made
  • why we pre-build content, instead of building it when the game runs
  • an overview of how the pipeline can be extended
  • and common ways to extend the content pipeline.

 

From the help pages, we've got a good overview and architecture page. One of the best pages is this How to, which is a walkthrough that adds a custom type to the pipeline and creates a custom importer and processor. It imports pixel shaders as an example, but don't be afraid to read this even if you're not interested in importing pixel shaders! There's a gold mine of info in there.

 

Ok, that's it. Hope this helps - hopefully in the next few weeks I'll find some time to sit down and give this topic a bit more detail.

Quick N Dirty Tutorial: Making Nice Explosion Sprites

When I was working on the alpha blending posts, I found myself in need of a texture to demonstrate how additive blending could be used to make an explosion effect. When I worked on a shooter a few years back I learned a little bit about how to do that, but couldn't really remember the specifics. So, I bugged an artist friend of mine, Michael Clark, and asked him how to do it. I saved the chat log, and I've been meaning to clean it up into a proper tutorial, but I just never had the time. Anyway, here's a really really rough edit of the chat log. Maybe I'll get around to cleaning it up eventually - or maybe someone else can work from this and post better instructions?

These instructions are for Photoshop but you should be able to accomplish something similar with Paint.Net. If I remember correctly, to use this technique you'll have to download a plugin to do more complex gradients.

For reference; the end goal should look something like the image below. I know it looks really dark, but that's OK. Remember we're layering a whole bunch of them together with additive blending. You can check out the particle samples (2D and 3D) to see how this looks in practice.

 

So, enough ado - here's the chat log. Again, sorry this isn't the greatest quality... but hopefully it's better than nothing.

Talented Artist: first make a 512x512 image or whatever. then hit D to pick black and white as your default colors.
Talented Artist: then filter-render-clouds
Talented Artist: then ctrl-shift-L for auto levels
Talented Artist: then filter-render-difference clouds
Talented Artist: then ctrl-shift-L for auto levels.
Talented Artist: if you don't have some white bits in the middle then hit ctrl-i to invert.


Talented Artist: now make a new layer
Talented Artist: select the gradient tool and select circular gradient and make a circular gradient with a white middle and a black edge. you want the radius to be 2-3 pixels less than the size of the texture, and centered.
Talented Artist: set the blending mode of the gradient layer to multiple
Talented Artist: hit ctrl-L and adjust the levels so you get a nice slope - put a point in the middle or so and move it up some so that you brighten the gradient a bit. You'll have to play around with the control points a bit. Make sure the outside of the circle doesn't become visible.

 

Talented Artist: now make a new adjustment layer(in the layer palette click the middle button on the bottom, the one that looks like a circle thats half black half white) and make a gradient map layer
Talented Artist: double click the black and white gradient in the window that pops up and make a gradient that...
Talented Artist: okay have you made custom gradients before

Me: once or twice

Talented Artist: okay basically you want to make a gradient that is like black at the 0.0 mark, dark red at the 0.75 mark, orangey-yellow at the .5 mark, and yellow-white at the 1.0 mark. You'll probably wind up needing to tweak this.

Me: explain what you mean by orangeyred

Talented Artist: one sec
Talented Artist: the orangey color was 231,188,85
Talented Artist: and the yellow white is 253 255 213

Talented Artist: also this whole thing might need to be tweaked and have like a solid black at 20% opacity laid over it to darken it.

 

That's it! So, does your explosion sprite look anything like mine? And more importantly, are there any brave souls out there willing to try this out and turn it into a proper tutorial?

What's the Most Confusing Part of the Content Pipeline?

I've started to look through some of the feedback from our recent survey, and noticed that there's a lot of you out there who are having trouble understanding the content pipeline. Documentation was a frequent complaint, and a few people mentioned a lack of a high level overview.


To remedy this, I'm considering starting a series of blog posts on the content pipeline. Are there any topics in particular you would like me to cover, or any parts that you find particularly confusing? I'll see what I can do to pay particular attention to those areas. Please, remember to be specific! "It's no good" doesn't help! smile_regular

 

I've filed a forum post asking the exact same question. Feel free to leave a comment or reply to the thread, I'll be checking both.

Extending the ContentManager for Models

I don't know about everyone else, but usually the first thing I do after loading models is loop over their effects and set up their lighting. (Check Shawn's blog to read more about the standard lighting rig and per pixel lighting. ) The other day I thought of a neat way to tuck this code away a little, so I thought I'd share.

A subclass of ContentManager could easily handle the model setup code for us automatically. It could check what kind of content it's being asked to load, and if the content type is a Model, it can set up the model automatically.

first, make a subclass of ContentManager. I've called mine SmarterContentManager, because I couldn't think of a better name. 

    public class SmarterContentManager : ContentManager
    {
        public SmarterContentManager(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }
    } 

The next step is to override the Load function, and do the extra bit of set up for models. 

    public override T Load<T>(string assetName)
    {
        T t = base.Load<T>(assetName);
        Model model = t as Model;
        if (model != null)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (Effect e in mesh.Effects)
                {
                    BasicEffect basic = e as BasicEffect;
                    if (basic != null)
                    {
                        basic.EnableDefaultLighting();
                        basic.PreferPerPixelLighting = true;
                    }
                }
            }
        }
        return t;
    }

This might not be the best design for larger projects, because it ties the content loading part of your engine to graphics rendering. You might argue that a better place to set up this stuff is in some other class that manages lights and rendering state or something. But for me, this works just fine. Most of my XNA games just use BasicEffect with the default lighting rig.

One more cool thing you can put in this content manager is a bit of code to calculate the absolute bone transforms. (Shawn writes in this post about models and bones and what absolute bone transforms are.) For static models that don't animate, the bones won't change. This means that rather than recalculating the bone transforms every frame, I can calculate them once and save the results. You can do this by adding this function to the SmarterContentManager:

    public Model LoadModel(string assetName, out Matrix[] absoluteBoneTransforms)
    {
        Model model = Load<Model>(assetName);
        absoluteBoneTransforms = new Matrix[model.Bones.Count];
        model.CopyAbsoluteBoneTransformsTo(absoluteBoneTransforms);

        return model;
    }

Now, if I change my game to use the SmarterContentManager, I can just load my models like this:

Model model;
Matrix[] bones;
model = content.LoadModel("models\\asteroid1", out bones);

and draw them like this:

    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect basic in mesh.Effects)
        {
            basic.Projection = projection;
            basic.View = view;
            basic.World = bones[mesh.ParentBone.Index] * world;
        }
        mesh.Draw();
    }

Shawn's probably going to hassle me for linking to his blog three times in one post (What's the matter - don't you have any content of your own?) but oh well. Maybe someone will find this interesting.

Posted 03 May 07 01:38 by etayrien | 1 Comments   
SwampThingTom on Performance Analysis:

Interesting read on SwampThingTom's blog today. I'd never heard of the NProf tool before, but the information it gives seems to be very similar to what you can get from much more expensive tools. Definitely worth reading.

Foreach and Garbage, Part 2

As promised, here's a bit more about foreach and garbage. Last time I wrote about the Windows Desktop CLR. This time I want to find out if my results hold true on the Xbox 360 as well.

What did I find out last time?

"When doing a foreach over a Collection<T> an enumerator WILL be allocated.
When doing a foreach over most other collections, including as arrays, lists, queues, linked lists, and others:
    if the collections are used explicitly, an enumerator will NOT be allocated.
    if they are cast to interfaces, an enumerator WILL be allocated."

For you impatient readers, here's my results: As I just found out, this holds true on the Xbox360 as well. Read on to find out how I came to this conclusion, and maybe learn something about (deep breath) The XNA Framework Remote Performance Monitor for Xbox 360.

Although the Remote Performance Monitor (RPM) is extremely useful, it gives you less data than the Desktop CLR profiler that I used last time. I'm not an expert on how to use the tool, but I couldn't figure out how to figure out what types of objects were allocated. So, how can I figure out if an enumerator is being allocated or not?

Luckily, the RPM is more realtime than the desktop CLR profiler: it'll give you information on allocations as they happen. So, hopefully I can make a game that makes no allocations per frame, and verify that in the RPM. After the game finishes initializing, the RPM should report that no new objects were allocated. To test whether or not different foreach loops create garbage, I can change my game's Update to do a foreach. If the RPM reports that objects are now being allocated, I can assume that an enumerator was allocated.

So, to start, I'm creating a new Xbox360 game using the default template. I called my game ForeachXbox. I didn't change anything, just F5 to build and deploy it. Now I'm ready to use the RPM to verify that this game causes no per-frame allocations.

I followed this document for instructions on how to set up the Remote Performance Monitor, entering ForeachXbox as the name of my game. (You can ignore the instructions on how to use PerfMon.exe, we won't be using that.) I hit launch, scroll down to the GC section, and after a few seconds, the Remote Performance Monitor should show something like this:

The bit that we're interested in is Managed Objects Allocated. Notice how that's 0? This is a Good Thing.

So now lets start testing the rules that we came up with for the Desktop CLR. Switch back to Visual Studio, and we'll change the Game code a bit. We'll create the same GameEntity class as last time:

    class GameEntity
    {
        public void Update()
        {
        }
    }

and add a Collection of those to our game.

Collection<GameEntity> entityCollection = new Collection<GameEntity>();

I'll add ten game entities to this collection in the game's constructor. Then in the game's update, I'll just foreach over the collection:

    foreach (GameEntity entity in entityCollection)
    {
        entity.Update();
    }

Then hit F5 to build and deploy the game. Quit the game, and then launch it again using RPM. After the game finishes initializing, you should see something like this:

As expected, there's those allocations. And from the looks of it, it seems like RPM collects data about once a second.

Now that I know this, if I want to check to see if different foreach loops allocate or not, all I have to do is just change my code to use that foreach then run it using the RPM. If it allocates, it'll show up in that lovely little Delta column. This is how I verified that the Xbox 360 foreach behaves the same as foreach on the desktop CLR. 

Hope this helps. I didn't dive too much into how to use the Remote Performance Monitor, I know. I haven't used it much, but it seems like a very useful tool. If I find myself doing something really neat with it in the future, I'll be sure to write about it.

Optimizing for Readability

About a week ago, Shawn wrote a post about different types of code, and how he designs them. In a few categories, he mentioned optimizing for readability. Today OldNewThing has a post about optimizing for readability. It's an interesting read, and the bit about boolean function parameters definitely rings true with me.

The other day I was working on my GSE game, and I had a class with an Update method. The Update returned a boolean indicating whether or not the class was finished updating. The idea was when the object was finished updating, I could stop worrying about it, and I could remove it from whatever collection was keeping track of it. The problem was I could never remember what the boolean was supposed to be: true if you're done updating? true if you're still updating? In the end I gave up trying to remember, and I just made a custom enum:

 

enum UpdateResult
{
      StillUpdating,
      Finished
}

 

Problem solved.

Opaque Data From Max and Maya

This post is kind of a brain dump from notes I made to myself, so apologies in advance if it's not very readable! In both Max and Maya you can set opaque data, which will be exported in fbx and then brought through the content pipeline. We do this in the NormalMappingEffectSample: the name of the file for the normal map is set by the artist in his creation tool. We've gotten a lot of questions on the forums about how the artist set the opaque data on the model, so I figured I'd write a blog post about it. Bear in mind that different tools have different levels of support for opaque data. I've had the most luck with exporting strings. Without further ado, here's how to create Opaque data in max and maya:

 

Max:

to create a custom attribute:

  • select your object
  • select the menu entry Animation, then Parameter Editor...
  • select the type of parameter (or attribute) you want to add and click "Add"

to see and modify the attribute:

  • Select the "Modify" tab in the right panel
  • your base object should be selected in the modifier list
  • your attribute is listed under the "Custom Attributes" tab.

 

Maya:

I scribbled this down months ago and I don't really remember what it means. Our license server is on the fritz, so I can't open Maya and decode it, but hopefully someone can make sense of this. If you do manage to decode it, post a comment so everyone can benefit smile_regular

  • click an object
  • go to the attribute editor
  • click attributes
  • add an attribute
Foreach, Garbage, and the CLR Profiler

 I've been doing a lot of thinking lately about foreach, and in what situations it may or may not create garbage. There have been some posts on the forums asking about it, and I recently found out that for some types, the enumerator that is created is a value type, not a reference type. So, foreach loops over those types shouldn't make garbage at all. Why does this matter? In an XNA Framework forum thread, Stephen Strychak, an XNA Framework Developer, explains:

"...the performance of your game will degrade if you allow garbage to be generated during gameplay. The problem again is not the for or foreach. The problem is that the garbage collector on the Xbox 360 is less efficient than the one on Windows, and doing a collection can take a long time. When you have many objects in your game, it can take longer than 1/60th of a second to do a collection. That means that you will drop frames. The more garbage you generate, the more frequently the GC will have to do a collection, and the more frequently your game will drop frames.

So the bottom line is that you should avoid generating garbage, and using for instead of foreach is one way to do that."

Shawn posted this in a follow up, which is an important point:

"...bear in mind that just because foreach creates garbage, this does not neccessarily mean you should avoid using it! I think people often get too hung up on optimisation. Fast code is good, but readable code is more important. If your program is already running fast enough, it's sometimes ok to do things the easy way, even if you know that is less efficient than it could be..."

I agree with Shawn: when I write code, I want it to be maintainable, easy to read, and easy to modify. But still, it's good to know what things may have a potential speed impact. And plus, since I'm a nerd, I wanted to look into this: the whole situation seemed like a lot of special cases and conjecture: you will make garbage if you go over this collection, but you won't if you go over that collection (but only if x is true). I wanted to get the whole story. Plus, this was an ideal reason to learn to use the Windows and Xbox 360 CLR profiling tools, which I've put off doing for some time. So, I figured I'd do just that: do a foreach loop over a bunch of collections, use the profilers to see if I'm making garbage, and hopefully we'd all learn something in the process.

I started with Windows, so the first thing I needed was the Windows CLR Profiler. I needed to learn how to use it, too. It comes with a pretty hefty document explaining it, but I found this link on msdn tv, where a friendly man gave me an overview. It's about half an hour long, and was worth watching, I think.

After watching the video, I felt fairly confident that I would be able to get the information I needed. It was time to start mucking around, to see what I can find out. In C# express, I created a new console app, and filled it in with what is quite possibly the world's dumbest code ever:

class Program
{
class GameEntity
{
public void Update()
{
}
}

static GameEntity[] entities = new GameEntity[100];
static Program()
{
for (int i = 0; i < entities.Length; i++)
{
entities[i] = new GameEntity();
}
}

static void Main(string[] args)
{
byte[] byteArray = new byte[1];
for (int i = 0; i < entities.Length; i++)
{
entities[i].Update();
}
}
}

I wanted to do this for a quick sanity check. If I had any idea what I was doing, when I use the CLR profiler on this app, I should see Main making one allocation, the 1 byte array. So I built it, and ran the CLR Profiler on it. Once it was done, I pulled up the allocation graph using by clicking this button:

 

My first look at the allocation graph was fairly terrifying. It's full of colored lines and boxes for methods I'm fairly sure I didn't write.

 

After fiddling for a minute, it turned out that as always, right clicking is the answer. I did "Find routine" and looked for Main, which gives me this view. Much better.

I've got no idea why my 1 byte array actually took up 13 bytes, but I'm not particularly worried about it. (I'm curious to know, however, so if anyone out there in readerland has any ideas, send them my way.)

 

As my next sanity check, I changed my program from an array to a Collection<GameEntity>, and changed Main to do a simple foreach over the Collection:

 

static void Main(string[] args)
{
byte[] byteArray = new byte[1];
foreach (GameEntity e in entities)
{
e.Update();
}
}

Under the hood, that should create an enumerator on the managed heap, which should show up in the profiler. So, following the exact same steps in the CLR profiler, I get this display:

There's that pesky enumerator right there, just as we expected!

Now, here comes the tricky part. Rumor has it that many of the types in System.Collections.Generic will not allocate an enumerator when using foreach. List's GetEnumerator, for example, returns a struct, which will just sit on the stack. Look for yourself with .NET Reflector, if you don't believe me. To prove to myself that a foreach over a List doesn't cause any heap allocations, I changed entities to be a List, did the exact same foreach loop, and ran the profiler. No enumerator! I tested a few other types as well, LinkedList and Queue, with the same result. Foreach loops over most of the types in System.Collections.Generic do not generate garbage. The most prominent exception is Collection. Collection<T> cannot declare its enumerator type as a struct, because Collection<T> is designed to be easily overridden.

However, there is definitely a caveat to the above. Foreach loops over Lists can still generate garbage. Take this code, for example:

const int NumEntities = 100;
static List<GameEntity> list = new List<GameEntity>();

static Program()
{
for (int i = 0; i < NumEntities; i++)
{
list.Add(new GameEntity());
}
}

static void Main(string[] args)
{
UpdateIEnumerable(list);
}

private static void UpdateIEnumerable(IEnumerable<GameEntity> enumerable)
{
foreach (GameEntity e in enumerable)
{
e.Update();
}
}

this will create garbage. Even though we're still doing a foreach over a List, when the list is cast to an interface, the value type enumerator must be boxed, and placed on the heap.

So, as far as I know, here's the final word:

When doing a foreach over a Collection<T> an enumerator WILL be allocated.
When doing a foreach over most other collections, including as arrays, lists, queues, linked lists, and others:
    if the collections are used explicitly, an enumerator will NOT be allocated.
    if they are cast to interfaces, an enumerator WILL be allocated.

Hope this helps some people, and maybe you're a little less terrified of the CLR profiler. Next time, I'd like to do the same thing for the .NET CF on the Xbox 360, or maybe figure out how to use the CLR profiler to track down the methods that are doing the most allocations. I have a pretty terrible track record with "Next times", though, so don't keep your fingers crossed.

First Person Shooter Cameras

 

So, it's been a long time since my last post.  Sorry about that.  We've been busy lately, which should put smiles on all your faces.  I like my job, so I can't give any specifics, but we've got some stuff coming up we think you'll be excited about. 

Anyway,I want to write a quick post about how to do a camera for a first person shooter. This won't be the king of cameras by any means, but hopefully it should be enough to give you a jumping off point.  Basically, it's just going to have mouse look and the keyboard arrow keys will control the movement.  To keep the code simple, I'm leaving out GamePad input, which you'll probably want to hook up if you're planning on using this on your 360 :) 

Sorry about the sketchy formatting throughout this post; either Windows Live Writer is messed up, or I am, and I'm too tired to argue with it to see who's fault it is. 

let's get the boring stuff out of the way first.  The camera is a DrawableGameComponent, which means I can add it to my game's Components collection and it will get updated automatically.  Although it will never draw itself, I've still made it a DrawableGameComponent, because I'll want to get at the GraphicsDevice every now and then.

Then we've got a bunch of different properties and fields that will store the data that's important to keeping the camera running.  The camera has a position in the world and a movement speed, which controls how quickly the camera moves.  It stores two vectors for up and forward, which other classes can use but not set.  It's got pitch and yaw, which represent rotations around the X and Y axes, respectively. 

Notice that I'm capping pitch between -89 and 89 degrees: almost straight up and down, but not quite.  This makes the camera a lot simpler, both from a coding and from a usability standpoint.  If you don't do this, it's possible to "flip" your camera over, and all of a sudden moving the mouse left turns the camera right, and things basically get topsyturvy. 

The two most important members of this class are the View and Projection matrices.  Without these two guys this whole class would basically be worthless.  There's lots of explanations about these out there on the web, so if you find my explanation insufficient, don't panic.  There's a ton of resources out there.  For now, suffice to say that the view matrix contains information about the position and orientation of the camera. The projection matrix contains information about the aspect ratio of the camera, and how far it can see (the near and far clip planes.)  Any time you want to draw a triangle, the triangle's vertices have to be transformed by the view and projection matrices.

So, here's the code for that bit: 

public class FirstPersonShooterCamera : DrawableGameComponent
{
    private Matrix view;
    public Matrix View
    {
        get { return view; }
    }

    private Matrix projection;
    public Matrix Projection
    {
        get { return projection; }
    }

    private Vector3 position = new Vector3(0,0,0);
    public Vector3 Position
    {
        get { return position; }
    }

    private float yaw;
    public float Yaw
    {
        get { return yaw; }
        set { yaw = value; }
    }

    private float pitch;
    public float Pitch
    {
        get { return pitch; }
        set
        {
            pitch = MathHelper.Clamp(value, 
                -MathHelper.ToRadians(89), MathHelper.ToRadians(89));
        }
    }

    private float movementSpeed = 100;
    public float MovementSpeed
    {
        get { return movementSpeed; }
        set { movementSpeed = value; }
    }
    private Vector3 up;
    public Vector3 Up
    {
        get { return up; }
    }

    private Vector3 forward;
    public Vector3 Forward
    {
        get { return forward; }
    } 

Got all that? Here's a short little blip, showing how the camera gets initialized (and why I need the GraphicsDevice.)  The constructor is really simple; it chains to the base DrawableGameComponent, and then sets Visible to false, so the camera's Draw method will never get called.  Similarly, Initialize calls the base class's initialize, and then calls UpdateProjectionMatrix.  You might be wondering why UpdateProjectionMatrix can't be called from the constructor, but if you look, you'll see that the GraphicsDevice property is used, which won't be available until base.Initialize has finished. 

Remember, the projection matrix is the one that controls the virtual "lens" of the camera, including the field of view, aspect ratio, and the far and near clip planes. 45 degrees is a pretty standard value for field of view, but you can get some pretty cool effects by messing with it.  Lots of games will stretch this out really wide, which is really weird looking, but makes the player feel like he is moving REALLY fast.  Burnout does this really well.  Aspect ratio we calculate from the viewport width and height. 

View distance we set to some values that make sense to me, 1 and 1000.  Those two numbers could be totally different for your game, feel free to put in whatever you want. Basically, they mean that everything closer to the camera than 1 unit will be clipped, and likewise for anything further away than 1000.  A common mistake is to make your far distance really really really far.  After all, if you can afford to spend the time to render it, why would you want to clip out part of your beautiful world?  Well, remember, your depth buffer has limited precision.  (I think the framework's default is 24 bit depth buffer, 8 bit stencil.)  Because of the way floating point works, you can either have large ranges of values, or precision in those values. The larger your range of values get, the smaller your precision is.  What's that mean? If you're not careful, setting your farclip to something very large can cause a loss of precision, which causes z-fighting. 

A more robust camera would have field of view, viewport, and clip planes as class properties, and their setters would invoke UpdateProjectionMatrix. 

    public FirstPersonShooterCamera(Game game) : base(game)
    {
        Visible = false;
    }

    public override void Initialize()
    {
        base.Initialize();
        UpdateProjectionMatrix();            
    }

    private void UpdateProjectionMatrix()
    {
        Viewport vp = GraphicsDevice.Viewport;
        projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 
            vp.Width / (float)vp.Height, 1.0f, 1000.0f); 
    } 

Here's the last bit of code needed to make this guy work - the Update stuff.  First, update needs to check the mouse for yaw and pitch changes.  Using that information, we'll figure out the orientation of the camera, using the Matrix.CreateRotation functions and our new yaw and pitch values.  Next, HandleMovement, which returns a Vector3, is used to check keyboard input.  Up and down change the Vector3's Z values, left and right change X.  This vector is then transformed by the camera's orientation, so that hitting left moves us to the camera's left, and not just to the left in the world.

One thing to notice in the HandleMovement function is that I normalize the move vector.  If I didn't do this, and the user was hitting both Up and Left, the camera would move along both X and Z at MovementSpeed units per second.  For a camera, this is probably not so much a problem, but if this were code that controlled a player, for example, it wouldn't take the smart players long to realize that they can move faster when they are moving diagonally.

Finally, in HandleYawPitch, I'm using a nullable MouseState.  Those are really neat, and let me keep track of whether or not I have a valid value for lastMouseState, so I know if the delta values will make any sense.  I use nullables a lot for this kind of pattern, where I compare "lastSomething" to "somethingNow." 

    public override void Update(GameTime gameTime)
    {
        float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;            

        HandleYawPitch(dt);            
        Matrix cameraOrientation = 
            Matrix.CreateRotationX(pitch) * Matrix.CreateRotationY(yaw);

        Vector3 movement = HandleMovement(dt);
        Vector3.Transform(ref movement, ref cameraOrientation, out movement);
        position += movement;
        
        view = Matrix.CreateLookAt(position, 
            position + cameraOrientation.Forward, Vector3.Up);

        up = cameraOrientation.Up;
        forward = cameraOrientation.Forward;

        base.Update(gameTime);
    }

    private Vector3 HandleMovement(float dt)
    {
        KeyboardState keyboardState = Keyboard.GetState();

        Vector3 move = Vector3.Zero;
        if (keyboardState.IsKeyDown(Keys.Up))
        {
            move.Z -= 1;
        }
        if (keyboardState.IsKeyDown(Keys.Down))
        {
            move.Z += 1;
        }
        if (keyboardState.IsKeyDown(Keys.Left))
        {
            move.X -= 1;
        }
        if (keyboardState.IsKeyDown(Keys.Right))
        {
            move.X += 1;
        }
        if (move.LengthSquared() != 0)
        {
            move.Normalize();
        }
        move *= movementSpeed * dt;

        return move;
    }

    MouseState? lastMouseState;
    private void HandleYawPitch(float dt)
    {
        MouseState mouseState = Mouse.GetState();
        if (lastMouseState != null)
        {
            Yaw -= (mouseState.X - lastMouseState.Value.X) * dt;
            Pitch -= (mouseState.Y - lastMouseState.Value.Y) * dt;
        }

        lastMouseState = mouseState;

    } 

So, that's it. A simple camera (look ma!  no quaternions!) which you might find useful, if only for just getting around in the world while you're debugging. There's definitely some improvements that can be made to make the camera more flexible.  Also, I've got a whole CameraManager system that someone out there might find interesting, but that's for another time.  Sorry the post was so short, but it's time to get some sleep, and then it's back to work tomorrow.  I've got some more stuff that I can't tell you about to do :)

Alpha Blending, Part 3

Ok, in this post I'll talk about additive alpha blending.  This time for sure, I promise.  In part 2 I talked about disabling depth write, and how that could give okay-ish results.  It'll come in handy with additive blending, so if you haven't read that post yet, you should.

 

Additive Blending:

In part 1 I talked about the blend function that is used to combine two colors.

FinalColor = (SourceColor * SourceBlend) + (DestinationColor * DestinationBlend)

For normal alpha blending, SourceBlend is set to SourceAlpha, and DestinationBlend is InverseSourceAlpha.  If we leave SourceBlend as SourceAlpha, but change DestinationBlend to be Blend.One, we get additive alpha blending.

FinalColor = (SourceColor * SourceBlend) + (DestinationColor * DestinationBlend)
FinalColor = (SourceColor * SourceAlpha) + (DestinationColor * One)
FinalColor = (SourceColor * SourceAlpha) + DestinationColor

So here's the cool part: with additive blending, the order objects are drawn doesn't matter.  Addition is commutative: no matter what colors you have, the order you add them together doesn't matter, you're always going to get the same result.  Imagine we have three objects that we're going to draw with additive blending.  Their colors are .5, .3, .2, and the alpha for all of them is 1.  No matter what order we draw the three, the result will be the same: .5 + .3 + .2 == .2 + .3 + .5 == .3 + .2 + .5.

Think about what happens when we combine that with the depth write disabled technique from part 2.  If you remember, drawing with depth write disabled solved our problem of transparent objects obscuring each other.  However, the order objects were drawn was still important.  With additive blending though, the order isn't important. So, if we draw objects with additive blending and depth write disabled, we don't have to sort them.

Cool! So there's a blending technique that doesn't require us to sort by depth.  It's easy to set up, too; just a couple of render states.  This is incredibly useful, and can save your engine a lot of complexity and make it faster too.  Some games actually use additive blending exclusively, and don't use traditional blending at all.

It's a little harder to work with from an artistic point of view, though. In the example above, notice how even though the alpha was 1, the objects weren't completely "opaque."  For example, if you draw a green object with 1 alpha using additive blending, your result isn't going to get a solid green object; it's going to be whatever was in the back buffer before you drew your green object, plus 1.0 green.  It usually takes a little while to get used to this style of blending, and to figure out how to make art that looks good with it.  It's invaluable though, so I'd definitely recommend spending the time.  Paint .Net lets you blend layers using additive blending, so you can start to get a feel how your art will look. (I'd be surprised if Photoshop didn't let you do the same thing, but I don't have it installed so I can't check.)

 

Explosions:

So, we've seen how to set up additive blending, but what's the best way to use it?  Additive blending is ideally suited to making explosions and clouds, among other things.  Let's see how we can use additive blending to make an explosion using a particle system.

(I just heard a huge racket over my shoulder, and looked over and saw a perfect example on Michael's TV.  He's playing Roboblitz, and there's explosions everywhere.  Apparently he's pretty bad at this game.)

A particle system is just a whole bunch of sprites (or other objects) drawn together to create a special effect.  You can use particle systems to make lots of things, but they're typically used for fire, explosions, and smoke.  I might write a detailed post about how to set up a particle system in the future, but for now I'm going to keep it simple. 

Let's just assume we have a particle system that draws ten sprites, and moves them a little bit every frame.  The sprites will use this texture: (it's not very good, I'm not much of an artist):

We'll draw ten of these babies with depth write disabled and additive blending, and all of a sudden we've got a pretty decent explosion.  Since depth write is disabled, the particles won't obscure one another, and since they're drawn with additive blending, the order they're drawn doesn't matter.

 

 

 

 

 

 

What next?

I hope this post was useful. It seems a little shorter than the rest of them, which is unfortunate, because I use additive blending more than any other blend function.  I'm going to stop here, though, because I'm a little hesitant to dive into the deep end of this stuff without knowing what the community would like to see next.  Should I keep going with this additive blending thing, and show a sample particle system?  Should I write more about game structure?  There have been some questions on the forums about cameras, so unless I get any other suggestions, I think my next post is going to be a quick and dirty first person shooter camera.

More Posts Next page »
Page view tracker