Flat Four User Guide

This is the future user guide for the Flat Four game and simulation framework. Right now I am using it as a place to document what I think the new version will look like. Everything in this guide is open for discussion, and subject to change at any time! Feel free to leave comments on any page, or hop over to the forums and open a discussion there.

Getting the Software

Development is underway, but it is not far enough along to warrant an official release. In the meantime you can get the source code from Subversion and build it yourself.

System Requirements

The Framework runs on Windows, MacOS X, and Linux. I'm looking for someone to help get it running on the XBox 360 via XNA.

You will need a .NET 2.0 runtime. On Windows, you can get it via Windows Update or the Microsoft website. MacOS X and Linux users should use the most recent Mono release.

Building the Framework

Creating the Project Files

To enable the type of community-developed system I'm dreaming about I plan to use the Premake build script generator for the build environment configuration. Premake is a small, single file executable that provides configuration abilities similar to Unix-style configure scripts, with the added ability of generating project files for a number of different tool sets (and it runs on Windows). Installing Premake is as simple as downloading it and putting the executable somewhere on your system search path. Right now I am only using Premake to generate the project files, but later on I will be adding more advanced configuration code.

To get started, download and install Premake (version 3.4 or later). Open a command shell and switch to the root FlatFour directory. From here, Premake can show you all of the project configuration options.

$ cd FlatFour
$ premake --help

Visual Studio 2005, SharpDevelop 2

Both of these development environments use the Visual Studio 2005 project file format. To generate a solution file, run the following Premake command (this will work for Visual Studio 2005 Express too):

$ premake --target vs2005

Mono with GNU Make (Linux and MacOS X)

The following Premake command will generate makefiles that use the gmcs compiler. You must have both the base Mono packages and gmcs installed, of course.

$ premake --dotnet mono2 --target gnu

MonoDevelop

I would like to support MonoDevelop, but it has some issues I haven't been able to work around. At the time of this writing, it doesn't support C# 2.0 even though gmcs does. Also, it doesn't handle App.config files properly (they need to be copied to the output directory and renamed). If things have changed let me know and I will take another look. In the meantime, you can use the GNU make instructions above.

Cleaning Up

If you ever decide that you want to restore the source code directory to its original state, you can use the Premake "clean" command. This will remove all generated project files and compiled binaries.

$ premake --clean

Building the Framework

At this point, there should be a solution or makefile in the root FlatFour directory. You can load it and build as you normally would.

Folks using the makefiles should note that it builds the debug binaries by default. To get a set of release binaries, set the CONFIG variable at the command line (case sensitive).

$ make CONFIG=Release

Running FlatFour.Host

The framework includes a hosting application called FlatFour.Host. This will eventually be an integral part of the framework, but for now it is just a sample application showing how to use the framework components.

Windows

You can launch FlatFour.Host.exe from the command line or from within the IDE.

Linux

Launch FlatFour.Host.exe from the command line. Depending on your system, you might need to invoke mono explicitly like this:

$ cd Bin/Debug
$ mono FlatFour.Host.exe

MacOS X

The Framework needs to be packaged into an application bundle in order to run properly under MacOS X. Use the supplied "macbundle" script to create a new bundle for the FlatFour.Host executable.

$ sh macbundle.sh
$ open Bin/Debug/FlatFour.Host.app

Subsystems

The framework is composed of multiple subsystems, which handle tasks like graphics, sounds, platform abstraction, and so on.

If you are new to the framework, you probably want to start with the Platform system to create a display window and catch user input. Then take a look at the Graphics system to get something up on that display. Use the Collada system to load scenes from your DCC tools. Subsystems for physics, collision detection, audio, and more are in progress.

I do my best to keep dependencies between subsystems to an absolute minimum. This increases reuse (you can pull out a subsystem and use it in a different project without too much trouble), flexibility (you can pick and choose which subsystems you need for any particular project), and extensibility (a subsystem can be upgraded or replaced without effecting the rest of the system).

Collada

The Collada subsystem loads COLLADA content files created by external tools into the framework.

This subsystem is unusual in that it needs to know about all of the other systems. For instance, it loads geometry from a COLLADA file by directly calling the graphics sytem to create and populate a mesh object. This goes against my philosophy of avoiding subsystem interdependencies.

To get rid of these dependencies, I split the Collada module into several different assemblies, one for each supported subsystem (this is my plan, I am currently lumping everything together to speed up the coding). The code to load graphics-related elements is stored in FlatFour.Graphics.Collada.dll, physics elements go in FlatFour.Physics.Collada.dll, and so on. The main subsystem, contained in FlatFour.Collada.dll, is responsible for finding and loading all of these driver modules at runtime and handing off elements when a file is loaded.

Breaking everything up this way buys me several things:

  • I can add new subsystems without needing to modify existing code: just add another *.Collada.dll and it will automatically get picked up and used.
  • I can remove any subsystem that I don't need: just remove the corresponding *.Collada.dll and everything will continue to work.
  • I can remove support for Collada entirely by removing *.Collada.dll, perhaps to replace it with an optimized binary format for a production release. No code needs to be changed.

OpenGL

The Flat Four OpenGL component provides a common, simple interface to get OpenGL up and running on Windows, MacOS X, and Linux/X11. It is implemented as a fully-managed .NET assembly: one binary, many platforms with no recompiling.

If you are looking for a full-featured graphics system, you might want to check out the Graphics component (hardly full-featured at this point, but I'm getting there) which provides (someday) mesh- and shader-based rendering over both OpenGL and Direct3D.

The OpenGL component is intended for developers who want to write small, lightweight 3D applications that can run on multiple platforms.  It requires the Tao.GL module, which provides a binding to the full API, and a windowing framework such as the Flat Four Platform component (see example), System.Windows.Forms (see example), or any toolkit that can provide a window handle.

The OpenGL component does not include a mapping to the full OpenGL API (use Tao.GL for that) or any type of rendering objects (use the Graphics component instead). Like I said, small and lightweight.

Platform Abstraction

The Flat Four platform component provides basic platform services -- such as window and display management, event handling, and user input -- as a common, simple interface across Windows, MacOS X, and Linux/X11. It is implemented as a set of fully-managed .NET assemblies: one binary runs on many platforms, no recompilation necessary.

using FlatFour.Platform;
 
public class VerySimplePlatformApp
{
   static void Main()
   {
      IPlatformSystem platform = PlatformSystem.Start();
      IPlatformDisplay display = platform.OpenDisplay("My Display", 640, 480);
      platform.RunEventLoop();
      platform.Dispose();
   }
}

The Platform System is developed and delivered as part of the Flat Four Framework (coming soon :). You can use it independently, in conjunction with the framework, or pair it up with FlatFour.OpenGL and Tao.OpenGL for a lightweight, fully-managed 3D development platform.

Status and Plans

In its current state, the available platform services include:

Read on to learn how to get started with the Flat Four platform services.

Getting Started

The Platform Services component is completely standalone, with no dependencies on any other Framework modules. To use it, copy all of the related assemblies (FlatFour.Platform.*) to your project, then add a reference to FlatFour.Platform.dll. The other assemblies (FlatFour.Platform.X11, etc.) contain the platform drivers, which are loaded dynamically at runtime as needed. Don't reference them directly.

Like all Framework subsystems, you initialize the component by calling its Start() method. You release it by disposing the subsystem instance.

using FlatFour.Platform;

public class MyApp
{
   [STAThread]
   public static void Main()
{
      PlatformSystem.Start();

      // ...application code goes here...

      PlatformSystem.Instance.Dispose();
   }
}

Displays

Display windows are created using the OpenDisplay() method. In order to see the display, you must also run an application event loop.

// Create a display with a 640x480 pixel client area named "My Display"
IPlatformSystem ps = PlatformSystem.Instance;
IPlatformDisplay display = ps.OpenDisplay("My Display", 640, 480);

ps.RunEventLoop();

Events

Displays expose several events (well, just one for now).

Resized

The resized event is raised when the size of the display client area has changed.

void OnResized(object sender, EventArgs e)
{
   IPlatformDisplay display = (IPlatformDisplay)sender;
   int newWidth = display.Width;
   int newHeight = display.Height;
}
 
display.Resized += OnResized;

Event Loop

The application event loop is started by calling RunEventLoop(). This method will not return until the QuitEventLoop() method is called, or when the last display window has been closed (need links to the API documentation).

PlatformSystem.Instance.RunEventLoop();

Idle Event

When the event loop is running, an idle event handler is fired repeatedly when there are no events to process.

PlatformService.Instance.Idle += new IdleEventHandler(OnIdle);

void OnIdle()
{
   // do update work...
}

Scene Management

This is area is a big question mark for me. I've tried implementing it in several different ways and none have felt entirely "right".

The scene manager is the component that collects all of the "stuff" that exists in the world. I like to think of the concept of a "world" which contains "actors". Each actor has "behaviors" associated with it that allow it to appear in the world, move around, and interact with other actors.

Ideally the scene system would be just another loosely coupled component, wired to the other subsystems via data. In practice, the subsystems always seem to end up needing to know about the scene system. While it might be a lack of discipline on my part, I find the scene management, serialization, and object model all tend to bleed together.

In the past I've used a composition system for actors, where each behavior is represented as a separate class. A "visual" behavior adds a visual representation (a 3D mesh) for the actor to the scene. A "shape" behavior adds a collision shell, allowing the actor to detect interactions with other actors. A "physics" behavior allows the actor to fall under gravity and bounce around in a realistic way.

In this system you don't know what behaviors an actor contains so you can't make direct method calls against it. Instead, you send a "message" which gets routed to one or more behaviors. Behaviors send messages whenever they detect an event, such as contact with another actor, or a health gauge ticking down.

Here are some design challenges for a good scene system. I realize that my terminology reflects a certain type of implementation; I do try to look past that and think out of the box.

  • How is an actor assembled? Can it be built by a designer using a visual tool, or is coding necessary?
  • How do the parts communicate with each other? Consider an actor with a visual, a shape, and associated physics. How is the physics notified of a collision? How does the visual know where to draw?
  • If a message-like system is used, how are message sinks detected and messages dispatched?
  • Can a behavior be "extended" by another behavior (see the WeaponCoolDown example below)?
  • How would behavior state by propagated to a UI? Consider a player health gauge.
  • How are actors queried? Such as "give me all actors within 10m of me" for a radar display.
  • How does a behavior add or remove actors from the scene?
  • How do behaviors find and monitor other actors? Consider an AI behavior that needs to follow another actor. How does it hold the reference? What if the other actor dies or leaves the scene?
  • How are multibody systems represented? Consider a car, with wheels and suspension parts, connected but otherwise independent. How does the logic find and manipulate (move, spawn, etc.) such an actor?
  • How are actors updated? Is update ordering important (and how is it maintained if it is)? Is it possible to extend the updating process to a multicore approach?
  • How do you add a new subsystem? Is it necessary to add code to the scene system, or can it be adapted by data?
  • How are scenes saved and reloaded? Can scene loading by streamed?
  • How would a save game feature work?
  • Does the behavior system enable (or at least not prevent) networking?

Some more thoughts on the subject.

Would a dynamic language, like Ruby or Python (or Scala), make things easier or more effective?

What about taking a more functional approach (Matt Hopkins), where an actor stores "properties" that are operated on by "actions" or "events"? The properties use well-known names to allowed them to be shared across code components.

The subsystems should all be maintaining their own internal state. The graphics components manages the collection of visuals, the physics components manages the collection of rigid bodies. If you want to know if something is visible, you query the graphics component, not the scene. This enables optimized data structures for each system and reduces the coupling with the scene system.

WeaponCoolDown

Scala mixins present an almost perfect model for a composition system. Unfortunately they are a compile-time feature, preventing (as far as I can tell) data-driven actors. The following is a bit of Scala code that demonstrates. Weapon is a mixin trait that contains a fire() action. WeaponCoolDown is another trait that modifies the behavior of this action. This simple example could be done in other ways, so try to think bigger. The basic idea here is: multiple inheritance done right.

trait Weapon {
   def fire = {  ... }
}

trait WeaponCoolDown with Weapon {
   var timeToFire = 0.0f;
   def update(dt: float) = {
      timeToFire -= dt;
      super.update(dt);
   }
}

Editor

I want the framework to include a visual scene editor. I'm not going to go gung-ho trying to define features for it. I'd like to see it get developed in the same fashion as the framework itself: as a small, extensible core extended by community-developed features.

There are two approaches I can use to develop an editor. The first, more obvious one is to create one mostly from scratch. I can use the Mono.Addins component to provide the extensibility, and maybe Managed.Windows.Forms for the UI toolkit if it progresses quickly enough. I'd use the framework itself to handle rendering, picking, and so on.

Another idea is to use Python for .NET to plug the framework into Blender. Blender already provides most of the scene assembly features I need, all I would really need to add (I think) is the ability to associate behaviors with Blender nodes and assign their properties.

This isn't an either-or situation, maybe both get developed.

Model File Format

COLLADA is the closest thing to a standard file format, directly supported by just about everyone. The downside is that files will be big and load times will probably be slow.  There is a runtime format in the works (I can't seem to find the link right now) but it is not done yet. If I write my own parser I could support binary data stored in external files via an href attribute or similar. I imagine the runtime format will use something similar to this anyway.

I could also use a simple format (like Wavefront) to get up and running, and then switch to the COLLADA runtime format when it becomes public.

If nothing else comes along, the .x format of Direct3D might not be a bad standard to use.

Links

At some point I will set up a proper resource list; for now this will have to do.