Thursday, June 28, 2012

Before creating posts discussing specific techniques, I feel an overview of the general design of SMPL will be useful. I won't bother with creating UML diagrams or anything that involved, but the general design is meant to make scripting intuitive and flexible. This is necessary because SMPL doesn't have any game-specific logic built into it; all logic is expected to be managed by Lua content scripts.

When a game using the engine is launched, after some basic initialization, SMPL loads its intial 'map' as defined in its startup configuration file. A map is an XML file describing assets such as interface scripts, the logic script, sounds, or lights. The logic script referenced by the map is the Lua script the engine communicates with to provide functionality. A map's logic script receives notifications of 4 events: OnLoad(), OnFrameMove(), OnFrameRender(), and OnUnload(). The way the logic script responds to these four events dictates how a game built on SMPL behaves.

  • The OnLoad() function is where the high-level objects, input methodologies, and input mappings are defined for the map. This function is called once when the engine is prompted, either by its startup routine or by an explicit call via a logic script, to load a new map. It is in the OnLoad() function that a script is expected to generate the bulk of its agents. Agents are generic scripting constructs used to communicate with several low-level objects in the engine. From the script's perspective, there are only a handful of properties and behaviors that are of any consequence for a particular object heedless of its exact type, so the precise nature of these objects is abstracted away behind a container called an agent. Agents can be fed user input, translated or rotated in the world, or have various internal properties common across all basic object types altered. SMPL allows scripts to create agents based on XML prototypes. Prototypes are references to other XML files describing a collection of assets, allowing multiple scripts to reference the same assets as described in one file, reducing redundancy.


  • The OnFrameMove() function is called prior to rendering every frame. Logic scripts may request the current input buffer for a particular participant to process as necessary. A participant is exactly as its name implies: it is a logical representation of a player (be it local, networked, or even virtual as would be the case for a computer-controlled player). There are a finite number of input channels supported by the engine, which are bound to participants though input sockets. A single input socket can receive input from multiple channels, and a single participant can have multiple sockets bound to it. This allows for complex input scenarios dynamically bound and unbound at runtime as dictated by the needs of the game. The OnFrameMove() function is also where per-frame logic is expected to be performed, though there are necessarily some caveats: the order in which calls to the engine are executed is linear within a script, but the results of these calls and the order scripts are allowed to execute is not guaranteed to follow a fixed order. The only guarantee of logic executed during OnFrameMove() is that all effects will have been resolved before rendering for each frame.


  • OnFrameRender() is called after SMPL completes rendering all scene objects to the backbuffer. All logic scripts are allowed to render before any UI scripts. Engine calls are not prohibited from within this function, but the effects will occur on the subsequent frame. The idea here is to allow scripts some measure of freedom to control the look of the final rendered output if desired.


  • OnUnload() is called when a map is being disposed, either because the game is shutting down or because a new map has been requested for load. The intent of this function is to allow scripts to save any persistent information to file.

These four functions are the only means by which the engine can communicate with scripts, but scripts can speak to the engine though a library of function calls. This is similar to the design implementation in many other games, though admittedly World of Warcraft's UI system provided the archetype for general scripting in SMPL. Intentionally limiting the number of entry points makes maintenance of the system simpler and enforces a cleaner design. Lower-level events like object opacity changes, animation sequence completion, and audio playback completion are handled implicitly though agent properties as opposed to explicitly with callbacks or other messaging schemes. Scripts are expected to handle all game logic, so events such as these are simply noted by the engine but never handled.

Things beyond the scope of game logic, such as texture loading, mesh loading, file I/O, etc. are handled by the engine and cannot be controlled by scripts. A script cannot explicitly call for the loading of a texture, for example, but it can request the creation of an agent based on a prototype that requires a specific texture or the loading of a mesh that defines the use of several textures and material shaders. A script cannot explicitly request the creation of a file with a specific encoding and write arbitrary bits to it, but it can request a Lua table be saved to disk.

The prevailing idea in SMPL's design is to promote a clean separation between implementation details and game rules. Should development on SMPL continue for three or four major revisions, scripts written for version 1 should still behave as expected in version 4 without any modification. Scripters and content creators should not have to consider (within reason) what version of DirectX or any other API the engine is running on; scripts and content should need only follow the rules set forth by the design of the engine itself, nothing else. Hopefully this overview has provided some insight into how I intend for SMPL to satisfy this goal.

Thursday, June 21, 2012

Vulpes Inferi is a telekinetic survival action game being developed by a single developer (that's me) on a proprietary engine. The original production of the game began in 2006 as an after-hours student project at Clayton State University. At the time it was a 2D, physics-based platformer called Reventa. The project never progressed beyond the pre-production and early prototyping phase, and in time the original dev team would graduate and scatter to the winds.

I continued development of the early codebase, iterating on lessons learned though trial and error, implementing new rendering techniques and technologies. This codebase eventually became what I now call the SMPL engine.

The guiding ethos of development on SMPL is to create the sort of platform my team needed in 2006 to be empowered enough to produce the sort of game we envisioned. It should be easy enough to use as to completely eliminate the barrier to entry for aspiring game developers so they can begin creating interesting, compelling experiences from day one. It should allow for a workflow so intuitive that creating experiences with a level of polish very close to that of professional, big-budget studios is simply a matter of talent and imagination instead of technology. It should be forgiving of mistakes, gracefully recovering from errors and making logical assumptions when data is accidentally omitted. In short, it should be SiMPLe.

This blog is intended as a window into development of both SMPL and Vulpes Inferi. Current builds of the game will be available for download, and concept art, work-in-progress screenshots, and additional development materials will be posted as well. Lessons learned articles will make regular appearances, as will posts discussing the technical challenges I encounter and the solutions implemented to overcome them.