Showing posts with label design issues. Show all posts
Showing posts with label design issues. Show all posts

Sunday, September 22, 2013

The History (and Future) of MHFramework's Layered Tile Maps

The original version of the MHFramework tile map system (circa 2004) consisted of the following layers:
  • Floors
  • Items
  • Walls
  • Ceilings
The overwhelming task of creating assets for the Beltzhian Marauders world led me to redesign the map structure to cut down on the number of raw assets that need to be created (and also reduce memory consumption in the process).  The evolved version (circa 2008) contained these layers:
  • Floors
  • Floor Decals
  • Items
  • Walls
  • Wall Decals
  • Ceilings
The lessons learned through creating various games and prototypes in the meantime has made me realize that by aggregating things together and using more sophisticated rendering and collision detection algorithms, the default layers for MHF3 might look something like this:
  • Floors
  • Floor Decals
  • Walls
  • Wall Decals
  • Actors


Wednesday, August 28, 2013

Planning for Tool Upgrades

Considering that the entire engine is being rebuilt from the ground up, it should come as no surprise that the engine tools could also use a redesign.  MHF doesn't have a lot of tools, but the few that it has are useful and worthwhile. Here's the list as it stands right now.

SpriteTester 2.0:Interactive animation viewer
LIME 3.6:Layered Isometric Map Editor
Floor Tile Transformer 1.1:Parameterized command line utility that splits rectangular textures and tile sheets into sets of isometric floor tiles.
Wall Tile Transformer 1.2:Batch utility that creates and applies shadows to a matching set of isometric walls from a single source image.

The most sophisticated of these tools is LIME, so it's the lucky recipient of the first redesign. It's also the most frequently used, as evidenced by the advanced version number relative to the other utilities.

So, What'll it Be?


I'll brainstorm a wishlist of features I'd like to see in the new LIME.  For starters, I'd like to keep certain features of the current LIME.

  • Support for diamond-rendered isometric maps.
  • Controls for independently toggling the visibility of each layer.
  • File formats that can be edited in other programs, like OpenOffice or Notepad++.
  • Full-screen view mode.
  • File browser for listing, selecting, and opening map files.

I would also like to incorporate features from the very first map editor I ever made -- the one for CTG 2.

  • Support for rectangular (non-isometric) tile maps.
  • A space-efficient tile palette that shows a large portion of the tile set with minimal scrolling.
  • A single file format for saving map data.
  • Highlight around the selected tile in the palette instead of a "Selected Tile" display.
  • An option for toggling grid lines.
  • An option for viewing tile IDs or perhaps the entire data set.
  • Menu bar with pop-up/drop-down menus to minimize the complexity of the UI and maximize the working area.

I may even want to bring in some features from the SpriteTester tools.

  • Ability to play, pause, and control the frame rate of animated tiles.
  • Ability to cycle through a set of background colors.

Finally, I'd like some new features that I have never implemented in any of the previous tools.

  • Support for staggered isometric tile maps.
  • An option for rendering an entire map to an image and saving it to a file.
  • Tile images stored in either a single tile set image or a directory whose name is the tile set name.
  • Context-sensitive mouse cursors (selected tile, tool, etc.)
  • Ability to load a background image instead of just a solid color.
  • A "play" command that drops a playable character into a map and lets you move around inside.
  • Perhaps add a zoom control to the view mode.
  • A mini-map generator showing a rectangular mini-map of the collidable areas.
  • Option for showing elevations of isometric map cells.
  • Object browser for viewing and selecting assets that may be separate from the tile set.

I'll prioritize those later, and will certainly be elaborating because, of course, I want it all.  For now, let's think about how the application flow might happen and what bits and pieces might be on the screens.







Wednesday, July 24, 2013

Proposed Changes and Tasks for MHF 3.0

As much as I'd love to put the cart before the horse and declare that MHF3 is ready for showtime, the truth is that a number of tasks remain.  I've also been contemplating some changes to the existing code.  This blog entry will document the state of those lists at this moment in time and serve as a checklist of preparations for an upcoming project (which I am not at liberty to discuss due to an anticipated nondisclosure agreement).


Remaining Tasks for Version 3.0:

  • Android platform functionality must be caught up to the SE-compatible code.
    • When my Android development environment quit working, I continued to work on the PC platform.  It's time to get the Android code up to speed with those advancements.
  • More testing of existing engine cores on all supported platforms is necessary.
    • The current demo program includes a lot, but some of the cores aren't addressed.  Also, it has only been tested on the PC/Windows platform.
  • The configuration management plan needs to be finished and validated.
    • The only reason this isn't done yet is because I've been experimenting with project setup alternatives in an effort to simplify the cross-platform development and testing.
  • Javadoc documentation needs to be completed and made more consistent.
    • Most of the existing engine is documented, but hasn't been updated to reflect recent changes.  Also, the documentation was never very consistent across packages.
  • The rest of the planned features need to be completed or formally moved to a later phase of development.
    • For instance, sprite sheets, textured particles, and predefined physics behaviors are all "wishlist" features that have yet to be implemented.


Proposed Changes to Existing Code:

MHGame should be renamed to MHGameLoop (or maybe MHGameCore).  In MHF 2.x and earlier, the MHGame class was a software controller that manipulated user-defined objects to run the game.  In MHF 3, however, the class is responsible only for the concerns of the game loop.  Therefore, it is still the very heart of the engine, but it no longer represents all of the other gameplay support functions such as message pumping.  It also has no direct association with user-created code or assets.

MHVector's arithmetic functions should apply directly to the vector object instead of always generating a copy.  I originally designed it for maximum flexibility, but the performance of these objects is super critical, and this change in priority demands changes in interface and functionality.

The steering calculations in the Artificial Intelligence core should be simplified to be more cohesive.  As they are at the moment, they cannot be used in an independent, atomic way, so their reusability is unnecessarily limited.

The AI core's finite state machine support should be updated to use generics.  This way, the MHState interface could be generalized to eliminate dependency on the rest of the core, allowing states to be used independently of any particular owner without the need for explicit type casting.

This may not require a change, but the relationship between tile map data classes and viewer classes should be revisited simply because I don't remember exactly how I divided these responsibilities, and this is a crucial element to virtually all upcoming projects.

Tuesday, May 28, 2013

Software Design and Configuration Management

Despite my years of experience, I still occasionally get slapped in the face by things that somehow slip by under the radar until they're close enough to strike.  One such thing that dominated a portion of my weekend is an unforeseen relationship between the design of the platform independence layer and the configuration of the projects in the development environment.  As with many of my posts in this blog, this one is intended to document the issue so that lessons are learned rather than lost.

MHFramework 1 and 2 were designed to be used as code libraries that existed as a separate project or an external JAR file.  Most of the games built with it simply absorbed the engine binaries into their distributable files.  Most often, they were just packaged into the game's executable JAR file.

MHFramework 3 introduces a new challenge, however.  One of the main objectives of this version is to add Android as a target platform while still maintaining compatibility with Jave SE platforms.  After a few days of puzzling over this challenge, I came up with a solid design that conforms to a number of object-oriented design principles.  That design is documented here.

The problem, as usual, is in the implementation details.  I could create a Java SE demo program that hooked into the engine with no trouble whatsoever, but the Android programs weren't so forgiving.  An Android app simply won't deploy if there is any reference whatsoever to SE-only classes.

My planned solution to this problem was to isolate all the SE stuff into a single package hierarchy that the running app would never encounter.  The problem with this solution is that the platform factory interface couldn't be completely generic.  (For example, mobile apps launch as an Activity while PC apps launch as a JFrame.)  There is no way to have a single branching point that accommodates both platforms because it creates problematic (and unnecessary) dependencies.

The solution I am attempting now, which seems to work so far, is to break the engine up into multiple projects.  The main engine core is now an Android-compatible library.  This main project contains the cross-platform heart of the engine along with the Android platform layer.  Java SE support has now been moved into a separate project, so now the main engine core is completely free of Java SE elements.  With just a few more alterations, I can completely remove all Android references from the execution path for PC games, and vice versa.

So, while the class diagrams are only slightly affected, the configuration management has changed dramatically.  Updated documentation to come.

Important lesson learned!


Thursday, February 21, 2013

Designing Cross-Platform Image Buffers

This post documents a design issue, some rationale, and the chosen solution. 

The Problem

As I was porting functionality from the old MHFramework to the new one, I realized that I needed a platform-independent solution for generating images from buffers.  Since the old engine was based entirely on Java's Abstract Window Toolkit (AWT) which isn't available on Android, I needed to find a way to accomplish the same thing for both of those platforms in a consistent manner.

Proposed Solutions

The first solution that came to mind was to encapsulate graphics contexts (MHGraphicsCanvas) into the the MHBitmapImage classes just like AWT does.  However, as with all such decisions, it comes with some immediate advantages and disadvantages.

Pros:

  • Our engine is never going to use a graphics context for anything other than drawing to a buffered bitmap.  Combining these classes would hide the coupling.  This cleans up the class structure of the  platform layer and also greatly simplifies the implementation of more advanced visual effects.
  • AWT and Android sort of reverse the association between bitmaps and canvases, and this would encapsulate those differences internally so we'd have a uniform way to work with image data.  (AWT's Image has Graphics, and Android's Canvas has a Bitmap, so even though they're semantically equivalent, their compositions are inverted.)

Cons:

  • MHBitmapImage no longer just stores image data.  It now also provides an interface for manipulating that data, so we may be in violation of the Single Responsibility Principle.
  • Not all image data requires a graphics context until it's rendered, so this could incur some memory overhead.
  • HOWEVER, we can solve both problems through composition and lazy instantiation.  Besides, this relationship already exists at the platform level anyway.

The Chosen Solution

 I decided to keep MHBitmapImage and MHGraphicsCanvas as separate classes, but I removed MHPlatform's factory method for creating a graphics canvas.  Now the only way to retrieve a canvas for drawing is to extract it from the image object.  Now you always have immediate access to the results of every rendering operation.

For example, the double-buffered rendering now happens by using an MHBitmapImage as the back buffer, and then passing its associated MHGraphicsCanvas to the screen manager.  When the call returns, the bitmap image is presented physically to the screen device.

I am happy with this solution.  Although I was unable to satisfactorily eliminate a class, I feel very comfortable with the design principles involved and the improvement in general usability of these critical elements.
 

Saturday, December 1, 2012

Debating Game Engine Quality Attributes

I love my MHFramework game engine.  It's like an old friend who has grown up with me through the years, rolling with the punches and becoming ever more resilient because of them.  I'm intimately familiar with its capabilities and its drawbacks, and can accommodate them like it's second nature.

I feel like Han Solo with his Millennium Falcon, where, in the face of outright criticism, he says, "She may not look like much, but she's got it where it counts, kid.  I've made a lot of special modifications myself."

However, recent events in the game industry as well as in my personal and professional life have given me reason to consider building a brand new game engine from the ground up.  The motivation for this new system is driven by two major influences:

  1. The strengths and weaknesses of MHFramework.
  2. The known challenges and opportunities of current and future projects and platforms.

At the moment, all I can do is speculate on the features and qualities that I want to create in the new engine.  After all, I want to avoid the mistakes I made in MHFramework, such as the lack of a comprehensive architecture, the lack of portability, and the lack of cohesion that is especially evident in some of the older modules.  Diving in head-first would simply be repeating the failures of the past.

So let's figure out which qualities will truly matter in this new system so that we can design a solution that will perform under the pressures that are currently storming their way down the pipe.  Let's run down the list and consider the priorities of each with regard to the high-level software architecture.

AttributePriorityRationale
FunctionalityMediumIn reality, functionality is priority number 1. Though it will not be a heavy influence on the architecture, it will obviously play a huge part in component design.
UsabilityLowAs the engine will be driven by a code library rather than a GUI, this attribute gets a low priority. However, the interfaces to classes will place a very high value on usability, and consistent documentation will receive focus throughout.
ReliabilityMediumAgain, reliability is an obvious desire, but not an influence on the architecture. Like functionality, it will be addressed at the component level.
PerformanceHighTypical of most game engines, the performance of the real-time player experience is crucial. Many decisions in the development of MHFramework did not prioritize performance, and instead favored usability and supportability. In the new engine, I would like to find better compromises between these equally valid aims.
SupportabilityHighMHFramework did a great job of allowing open extension of engine classes, and also left many decisions up to the programmer regarding which engine parts to use and which to ignore or replace. I want to maintain that same degree of freedom in the new engine. Also, portability will be a top concern because I want the engine to enable games for the PC in full-screen exclusive mode as well as in a window, games to be played in a web browser, games for Android smartphones, and, of course, games for the upcoming OUYA console.

So, by this reasoning, the highest initial priorities, in order, will be:

  1. Portability
  2. Performance
  3. Extensibility

TL;DR: I'm going to build a new game engine that emphasizes portability, performance, and extensibility.