Attributes

Attributes are objects that have a name and a value. The value can be of any type. By default all of the common types (int, float, string, Vector3, etc) can be selected within the attribute dropdown box. New types can be added with the Unit Options window. The Unit Options window can be accessed from go to Tools -> Opsive -> Unit Options.

The Attribute class can also be extended in order to change the expression parsing logic. After you have created a new class make sure you add that class to the Unit Options editor. The editor will automatically detect classes that inherit the Attribute<T> class and you’ll be able to choose it like any other type in the dropdown.

Attributes can get their value using three different variants:

  • Inherit: The attribute will get its value from its parent attribute. The parent attribute is defined based on the object attached to it. This table shows the inheritance relations:
Child Attribute Attached ToParent Attribute Attached To
ItemDefault Item
Default ItemDefault Item of Item Definition parent
or
Item Category (Item Attributes)
Item DefinitionItem Definition parent
or
Item Category (Item Definition attributes)
Item CategoriesItem Category (first parent containing attribute)
or
Null (or type default)
  • Override: The attribute gets its value directly from itself.
  • Modify: The attribute evaluates the modify expression and gets a resulting value.

Modify Variant

If your attribute variant is set to Modify then a Modify Expression field will appear. This expression works with the following attribute types:

  • Int
  • Float
  • String

It is impossible to make a modify expression evaluator that works for all types. Therefore you must create your own Attribute Type if you wish to add a custom modify expression parser and evaluator. For consistency we recommend that you create your own modified expression parsers using the same syntax as we do.

In some cases, it makes more sense simply making a custom function to compute a value rather than using a “Modify” attribute. This is especially true when taking external values into account. Here are some ideas:

1) Make a static utility class with functions that can compute your values by combining multiple attributes and external values

2) Take the option above a step forward by using extension methods

3) Make a custom class that contains all functions with an enum and a switch to select the function to use. And set it as your Attribute type

4) Make a scriptableObject with a virtual function that computes your value. Then make subclasses to compute values with any logic you want. Create one or more assets for each subclass. Then assign the relevant one to your item as an Attribute, allowing each item to have a custom logic. (Pass in your item as a parameter of the function to easily get the other attribute values)

Modify Expression Syntax

The Modify Expression is very powerful tool as it lets you grab values of other attributes as well as perform logic when retrieving the attribute value.

Important: Let’s consider the case where you have Attribute A1 which inherits from Attribute A2. A2 is set to the “Modify” variant type. In this situation the attributes that are part of the expression will be picked from the perspective of the attribute that first requested the value, in this case A1. If the values should be picked from the perspective of the attribute holding the expression, in this case A2, one can add the ‘$’ character in front of the attribute name.  

As the expression is a string you can create your very own syntax by writing your own parser. But for consistency it is recommended to use the syntax below to get other attribute values. 

  • [myOtherAttribute]: Gets the value of an attribute named “myOtherAttribute”.
  • <Override>: Returns the override value.
  • <Inherited>: Returns the inherited value.
  • $: The dollar symbol can be added in front of any “get value”. It will get the value of the attribute from the perspective of the attribute holding the expression.
    Example: “$[myOtherAttribute]” or “$<Inherited>”.

Using Attributes on Item Objects

Most people will use Item attributes to affect Item Objects in certain ways. For example affecting damage amount using a float “Damage” attribute.
The easiest way to achieve this is to add an Item Binding component next to the Item Object on your game object prefab. It allows you to bind any attribute on your item to any component property in your prefab.
When needing more control over what happens when an Item is bound to an Item Object, you may listen to the event. This can be useful to spawn the correct Model/Sprite, play an audio clip, etc… which could all be defined as attributes on the Item, Item Definition or Item Category.

Get/Set Attribute API

Attributes are found in Item Categories, Item Definitions and Items. The methods used to get and set attribute values is similar for each object type. Here are examples on how to get attributes on an item:

// Retrieves an attack attribute value by getting its attribute.
var attackAttribute = item.GetAttribute<Attribute<int>>("Attack");
if (attackAttribute != null) {
    // The real attack value.
    var myAttack = attackAttribute.GetValue();
    // The override attack value.
    var myAttackOverride = attackAttribute.OverrideValue;
    // The inherited attack value.
    var myAttackInherited = attackAttribute.GetInheritedValue();
}

// Retrieves an icon attribute value.
if (item.TryGetAttributeValue("Icon", out Sprite myIcon)) {
    // myIcon exists.
}

// Loops through the Item attributes.
var indludeItemDefinitionAttributes = true;
var includeItemCategoryAttributes = true;
var attributeCount = item.GetAttributeCount(indludeItemDefinitionAttributes,includeItemCategoryAttributes);
for (int i = 0; i < attributeCount; i++) {
    var attribute = item.GetAttributeAt(i, indludeItemDefinitionAttributes,includeItemCategoryAttributes);
    // We don't know the type of the attributes but we can get their values as objects.
    Debug.Log(attribute.GetValueAsObject());
}

You are not allowed to create new attributes on the item as the Item Category will define which attributes the item must have.
Important: You are allowed to set attribute values at runtime ONLY when the item is mutable. You can set an item as mutable from its Item Category.

if (!item.IsMutable) {
    // Immutable items cannot have their attribute set.
}

// Retrieves an attack attribute.
var attackAttribute = item.GetAttribute<Attribute<int>>("Attack");
if (attackAttribute != null) {
    // The variant type can be changed.
    attackAttribute.SetVariantType(VariantType.Inherit);
    // Sets a new attack of 10. This will automatically set the Variant Type to Override.
    attackAttribute.SetOverrideValue(10);
    // Sets a new modify expression. This will automatically set the Variant Type to Modify and mark it as pre-evaluated.
    attackAttribute.SetModifyExpression("<Inherited> + 5",true);
}

// Be careful when using classes.
var itemSlotsAttribute = item.GetAttribute<Attribute<ItemAmounts>>("Slots");
if (itemSlotsAttribute != null) {
    var itemSlots = itemSlotsAttribute.GetValue();
    // Do not change the itemSlots directly as it might be inherited!
    // The overriding value should be checked first.
    if (itemSlots == itemSlotsAttribute.OverrideValue) {
        // The itemSlots can be modified.
        itemSlots.Add(newItem);
    } else {
        // The itemSlots is inherited form another object. The content should first be copied.
        itemSlots = new ItemAmounts(itemSlots);
        itemSlots.Add(newItem);
        // Do not forget to set the new itemSlots value to the attribute.
        itemSlotsAttribute.SetOverrideValue(itemSlots);
    }
}