Click or drag to resize
DigitalRuneGame Object Properties
Game object properties

Game object properties (also called ports, signals, or attributes) extend the functionality of common language runtime (CLR) properties. The game object properties are represented by the type GamePropertyT. The game object properties of a particular game object are listed in GameObjectProperties.

Creating a game object property

A new game object property can be defined by calling the static method GameObjectCreatePropertyT(String, String, String, T). For example:

C#
GamePropertyMetadata<float> metadata = GameObject.CreateProperty<float>(
  "X",                       // The name of the property.
  "Common",                  // The category (for use in game editors).
  "Defines the x-position.", // The description.
  0.0f);                     // The default value.

Once this method is called, the game object property is globally defined. If this method is called a second time with the same properties, it will not create a new game object property. Instead it will only return the metadata of the previous call. There cannot be multiple game object properties with the same name and type!

The metadata of any game object property can be queried by calling GameObjectGetPropertyMetadataT(String) where game object properties can be identified by

  • the automatically assigned ID stored in the metadata (see Id), or
  • by name and value type.

Either of these ways uniquely identifies a game object property.

Reading a game object property

Once a game object property is defined it can be set and queried on any game object. There are multiple ways to get the value of a game object property:

C#
var gameObject = new GameObject();

// Method #1: Get value by name from game object.
float x = gameObject.GetValue<float>("X");

// Method #2: Get value by metadata from game object.
x = gameObject.GetValue<float>(metadata);

// Method #3: Get value by ID from game object.
x = gameObject.GetValue<float>(metadata.Id);

// Method #4: First get GameProperty<T> from game object. 
//            Then get the value from the GameProperty<T>.
GameProperty<float> property = gameObject.Properties.Get<float>("X");
x = property.Value;

Note that the game object property will return the default value as defined in the metadata if the property was never set on the object. (If the game object has template, the property will return the value of the template. See Templates.)

Writing a game object property

Setting the value of a game object property is similar:

C#
var gameObject = new GameObject();

// Method #1: Set value by name.
gameObject.SetValue("X", 10.0f);

// Method #2: Set value by metadata.
gameObject.SetValue(metadata, 10.0f);

// Method #3: Set value by ID.
gameObject.SetValue(metadata.Id, 10.0f);

// Method #4: First get GameProperty<T> from game object. 
//            Then set the value of the GameProperty<T>.
GameProperty<float> property = gameObject.Properties.Get<float>("X");
property.Value = 10.0f;

The above described methods for reading or writing game object properties are basically equivalent.

In the last method a GamePropertyT is acquired. This object is a handle to the property of a particular game object. Other objects can store a GamePropertyT directly if they regularly need to read or change the property of a game object. When the object stores the GamePropertyT it is not necessary to also keep a reference to the game object that owns the property.

Wrapping a game object property

In some cases it is convenient to wrap a game object property using a standard CLR property. Here is an example that shows how to define a game object property in a class and wrap it using a CLR property:

C#
public class MyGameObject : GameObject
{
  /// <summary> 
  /// The ID of the <see cref="X"/> game object property.
  /// </summary>
  public static readonly int XPropertyId = 
    CreateProperty<float>("X", "Common", "Defines the x-position.", 0.0f).Id;

  /// <summary>
  /// Gets or sets the x-position. This is a game object property.
  /// </summary>
  /// <value>The x-position.</value>
  public float X
  {
    get { return GetValue<float>(XPropertyId); }
    set { SetValue(XPropertyId, value); }
  }

  ...
}

The game object property can then be used like any other CLR property. (This pattern is extensively used in the UIControls of the DigitalRune Game UI library.

Value coercion

A GamePropertyT has a Changing event which is raised when the value is about to be changed. This event can be used for value coercion. The GamePropertyEventArgsT passed to the event handler contain the current value of the property (see OldValue) and the new value which is about to be set (see NewValue). The event handler can override the new value by setting the CoercedValue. For example:

C#
var valueProperty = slider1.Properties.Get<float>("Value");
valueProperty.Changing += (s, e) =>
{
  if (e.CoercedValue < slider2.Value)
    e.CoercedValue = slider2.Value;
  if (e.CoercedValue > slider3.Value)
    e.CoercedValue = slider3.Value;
};

Change notification

A GamePropertyT has a Changed which is raised when the value of the property has changed. Other objects can attach an event handler to this event to listen for changes. The event handler will receive a GamePropertyEventArgsT which contains the previous value of the property (see OldValue) and the new value (see NewValue).

Caution note Caution

An event handler of a Changed event of a property should not change the value of the property. This leads to nested Changed event calls and might have unexpected results. Value coercion can be done in the Changing event using the CoercedValue property of the GamePropertyEventArgsT.

Connecting game object properties

Game object properties have a property Change, which is an event handler. This event handler can be connected with the Changed event of another property. This is very useful to connect game object properties of different game objects. Changing one property automatically updates the connected properties. Here is an example:

C#
// mySlider is game object that represents a slider GUI control.
// myAvatar is a game object that represents the avatar of the player.
// Connect the "Value" game object property of the slider with the "Height" game object 
// property of the avatar. This way the slider automatically changes the avatar height.
GameProperty<float> sliderValue = mySlider.Properties.Get<float>("Value");
GameProperty<float> avatarHeight = myAvatar.Get<float>("Height");
sliderValue.Changed += avatarHeight.Change;

// Uncomment following line to create a two-way connection.
//avatarHeight.Changed += sliderValue.Change;

Limitations

Read-only properties are not supported.

Mismatched property value types

Properties are identified by their name and type. That means that a game object can have two or more properties with the same name, as long as the types are different. It is important to match the value types exactly when setting property values. Following examples show that the type of the properties matter:

C#
gameObject.SetValue("X", 10);                   // Setting the integer "X" property.
gameObject.SetValue("X", 20.0f);                // Setting the float "X" property
int intX = gameObject.GetValue<int>("X");       // Returns 10!
float floatX = gameObject.GetValue<float>("X"); // Returns 20.0f!
Best practices

The Description of a game object property should use a general wording. For example, the description of the bool "IsEnabled" property could read: "Indicates whether this object is enabled."

It should not read: "Indicates whether this GuiControl is enabled."

Reason: All game objects that want to have a bool "IsEnabled" property (e.g. the "PointLight" and the "GuiButton") share the same property metadata. There can only be one description that should fit all possible uses of the bool "IsEnabled" property.

Performance considerations

Accessing game object properties is not as fast as accessing CLR fields or properties - although the overhead is neglectable in most case. Here are some tips to minimize the performance impact.