Monday, March 22, 2010

Here's that screenshot

A slightly less than pretty picture of more than one person in the game. I just got the first proper ship type added today.


BANZAI!!!1one

Saturday, March 20, 2010

Slightly late. Slightly different.

So I show up to school on Friday, slightly bleary from a late night, ready to get cracking on the testing the game and... pretty much nobody shows up. In fact, those who did show up were far too busy to help out with testing. The screenshots I promised on Thursday were supposed to be from some multiplayer testing but that didn't work out.

Today I added point lights to the game. These are nice for lights which come from a location and affect a specific, limited area. Great for more dramatic explosions, shooting and so on. Here's a pic


Lighting in Space Combat Sim is done using some fairly simple math. One topic covered in high-school geometry was the dot product. The dot product is a scalar product of two vectors. One property of a dot product is that, when it's produced from two unit vectors, its value is equal to the cosine of the angle between the vectors.

Why is that useful? Try shining a flashlight on a marble, or ball bearing, or basketball (anything round will do.) Notice that the brightest parts are those that face the light. As the surface of the ball faces away from the light it darkens. If you remember that old high-school math cosine is one for an angle of zero and zero for an angle of ninety degrees.

Cool. But this still doesn't say how light or dark a surface is... We need one more piece of data. The surface normal. This is a unit vector points straight off of the surface being lit. Let's express the direction of some infinitely far away light source with another vector. Remember what I said in the previous paragraph? The dot product of the two vectors is one if the surface is facing the light perfectly and zero if it isn't facing the light at all. Cool, eh?

This is the shader code for a directional light. It's a nice stand-in for some sunlight. Notice there's a little bit of extra work here so that it can render eight lights at a time.


float4 outputColor = float4(0, 0, 0, 0);
for(int i = 0; i < GlobalLightCount; i++)
{
float LightFactor = dot( GlobalLights[i].Vector, input.WorldNormal );
float4 color = GlobalLights[i].Color * LightFactor;
outputColor += color;
}
outputColor.a = 1;
return saturate( outputColor ) * tex2D( DiffuseSampler, input.TextureCoord );


Now to extend this to point lights.

Point lights are a little different in that they have a definate location and a limited range. Both of these are fairly easy to deal with. Instead of specifying the direction of the light explicitly we calculate it by finding a unit vector that points from the light source to the object being lit. Cake.

The range limitation can also be made quite simple if we don't care about being 100% realistic.
Get distance between the light and the object being lit.
Divide that distance by the radius of the light.
Clamp the value to a range between zero and one
Subtract the value from one

This gives an additional value that we use to adjust the light's intensity based on distance. Here's the resulting HLSL shader code
float3 lightVector = GlobalLights[7].Vector - input.WorldPosition;
float lightDist = length( GlobalLights[7].Vector - input.WorldPosition );
float3 directionToLight = lightVector / lightDist;

// falloff light intensity linearly between alpha*radius and radius
float baseIntensity = 1 - saturate( lightDist / GlobalLights[0].Radius );
float diffuseIntensity = saturate( dot( directionToLight, input.WorldNormal ) );
outputColor += GlobalLights[7].Color * baseIntensity * diffuseIntensity;

outputColor.a = 1;
return saturate( outputColor ) * tex2D( DiffuseSampler, input.TextureCoord );



Thursday, March 18, 2010

First killin's

All this week I've had the chance to test out and refine the network code of Space Combat Sim. I've also managed to get some of the guys from class to play the game and hash out bugs and glitches. Currently I've only been able to run small test sessions with four players but that's been enough to get a flurry of random bugs that needed fixing.

Here's an abridged list of SVN commits I've made since my last blog to give you an idea of the number of fixes and tweaks being done. They're in order from newest (yesterday night) to oldest.

  • Got the game closing down nicely now.
  • Mostly working game exit sequence... still doesn't unload DynamicTextures yet.
  • Precess rotation axes when in strafe mode.
  • Kill beams when its parent dies.
  • Fix wrong thrusters becoming visible.
  • Clean up ALL references to a leaving player (fix crash on join after a player quits)
  • Proper fix for weapon toggle not working on remotes
  • Minor improvement to weapon switching from remote
  • Fix scaling in billboard shader
  • Add state items for targeting, and thrust
  • Recursively update scene nodes as well as entities. (FIXED THE GLITCH)
  • Another attempt to fix off-by-one graphical glitch. Didn't work.
  • Ensure that entities on host will still receive remote input properly.
  • Don't collect input if using a remote controller
  • Implement bandwidth usage tracking and some measures to reduce usage
  • First stab at fixing crash on >2 players at once in game.
  • Fix wrong name in kill news item
  • Fix bug where more than one join attempt would cause a crash
  • Assign gamertag to player slot when initializing on remote.
  • Remember to dequeue pending notifications after sending them.
  • Finish up host side of sending player notifications
  • Implement remote side and part of host side of player notification packet.
  • Don't actively produce score updates unless hosting the game.
  • Fix kill detection bug.
  • First stab at fixing weird off-by-one-frame glitch.
  • Fix stack overflow in resolving entity creations.
  • First stab at queuing creations that arrive out of order.
  • Fix up absurd level of brightness.
  • Fix impossible to see through HUD
  • First shot at multipass lighting. Tested with single light
  • CoalesceDeadEntities called in response to entity destruction packet
  • Add weapon states to data to be sent.
  • Make sure criteria for StateItems being sent are updated.
  • Adjust send criteria to keep bandwidth more sane and keep lag down

After all the pain of February this switch from hacking and designing to steady bug fixing and feature creation is a very welcome change of pace. My most satisfying fix was to finally get rid of a bunch of annoying graphical glitches that occured when players would be killed and/or respawned. Their causes are rather boring so I'm not going to bother with any details.

Screenshots coming tomorrow. I might also talk about how to create a simple radar display.

AAAAAA 'Splosions!

AAAAAAZone is proceeding nicely. The first project milestone is due tomorrow and what's left to do is implementing enemy AI, which is fun, and force feedback on the gamepad.


I'm currently using JigLibX to provide physics and collision detection in the game. This is working quite well right now though the lack of documentation is rather irritating. The biggest issue currently is the amount of jittering that occurs with the tank's suspension. That's going to be a pain to work out but that's for later... What's more important is EXPLOSIONS!


Yes, EXPLOSIONS! the staple of Michael Bay. They're really simple too. What I do is apply a force to any object within a certain distance of the EXPLOSION!'s position. The force gets weaker as the distance from the EXPLOSION! increases. This is great for sending tanks flying around when shooting them.


The other trick is applying blast damage. If an EXPLOSION! force is applied to a tank then the tank will take damage. The more force the more damage. Once enough damage is done all of the tank's parts, wheels, axels, guns and such, are detached from eachother.

Wednesday, March 10, 2010

Space Combat Multiplayer

Debugging network code for XNA is a huge pain in the arse! Typically when I'm bug hunting I'll place a breakpoint where I roughly think a bug is taking effect, look at the state of any relavent data, then step through the code until the bug manifests. This doesn't work in XNA. If you leave one player or the other player in the debugger for more than about ten seconds their network connection will drop. This means that I have to then reset the game and re-join the session. It gets quite frustrating spending an hour on a simple one line logic error with about ten minutes of that hour being spent actually debugging with the rest being hitting a breakpoint, checking variables for a few second and having the connection drop requiring yet another restart.

But it was worth it. After much pain multiplayer is now working. I got to show it off to the prof' this Monday. It's still buggy as hell but man, it's satisfying to be able to finally fly around and shoot other people. Even if getting a kill means crashing the program immidiately afterward.

As promised I'm going to talk about how I've implemented multiplayer so far.

The game uses a basic client/server architecture. Whoever starts the game acts as the server and has final say on stuff like where players are and who scored what kills. All other players are clients and passively receive data from the server. The only data sent to the server by the clients are an initial "I'm ready to receive data" signal and player input. The data sent to the players dictates stuff like what direction they're facing and how fast they're going.

There is a bit of hairyness here however. The client needs to know stuff like what graphics to use to display the game. If you remember my earlier entry on multiplayer, XNA allows a game session to have properties, in the form of integers, associated with it. By associating a particular play scenario with a number the client can find out what to load immidiately by checking the properties of the session he's joining.

The whole sequence of joining an active game goes like this

  • The client player chooses a server to join
  • The server notices the new client and creates an object to track what is to be sent to him
  • The client grabs the scenario number from the server's session properties
  • The client then runs the normal load sequence
  • After loading concludes the client tells the server it's ready to receive data
  • In response, the server sends the client basic information on all players (their score, team, assigned spawn points and so on)
  • At this point the client can begin playing. Normally they're waiting to spawn.

Wait... there's no state data sent yet. In fact, if nothing else were sent the player would see nothing except for some background scenery. The magic occurs in the object the server creates to track the data sent to the client.

Each client tracking object has a list of game entities which matter to the clients. The tracking object allows the server to know stuff like whether the client has been informed that the entity exists (or has ceased to exist) and if any state updates are pending. The server sends entity creation data first, updates second and entity destruction data last. The server can also check for certain combinations (e.g. neither creation nor destruction have been sent but both are pending) to save on bandwidth. In the case of the previous example the server would give up on sending either to the client since creation and destruction effectively cancel eachother out.

State updates are a little more complicated than creation and destruction of entities. These are managed by a class called StateItemCollection. StateItems are objects which describe stuff like an entity's position, movement, damage and so on. When some significant change occurs, say the player suddenly accelerates, the associated StateItem flags that interested players should be notified of the change. The client tracking object queries each entity's StateItemCollection for this flag. If any StateItem's flag is set then the server queues up an update for the client. Once the server has queued the update for all relavent players it clears the flag.

There are a couple advantages to using this flagging method. One is that I can allow the server to choose which players receive the state updates. This allows for the server to throttle itself if it's sending too much data, or to skip sending state data that isn't relavent to a particular player. Other advantages lie in the ability to compensate for lag and in merging updates by having later updates replace earlier updates if neither have been sent.

I'll leave you with this screenshot of one (poorly lit) player shooting grey bullets. Notice I have some (glitched) lighting working now :)

Friday, March 5, 2010

Another Project

I haven't decided how much I'll talk about this one but it is fun. For our Game Development course in my program (Software Engineering at Conestoga College) we have to make a clone of Battlezone using XNA.

The prof has given a decent amount of leeway in the project to have some fun with it. So, finally I have a chance to try out a physics engine without trying to shoehorn it into a partially completed project.

I'll probably just update in shorter posts like this one for Battlezone (for now titled AAAAAA Zone due to a naming theme I've been using for the other projects in the Gamedev course)

Cheers!

Networking Remark

Preview of my next entry:
Debugging network code in XNA Game Studio 3.1 sucks. I spent way more time than I care to think about restarting my game due to stupidity that has nothing to do with actual bugs in the game.