Skip to content

Type System

The type system allows ERS to understand the data that is part of a simulation model. Most of the time ERS uses it in the background without requiring users to do anything. But sometimes it may be useful to provide additional data to ERS, or retrieve data from the type system. Some features of ERS require providing the additional data. Adding type information is required to:

  • Allow the entity inspector widget in the ERS Debugger to correctly display the component's name and fields in the user interface. When a component is not registered in the type system, it will only show that an entity has the component, but the data of the component will not be visible.
  • Perform serialization.

TypeInfo and Field

ERS stores the information about types in two objects: TypeInfo ("Type Information") and Field. The first stores the information about a whole type, like a data component. The latter stores information about the fields inside the type.

struct SomeComponent : public Ers::DataComponent // Type "SomeComponent"
{
    int X;  // Field "X"
    bool Y; // Field "Y"
}
struct SomeComponent : IDataComponent // Type "SomeComponent"
{
    public int X;  // Field "X"
    public bool Y; // Field "Y"
}

Not all types are supported by the type system. The table below shows all types that are supported by ERS.

Type C++ C#
32-bit float (Single precision) float float
Boolean bool bool
8-bit unsigned integer uint8_t byte
32-bit signed integer int32_t int
32-bit unsigned integer uint32_t uint
64-bit signed integer int64_t long
64-bit unsigned integer uint64_t ulong
ERS Entity EntityID ulong (often aliased as Entity)
String std::string string
2D Vector Ers::Vector2 Vector2
3D Vector Ers::Vector3 Vector3
4D Vector Ers::Vector4 Vector4
RGBA Color Ers::Color Color

Built-in types in the type system.

Adding type information

Data components

To add type information to a data component, implement the GetTypeInfo() method. Inside the method, register a struct using the TypeRegistry and add each field.

struct StorageBoxComponent : public Ers::DataComponent
{
    Ers::Vector3 Dimensions;
    float Weight;
    EntityID Owner;

    static const Ers::TypeInfo* GetTypeInfo()
    {
        Ers::TypeInfo* typeInfo = Ers::TypeRegistry::RegisterStruct("storage_box");
        typeInfo->AddField("dimensions", Ers::BuiltinType::Vector3, offsetof(StorageBoxComponent, Dimensions));
        typeInfo->AddField("weight", Ers::BuiltinType::Float32, offsetof(StorageBoxComponent, Weight));
        // Third parameter: set the field to be read-only
        typeInfo->AddField("owner", Ers::BuiltinType::Entity, offsetof(StorageBoxComponent, Owner), true);
        return typeInfo;
    }
};

To add type information, use the TypeInfo and Field attributes and set the struct layout to "sequential". If the struct layout is not set to sequential, ERS will not generate the type information.

[TypeInfo("storage_box")]
[StructLayout(LayoutKind.Sequential)]
struct StorageBoxComponent : IDataComponent
{
    [Field("dimensions")]
    public Vector3 Dimensions;
    [Field("weight")]
    public float Weight;
    // Third parameter: set the field to be read-only
    [Field("owner", BuiltinType.Entity, true)]
    public Entity Owner;
}

Fields can be made read-only so they cannot be modified in the entity inspector widget or other UI. This way, it is possible to view the data, but only allow the simulation itself to modify the data.

Note for C#

Notice how (in C#) the attributes are automatically able to determine the types of the fields, except for the entity. This is because entities are aliases of ulong in C#, which makes it so ERS cannot know automatically whether to interpret it as an actual ulong or an entity. Therefore, the field type should always be explicitly defined for entities.

C++ does not have this issue, because there all types are always explicitly defined.

Script behaviors

Adding type information to script behaviors is currently not supported. Serialization of script behaviors is performed via the serialization callback.

Retrieving type information

Type information can be retrieved via TypeRegistry's GetTypeInfo function.

const Ers::TypeInfo vector2TypeInfo = Ers::TypeRegistry::GetTypeInfo(Ers::BuiltinType::Vector2);
const std::string expectedName = "vector2";
assert(vector2TypeInfo.GetName() == expectedName);
assert(vector2TypeInfo.GetSize() == 8);
assert(vector2TypeInfo.FieldCount() == 2);

for (const Field& field : vector2TypeInfo.GetFields())
{
    // ...
}

// Custom types
const Ers::TypeInfo customTypeInfo = Ers::TypeRegistry::RegisterStruct("custom_type");
const uint32_t customTypeId = customTypeInfo.GetID(); // Save for later
// Somewhere else / later
const Ers::TypeInfo* customTypeInfo = Ers::TypeRegistry::GetTypeInfo(customTypeId);
// or
const Ers::TypeInfo* customTypeInfo = Ers::TypeRegistry::GetTypeInfo("custom_type");
TypeInfo vector2TypeInfo = TypeRegistry.GetTypeInfo(BuiltinType.Vector2);
Debug.Assert(vector2TypeInfo.Name == "vector2");
Debug.Assert(vector2TypeInfo.Size == (nuint)8);
Debug.Assert(vector2TypeInfo.FieldCount == (nuint)2);

foreach (Field field in vector2TypeInfo.GetFields())
{
    // ...
}

// Custom types
TypeInfo customType = TypeRegistry.RegisterStruct("custom_type");
uint customTypeId = customType.ID; // Save for later
// Somewhere else / later
TypeInfo? customTypeInfo = TypeRegistry.GetTypeInfo(customTypeId);
// or
TypeInfo? customTypeInfo = TypeRegistry.GetTypeInfo("custom_type");

The GetTypeInfo function returns nullable TypeInfo objects when retrieving custom types, because it may be possible that an invalid ID or name is given.

Was this page helpful?