Tuesday 23 February 2010

Any improvements I planned to make to Hostile Skies today have been scuppered by mini Tower Defence. Don't click if you value your time.

Monday 22 February 2010

HS: movement

So I'm now actually at the stage that I can set destinations for squads. For now, they'll go by "public transport" - the end goal here is that they will fly to the city nearest to the target site, and then drive the rest of the way. Yeah, I'm anal like that.

But, for now we'll settle with them taking the airliner direct to the site. I could cheat here and use the movement trail you can see here:


... as, effectively, a progress bar; indeed, it shouldn't really be possible to alter your squad's destination while they are in flight, not until you get your own air fleet. That would mean I could escape having to figure out how to reposition the squad as it flies through the sky, but I really want to add a neat airliner icon onto the movement trail, so that's my next job.

This is going to get increasingly complex, as I now have to build something similar to the QuadRenderer to draw multiple movement "trails", and also the icons for individual conveyances (which I'll refer to as "Conveyances", since that's my top-level moving-quad-icon class, or "blips" for simplicity). This is problematic since every blip will move pretty much every frame! So I don't really know how much benefit there is to precompiling a vertex buffer for every conveyance.

HS: mouse picking simplicity

One of the nicest things about XNA in comparison to Java3D is the flexibility afforded when drawing and picking, since you have complete control of the pipeline. For picking, this means that you can implement a lot of low-level smarts that make your job easier.

Whereas in Java3D, a pick ray would pick everything in the scene, even stuff that was occluded or facing the wrong way, with XNA you can implement specific pick tests for each of your objects and pick in whatever order you like. So for now, I run a pick on my Scene, which calls a pick on the Planet, which only calls a pick on the list of Sites (the cities and zones and so on). Later I am sure I'll have to adjust this to encompass the planet too (for adding new bases, and arbitrary waypoints for aircraft).

My picking architecture is to pass a MouseMode and a PickMode through to each object so it can decide whether it can be picked at all, and what the relevant pick action is. Cities can only be picked with the "hover"£ PickMode, which returns their label to be displayed, unless the city has a base in it, in which case they also accept "right click" PickModes, with the pick action being a context menu to display. The MouseMode part is used for things like squad deployment. As you can see here:


By clicking the green button on the Team window, I set my MouseMode to a "squad select destination" mouse mode. This overrides the regular picking behaviour, and it itself knows that squads can currently only have their destination set to a Site, no matter what else is available for picking.

HS: clock and events

One more short snippet on the variable-rate clock: if you're interested in implementing a priority queue in C#, in my case for use as an events queue, then you want a SortedList . I use long as the key since I am trying to operate everything based on "ticks", since that's what MS seems to use as the finest-grained time measurement available in DateTime. A tick is 100 nanoseconds, which led to an out-by-1000 conversion error in my code that took an hour or so to pin down.

The upshot of implementing that was that I was able to create an AI "AlienPlayer" class that, on construction inserts an event to trigger 10 minutes into the game. That event is an "InvestigationZone". Not to talk about the game design too much, but I want to incorporate X-Files-esque "investigations" into the game as an alternative to pure tactical combat to give more variety. They'll just run in a Neoforce window and every so often prompt the player for a new action. I'm unsure as to the mini-game design as yet.

Here's a picture of an InvestigationZone on the world view- marvel at my graphic design skills:


My source image actually has a black border around the question mark - I have yet to figure out how to get XNA to render compound alpha blending rather than additive blending. More fiddling with the effects file required, I suspect.

Wednesday 17 February 2010

HS: Neoforce and variable rate game clock

Implementing the game clock was really very easy. I just have a class with an internal DateTime that has its ticks incremented by gameTime * timeRate, where time rate is set by one of the Neoforce buttons in the top left of the screen:


I initially subclassed Neoforce.Button, because I couldn't see how to otherwise pass custom data into the button click. I don't think I've really worked it out properly now, either, but I am now using the "Tag" property, both for this and for game difficulty selection:


Pushing the 'new game' button toggles the dialog box open and closed. Annoyingly, the Dialog box comes with default caption and description set, and it took me a while to figure out that you need to do dialog.TopPanel.Description.Text to access (and remove) it, despite C# allowing you to make string assignments to dialog.TopPanel.Description.

Anothing thing to note is that when a dialog is closed, it is not removed, simply marked as visible=false - which was exactly what I wanted, since I wanted the ability to bring it back easily. But I can see how it might not be everyone's preferred choice. Presumably to remove it entirely you need to remove it from the Neoforce Manager.

Finally, the background image for the start screen was taken from here. I am always grateful that so many people make their flickr photos CC-Attribution licensed. You can see that the photographer is credited on my start screen, too.

HS: QuadRenderer

Well, a lot has happened since my last update. I added a set of cities into the game and was rendering them individually until I realized that in order to have them have different textures on them, I would need to set an Effect parameter, and constantly doing fx.Begin() and fx.End() was of course a ridiculous performance hit.

So most of yesterday was spent building a QuadRenderer class to manage the individually textured cities. The QuadRenderer maintains a dictionary of vertexbuffers and indexbuffers by texture, which has to be recompiled if the list of cities / sites changes - I plan to use this also for managing bases, crash sites and really any world elements that don't actually move.

One thing that I learned yesterday was that storing the GraphicsManager.GraphicsDevice in a shortcut variable is a really bad idea - it caused a lot of "NullReferenceException" problems until I removed the reference and just went the long way around.

The other challenge was figuring out where to put the QuadRenderer in my architecture, since ideally there would be a complete split between model and view, but as individual cities are added and removed from the model, they need to to work directly with the QuadRenderer. Since my GameManager is a singleton, I have added a QuadRenderer there, but I am not entirely happy with this solution and can see the GameManager class getting filled up with all sorts of optimization rubbish like this.

Here's a picture of the QuadRenderer in action:

Saturday 13 February 2010

HS: hires Earth texturing, and bump maps revisited

I've been investigating the thoroughly awesome NASA Blue Marble images, particularly these monthly ones, which I plan to incorporate once I can figure out how to cleanly load 24 separate 4096x4096 textures without running out of graphics card memory. Even the 2km-to-pixel ones are far, far too hires to load all in one go.

So, I've kinda cheated with getting a 8192x4096 Earth texture by splitting it into two and then using the pixel shader to double the y value of the texture coordinates, and use two separate texture samplers, one for each 4196-wide image. It would probably have been much cleaner to create two hemispherical Earths with different textures! Perhaps I will do that another time, if it'll allow me to increase resolution even further. Alternatively, I'm going to have to come up with some sort of mental Level-of-Detail texturing algorithm which, since you can scroll around the earth pretty quickly, would be quite complex. Here's a screenie of where I'm at right now:




You probably couldn't have noticed, but the bump mapping I was so pleased with the other day isn't working properly - obviously, now I think about it, one needs to rotate the texture normal to bring it into the same coordinate space as the vertex normal. There are a few tutorials out there on doing just that, this one appears to be pretty informative.

Next up: variable-rate clock, which will include actually programming a fair number of Neoforce buttons, which'll be interesting. Unlike Java3d, XNA doesn't appear to have a bunch of interpolator classes all hardwired into the system clock, so hopefully this will be quite an easy job.

HS: mouse camera

There are lots of tutorials around on how to create an FPS-style camera in XNA. However, no easy to find ones on how to create a click-and-drag mouse camera similar to the Java3d OrbitBehavior class.

Digression: if there's one thing to be said of Java3d it is its suite of sensible helper classes to get you up and running faster, even if you can't do much once you're there. Here's hoping JavaFX finally integrates shaders into Java3d.

My problem is comparatively simple: I want a camera I can click and drag to rotate a camera view around a sphere. In Quake, I'd store the X and Y angles of the rotation and call makevectors() on them to turn the angles into a vector. Not so in XNA, I spent quite a while fiddling around with different ways to solve the problem. Eventually, the easiest way was to only calculate the rotation about the poles using this code I found on a forum somewhere:

static Vector3 RotateAroundPoint( Vector3 point,
Vector3 originPoint,
Vector3 rotationAxis,
float radiansToRotate)
{
Vector3 diffVect = point - originPoint;

Vector3 rotatedVect = Vector3.Transform(diffVect, Matrix.CreateFromAxisAngle(rotationAxis, radiansToRotate));

rotatedVect += originPoint;

return rotatedVect;
}


And then to magick up the vertical position with a little bit of maths, thus:

pos.X = xpos.X * (float) Math.Cos(ang.Y);
pos.Z = xpos.Z * (float) Math.Cos(ang.Y);
pos.Y = (float)Math.Sin(ang.Y);


... where xpos is the output from using the above function. My camera recalculate position just needs to set the camera position, then it figures out the new view matrix using CreateLookAt.

KRPS: feature effects

It seems hardly worthy of note, but I updated my homebrew RPG character database to start making use of "feature effects" the other day. This is in order to cater for things like Combat Reflexes, which gives a +1 bonus on Dodge and active defences. Each feature has a number of effects, which use a scratty little generic table to target various parts of the character. Combat reflexes, for instance, targets "skill:parry", "skill:block" and "dodge". When skill levels are calculated, each skill checks in the database for named effects that the current character has, and applies them. Stuff like dodge is handled directly on the character pdf generator, which is very naughty; it really ought to be the character's responsibility to calculate its own dodge. There is little about this project that isn't a kludge!

Thursday 11 February 2010

HS: SVN install

Just a note to say that getting set up with SVN in Windows was pretty easy thanks to this article. The one issue I had - repeatedly - was with Vista permissions. You need to start up your cmd.exe in administrator mode (particularly in order to start the service), and then you need to remember to set your svn and svn\repository directories up as accessible by people other than just the administrator.

HS: normal maps and spheres

So, I've been experimenting with lights and so forth. I had a normal map lying around for the Earth, so I've gone ahead and banged a moving light source in:


Looking a little nicer now isn't it? I've jacked up the sphere quality a bit, too. There was a bit of a struggle getting the normal map integrating with everything else: one has to combine the flat normal map composed of mostly blue (indicating a vector of approx {0.5, 0.5, 1} with the normal as interpolated from the nearby vertices. This is in the pixel shader, of course. Here's mine:

pf_col PS_LitTextured(vp_pos_tex PSIn)
{
pf_col Output = (pf_col)0;

float3 baseNorms;
baseNorms.xyz = tex2D(NormalsSampler, PSIn.TexCoords).rgb;
baseNorms.r -= 0.5;
baseNorms.g -= 0.5;
baseNorms.b -= 1;

baseNorms = (baseNorms * 16) + PSIn.Normal;

float diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, baseNorms);
diffuseLightingFactor = saturate(diffuseLightingFactor);

diffuseLightingFactor *= xLightPower;

float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords);
Output.Color = baseColor*(diffuseLightingFactor + xAmbient);

return Output;
}


In order to compose the flat normal vector with the interpolated vector, I figured the sensible thing to do would be to subtract the perpendicular vector from the value from the normal map, then add that to the normal coming from the vertex shader. In order to give the bumps a little more oomph I multiplied the difference vector up by 16.

HS: trouble with spheres

Having got my mode switching working and Neoforce mostly behaving itself (though I have noticed that it's perhaps stealing keyboard events, something worth investigating), I turned to the task of getting myself a spherical Earth model displaying itself.

My first instinct was to use a prefabbed sphere mesh, one of which I found here. However, I couldn't figure out how to load the vertices out of the model, knowing that I have certain rather advanced plans for my Earth. So, I turned to this code to procedurally generate a sphere for my Earth.

I had to adjust the code to use my homegrown vertex declaration that includes texture coordinates, and discovered in the process how to draw a sphere with texture coordinates that go all the way from 0 to 1 in both axes. There were two parts to this:

1. Adding an extra column of vertices that overlap x=0 with x=1 for texture coordinates (essentially, generating the first vertex in every row twice, once for 0 and once for 1).
2. Removing the triangle fans at the top and bottom of the sphere and replacing them with strips. This is because otherwise you have to set the texcoord of the poles to {0.5,1}, which leads to texture twisting in the triangle fans. Instead, I generated an additional vertex at each pole for each column, these overlapping vertices having gradually increasing x coordinates for the texture.

So, here's my Earth! You can also see some axis lines I'm drawing on, and a Neoforce button on there for testing purposes:


Eventually, as well as lighting it, I plan to calculate surface normals based on a NASA bump map, potentially multitexture it with a nighttime texture, and of course add all the game components such as cities, bases and UFOs.

Two things to do next: allow proper click-drag modification of the camera view, and add a variable-rate clock. Perhaps I will try getting a default light source in first, though.

Wednesday 10 February 2010

HS: Neoforce "no skin loaded" bughunt

Neoforce threw up another problem late last night: no matter what I did, I could not manage to get it to run without throwing up a runtime error the first time I try adding a button to my game mode: "Control cannot be created. No skin loaded." - another user had the problem on the Neoforce forum, but no solution was identified there.

The only way I found to get the project to start correctly was to ensure I was using the 4-parm constructor for the Neoforce Manager class and setting the register parm to true. The downside to this is that the manager automatically registers itself with your game class and the base game class calls the manager's draw and update methods. This could be inconvenient for you, but seems to be working okay for me at the moment.

Since I am writing a game with a mode switcher, I added an Exit method to the IGameMode interface that gets called on the current mode whenever the GameModeManager is about to change mode. In the GameModeMenu's Exit method I call Dispose on the Neoforce manager and set my reference to the manager to null. Hopefully this is enough to release the manager to be garbage collected; I don't see a method in either Game or the Neoforce manager to deregister it.

Tuesday 9 February 2010

HS: Neoforce setup

I am on my way to getting neoforce controls set up. The first infuriating incident I came across was that neoforce requires its skins to be available - fair enough - but it's impossible to add folders to the XNA game studio's Content folder using the "add" method; you have to just drag the folder over. And again, to stop the auto-importer from getting confused, all of the skin files needed to be individually set to "copy if newer" and "no build action".

HS: UI beginnings

I had a hell of a time trying to find a window manager for XNA because I wasn't using the right search keyword, which turned out to be GUI. There are a whole load of XNA GUIs, I am going to try out Neoforce controls, because despite barely being in Beta, they look like they are still under development and the demo looks pretty snazzy. I'll let you know how I get on with it.

My first game mode is part-way written now; I've built the main menu mode, and got an image that I got from flickr creative commons loading as the background. It'll do for now. Eventually I plan to replace the static image with some sort of 3D thing, though I'm not sure what of yet.

HS: setup & config

Hello and welcome, nobody, to LTH's dev diary. I plan to cover the fiddly bits of developing a game in XNA here over the next few weeks and months.

My plan is to write a game similar to turn-based tactical classic UFO: Enemy Unknown but with my own particular twist. It will be called Hostile Skies, so that name is now (c) and (tm) me! Those of you who have encountered my Quake mods will know that the standard LTH twists are "more customisability" and "more realism". So it will be here.

Strategy


I plan to build a game mode manager similar to the GameScreen project described here and here. I am sure their way is perfectly fine, but I am building this to learn XNA so I might as well start from scratch.

What they are using here is the Strategy pattern - XNA automatically calls Update and Draw on your game, so desgnating your game as the strategic authority makes sense; it can then delegate responsibility to whichever game mode is most appropriate. One starts on the menu screen and proceeds to what UFO calls the "geoscape" but I will call the "world view". Eventually one will proceed from the world view to the "tactical view", an individual battle.

So, I have my GameModeManager in place; it's just the default XNA new project game1.cs renamed, and moved to a directory called /engine.

Config


The first thing I want to do is be able to alter the screen resolution of my game easily; I have a laptop with separate monitor. I downloaded EasyConfig (1.02) to manage this. I added the 3 .cs files from the download to a /engine/config folder, and created a simple config.ini file in my Content folder, containing the following:

[Video]
width=1680
height=1050


In order to get the project to compile, I needed to alter the settings on my config.ini file, to set "Build Action" to "none" (otherwise it tries parsing the file itself and falls over) and "Copy To Output Directory" to "Copy if newer". Hopefully this will help someone out!