OPTIMISE VISTA FOR GAMING ISSUE 224 MAR 2009
PERFORMANCE GEAR & GAMING
WORLD’S FASTEST CARD!
NOT BAD FOR A HUMAN
GEFORCE GTX 295
ISSUE 224 NOT BAD FOR A HUMAN
FR EE
59 Ga m 134 Appes s S ee pa ge 118
Benched NVIDIA’s dual-GPU monolithic beauty kicks gaming to the next level
MAKE YOUR OWN USB VIA ETHERNET CONNECTOR
Extend the range of your peripherals
WWW.PCFORMAT.CO.UK
THERE’S MORE… HD Projector group test Code your own games The God sims you need to play
PLUS!
HARDCORE PC ADVICE ¤ HACKING &TWEAKING ¤ OVERCLOCKING ¤ MODDING MADNESS
Fallout G.E.C.K. guide
Issue 224 Mar 2009 £5.99 Outside UK & ROI £6.49
BUG HUNTING DAWN OF WAR II PCF224.cover 1
F.E.A.R. 2 SCARY GIRL’S BACK 22/1/09 10:34:17 am
Game coding
Become a game coder Sick of Fallout 3 crashing your PC? Paul Hudson gives you more skills and knowledge needed to make a triple A masterpiece…
W
elcome back to our games programming tutorial! You’re probably interested in getting started learning how to create your best-seller – particularly if you’re one of the Ensemble Studios, EA Black Box, Free Radical, Factor 5 or Crystal Dynamics employees who have recently been laid off, despite video game sales having shot up last year. Don’t worry, you’re all welcome, even if Tomb Raider: Underworld’s camera was a bit wonky. For the rest of you, this is even more proof that big money doesn’t always make a good game. Or if it does happen to make a good game – Underworld was actually surprisingly excellent – that doesn’t necessarily transform into good profits. This is where indie coders win: when your costs add up to the grand total of £0, even selling £100’s worth of copies is a bonus, because that’s your beer money for the month sorted out.
Catching up from last issue
Four weeks ago I set you some small pieces of homework. Of course, if that was your first experience with 68
PCF224.prog 68
programming then perhaps “small” might not be the word you’d use to describe the tasks, but I hope you at least had a crack at them. If not, relax: I want to explain how to do three of the four just so that everyone can see how it’s done. If you did manage to do some or even all of these yourself and your code varies from mine, that’s okay – use what you wrote, because you’re always going to have a better grasp of something if you wrote it yourself. Let’s start with the Tricky task: make asteroids that move off the screen reappear on the other side as if they had wrapped around. This is very easy, because we already have an UpdateAsteroids() method that loops over every asteroid in our list and moves it according to its velocity. To add this feature, all we need to do is check whether the asteroid has moved off the screen, then if it has re-set it so that it appears on the opposite edge, like this: if (asteroid.Position.X > ScreenWidth + sfcAsteroid.Width) asteroid.Position.X = -sfcAsteroid.Width; if (asteroid.Position.X < -sfcAsteroid.
Above Our game has now had its res bumped to 720p and we have different coloured asteroids
Width) asteroid.Position.X = ScreenWidth + sfcAsteroid.Width; if (asteroid.Position.Y > ScreenHeight + sfcAsteroid.Height) asteroid.Position.Y = -sfcAsteroid.Height; if (asteroid.Position.Y < -sfcAsteroid. Height) asteroid.Position.Y = ScreenHeight + sfcAsteroid.Height;
Once you understand the first line, the other three are a doddle. So, the first line means ‘if the asteroid’s X position is greater than the total screen width added to the width of the asteroid sprite, changes its X position to be equal to minus its width.’ That last part often confuses folks, but think about it – if you want a 64x64 pciture to sit off screen without being miles away, all you have to do is place it at ‘-64’, which means its right-most pixel is still off the left of the screen. Moving on swiftly, the taxing task was to make the player’s laser recharge rather than have infinite shots. Believe it or not, this takes just four measly lines of code, so if you dismissed it as being too hard last time around, give it a try now because just having a crack at
March 2009
27/1/09 11:07:3 am
Game coding something (even if you fail!) can teach you a lot more than just reading someone else’s code. Okay, if you’re still here then either you tried and succeeded, tried and failed, or tried and died in true Dune homage. Either way, here are the magic four lines of code required to solve this particular question: 1
Near the top, up where it says ‘int LaserRadius’ and similar, add this line: int LaserRecharge = 1000;
That defines an imaginary laser energy tank that can hold 1,000 units of lasery goodness. 2
Add this line at the top of the FireLaser() method:
you pause for thought: load more than one asteroid image, then create different types of asteroid rather than just one type. This requires more than just code, because you need to actually fire up <YourFavouritePixelApp> and create some art! Of course, here at PCF Towers we take the lazy man’s way out, which is not entirely uncommon in the world of coding, as anyone familiar with EA’s game franchises might tell you: we just recoloured our existing asteroid and called it ‘asteroid2.png’ and ‘asteroid3. png’. It doesn’t really matter what changes you make, as long as the finished product is roughly the same size as the existing asteroid. Now, to actually make the game hold multiple asteroids, there are several steps that must be followed: 1
LaserRecharge -= 100;
This will ensure that each time the player fires their laser, 100 points of laser energy will get docked from the player’s reserve.
Add all the asteroid pictures to your project. As in last issue, right-click on ‘Content’, choose ‘Add’ > ‘Existing Item’, then add all your pictures. If you don’t do this, XNA won’t make the files available to your code, which will break everything.
The audio alternative
Above Microsoft has some tutorials on XNA’s SoundEffect class on MSDN at tinyurl.com/ soundeffect
“WHEN YOUR COSTS ADD UP TO THE GRAND TOTAL OF £0, EVEN SELLING £100’S WORTH OF COPIES IS A BONUS” 3
Just before the first IsKeyDown() call inside CheckForFiring(), add this:
2
Where you have the declaration ‘Texture2D sfcAsteroid;’ change it to a list, like this:
if (LaserRecharge < 500) return;
That means, ‘if there’s less than 500 points of laser energy left, bail out – don’t fire a laser even if the player wants to.’ 4
And finally, add this line at the top of the UpdateLasers() method: if (LaserRecharge < 1000) LaserRecharge += 2;
And in English that means ‘if the tank isn’t already full, give it two more units of energy back’. I’ve used 1,000, 500 and two so that you can adjust them freely without having to change the logic – if you want the tank to be able to fire in longer bursts, change 500 to a lower number; if you want it to hold more shots in total, change 1,000 upwards; if you want it to recharge faster, change two to five. The Nightmare level task was nothing like so devious as its Lemmings namesakes, but it may still have given
List<Texture2D> sfcAsteroid = new List<Texture2D>();
As soon as you make that change, Visual C# Express (VC#) will almost certainly fill its little errors list pane with a big pile of whinging that makes very little sense. That’s because previously sfcAsteroid was a texture that could be loaded and drawn to the screen, whereas now its a whole array of textures and thus needs slightly more careful handling. 3
Down in LoadContent(), you’ll see the Content.Load() line for sfcAsteroid now has a red, squiggly line underneath – that’s not because you’ve spelt it wrongly, but it’s because we’re trying to load a texture into the space allocated for an array, which isn’t quite correct. Instead, we need to load a texture then add it to an array, like this:
I mention (on page 71) that there are two basic ways to play back audio in XNA, and I’ve shown you the most common method for general Xbox games – but that isn’t the most common XNA method. XNA includes a much simpler method for playing back sounds and music, but it has the downside that all your work has to be done inside code – you can specify a pitch and volume, but it’s all done inside C# rather than just dragging things around in XACT. Imprecise as XACT may seem, there’s a reason it’s the standard tool on the Xbox 360, and that’s because it eliminates a lot of work that you would otherwise need to do yourself. It also allows audio engineers to fiddle around with their creations without having to talk to programmers, allowing everything to come together at once. The big advantage to using the simple XNA audio methods is that you can play back your music as MP3s, but there can be a slight delay in loading them. To work with songs, use the Song class; for sound effects, use the SoundEffect class. Yes, that’s a fairly obvious point! Both of them work like Textures in that they are loaded using the Content.Load() method inside LoadContent() One other small advantage to using SoundEffect and Song, rather than XACT is that XACT isn’t supported on Zune, so if you intend on using your XNA skills to write a Zune game and thus double the number of games available for Zune, XACT isn’t for you. Microsoft has helpfully supplied some tutorials on XNA’s SoudEffect class on MSDN at tinyurl.com/ soundeffect. ‘MSDN’ stands for Microsoft Developer Network, and is for developers only. Fortunately, if you’ve made it this far I can say with conviction “that’s you, that is”.
sfcAsteroid.Add(Content.
March 2009
PCF224.prog 69
69
27/1/09 11:07:4 am
Game coding Load<Texture2D>(“asteroid”));
For me, as I also want to load asteroid2.png and asteroid3.png, I need these lines too: sfcAsteroid.Add(Content. Load<Texture2D>(“asteroid2”)); sfcAsteroid.Add(Content. Load<Texture2D>(“asteroid3”)); 4
A few lines lower, change ‘sfcAsteroid.Width’ to be ‘sfcAsteroid[0].Width’, which will use the width of first texture in the array for the general asteroid width. You’ll need to make two similar changes in CreateAsteroid() (one for Width and one for Height) and eight more in UpdateAsteroids(). You can do a search and replace if you’re feeling particularly confident.
5
Now comes the big change: in the Draw() method we used to just draw sfcAsteroid, but now we need to know exactly which asteroid to draw. Change this: spriteBatch.Draw(sfcAsteroid, asteroid. Position, blah, blah...
To this: spriteBatch.Draw(sfcAsteroid[asteroid. Type], asteroid.Position, blah blah...
That will use each asteroid’s Type value for determining which texture to use. This is where you say, ‘wait a minute, asteroids don’t have a Type value’, or at least that’s what you ought to say if you have been following closely. So here’s what do: 6
Open the file ‘BangAsteroid.cs’ for editing, then add this line just beneath the existing Vector2 declarations: public int Type;
7
Variables of type ‘int’ automatically hold [0], which means the first asteroid texture will always be used. That can be changed in the CreateAsteroid() method, so go back to Game1.cs and add this line just beneath the ‘BangAsteroid’ line in CreateAsteroid(): asteroid.Type = RandNum. Next(sfcAsteroid.Count);
RandNum.Next(), as you might recall, returns a value between 0 and the number specified as its parameter. It’s inclusive on the bottom and exclusive at 70
PCF224.prog 70
the top, meaning that if sfcAsteroid. Count is ‘3’ (meaning that there are three textures in the array) then RandNum.Next(sfcAsteroid.Count) will return either 0, 1 or 2, which is exactly what we want to have it choose a random asteroid type each time. So, that wraps up the three of the four changes I asked you to look at, with the fourth change (having asteroids collide with the player) being so easy that I hope you’ve managed to do it yourself – I’ve not included it in my own code because it makes testing quite hard! Hopefully you can see how easy the changes are, perhaps with the exception of the Nightmare challenge. But to be fair, if you managed to follow my solution to Nightmare (or, better, do it yourself!) you’ll have learnt a great technique that will come in handy in every single game that you make, and that’s guaranteed.
SynchronizeWithVerticalRetrace = true; graphics.ApplyChanges();
Before you think to yourself, ‘Aha! I can monkey with those numbers a bit and have 1,080p by using 1,920 and 1,080,’ please stop. I have a 52-inch HDTV and 720p looks almost flawless. If you think 1,080p makes a big difference, then by all means go ahead and try to use it, but be aware that the Xbox downscaler has big issues scaling from 1,080p (the top end) down to 480i (the bottom end) and your game is very likely not to work. If you were wondering, “back buffer” refers to the hidden space in memory that the Xbox draws to before it presents its results to the player. This double buffering is used so that players never see a half-finished render.
The way of the WAV
Some quick tweaks
Now that we’ve added the features from last issue, I want to show you how to make two small but very important changes: adjusting the screen size and enabling vertical syncing. The first is important because 800x600 is so very 1998, particularly when most Xbox games try to target 720p (otherwise known as 1,280x720). The second is important because if you’re part-way through drawing your frame when your TV screen updates its picture, you’ll get tearing – half of one frame drawn next to half of another frame. If you enable V-sync (it’s on by default), you’ll get a nice, smooth 60 frames per second, give or take a few depending on the TV set. If you’re doing these two already, you might as well take the opportunity to
Below Get a full list of what an object can do by typing its name followed by a full stop, then using [Pgup] and [Pgdn]
Now that your game has new fixes, a completely new screen size and even multisampling, it’s time to add something completely new: sound and music. This isn’t an aspect of your game that you should skip over lightly, largely because it’s a lot of fun to create your own sound effects! In XNA there are two ways to handle this, but we’ll be using the method that most Xbox games use known as XACT: the Cross-platform Audio Creation Tool. This has lots of clever features that you’ll learn about shortly, but there is one major downside: it only works with WAV files. Well, at least that’s the case in regard to the input it uses – when it outputs, Xact can output in XMA format, which is the Xbox-specific super-hi-def audio format.
“SOUND AND MUSIC SHOULDN’T BE SKIPPED OVER LIGHTLY, LARGELY BECAUSE IT’S A LOT OF FUN!” enable multisampling, which is a super-fast technique to anti-alias your game and thus make it look nicer. This is basically free on the 360, and it only takes one line of code, so why not? Put all this at the top of your Initialize() method: graphics.PreferredBackBufferWidth = 1280; graphics.PreferredBackBufferHeight = 720; graphics.PreferMultiSampling = true; graphics.
March 2009
27/1/09 11:07:5 am
Game coding Left Once you get the boilerplate code in, XACT allows you to customise your sound effects
Below Make sure you add your assets to your solution on the right-hand side
Below Recording and editing sounds is best done with a free copy of Audacity
Above Hint #1: this is Windows showing you one of the most important textures in games. Hint #2: yes, it’s completely white Left We’ve taken the lazy route of adding new asteroid types just by recolouring the existing picture
From your Start menu, choose Microsoft XNA Game Studio 3.0 > Tools > Microsoft Cross-Platform Audio Creation Tool (XACT) to launch XACT. Now open up Explorer and point it at the Content directory of your game. Now you need to create two folders in precisely the correct way – XACT seems to have been designed by the same usability team that thought removing the Up button from Explorer in Vista was a smart move, so do the following instructions exactly as I say! 1
Using VC#, right-click on ‘Content’ and choose ‘Add’ > ‘New Folder’, and call it ‘Audio’.
2
Inside Explorer, go into the ‘Content’ > ‘Audio’ directory, then create a new subdirectory called ‘Waves’. So your directory structure should be ‘Content’ > ‘Audio’ > ‘Waves’. You should put all your source WAV files directly into the Waves folder; we’ve included ‘bang.wav’ on your disc (a short recording of me saying “bang!” for our lasers)
Once that’s done, switch to XACT and choose ‘File’ > ‘New Project’ then browse to the ‘Content’ > ‘Audio’ directory and save the XACT project as ‘GameAudio’. One XACT project contains one or more wave banks (actual sound files) and one or more sound banks (sets of sound files with names and configuration settings). We only need one of each, so right-click on both ‘Wave Banks’ and choose ‘New Wave Bank’, then right-click on ‘Sound Banks’ and choose ‘New Sound Bank’. You might want to follow that up with ‘Window’ > ‘Tile Horizontally’ just to make things easier to follow. To add a sound to your game, first drag it from Explorer into the ‘Wave Bank’ window. Then once it appears in the Wave Bank window in an italic, red font, drag it from there (not from Explorer) into the ‘Cue Name’ half of the Sound Bank window (not the ‘Sound Name’ half of the Sound Bank window). That’s it: press [Ctrl]+[S] to save your, er, ‘work’, then go back to VC#. Under your Solution Explorer pane you should see the ‘Content’ > ‘Audio’ folder you created a few minutes ago. Right-click on that and choose ‘Add’ > ‘Existing Item’, then choose the ‘GameAudio.xap’ file from inside your ‘Content’ > ‘Audio’ folder. All that’s done so far is create an audio project and add it to the code – it’s not accessible yet, because we don’t have any programming variables pointing to any real sound. To do that, we first need to grab hold of the wave March 2009
PCF224.prog 71
71
27/1/09 11:07:6 am
Game coding bank and the sound bank created inside XACT, so add these lines into ‘Game1.cs’ under the enigmatic ‘SpriteBatch spriteBatch’ line near the very top: AudioEngine Audio; WaveBank Waves; SoundBank Sounds;
These variables aren’t used very often, so don’t worry about giving them clever names. All these three need to be initialised inside the Initialize() method surprisingly enough, so all you need to do is put these three in there just after the ApplyChanges() call: Audio = new AudioEngine(“Content\\ Audio\\GameAudio.xgs”); Waves = new WaveBank(Audio, “Content\\Audio\\Wave Bank.xwb”); Sounds = new SoundBank(Audio, “Content\\Audio\\Sound Bank.xsb”);
You might wonder where the .XGS, .XWB and .XSB files came from given that you’ve only added a .XAP file to your project, but that’s part of the XNA magic: it flattens the single project into those three files containing all the audio your project needs. Before you can go ahead and actually play some sounds, you’ll need to make sure you include this line below just before base.Update() in your game’s Update() method. What this does is ensures the audio subsystem has time to update itself as your game progresses, which might seem worthless now but thinking in the longer
term it means you get support for things such as voice chat for free: Audio.Update();
The sound of music
All that noodling around is something that you will only need to do once, during the whole of a project. And now you’ve done it for Bang, you can get onto the important stuff: playing sounds in your game. There are two ways to do this, but we’re just going to use one of them for the meantime. This one’s called the PlayCue() method. Go to the bottom of your FireLaser() method and add this line: Sounds.PlayCue(“bang”);
And that’s it. You see, regardless of how painful all that setup tosh is to do, it’s done now, so all you need to do to play any sound through your game is to call PlayCue() with the name of the sound you created. You can test it out now if you want to, but as soon as you’re done you get to discover why we used XACT in the first place!
Below A Tricky clue: If we’ve checked an asteroid at point 1 do we need to check it again at point 2?
All set? Okay, lets head back into your XACT window and start adding sounds into your game. Select your ‘bang’ sound from the Sound Name pane on the Sound Bank window. In the bottom left corner of XACT you’ll see some options – select the ‘Enable boxes’ under ‘Vol Variation’ and ‘Pitch Variation’ then press [Ctrl]+[S] to save, switch back to VC# then press [F5] to play your game. Can you spot the difference when you fire a laser now? That’s right: XNA automatically plays the laser sound at a slightly different pitch each time, which helps stop your sounds from being repetitive without you having to record dozens of variations. XACT is capable of a great deal more, including jazzy things like 3D spatial effects (hurrah for doppler!), audio compression and DSP effects, and it can even group multiple sounds together under one cue, allowing you to specify by percentage how often each sound is likely to be played when a cue is played! The best bit is that it’s all pointy clicky – all you need to do is say PlayCue() and let XACT handle all the audio settings. ¤ Paul Hudson
1
Your homework is here Hopefully by now you should understand the importance of actually trying your hand at programming tasks, even if they seem very hard. My rule here is very simple: I don’t care how bad, confused or slow your solution is, because if you’ve managed to solve a problem in code then you’ve almost certainly learned a lot along the way. Sure, once you see my solution you may want to replace yours with it, and you might even take some hallucinogenic drugs to try to forget the evil, twisted mess that was your own code, but that’s how we all start, honest. So, without further ado here are your challenges until the next issue. I suggest you work your way through them in order rather than just diving in with the hard ones! Fun: Now that you’re using a sensible screen resolution, how can you make your game play as full screen? Remember that typing a variable name, such as ‘graphics’, followed by a full stop, brings up a list of things you can do with it – you can learn a lot
72
PCF224.prog 72
2
just by browsing around the list of options and seeing what’s available! Tricky: I’ve explained in this article what to do when asteroids move off the screen so that they reappear on the other side. But my code could be optimised a little to make it faster. How? If you’re find Tricky a little bit too tricky? Think about it: if we’ve already checked an asteroid at point 1 and found it to be off the screen, do we really need to check it again at point 2? Taxing: We’ve used PlayCue() here to play the bang sound, but we’re using it a lot so why don’t you try storing it in a variable then
playing it back at will? Use VC#’s code completion for the Sounds object to figure out what kind of variable you need. Nightmare: Add support for 3D sound so that if the UFO is towards the left of the screen the sound comes from the left speaker. You should look into the AudioListener and AudioEmitter classes for help with this, then use PlayCue() with those as extra parameters. The Nightmare task is particularly hard this time, particularly if you’ve already completed the Tricky task. Hint: Apply3D() is your friend!
March 2009
27/1/09 11:07:7 am