Creating an Actor Tutorial, Part 1 – Overview and Concepts
This tutorial teaches you all you need to know to make your own Actors. By following this 3 part tutorial, you will learn how to create a waving flag that you can use right in the editor. You will learn how to create a complex tessellated Actor, expose its size, speed, and height properties via an ActorProxy, and even make your flag accessible to the editor.
This tutorial assumes you have already worked with STAGE. If not, please see the ‘Getting to Know the Editor’ tutorial. Note - this tutorial uses the word ‘Game’, ‘Simulation’, and ‘Training Application’ interchangeably.
Part 1 – Actors, Properties, and Drawables, Oh My!
Below are 3 key concepts you need to know.
Actor – An Actor is any game object that has properties you want to work with in the Editor. It subclasses BaseActorObject. In part 2 of this tutorial, you will create your first Actor called ‘TesselationActor’. These used to be called ActorProxies, but now the focus is on the BaseActorObject being the basic simulation object.
Drawable – Actors can have a Drawable, which is the visual for the actor. At the time of writing this, many of the Drawables in examples in delta3d call this class an Actor, but they derive dtCore::DeltaDrawable. These are being referred to simple as Drawables now.
ActorProperties – ActorProperties are the properties of an Actor that show up in the PropertyEditor. Actor properties allow level designers to tweak and change how an object works. The more properties you expose, the more useful your object becomes. When you expose numerous important properties for your object, you allow level designers to take full advantage of your Actors without changing your code. In part 2, you will expose Actor properties for ‘Width’, ‘Height’, ‘Number of Steps’, ‘Period’, ‘Amplitude’, ‘Phase’, and ‘Texture’. By changing the values of these properties, you can change the size of your flag, make it move faster, give it more ripples, or even change the texture. The following picture shows some of the properties in the editor.
Figure 1 – Tesselation Actor Properties
Now that you know the basics about Actors and Properties, there is one final step to address. Since the editor can’t magically find your Actor classes, we need to tell it about our new object. What you need to do is put your Actor into a registry that the editor can load from a shared library or DLL. Once you create this library you will be able to import it directly into the editor. As you’ll see in part 3 of this tutorial, it takes about 5 lines of actual code to do this. A registry can declare many actor types.
OK. So, let’s sum up! To create any new Actor for STAGE, you will need to do the following steps:
1) Create an Actor
2) Create a Drawable or use an existing one
3) Create an Actor Registry if you don't already have one to add it to.
4) Add your Actor to the Library
Now, you know enough about the basic architecture of STAGE to get started.
If you just want to know how to do something by example, it’s time to move on to Part 2 of the tutorial.
However, if you’re the technical type and really want to know what’s going on, then you’ll be happy to know that we’ve got some great information ahead. As it happens, the Dynamic Actor Layer (DAL) was thoroughly designed before we ever wrote a line of code. Below is some of the detailed design material used in the construction of the DAL.
2. Dynamic Actor Layer (DAL): part of dtCore
The Dynamic Actor Layer (DAL) provides a flexible, non-intrusive mechanism for generically exposing the properties of game actors in C++. The two primary components of this design are BaseActorObjects, PropertyContainers, and ActorProperties. The BaseActorObject (actor) is a subclass of dtCore::PropertyContainer and it provides some features needed to make it work dynamically in the system. The ActorProperty exposes the data for a single accessor. It can also expose things more complicated, like an array, a property container, or another actor. The property container knows about its properties and the properties know how to access their data. These two components are used to generically expose all underlying data in a query-able and generic fashion. Use of these data driven components promotes data encapsulation and code re-usability.
Figure 2 - Overview
The Dynamic Actor Layer and editor architecture overview is noted in Figure 2. The main purpose of the Editor is to allow for the manipulation of actors to create a simulation. To support this, the DAL exposes a generic template allowing STAGE to work with actors as generic objects. The core of this concept is that each actor should be treated generically using only its name, id, type, and a collection of properties. A detailed description of the components of the DAL follows.
3. Actor (BaseActorObject)
As mentioned above, Actors are any objects in your training application that have behavior and need to be manipulated in STAGE. It can represent system components, drawable objects, and other simulation objects.
Figure 3 - ActorProxy Class Diagram
The following is a description of the classes portrayed by the class diagram in Figure 3.
This is the base class for all scene objects in the delta3d. They can be arranged in a simple scene graph and have some hooks for dealing rendering related tasks.
This is an example of a user built Actor class. Some of these will be built internally as part of the Editor, but many of these will be built by end developers and may be known only to their own particular application. Actor classes can be represent much anything. They can have lighting, articulated parts, movement, AI, meshes, sounds, triggers, and anything else that you can do with Delta3D. A Game Actor (dtGame::GameActorProxy) can aggregate features using dtGame::ActorComponents, and can also working in the messaging system of the GameManager. They can have a DeltaDrawable so they can draw themselves in anyway they see fit within the Delta3D architecture. They can have many or zero properties, can have references to shared resources, and can be anything from a square billboard to a full blown landscape. The plan is to eventually make the DeltaDrawable just another dtGame::ActorComponent.
This is the DeltaDrawable that actually draws the truck in this examples. Many of the properties on the Actor set values on this drawable.
As stated above, the ActorProperty class provides a getter/setter mechanism for accessing properties of a class similar to the Java beans programming paradigm. Figure 4 depicts a more detailed view of the ActorProperty class including a subset of the property classes for a particular data type.
Figure 4 - ActorProperty Class Diagram
The getter and setter portions of the ActorProperty class are Functors (Function Objects) which reference a getter and a setter method on a given property class. Therefore, when an ActorProperty is created, it knows how to store and retrieve the data it represents. The ActorProperty class is also used to pass information about the properties of an Actor up to the Editor. It has a name, a display name, a description, and a type enumeration. The property class may also have a list of enumerations if the property is itself an enumeration. For example, a property of truck may be speed and it may support Slow, Medium, and Fast or a light may have a type property with settings of Directional, Strobe, Pulse, Omni, etc…
Actor libraries are distributable components that serve to package groups of related actors and actor proxies. Actor libraries are dynamic libraries of C++ code that are loaded into the editor or other application. Figure 5 shows an overview class diagram of the Actor Library subsystem. A detailed description of each component is given below.
Figure 5 - Dynamic Actor Layer Components
This class is the main class in the Library Manager. It has a list of the libraries that are registered with the Manager as well as a list of which Actor Types each of the libraries can create. It is also the main vehicle for creating a new Actor Proxy. The Manager uses the singleton pattern.
This is the base class that developers extend to build their own registry. The most important behavior in the registry is the ability to take an Actor Type and create a new Actor. A registry is responsible for knowing which Actor types it can build. Since the Editor doesn’t know anything about object classes, the registry is responsible for figuring it out. The registry base class also exposes an object factory which provides most, if not all, of the ActorPluginRegistry’s Actor Type to Actor mapping capabilities.
This is a 3rd party implementation of the Registry. The developer will need to create and track which Actor Types it understands as well as be able to take an Actor Type and create a new Actor Proxy. This is likely a very simple class which creates and registers its actor types with the factory so that they are available at run-time.
This is a simple data class that has information describing a particular Actor type. There will be one Actor Type for each unique Actor class like ‘BouncyTruck’ or ‘AgressiveSoldier’. For each actor class, there must be an Actor Type with a name, a category, and a description. The category is of particular note because it is used to visually sort classes in the editor. It follows a hierarchical dot notation (ex. “Vehicles.Trucks.NeatTrucks”) used to group related classes in some of the UI panels. It can also be used to search for objects.
6. Project / Map
The project context is basically the “home” directory of your map. It is responsible for storing resources imported into the Editor; such as static meshes, particle systems, textures, and sounds. All of the imported actors in a map are stored and referenced relative to the project context of the map. The details of the Project and Map class interactions are beyond the scope of this tutorial; however Figure 6 shows a class diagram which depicts the basic interactions available to the Project and Map classes.
Figure 6 - Project - Map Class Diagram
This concludes the overview of the architecture of STAGE. If you followed all of this, then you are more than ready to go on to Part 2 of this tutorial.