Monday, 20 July 2009

Dysnomia - Development Diary Part Six

This week, I'd like to talk a little about Dysnomia's audio "engine". It's no so much an engine as it is a simple class to control the XACT project behind the game. XACT (Cross-platform Audio Creation Tool) is Microsoft's high-level audio library and engine, releaed as part of DirectX.

The XNA community seems to besplit between those that love XACT and those that prefer to use XNA's built-in audio calls. Because I started using XACT in XNA 1 when it was the only option, I'm reasonably comfortable with both the tool and the API calls in XNA so it was my first choice when thinking about the audio in Dysnomia.

There were two tasks I needed the Dysnomia audio engine to perform, alongside the usual simple sound playing. The first was to have some sounds play positionally, according to the location of certain events on screen. Enemy footsteps, weapon hits, doors opening and so on can all occur at any position on the screen, as opposed to simply coming from the player. For this, I needed to set up the Attenuation RPC preset in XACT and use it in my audio class when playing the appropriate sounds.

First of all, I modified the "Distance" Cue variable, giving it a range of 0 to 1000. The range can represent anything you want it to, but in my case I've set it up to be the distance in pixels between the player and the sound emitter.

Next, we set up an "Attenuation" RPC preset. As you can see in the screenshot, we are setting up the parameter to control the volume of the playing sound, using our Distance variable as input. The curve is set up to roll off the volume of the sound as the Distance variable increases, with a sharp rolloff at the end to completely mute the sound when Distance reaches the point where the player should no longer hear the sound.

Next up, we need to attach the Attenuation RPC to all sounds that need to be played positionally. Simply right-click the Attenuation preset and select "Attach/Detach Sounds". Select all neccessary sounds and click "Attach" to move them to the attcahed column.

That's it for XACT. Now for some code. First of all, we need to set up an AudioEmitter and an AudioListener. Note that all code here comes from my Audio class, which is static simply because I'm using the same XACT project and calls for the entire game.

public static AudioEmitter aE = new AudioEmitter();
public static AudioListener aL = new AudioListener();

private static Vector3 aEVect = Vector3.Zero;
private static Vector3 aLVect = Vector3.Zero;

As you can see, I've set up a Vector3 for the Emitter and the Listener as well. We'll use these in the play method. First though, I'm initialising a Velocity for the Emitter. I do this in the LoadContent method of my Audio class:

public static void LoadContent(ContentManager content)
aE.Velocity = new Vector3(30.0f, 30.0f, 30.0f);

You might have to play around with this to get the velocity just right. 30 seems to work for me. Next up, we define a method to play any sound positionally:

public static void Play3D(string EffectName)
aL.Position = aLVect;
aE.Position = aEVect;
EffectsSoundBank.PlayCue(EffectName, aL, aE);

So, we're passing in only the name of the sound we wish to play. The method sets the position of the AudioListener and the AudioEmitter to the values of aLVect and aEVect, which we set up earlier. Lastly, it calls PlayCue on our SoundBank, using the positional override.

Now for an example of the methods we call from the actual game classes:

public static void DoorOpen(ref Vector2 DoorPos)  
aEVect.X = DoorPos.X;
aEVect.Y = DoorPos.Y;

So, this method is for playing the sound of a door opening. We pass in the location of the door in the game world, and copy the values to the X and Y positions of our Emitter Vector3.

Time out for a quick explanation of what's going on. In my game, I know the position of the player in the world. The AudioListener must be set to the player's position in the world, which I do on each update:

(in Player.Update:)
Audio.Audio.UpdateListenerPosition(ref Position);

(in Audio:)
public static void UpdateListenerPosition(ref Vector2 PlayerPos)   
aLVect.X = PlayerPos.X;
aLVect.Y = PlayerPos.Y;

Every time we play a sound in the world, we know where the sound is coming from. A door, an enemy - they all have a position in the world so we set the AudioEmitter position to the same. When PlayCue is called, XNA takes care of positioning the sound for us based on the Player's position (AudioListener) and the sound position (AudioEmitter). Our Attenuation RPC further affects the resulting sound by rolling off the volume according to the distance between Emitter and Listener. Simple? Don't worry - follow the steps and you'll get it eventually!

In my next diary post, I'll write about the second task I needed the engine to perform - crossfading audio tracks. For now, I leave you with another gameplay video.

Monday, 13 July 2009

Dysnomia - Development Diary Part Five

Another week rolls by!

Loads of exciting progress this week, a great deal of which involved adding new features at long last. I'm now extremely confident that we'll meet the proposed November release date and maybe even with a month to spare to do some really in-depth playtesting and polish. We're also considering a possible Windows release, maybe around the holidays or early next year.

I'm going to break up the text this week with some of the great graphics that Leon's been working on. I'm sure you'll all agree that everything is looking rather awesome. Usual screenshot selection at the end.

This week saw the implementation of Medikits and Ammo packs (Batteries). I also added some on-screen control prompts to give the player a better idea of which buttons perform which actions when near something they can interact with. There's also been some changes to the game mechanics again - although really they're constantly evolving throughout development. These changes will be the meat of this week's entry.

As discussed in earlier diary entries, Dysnomia isn't all about running and gunning. Although a lot of time will be spent dispatching the alien hordes, it will be exploration and survival that will provide the path to completing the game.

I didn't want to go too over the top with the whole survival thing and end up giving the player an inventory to manage. I also didn't want the player to carry as many medikits and ammo packs as they liked. I think it takes away some of the sense of danger when all you have to do is hit a button or hide behind a wall to regenerate health.
It's with that in mind that I have made medikits and ammo in Dysnomia single-use pick up and consume objects. I had a bit of criticism about the way fuel was managed in Gravsheep, whereby the player could accidentally pick up fuel they didn't need. In Dysnomia, I've addressed this issue by requiring the player to press X to pick up and use both health packs and ammo.

On the subject of ammo, it may seem a little harsh not allowing the player to carry more "magazines" and reloading as required. I think my idea behind this is solid, but I'll be listening carefully to thoughts on this during playtest. Essentially, the player's gun in Dysnomia is an energy weapon. It is recharged by using batteries (the ammo packs). The gun has five modes of fire, each represented by a different colour. The colour of the battery will determine which firing mode it recharges.

For the first couple of areas, the only ammo available will be the blue ammo for the first firing mode. A full charge of blue energy will more than likely last a very long time. Subsequent colour batteries will start to appear on further levels, giving the player more firing modes that will get progressively more powerful. The player will be able to switch between modes at will. I plan to implement a reasonably powerful melee attack for when the player completely runs out of charge on all modes. They'll at least be able to defend themselves whist they head towards the nearest ammo cache.

Another small but nonetheless significant change I've made is to put a progress bar on a device when it's powering up. I've already written a little bit on devices such as doors, turrets, switches and terminals. Each device requires a number of boards to be installed in it before it powers up. What I've done is to add a little delay between the last board being inserted, and the device being usable. I think that makes sense in a world perspective, but it also adds a little extra challenge. It might make players think twice about taking boards out of a door they use frequently. If a turret is guarding a particularly busy corridor, maybe the player won't want to borrow a board from it if it will take a little while to power up again.

They're all small changes, but ones that I think add to the depth of the puzzle/exploration/non-combat side of the game. I'll be waiting for the results of playtests to see if potential players agree with me though!

Monday, 6 July 2009

Dysnomia - Development Diary Part Four

I've been doing a great deal of work under the hood this week. I was lucky enough to get an entire weekend with my 360 and my development PC in the same place. I spent that time wisely, doing some 360-specific bug hunting and feature additions.

Firstly, I was hunting down an annoying little hitch that was rearing its ugly head on the 360. Although the game was running at a solid 60fps and the Remote Performace Monitor was showing few if any allocations and collections, there was still a definite little hiccup occurring every few seconds.

After reading through the XNA forums a little, I decided to give IsFixedTimestep = false a go. Shawn Hargreaves explains the difference much better than I'll ever be able to, but essentially it was a last-ditch attempt at solving the problem after going back over all the code several times. This was the XNA thread that gave me the idea to try it.

And after updating all the movement code in the game to take ElapsedTime into account, the problem went away. We're talking one hundred percent smooth-as-silk on the 360 now. I celebrated by jumping straight into getting some rudimentary game saving done.

I'm still not quite sure how to handle saves in Dysnomia - I plan to make the game saveable anywhere, as I believe all casual games should. Right now, the game is saved by pulling up the pause menu and hitting "save". Can't get any simple than that.

Of course, I need to consider things like autosaving and the like. On the 360, I will be attempting to find a storage device as soon as the player hits start before the menu screen. This will allow for a main menu "continue game" option if the current player has a save file already. I now need to work out the little details such as when to autosave, what I'm going to do in a co-op game (although I haven't started on co-op play yet, everything is being coded to allow for two local players) and whether to offer multiple save slots. It's all easily do-able, but one needs to consider the end-user and the most intuitive way to handle their save games.

Talking of co-op play, I still haven't decided how to handle death with two players. In a single player game, if the player dies, it's game over and he's given the option to load the last saved game or exit to the main menu. With two players, that's not a great option - a surviving player should probably be able to play on without the dead player. I'm currently considering a one-minute respawn where if the surviving player can hold his own for a minute, the dead player will respawn right next to him with perhaps a half-bar of health.

Implementing co-op is a way off yet, but I'm keeping it in mind as I go. It's on the "nice-to-have" list along with boss fights. I get to that list when all the other game mechanics are in.

I'm often asked when the game will be finished. In my original design document, I stated October as a release-to-playtest month, and projected November for release. It's safe to say we're on track for that if things keep going at their current pace. My hope to have a playable two-level demo with all neccessary game mechanics by the end of July is, however, looking less likely. I can't release a demo without a tutorial and the tutorial is still way down my to-do list, underneath things like: Health packs, ammo packs, weapon powerups, fuel rods, ship status, terminal screens and so on...