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.
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.
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.
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
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
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
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.
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
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
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.
You can launch FlatFour.Host.exe from the command line or from within the IDE.
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
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
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).
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:
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.
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.
In its current state, the available platform services include:
Read on to learn how to get started with the Flat Four platform services.
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();
}
}
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();
Displays expose several events (well, just one for now).
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;
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();
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...
}
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.
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.
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);
}
} 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.
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.
At some point I will set up a proper resource list; for now this will have to do.