In the next two weeks I have deadlines for two different contests coming up, so things are getting pretty hectic. Lots of things changed in the ~100 or so commits since the last blog post, so I’ll pick a few to describe.
Archive for July, 2009
Home stretch
Jul 30
Event-driven audio
Jul 24
One of the older items on my to-do list was to give my sound designer a way to change the game’s audio without having to recompile the game in Visual Studio and start it up. Based on some of the improvements I made recently, I was finally able to knock that item off my to-do list.
Below, you can see a short annotated video walkthrough where I demonstrate the technique and show how it integrates with XACT.
There are a few key pieces necessary for this to work.
Good music adds a lot to a game, but only if it fits the action occurring on screen. Badly chosen music that doesn’t match the feel of your game, or is inappropriate for the tone of the gameplay, won’t do you much good. Dynamic music is one way to help make sure your music is always a good fit for the action occurring on-screen – it lets you adapt the music the player is hearing based on what’s happening.
There are various approaches for building dynamic music – some games go so far as to create hundreds of short music ‘snippets’ and string them together to create longer, more dynamic songs that never sound the same to the player once. Unfortunately, this is pretty complicated to do, especially if you’re just working with XNA and XACT. Luckily, there are some simpler techniques you can use to build dynamic music.
Below, you can see a short video clip showing one of my experiments with dynamic music. I’ll explain how it works in this post, so you can apply the technique to your games. In this video clip, I have two music tracks playing, and control the volume of one of them depending on whether or not the player character is within the blue rectangle. You can hear the track fading in and out as he moves.
The approach I’m using works like this: You take a song that you intend to use in your game, and in an audio tool, split it out into multiple wave files, one for each ‘track’ that you want to have control over. Depending on the complexity of your song, you might just want to split it out into a pair of tracks, and fade one of them in when you want the music to get more intense. If your music is more sophisticated and your gameplay has lots of variation, you may want lots of tracks that you can control individually based on what’s happening – fade in some anxious-sounding strings when powerful monsters are on screen, and temporarily fade in some ominous-sounding bass notes when the player is injured, for example.
Threaded Renderer
Jul 20
Lots of changes since last week, but the majority of my efforts were focused on overhauling my approach to rendering.
My basic approach is primarily inspired by Christer Ericson’s post about the approach he uses for ordering draw calls. Some other useful sources of information were Tom Forsyth’s post about the cost of renderstate changes and Guerilla’s presentation on Killzone 2′s renderer from DEVELOP 2007. The Killzone 2 presentation is especially worthwhile since it describes the way they take advantage of concurrency techniques in their renderer.
Until now, the rendering code in my game has been almost entirely immediate-mode; various objects in the game world like tiles and entities had Draw methods that would utilize the game’s GraphicsDevice and SpriteBatch to render themselves, changing render states as needed and generating dynamic geometry where necessary (like for water).
Shipping a Brick
Jul 15
Computer software, and by extension, video games, are getting more complicated every day. It used to be that a game might be a few hundred thousand or maybe a million lines of code. Now, some of the third-party libraries we use in our games are approaching that size, if not already larger! Keeping that in mind, it’s quite impressive that modern games still run for the majority of users. But here’s a simple fact: Sooner or later, someone is going to be unable to play your game. What you do then determines whether or not they will remain your customer.
A few years back I was doing design work on an MMORPG. Really satisfying work – come up with characters & stories, try to construct gameplay around them, and then watch the rest of the team turn it into a living, breathing part of your game world that your players are going to interact with for months afterward.
So, at one point we decided to run a weekend promotion to promote our upcoming title. We temporarily enabled access to the content from our next game, and allowed people who didn’t own the game to create characters and play for the duration of the weekend. Great idea – get people in there at no cost, try and convince them your game is fun and worth playing. Works good for existing customers, too, because now they get excited about what’s coming down the pipe because you’ve let them play it for a couple days.
I was pretty excited about it, since it was the first promotion of its kind that we’d run since I had started working there – which meant I could finally show some of my friends what I was working on, and try and convince them it was worth playing. Most of them hadn’t even looked at the game since they had to pay for it first – and who can blame them, really?
Friday rolls around and I head home late and get some rest. The rest of the team does the same.
On Saturday morning, I send a message to a couple of my friends explaining how to download our client and try out the game. They’re both pretty enthusiastic about it and get started. A few minutes later, one of them says:
‘Why can’t I use any skills?’
What? What do you mean you can’t use any skills? Are there buttons on the bar at the bottom of the screen?
‘Yeah, but they have little lock icons over them. Nothing happens when I press them.’
Well, uh, s–t. That’s not supposed to happen. I emailed one of my coworkers to ask – uh, did anyone try creating a brand new account for the promotion to see if it worked? – and the answer is depressing: Nope. Luckily, it wasn’t a total loss – our existing customers were still able to participate in the event, because they already had working accounts. But for new, potential customers – the game didn’t work. They downloaded and installed our game, and it was mostly useless to them.
Read the rest of this entry »
Camera Constraints
Jul 15
As the level I’m currently building has gotten larger, it’s become obvious that I need a way to control the behavior of the camera – simply centering it on the player isn’t sufficient. While I already had some usable support for panning the camera to show points of interest, and locking it in place while the player performs an action like climbing onto a surface, I didn’t have anything in place for more advanced control of the camera, like constraining it to a region of the level.
So, the first approach I took was simple: I placed a rectangle around the entire level to constrain the camera. For smaller, rectangular levels, this worked good enough – all I had to do was place the rectangle so that you could see all the important parts of the level, and make sure I filled the entire space with tiles so the player didn’t see any ugly empty space while the camera panned around.
But to be honest, that kind of sucks. It means I have to waste time filling in the entire rectangle with tiles, and the camera doesn’t do anything to help give the player a sense of the layout of the environment. Ideally, the camera should be able to automatically position itself so that you can see the important parts of the area around you, and not show you unimportant sections of the level that you might have already passed through, or might be encountering later. Furthermore, it should avoid showing you empty/boring space whenever possible – no point in showing boring repeated tiles to the player when we could be showing interesting parts of the level instead.
The first approach I tried was a relatively simple one: I placed points of interest throughout the level, and then drew lines to connect them using the editor. After that, I assigned a radius to each point, controlling how far the camera would be allowed to ‘drift’ away from the point. The end result was that I had a series of ‘rails’ the camera could follow through the level, of varying thickness depending on the radius of each point.
During this last week, some of the work I did was to optimize my particle system, since it was showing up consistently on my profiles and I was adding more and more particles to my environments.
There are a few basic approaches you can take when trying to optimize code like my particle system.
- The fact that you have a large number of particles all behaving in the same way means that you can easily distribute the work of updating/rendering particles across multiple cores, as long as your data structures and libraries are set up correctly to handle it – so one option is to multithread your particle system.
- The parallel-friendly nature of a particle system also means that it’s possible to offload much of the work involved in rendering particles directly to the GPU, and do it in a shader instead of on the CPU. This is almost always faster.
- In fact, in many cases, you can even update your particles on the GPU, by storing their state in a texture or vertex buffer and having a shader run over all the particles and write their new state to another texture/buffer. You can then take the new state and feed it into another shader as input to render your particles.
- And of course, you can always take the standard approach of brute-force optimization, by making your particle system as efficient as possible with the same basic algorithm.
If you want to use PerfHUD with an XNA game, you’ll need to make some changes to your source code. Since the PerfHUD documentation only explains how to do this when using the native DirectX interfaces from C++, it can be a bit confusing to figure out how to make it work with XNA, especially since the XNA Framework goes to some effort to prevent your games from starting on graphics devices that might not support its feature set (for example, reference rasterizers). Since PerfHUD presents itself as a reference rasterizer, you have to jump through some hoops.
The solution is to write a custom GraphicsDeviceManager that replaces the default XNA one. Luckily, you don’t have to write very much code to make this work – you just have to do things in a particular way so the framework remains happy. The source code can be found here. All you need to do is add this class to your project (or use the library it comes in), and modify your game’s initialization code to look something like this:
#if !XBOX
Graphics = new PerfHUDDeviceManager(this);
#else
Graphics = new GraphicsDeviceManager(this);
#endif
After this, you should be able to start your game under PerfHUD on Windows.
Please note that since the PerfHUD device presents itself as a reference rasterizer, I believe this disables use of some types of hardware acceleration within the framework, so your framerate may suffer as a result when running under PerfHUD, and you might get slightly misleading results when profiling. I consider this to be the unfortunate intersection of two bad design decisions, but not really a bug. If you felt so inclined you could probably work around this issue by hacking up the XNA Framework classes a bit so that they believe the PerfHUD device is a real hardware device.
If you haven’t gotten around to trying out VirtualBox 3 yet, you should. I finally made some time to install the Windows 7 RC inside of it, and I’m stunned. It’s a much better experience than Virtual PC or VMWare and it’s totally free. Whod’ve thought that Sun would release something so useful for free? I didn’t even have to install Java.
After a little fiddling, I even managed to get hardware Direct3D and OpenGL working inside of Windows 7. Here’s a short clip of me fiddling with a hardware-accelerated Windows 7 game, and Paint, on top of my Windows XP desktop (thanks to VirtualBox’s neat ‘Seamless’ mode, which appears to be similar to VMWare’s Fusion feature.)
A quick how-to guide if you’re interested in trying out the setup in the video:
- Install VirtualBox 3
- Create a virtual machine configured for Windows 7. Enable all the shiny goodies, like hardware 3D.
- Install Windows 7, preferably using the downloadable Windows 7 RC ISO. You can grab it from Microsoft’s site and set the ISO as your CD/DVD-ROM drive in the VirtualBox settings.
- Once Windows 7 is installed, install the VirtualBox Guest Additions. This enables pointer integration and makes graphics work better, along with enabling Seamless mode.
- Download the WineD3D installer onto your Windows 7 virtual machine, preferably the desktop. You should be able to just use IE to do it inside win7, if you turned on NAT when installing VirtualBox and configuring your virtual machine.
- Boot Windows 7 into safe mode (you may need to spam the F5/F8 keys when the virtual machine is booting to get the safe mode menu).
- Open Windows Explorer as Administrator (Start menu, accessories, Windows Explorer, right click it and pick Run as Administrator)
- Navigate to C:\Windows\System32
- Right click the downloaded WineD3D installer, and select Run as Administrator.
- Continue with the WineD3D install process. Each time you get an ‘unable to write to file foo.dll’ error message:
- Go to that Explorer window and find the respective file (like d3d8.dll)
- Right click the file and select Properties
- Click over to the Security tab and click the Edit button
- Grant ‘Full Control’ to the Administrators group.
- After following those steps, you can click Retry on the error message and the install will continue to the next file.
- After changing permissions to a few DirectX files, the install will complete. Reboot your virtual machine into normal mode and you should now have hardware 3D in both Direct3D and OpenGL apps inside your Windows 7 virtual machine.
For anyone trying to use NVPerfHud, if you get stuck trying to figure out this error message:
This application is not configured to use PerfHUD.
Consult the User’s Guide for more information.
You may have noticed that there is no reference to this error message, or ‘configuring your application’ in the User’s Guide. The solution is actually documented in the ‘NVPerfHud Quick Tutorial‘, and involves changing the parameters you pass to CreateDevice.
Hopefully posting this here will help other people find this information, since googling the error message turned up zero results for me. Good work, NVidia!
P.S. Preventing ‘unauthorized profiling by third parties’? What the hell is this, the cold war? This is computer graphics, not espionage. Can you imagine if other software worked this way?
To prevent unauthorized pixel modification by third parties, any images you wish to view with Adobe Photoshop must contain a hand-written sample of dialogue from the second act of A Midsummer Night’s Dream, by William Shakespeare. Please note that dialogue from modern adaptations like the 1999 version featuring Kevin Kline is not sufficient. Also please note that handwriting in sufficiently obscure cursive scripts is unlikely to be recognized.



