Sunday, 20 November 2016

Interfaces vs Concrete Wrapper Classes

Software development can often be very rewarding, with interesting problems to solve and little pockets of endorphin releases as a reward.  It can also be a pretty tedious drudgery of box ticking with little to no fanfare.  The last few weeks have mostly been the latter.

Previously, when working on game scripts, a developer would be using C# interfaces defined within a standard interface library (XAGE.Interface.dll), which is how the scripts are de-coupled from the engine runtime.  The .NET naming convention for interfaces is to use an 'I' prefix e.g. ICharacter.

However, it's not possible to define a static method or property as part of an interface (which, when you think about it, wouldn't make sense anyway), so these were instead implemented as part of a second class e.g. Character.  This leads to some confusing and inconsistent typing in game scripts, e.g.:

ICharacter myCharacter = Character.GetAtScreenXY(mouse.x, mouse.y);

To work around this, I've created a concrete wrapper class for each interface:

// Instance of interface injected into concrete wrapper class
private ICharacter engineCharacter;

// Properties
public int x
{
    get
    {
        return engineCharacter.x;
    }
    set
    {
        engineCharacter.x = value;
    }
}

// Methods
public void SayAt(int x, int y, int width, string message)
{
    engineCharacter.SayAt(x, y, width, message);
}

Creating these wrapper classes has not been fun, even though it was partially automated, as there were of hundreds and methods and properties to wrap.  It does however have the following benefits:

  • The developer only needs to know about the single concrete class instead of the interface.  The newly styled documentation is also more straightforward as a result.  This will be fleshed out in future with examples and annotations.
  • Any discrepancies between the interface class and AGS scripts can be hidden as part of the wrapper class.  The main one being how AGS primarily uses integer IDs compared to XAGE's string IDs.
  • Automatic AGS conversions now no longer need clumsy string swaps for certain types.

There are some downsides however.  There is a tiny performance overhead in some instances, and some additional complexity when maintaining lists of objects in both the script and the engine.  These are outweighed by the benefits however.

So the last few weeks of spare evenings have been pretty dull, much like this blog post, but it's another small step in the right direction.  Next up:  Re-wiring and profiling.

Monday, 12 September 2016

Still Alive

Whatever tiny web presence XAGE has is usually followed by a comment including the word 'discontinued' or 'abandoned'.  Lack of any new releases and updates on this blog hasn't helped that perception.  In reality development has continued at a fairly steady pace (416 commits since I switched version control to Bitbucket in February 2014).  I've been posting the occasional progress tweet but recent work justifies (and my daughter's now almost sane sleeping habits allow) a new blog post.

Graphics Rewrite

A hangover from the earliest XAGE prototype had become increasingly a problem and the effort required to solve it meant it had been put off not just for months but years.  Originally, XAGE required that SpriteSheets be built by hand.  The earliest engine runtimes contained hardcoded animation frames (X/Y pos, Offset etc) for borrowed Monkey Island 2 graphics.  Later these animation frames were editable via XAGE Editor but were time consuming and fiddly to set up.  The situation was eased when converting AGS games, where SpriteSheets were built automatically at the point of conversion, but there were still problems with the whole process:

  • The placement of the assets on each SpriteSheet was not optimal which resulted in larger Png & .Xnb files.
  • Each object was directly tied to a single specific SpriteSheet, so anything that did not fit onto the specified maximum texture size (e.g. 1024 x 1024) would not be available to that object (you can see the visual glitches this would result in on old TGP videos).  SpriteSheets could be shared between objects but each object would need to have its own set of frames, animations and views etc.
  • Creating a game from scratch would still require manually building a SpriteSheet.

The solution was to move towards an approach much closed to how AGS handles art assets:

  • XAGE Editor will automatically create SpriteSheets on build (rather than during conversion) from the raw image assets.  The bundling of these images can still be dictated by the end user by arranging them in different folders or providing overrides.
  • Objects are no longer tied to a specific pre-built SpriteSheet, but now can use any image or globally shared animation and view.  This should make it much easier to eliminate any outstanding animation glitches found in converted AGS games.
  • SpriteSheets are automatically created under the hood using SpriteSheetPacker which arranges the images in a more optimal way, saving space.  Performance is improved by using FastBitmap and by only rebuilding whatever SpriteSheet has had a raw image asset change (or recreate everything when the per-platform setting has changed i.e. from maximum texture of 1024 x 1024 to 2048 x 2048).
  • Some components that were split out into seperate elements in the XAGE Editor Treeview have now been simplified by using DataGrids, to make it quicker and easier to create animations and views.
  • Managing raw assets now uses Cyotek's ImageBox component, which is far more polished than the clunky old custom viewer.

The take away is that everything is far simpler for the end user.  The rewrite is about 80% complete but the results are already positive.

This also represents a slight change in direction for XAGE.  Previously I'd been reluctant to model both the editor and engine on AGS too closely, which resulted in forcing some square pegs into round holes.  Going forward I'll be reworking any sensible AGS paradigm directly into XAGE as an individual component, rather than forcing them into a component that doesn't quite fit its needs (e.g. AGS Regions and HotSpots into XAGE WalkBoxes and Room Objects).  Anything I'm still not fond of (e.g. Bitmap masks for walkable areas) will not make their way in, but overall the ease and quality of AGS conversions should improve.

The above means ripping out a lot of old code written five or six years ago and replacing it with something a bit more elegant and appropriate.  This should put the entire engine as a whole on better footing for a maintenance perspective.

Other bits & pieces:

  • The 'X' in XAGE no longer stands for XNA but instead 'Cross-Platform'.  This felt like an important distinction as XNA is essentially dead whereas the libraries that XAGE is built upon, MonoGame and FNA, are very much alive.  More importantly, keeping the same acronym meant I didn't have to put any effort into rebranding.
  • A simple static website serves as its new home:  clarvalon.bitbucket.io
  • Bitbucket also hosts a public-facing Issues/Enhancements list and Documentation Wiki.  The latter will be fleshed out once the next alpha release becomes available and focus moves away from plumbing towards implementing more of the scripting hooks within the engine itself.