Monday, 16 November 2009

Dysnomia: First Playtest

It's been six months in the making, almost to the day. Tonight, we release the first playtest of Dysnomia to the XNA Creator's Club community. Although only containing the first three levels of the full nine levels the final game will have, the playtest is extremely polished. I've wrapped up most of the engine code, all there is to do now is to get down to designing the final levels. I'm going to go ahead and call it for a February release now. That's not too bad - only three months overdue!

I just spent a week's holiday polishing off some of the important Xbox integration features of the game. Handling things like gamer profile sign ins/outs, save locations and a trial mode are all essential things to have in an Indie Games release - yet so many games fail to implement all of them correctly.

I then moved on to creating a fantastic boss fight that I hope will be the first of four or five battles against epicly-sized denizens. Here's a screenshot:


He's an ugly mofo, that's for sure!

I'm planning on at least another two playtests depending on the responses I get from fellow Creator's Club members. If all goes to plan, the second playtest will include one more level and the third one will have most of the content in place. I'm guessing playtest two will be out over Christmas.

So close to completion, yet still more hard graft to get there.

Wednesday, 16 September 2009

A Quick Reminder

If you're interested in Dysnomia, the best place to keep up with announcements (and to discuss the game) is over on the Team Mango Facebook page. Become a fan!

Friday, 21 August 2009

Dysnomia Update

I can't believe it's been over a month since my last diary post. Time is flying this summer. There has been a lot of work going into a lot of behind-the-scenes stuff on Dysnomia. The sort of stuff that needs to get done but isn't really worth blogging about.

So yes, work is still progressing well. Tutorials are in. The starting area is more or less complete. Work has begun on local co-op. Tiles for the later areas are starting to roll in and they are looking fantastic. The very last area especially is looking very tasty.

I've been trying to work on some of the more boring stuff like Xbox controllers and profiles, loading and splash screens, XNA best practices and other things. I learnt during development of Gravsheep to get that stuff out of the way as soon as possible, as putting it in at the end when all you want to do is release the damn game already proves to be a drag.

I've been trying to figure out whether we're still on track for a November release, and I'm still as yet undecided. It all really depends on how much time I get to work on the game - but I have a feeling that while the game will definitely be out before Christmas, it may not be as early as November. Having said that, if we used November 15th as an arbitrary release date, we're still only just over halfway through development time. And if I'm honest, the game feels about half-done at this point.

So there you have it, folks. Dysnomia is officially halfway through development. Time to update the Wacky Idea-o-Meter!

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;
Play3D("door1");
}

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...


Monday, 29 June 2009

Dysnomia - Development Diary Part Three


A bit of a shorter diary this week as I'm busy working on a number of things in Dysnomia that I'm not quite ready to show yet! Also, this week is more about coding than anything else, so non-techies may want to wait until the next entry when I should be showing off a little more of the game!

Optimisation is a very important part of game development. However, for simpler games you may not ever need to worry about performance - let's face it, if your game is running smoothly on your target platform from start to finish then going back over the code and optimising for the sake of it is ultimately pointless. Put the energy into starting your next game!

Likewise, spending hours and hours pondering over every line of code to ensure it's as efficient as possible will likely result in a convoluted and hard-to-follow program.


If you know your game is likely to use a lot of memory, either graphically or object-wise then you'll need to keep performance and optimisation in mind right from the start. Because I've never had to optimise a game before, I completely ignored all the issues. That is until I started to get frame hitches when running on the 360. Then I started to realise and put right many of the mistakes I had made.

Shawn Hargreaves, one of the XNA framework developers, has written a number of blog entries regarding .NET garbage collection and the 360. These three posts are the ones I found the most useful when starting to track down where I had gone wrong.


I noticed a definite "jerk" in Dysnomia that was occurring once every few seconds or so. After going through the code and commenting out the heavier update and draw methods, I couldn't narrow it down to one particular place in code. That's when I started to pay attention to the posts regarding garbage collection and how to either avoid or manage collections. I ran the CLR profiler against the game and found that (on Windows at least), I was seeing a level 2 (heavy collection) occurring every few seconds which would definitely correspoind to the glitches on the 360.

I started to look at my code and figure out where it all went wrong. To start with, I was using generic List() lists to hold my game classes. Enemies, spawn locations, lights, bullets - pretty much anything that changes in game is allocated on the fly and added/removed from its list. Each time an enemy died, it was removed from the list. When an enemy spawned, a new Enemy() was allocated and added to the list. Same with bullets. Those are quite complicated classes to begin with, and to top it off I was giving each enemy and bullet object its own Texture2D sprite. What the hell was I thinking?!


To fix the issues, I decided to follow Shawn's advice and went with his first "path" for avoiding garbage collection worries: Allocate as little as possible during the game loop.

I went back and re-worked how I managed my in-game objects. Rather than letting the List allocate for me, I created a Manager class for each of the lists. They work in much the same way as the ParticleEngine XNA sample in that the Manager classes pre-allocate all of the objects I will ever use in game, and use a queue to select the next object for use. When the object in question is no longer needed (if an enemy dies or a bullet hits something), it is set as inactive and placed back on the queue to be used again.


All of the pre-allocation is done when the level and content is loaded. After I've performed all my loading a allocating, I do a GC.Collect() before fading the level up and starting the game. This gives me a clean slate for the smaller allocations (Vector2s and value types mostly) that occur in-game.

Net result is that garbage collections no longer occur whilst the game is in progress. Okay, so now I'm starting to get into this optimisation stuff. I looked at the way I was using Texture2D for loading game content. I realised that having a spritesheet for each instance of a game class (enemy, door, light, bullet) was not the way to go. Too much unneccessary memory wasted.


Instead, in each of my game object Managers, I loaded the spritesheets for each type of object. For instance, I have a Spider enemy and a Slug enemy. Each enemy in my List in my enemy manager class can be a Spider or a Slug type. My manager loads one instance of a Texture2D for a Spider enemy, and one for a Slug enemy. In the EnemyManager.Draw() routine, I loop through all the active enemies in the List and choose the correct Texture2D to draw from, rather than having each Enemy class draw itself from its own Texture2D.


So that's just a couple of the ways I'm handling memory usage and garbage collection in Dysnomia. Allocate lots during load, allocate close to nothing during game. There are still some glitches to iron out in the AI routines that can get a little heavy, but I'm getting closer to that constant smooth 60fps gameplay I'm after.

Here's a bonus off-screen recording showing the action on the 360:




Monday, 22 June 2009

Dysnomia - Development Diary Part Two


Developing Dysnomia in my spare time makes the weeks fly by. This week, I'm going to write a little about the level editor behind Dysnomia and how I'm linking together map files, save game files and in-game "devices" which need to keep their status throughout a full playthrough.

The map editor is a functional but clunky Winforms application that doesn't use any XNA, but opts for GDI instead.


It allows the level creator to paint using tiles selected from several tilesheets, one for each map layer. The layers are drawn from the bottom up, starting with the floor tiles, then the walls, then anything overhead. There are a number of in-between layers as well, including separate layers for shadows and decals, which break up the tiled graphics without needing to duplicate tiles. For instance, rather than having "floor tile 1" then "floor tile 1 with top shadow", we simply place down "floor tile 1" in the floor tiles layer, then in the shadows layer we place down "top shadow".

The decals layer allows us to place down little "junk" items like littler, blood spatters, corpses, computer terminals and so on. We have a decal layer for the floor and one for the walls, allowing us to place rubbish on the floor and a computer on a table (tables being wall tiles).


The "specials" layer is used to place items that are replaced in-game with objects. Things like lights, enemy spawns, doors, healthpacks and other pickups all have a tile on the "specials" sheet which is used to represent them in the editor.

The editor also allows us to place down Devices used in the game. Door controls, turrets, terminals and all other interactive objects are placed in this way, and then a few controls allow us to change parameters for each device.


The editor saves each level to a separate text file, which contains grids of three-digit numbers representing a tile number. There is a grid for each layer. After the grids are saved out, additional information about the level is appended - the lighting level, map size and all the information about Devices.

The game will be split up into a number of levels, somewhere around ten in total. This number includes the area outside the mining outpost, on the planet surface. The player needs to scavenge items from the base to repair his ship and thus will need to return to the outside area (and the ship) every now and again.


All of the lower levels of the outpost are reachable via a central liftshaft. At the start of the game, the liftshaft does not work, and requires a number of motherboards to be replaced for each additional floor the player wants to reach. The map layout could be drawn as follows:

Outside -> Entry Halls
|
v
Floor 2
|
v
Floor 3
|
v
...

When the player crosses between levels, there will be a short pause for loading as the next map is read in and prepared for use. However, because the player can explore the entire base at will and return to any previous area at any time, we need to hold a state file on all the objects that the player can interact with. This state file will stay active from the start of a game until the player dies or finishes the game. The same state file will also be used for loading and saving games, so it must hold information on the player's status too - current map, position, health, weapon powerups, ammo etc.


I decided to wrap up all this information in a single class that is instantiated at the beginning of play. The class has its own Load() and Save() methods. The Load() method takes a string as the only argument, the string being the contents of a saved game text file. The Dysnomia editor has a generator to create a save file that represents the intial state of everything in the game. If a player starts a new game, it will be that file that is supplied to the GameData class, essentially creating a "new save" in memory.


Throughout the course of the game, the GameData class will be updated to reflect the current state of the game. For instance, if a player takes a motherboard out of a device, that device will be updated in the GameData class to show that it holds one less motherboard. The GameData class also holds the number of motherboards currently held by the player, which would be incremented at that point.


Some data in the GameData class will only be updated when the game is physically saved. The player's health, level and location would be some examples, as would the location of any enemies currently on screen. I feel that on the whole, it's a solid way of dealing with saving game progress, especially given the open-ended exploration nature of the game.

Tuesday, 16 June 2009

Dysnomia - Development Diary Part One




I now feel confident enough to write a little about Game 2, which now has the working title "Dysnomia". Dysnomia is the name of a real
moon of the planet Eris, and also means "lawlessness" in Ancient Greek. Dysnomia is the setting for the game - in fact, a mining outpost on the moon.

The backstory (though not fully fleshed out) goes something like: the main character, a space marine, was engaged in a firefight aboard a two-man fighter craft launched from a large battleship. Badly damaged in the fray, and with the mothership destroyed, the ship is forced to lock in a course for the nearest friendly base - a mining outpost on Dysnomia.




After drifting for three days and with systems failing, the marine lands the ship manually just outside the base. All attempts to make contact with the outpost have failed. The marine now has two missions:

1. To scavenge supplies to fix the ship. The ship requires:
- A number of replacement motherboards
- Several fuel rods, which are dangerous to handle and replace
- Navigational data which has been corrupted

2. To complete a report on the base and find out what happened to the inhabitants.

The game is a top-down shooter and borrows ideas from classics like Alien Breed and Gauntlet, whilst adding a few of my own ideas and mixing in a bit of light puzzle solving.


During the course of the game, the player will find many electrical devices in the outpost which require a certain number of motherboards to become operational. Some devices will be up and running, others will be missing some or all of their motherboards. It's up to the player to decide which devices to leave powered up, and which to borrow from for other parts of the outpost. Such devices include:

- Doors
- Computer terminals
- Automated Turrets
- Medibeds
- Electrical fences
- The main liftshaft which links the ten or so floors of the outpost

And of course, once the player has finished his report, he'll need to scavenge a large number of the same motherboards to fix his ship and leave the base.


The game's story will be revealed through visual clues and computer journal entries. As the character discovers more, the Report progress will go from 0-100%. Certain events will be worth a large amount of report progress, so the player won't need to access every single journal entry or discover every part of the base.

And now onto the technical side of things.

Dysnomia is my second XNA project. I began it directly after releasing Gravsheep on Xbox Live Community Games. Gravsheep didn't sell very many copies, but then again it was a fine example of a "polished turd" in that the gameplay and graphics were lacking in several departments, but it was very well finished with a good UI, smooth transitions and intuitive controls. I spent a long time making sure it played nicely with the features of the dashboard and the 360 which are often overlooked by other XNA developers. I of course intend to do the same with Dysnomia, only this time I hope the gameplay will be present too.


Dysnomia represents a huge leap in my abilities as a hobbyist game developer. I've been developing games for years, but this is the first time I've really put maximum effort into every aspect of the game. In other words, I'm not taking the easy way out. Every obstacle I've come up against, I have defeated with research and code, rather than simplifying my vision, or coming up with a half-assed solution.

It's also the first time I've worked with an artist. Leon Arellano replied to an ad I posted on the XNA forums, and his art style immediately fit the bill. He also has the exact vision I have for the game, and readily understands tiling and formatting graphics to work in a game. A perfect partnership so far.


I began the Dysnomia project with the GameStateManagement sample. In Gravsheep, all the gamestate and transitions were done by hand I was very proud of it. My implementation was nowhere near as elegant as the one offered by the GameStateManagement sample, however. All developers that want to eventually release on Community Games should be using this sample as a base, or developing their own solution to handle menus, transitions, multiple controllers, live profiles and so on.

I then began looking at Nick Gravelyn's TileEngine tutorial. A brilliant set of videos indeed. After part 3 I was happy that my way of doing tiling and scrolling was the right way (though I had never thought of layering before), and Nick's sample very closely matched the way I would implement such an engine. I took his code as of Part 3 and began implementing and expanding it to suit my needs. I already had a tile editor from Gravsheep, so it was a case of simply extending that to handle multiple layers. Layering in a tile engine was all new to me - I have no idea why I hadn't ever thought of it before.


Next, things all got a bit mathematical. For collision detection and for doing some fake dynamic shadows on the "lights" I had placed down, I needed some trigonometry. I'm sure most of this stuff would be simple for A-level or further Math students, but for me it was quite a stretch and took me a while to work out!

Firstly, a function to work out the point on a circle. I've actually done this previously, so I wrote this one myself.
private static Vector2 PointOnCircle(ref Vector2 C, int R, int A)
{
A = A - 90;
float radA = MathHelper.ToRadians(A);
int endX = (int)(C.X + (R*(Math.Cos(radA))));
int endY = (int)(C.Y + (R*(Math.Sin(radA))));
return new Vector2(endX,endY);
}
I use this in collision detection to cast a point out in front of the player according to the player's angle of rotation. The tilemap is then checked for walls at that pixel location and if a wall tile is found, a per-pixel test finds out if a collision has actually occured. This per-pixel test uses the colour information of the sprite of the tile in question to allow for oddly-shaped walls.

private static bool PointInTriangle(Vector2 P, Vector2 A, Vector2 B, Vector2 C)
{
Vector2 v0 = C - A;
Vector2 v1 = B - A;
Vector2 v2 = P - A;

float dot00 = Vector2.Dot(v0,v0);
float dot01 = Vector2.Dot(v0, v1);
float dot02 = Vector2.Dot(v0, v2);
float dot11 = Vector2.Dot(v1, v1);
float dot12 = Vector2.Dot(v1, v2);

float invDenom = 1 / ((dot00 * dot11) - (dot01 * dot01));
float u = ((dot11 * dot02) - (dot01 * dot12)) * invDenom;
float v = ((dot00 * dot12) - (dot01 * dot02)) * invDenom;

return (u > 0) && (v > 0) && (u + v < 1);
}
I'm not even going to pretend to know what's going on here. I took these calculations from a maths site and converted them to C#. All I know is that it's using Normals to calculate whether a given point (P) is inside a triangle made up of three points (A,B,C clockwise). I use this to calculate the position of the player's shadow when he walks past a directional wall light. All that math for an effect that half the players won't even notice. That's what I mean by not taking shortcuts this time. If I understood how all this math worked, I'd be developing 3D games for a living.

private static float fAngleBetween(ref Vector2 vPointA, ref Vector2 vPointB)
{
float fAngle = (float)Math.Atan2((double)(vPointA.Y - vPointB.Y), (double)(vPointA.X - vPointB.X)) - MathHelper.ToRadians(90);
return fAngle;
}
This function calculates the angle between two vectors. I use this during enemy AI routines to cast out a ray between the enemy and the player to see if there's anything blocking the enemy's path. First of all I use this function to find the angle between the enemy and player, then use a series of collision checks using cocentric PointOnCircles, increasing the radius each time.


That brings me onto the last bit I want to talk about for now - the AI. A simple enemy chase/wander routine just wouldn't cut it for Dysnomia as the level layouts pretty much require aliens to be able to find their way to the player. After sitting and thinking about how to implement pathfinding behaviour, I decided to find out how the professionals do it. A* is one method, and is touted as being the most efficent method of pathfinding in games. I had heard of A* before, but didn't know it was an AI algorithm.

After reading and actually understanding the concept, I wanted to see if anyone had written a C# implementation of A* pathfinding. Sure enough, Roy Triesscheijn had everything I needed on his blog.


However, after implementing Roy's source and modifying it to work in Dysnomia, I found that trying to calculate paths for multiple enemies each update (or even every 50-100 updates) was too slow, especially as the paths became more complicated. I got around this by implementing a number of behaviours.

- Enemies have three AI states - Idle, Following and Pathfinding
- Enemies start out with an Idle state
- Every update:
- If the AI state is Idle, we check to see if there's a direct line of sight between the enemy and the player. If so, we set AI State to Following. If not, we calculate a path and set AI State to Pathfinding.
- If AI State is Following, move enemy toward player.
- If AI State is Pathfinding, move enemy to next node in the A* discovered path.
- AI Counter is incremented
- If AI Counter reaches 50, reset AI State to Idle

Using this method, the enemies will follow a set AI pattern for 50 updates before calculating a new path. We don't calculate a path if the enemy can head directly towards the player. There are also path length limits in place, so if the A* algorithm doesn't find a path fast enough the enemy AI state is set to follow the player. Whilst this means that enemies that are too far away will not be able to get to the player, it also allows the AI to run as smoothly as possible.


There's a lot more to talk about, but that'll do for a first update. Enjoy this video! :)