Recent Posts

This month included a somewhat mixed  bag of features I wanted to work on. I had a separate post about all the features I would like to touch, and some of those goals weren't met, but I still think October was pretty productive. Some of these new features are still very much part of the game's core, and need to be robust so I can build higher level functionality on top of them.

The most important achivements from October include: Collision avoidance, game state saving and loading, refactoring for better multi-threading (performance), and AI foundations with basic ship and fleet level implementations. Please check out the video below (in HD) for a visual presentation of these features.

Dynamic lights, Saving & loading

At the turn of the month I decided to spend few days on a bit different side-task, implementing dynamic lights for projectiles and beam weapons. I have had basic implementations for point lights since the adoptation of the dynamic lighting system in January 2014, but those hadn't really been tested before. The beams also required linear light source so they can emit light from their entire length. Implementing the pixel shaders for these was quite straight forward, but I had to create some framework for managing the scene lights so that invisible ones are not drawn, and they can be rapidly added and removed without noticeable impact on performance. The beam lights use a simple approximation for diffuse and specular coefficients that is based on the pixel's distance from a the beam's line segment. Both the point and line lights also have falloff radiuses for diffuse so that their "size" can be more easily managed and used in the optimization logic. Projectile hits are highlighted with white point lights that have a very quickly decaying intensity. At normal speed, the flash is just 1 or 2 frames, but when the time is slowed down, the decay of the flash effect can be seen over a longer period.

Another system that needed to be extended to actually working state was the full battle state saving and loading logic. The game uses .NET's DataContractSerializer for serializing and deserializing objects to XML and JSON. XML is quite verbose, but human readable, and is used for saving the ship designs (blueprints), fleets, and now full level objects to disk. A battle save file may take anywhere between 1 to 20 megabytes of space, and contain 300,000 lines of nicely formatted XML. However, these XML files compress very well when zipped, reducing the size to as low as just 1% of the original! Having a convenient way to save & load both fleet compositions and level states paves way to fleet deployment, combat scenarios (levels), and of course save games. Battle state saving is also crucial for various kinds of testing such as performance comparisons, AI, and debugging as a gameplay state can now be perfectly recreated by simply loading a file.

Implementing the actual game state saving and loading was pretty easy because the DataContractSerializer does all of the heavy lifting (and is quite fast as well), but it did expose quite a lot of minor problems that took several days to fix. These problems are related to the fact that the complete state of (highly intervined) objects isn't practical or even possible to be serialized and thus some initialization always needs to be run after deserialization. Examples of such data are globally shared objects (configuration), textures, and required objects that can be re-created based on other information. This initialization needs to be performed in correct hierarchical order so that object references, event handlers etc. can be set correctly. A process that shares much logic/code with the object constructors.


    Another big part of October's work was various performance optimizations. These include utilizing a spatial acceleration structure for queries about nearby enemy ships  and missiles, refactoring most of the drawing code so it can be run in parallel, and making sure some function call chains get inlined etc.

    I had hoped to gain a significantly bigger performance boost from wider use of parallellization (running independent pieces of code in multiple threads), but now it seems that I was only able gain maybe 15% increase in overall framerate. This is because only some parts of the game's cycle can be parallelized, the heavy physics simulation update and all drawing commands to XNA need to be run on a single thread because those libraries are not designed to be thread safe. I now have nearly all of my own update and drawing logic running in parallel by utilizing .NET's task parallel library, which provides an exceptionally easy to use interface for multi-threaded execution. However, task allocation and distribution was something I had to touch on, as a battle can have both ships with hundreds of modules and pieces of debris with just a single one. If I were to create a task for each object, the small tasks would have significant overhead or the process may need to wait for the larger tasks to finish. I solved this by keeping the objects sorted by size and grouping update calls to several smaller ones into single tasks. However, these tasks (update and predraw) only make up maybe 50% of the whole cycle time, and thus optimizing them can't yield any extreme changes such as doubling or tripling to the framerate.

    The heavy single threaded stuff such as physics updates and sprite drawing commands are also quite highly optimized and I don't expect to be able to gain nearly anything from those anymore. In practise, this means that I have probably reached a level of performance that can't be much improved without really major effort, and thus isn't worth spending more time on at this stage. Large ships with hundreds modules do cause some performance issues (especially when they split), but it is now possible to have a battle with 40 or more small ships and maintain a quite decent framerate.

    Ship and Fleet AI

    With the implementation of individual ship movement logic (that took the better part of this year, actually) and formation-based movement orders it soon became clear that the ship's colliding with each other is a real issue even if you don't necessarily need path finding in open space. To tackle this, I first tried to implement my own solution that calculated closest approaches for linearly moving objects and had the ships accelerate to direction that increases the minimum distance and time to closest approach. It only worked partially, with some test setups always failing to give good enough results, so I had to scrap it and look for a better solution. The current implementation uses a well known "velocity obstacle" approach, and the core implementation isn't mine, so at least the math is now quaranteed correct! :)

    The collision avoidance logic is integrated to the existing ship flight control system so that it can override other thruster group commands if a certain calculated value indicates that a collision is imminent and the current velocity needs to be changed to avoid it. This approach allows the existing movement logic to function without interruptions or interference when the ships isn't on a collision course. However, since a threshold needs to be met before the collision avoidance takes control, ships may get "stuck" when they try to push through a formation at low speed, as the other ships won't give way to the incoming ship because it doesn't pose a significant threat.

    I also started working on ship and fleet level combat  AI. The ships are now by default in "guard mode" where they will follow movement commands and shoot at enemies that come within their weapon range. This basic behaviour was actually already seen in the last month's dev update video. When a ship is given a target, it will now move to weapon range and start engaging it. During the movement it will still shoot at targets of opportunity, but when the engagement range is reached, the ship will concentrate all firepower on the target and follow it by matching its velocity. In addition, if the target ship is significantly larger, the ship will try to engage it from the side instead of directly at the closest approach.

    Fleet-level AI currently only has a very simple strategy: all ships engage the closest enemies, and targets of all ships are updated at a fixed interval. This leads to the enemy AI to always rush the player fleet with everything it has, making the battles very quick and hectic. While the ship AI is used for all ships, the fleet combat AI is meant to replace the player commands for enemy fleets. Obviously the fleet AI will be one aspect of the game I will need to spend significant time on to make the battles interesting.

    Goals for November

    On November the development will focus on gameplay and user interface. Thes include fleet deployment, many improvements to manual ship control, fleet AI etc. Missed goals from October will be at the top of my list as well.

    We are now reaching a stage where I can finally start adding some real gameplay elements with relatively small amounts of code. The next couple of months should see the game truly become alive. Stay tuned!

    Posted by Tomi Thursday, November 6, 2014


    Post a Comment