CREDIT CRUNCH UPGRADES ISSUE 225 APR 2009
PERFORMANCE GEAR & GAMING
ALL HAIL THE NEW KING
WAIT ‘TIL THEY GET A LOAD OF ME
PHENOM II X4 94O CPU Supertest Forget Core i7 AMD’s budget CPU is the king
ISSUE 225 WAIT ‘TIL THEY GET A LOAD OF ME
PLUS!
HARDCORE PC ADVICE ¤ HACKING & TWEAKING ¤ OVERCLOCKING ¤ MODDING MADNESS
TAKE STALKING TO THE NEXT LEVEL: BUILD A USB TELESCOPE
Webcams have never been so much fun
WWW.PCFORMAT.CO.UK
THERE’S MORE… GeForce 3D Vision reviewed Can PC gaming survive? Top tips: Windows 7 Beta
FR EE
59 Ga m 134 Appes s S ee pa ge 118
GTA IV Tweak Guide
Issue 225 Apr 2009 £5.99 Outside UK & ROI £6.49
CRYOSTASIS TERRIFYINGLY COLD PCF225.cover 1
EMPIRE: TOTAL WAR TOTAL DOMINATION 20/2/09 12:21:52 pm
Game coding
Become a game coder Tired of waiting for Duke Nukem Forever? Paul Hudson thinks now is the time to kick ass and create your own game…
T
his is the third instalment of our games programming series, so by now you’re either well on your way to creating a masterpiece, which is soon to be unleashed on an unsuspecting world, or you’re having serious problems and have come to flame me to a crisp with your spitting rage. If it’s the former, please don’t forget to send an envelope full of crisp £50 notes to PCF Towers with my name on it. If it’s the latter case, please don’t bother – my wings are like a shield of steel! And not entirely coincidentally, did I mention that my name is actually Jeremy Ford? But as the seagull of proof-reading swoops low over the tourist of word counts, I’d better stop wasting space and get to the point: you’re here to code, and I’m here to look over your shoulder and say, “you’re doing it wrong.” But perhaps you’re different. Perhaps you’re not doing it wrong. And perhaps you are indeed on your way to producing something truly epic. Well, let’s take a look at your homework and see how well you’re actually doing…
70
PCF225.prog 70
Main screen turn on!
The first piece of homework was: “How can you make your game play as full screen?” Given that I provided a hint bigger than the US national debt, this one shouldn’t have taken you too long: in the Initialize() method, you just need to add this line somewhere before the call to ApplyChanges(): graphics.IsFullScreen = true;:
When you run your game now, it will run at the same size as before, but the screen will stretch that so it fits the full screen size. Note: full screen games are great for immersion, but dreadful for debugging – if your game has problems, Visual C# (VC#) will have to pull control from it to flag up the problem, causing your screen to flicker between resolutions each time VC# wants to take over. So, while your game is under development, we’d recommend commenting out the IsFullScreen line ‘commenting out’ is codie parlance meaning ‘mark the line as a comment for human eyes only so that the
Above Now the game has a visible score readout, but surely every game needs lots of blinking lights?
computer ignores it’. In C#, that’s done by putting two forward slashes before a line, like this: //graphics.IsFullScreen = true;
When you type those two ‘/’ symbols, VC# will show the line in green to show that it is being ignored until those slashes are removed.
The root of all evil
A famous computer scientist once said that premature optimisation is the root of all evil, meaning that it’s important to get the function of your code correct before you try to make it work as fast as possible. Mac OS X, for example, had a very simplistic system for letting programs talk to the operating system kernel up until 10.4 was released – they got it correct first, then added the speed later. You see, the process of optimisation is to write smarter code that takes less time (or fewer system resources) to execute, but it usually comes at the cost of the finished code being harder to read, so if your game is running just fine without using
April 2009
24/2/09 11:47:39 am
Game coding optimisation then you’d be forgiven for ignoring it entirely. The Tricky homework was to look at my code for handling asteroids moving off the screen, then attempt to optimise it. This optimisation is clearly useless given how light our game is so far, but it’s still a worthy exercise in logic for beginners. Here’s the code: if (asteroid.Position.X > ScreenWidth + sfcAsteroid[0].Width) asteroid.Position.X = -sfcAsteroid[0].Width; if (asteroid.Position.X < -sfcAsteroid[0]. Width) asteroid.Position.X = ScreenWidth + sfcAsteroid[0].Width;
The first check means ‘if the asteroid X position is off the right-hand of the screen,’ and the second means ‘if the asteroid X position is off the left-hand of the screen.’ Now, think about it: if the first check proves to be true (the asteroid is off the right), we loop it around so that it appears on the left. Why, then, do we bother to check whether the asteroid is off the left of the screen when we know it’s definitely off the right? The answer is that there’s simply no point – the second conditional statement is only worth evaluating if the first is not found to be true. So, we can save a few nanoseconds of CPU time by using the ‘else’ statement, which will execute a block of code only if a test proves to be false. So
The two lines above can be rewritten to look like this:
Lerp – more than just a silly word
if (asteroid.Position.X > ScreenWidth + sfcAsteroid[0].Width) { asteroid. Position.X = -sfcAsteroid[0].Width;} else if (asteroid.Position.X < -sfcAsteroid[0].Width) { asteroid. Position.X = ScreenWidth + sfcAsteroid[0].Width; }
You can have as many of those ‘else if’s as you need.
Play on cue
Now, on to the Taxing homework, which went something like this: ‘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, which will then play it back at will?’ This is a lovely 30-second fix that makes much smarter use of system resources and lets you manage your audio more effectively. First up, go to the top of Game1.cs then scroll down to just after the line ‘int LaserFireSpeed’. Now add this line:
Above If we were to draw a smooth step interpolation, this is what it’d look like
Cue sndLaser;
A ‘Cue’ is a piece of memory allocated to store a sound that we’ll play on, yes you’ve guessed it, a cue. Clever naming, that. Previously we were using Sounds. PlayCue() to play cues on demand, but this time we’re getting the cue and
“GET THE FUNCTION OF YOUR CODE CORRECT BEFORE YOU TRY TO MAKE IT WORK AS FAST AS POSSIBLE”
Left For a spine-tingling example of how good 3D audio can be just by positioning the emitter, visit gprime. net/flash.php/ soundimmersion
Linear interpolation is a wonderful thing. To interpolate something means to calculate a value between two other values, eg. if I asked you to calculate the value half way between 1 and 5 the answer is 3. A linear interpolation is one in which the interpolation is done with no weighting, meaning that interpolating three points between 1 and 5 would give you 2, 3 and 4 – each position in the range is an equal distance apart. Linear interpolation (or lerping, as it’s usually called) allows you to make smooth movement, gradiated colours, customised AI difficulty and other in-betweens – you just define two ends of the scale, then tell XNA the point you’d like to read back. In our example, we use a colour lerp between red and green, where position ‘0’ is red and position ‘1’ is green. In the code, we use ‘LaserRecharge / 1000.0f’ to calculate the position, which will always yield a value between 0 and 1. One of the few things neater than lerping is called a smooth step, which is like a lerp except it applies a curve function to your position value. If you’ve ever created Flash animations before, a smooth step interpolation is basically what ease in and ease out when tweening – the interpolation starts off slow, picks up speed, then slows down again. Smooth step interpolation is great, because your eyes are not only sensitive to the speed movement, but also sensitive to the speed of change of movement. Put simply, movement looks a lot cooler when it accelerates and decelerates!
April 2009
PCF225.prog 71
71
24/2/09 11:47:40 am
Game coding storing it in sndLaser so that it can be manipulated later. So, go down to FireLaser() and change the Sounds. PlayCue() line to this: sndLaser = Sounds.GetCue(“bang”); sndLaser.Play();
Magic.
Into the nth dimension
3D sound is something that Creative Labs has been telling us about for years, usually with adverts involving bullets flying out of the screen to prove that gaming hyperbole is as alive as ever. Depending on what kind of programmer you are, 3D audio programming can be fairly simple, if you’re into games programming. You just need to tell XNA where the listener’s virtual head is (the ‘listener’) and where a sound is coming from (the ‘emitter’), and Carmack’s your uncle. Of course, if you’re a sound engineer, it’s somewhat trickier and usually involves a degree in Highly Cunning But Terrifically Dull Mathematics, so just be glad you’ve got the easy end of the deal. The Nightmare homework last month was this: ‘Add support for 3D sound so that if the UFO is towards the left of the screen the sound comes from the left speaker.’ How you solved this really depended on whether you had already managed to work your way through the Taxing homework – I’m presuming you did that already like a good student. If you didn’t, neener. As I mentioned last issue, this Nightmare homework is particularly hard because of the way XNA handles cues. You see, a cue is actually a single
Above Sprite Fonts are yet another component of the content manager, which makes them very easy to specify
Above In The Pit is an Xbox 360 game, where you must use 3D audio to catch and eat terrified prey. Don’t bother buying the full game – the demo shows off the technique well enough
instance of a sound rather than the whole data for the whole sound effect. That is, a cue is a sound you can play only once, which is why we allocate sndLaser using GetCue() before calling Play() in the previous code block. Once you get into 3D programming, this works in our favour because it means we can attach each sound to a laser object, then move it accordingly. So, go back to the top of Game1.cs and remove the ‘Cue sndLaser;’ line. Now go into BangLaser.cs and change the BangLaser class to this: class BangLaser { public Vector2 Position; public Vector2 Velocity; public Cue sndLaser; }
That means each laser now has its own laser sound ready for use. You’ll need to add ‘using Microsoft.Xna. Framework.Audio;’ to the top of that file so that it knows what a Cue is. Back in Game1.cs, VC# will flag up multiple errors because you’ve taken away sndLaser, but that’s easily fixed by adding the following: laser.sndLaser = Sounds.GetCue(“bang”); laser.sndLaser.Play();
Above Before you even think about optimising your code to make it run faster, run Task Manager and look at how much CPU time it’s using – optimise only when it’s needed, otherwise you could be wasting your time.
72
PCF225.prog 72
Now onto the 3D business: to create a 3D sound we need to tell XNA where our ears are in the virtual world and also where the sound source is. In a 3D game you will almost certainly want your ears to be attached to the player (unless you’ve just been fragged and your ears are several metres away), but in this kind of game it’s best just to have the listener smack in the middle of the screen regardless of where the player is.
April 2009
24/2/09 11:47:41 am
Game coding Converting all that to XNA code, we end up with this: // create a new listener (our “ears”) AudioListener listener = new AudioListener(); // then give it X and Y co-ordinates at the centre of the screen; the third co-ordinate is for depth, and can be ignored. listener.Position = new Vector3(ScreenWidth / 2, ScreenHeight / 2, 0.0f); // create a new emitter (the laser sound) AudioEmitter emitter = new AudioEmitter(); // then give it the same position as the laser’s graphic emitter.Position = new Vector3(laser. Position.X, laser.Position.Y, 0.0f);
Once you’ve created listener and emitter objects, all you have to do is pass them into a call to Apply3D() then play the sound, like this: laser.sndLaser.Apply3D(listener, emitter); laser.sndLaser.Play();
The added benefit to this method is that you can call Apply3D() as often as you want to, changing the emitter position as you go. For example, if you want the sound effect to follow the laser as it moves around the screen, just put everything except the Play() line (that should only ever be called once per cue) into UpdateLasers() so that the emitter position is always tracking the laser position.
Time for text
Okay, I admit that the Nightmare homework from last issue may well have made your brain hurt, but hopefully it was worth it because you now have real 3D sound – try having some fun making your own sounds whizz around the screen before continuing. All done? That was fast! Let’s crack on with another important new feature. Right-click on ‘Content’, then choose ‘Add’ > ‘New Item’. When the Add New Item window appears, choose ‘Sprite Font’ then give it the name ‘fntMain’. A Sprite Font is a high-speed way of showing text in your game that works by converting a standard font into a series of sprites then re-arranging them on the fly. So, if you want to draw the text ‘Hello’, XNA will automatically find H, e, l, l, o in the Sprite Font and draw them appropriately spaced on the screen. Trust me when I say that this saves an awful lot of work – Sprite Fonts then are one of the cooler features in XNA. When you create a new Sprite Font, XNA uses the Kootenay font by default. If you don’t have that, go to creators. xna.com/downloads/?id=143 and
download a free pack of fonts from Microsoft that come with redistribution rights. The fact that these come with those special rights should tell you that most fonts – including, almost certainly, nearly all the ones on your computer – do not come with redistribution rights, meaning that you can’t use them in your games. The fonts in that pack, however, are free for you to use – drop them all into your Windows fonts directory and you’re all set to go. Don’t worry about the contents of fntMain.spritefont for now – you can fiddle with the values later, because it’s all neatly self-documenting. Instead, go back to Game1.cs and add this line underneath ‘int LaserFireSpeed’:
spriteBatch.DrawString(fntMain, “Power: 0”, new Vector2(30, 30), Color.White);
There are four parameters being passed in this call: the actual font to use for drawing the sprites, the string of text to write, the position, and the colour to use. We’re just asking it to output ‘Score: 0’ in there, but you can send in whatever data you like. For example, if you wanted to have it print the amount of remaining laser energy in a nice bright red colour, you would need to use this: spriteBatch.DrawString(fntMain, “Score: “ + LaserRecharge, new Vector2(30, 30), Color. Red);
Or if you wanted to be really fancy, you could use a linear interpolation (ie., a smooth transition) between one colour and another using the Lerp() method (see page 71) of the Colour class to gradiate between red and green depending on how much laser recharge is left, like this:
SpriteFont fntMain;
That allocates space for the font in memory, but to actually load it you need this line somewhere in LoadContent(): fntMain = Content. Load<SpriteFont>(“fntMain”);
That will fill fntMain with the actual data of the Sprite Font, meaning that the only thing that’s left to do is draw it somewhere on the screen in a colour of your choosing. Put this inside your Draw() method, just before the call to spriteBatch.End():
Color col = Color.Lerp(Color.Red, Color. Green, LaserRecharge / 1000.0f); spriteBatch.DrawString(fntMain, “Power: “ + LaserRecharge, new Vector2(30, 30), col);
…but that would just be showing off, now, wouldn’t it? ¤ Paul Hudson
“3D AUDIO PROGRAMMING CAN BE FAIRLY SIMPLE, ESPECIALLY IF YOU’RE INTO GAMES PROGRAMMING” Your homework is here With the power of 3D sound under your belt and the ability to write text to the screen happily added to your toolbox, you should be ready for some /real/ challenges – try these ones for size: Fun: Try working out how to change your chosen font so that it looks bigger and bolder. Tricky: Try figuring out how to play a different sound when the player tries to fire, but their energy supply is too low. Taxing: Keep score of how many asteroids have been shot. Nightmare: Attach a sound to asteroids so they make a sound when hit by a laser.
Above The Sprite Font definition is written in XML, which is like a very fussy version of HTML. Just follow the comments (in green again) to learn what all the bits do.
April 2009
PCF225.prog 73
73
24/2/09 11:47:42 am