Monday, March 22, 2010
Here's that screenshot
BANZAI!!!1one
Saturday, March 20, 2010
Slightly late. Slightly different.
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
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
Screenshots coming tomorrow. I might also talk about how to create a simple radar display.
AAAAAA 'Splosions!
Wednesday, March 10, 2010
Space Combat Multiplayer
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
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
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.