Sunday, 7 March 2010

HS: investigations

Dev Diary has been on holiday skiing in Leysin, which was an interesting demonstration of how hopeless it is trying to render bump maps when your diffuse lighting component is too high.

Now that it is possible to assign squads to attend an investigation, the first part of the game that is actually to do with gameplay can be programmed! Each investigation will comprise a number of elements, and "solving" one element will open up other potential elements to investigate. Some elements may result in the retrieval of alien artefacts or, later, switching to tactical combat mode.

The idea is to make investigations feel as much like "simulations" as the tactical combat mode, giving the player lots of realistic choices to take with limited resources.

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: