Objects which have stats should implement IHaveStats. This, and similar interfaces such as IHaveConditions and IHaveQuests each themselves implement IUseGameModules which in turn, implements IHaveGameId


A GameId is a unique identifier for runtime objects. Objects like Stat and Condition, which are Game Module Scriptable Objects, have a unique Uid() value. However, a GameConditionList may have multiple instances of a GameCondition, all of which have the same Uid(). The Uid() on a "Game" version of a Game Module type is the same as the Scriptable Object version.
Each of these instances will have a unique GameId(), which enables specific objects to be handled in cases where there are multiple similar objects.
Here is some sample code you can use, which will create a random value when the _gameId is empty. Afterward, it will return the value that was created.
[SerializeField] private string _gameId;
public virtual string GameId(bool forceNew = false) =>
string.IsNullOrWhiteSpace(_gameId) || forceNew
? _gameId = Guid.NewGuid().ToString()
: _gameId;
Once a GameID() value is set, something that generally will happen at runtime, it should never change. The value will be saved along with the rest of the data.
Each GameID() must be unique. If you know you have only one instance of an object, and you will never have another, you can explictly set the value to something more human readable.
[SerializeField] private string _gameId = "Human Readable Id";
public virtual string GameId(bool forceNew = false) => _gameId;
The GameId() is used in the Blackboard module for BlackboardNote and BlackboardEvent objects to identify the object mentioned in the note or event.
Stats can automatically post their values to the Blackboard, and they use the randomly generated GameId() string in that context. Other modules use the value simialrly.


The various modules utlize an "Owner" concept to automatically operate. The "Owner Name" is a string, often something human readable, but may also be the GameId() value on the object. In some cases, this method may not be used at all.
// Hard coded owner name
public string GetOwnerName() => "Game Data";
// GetOwnerName() used in debug logs
private void PlayerGotHit()
var target = gameData.RandomPlayer;
foreach (var condition in onHitConditions)
Debug.Log($"Will give condition {condition.objectName} to {target.GetOwnerName()}}"); target.AddCondition(condition, this);
The SetOwner() method is important, and is used to ensure that various Game Module Lists know who their owner is.
In the example below, from the Party Based RPG demo game, the SetOwner() method calls the same method on the two Game Module lists, a GameStatList and GameQuestList. The methods on those lists will ensure all items in the lists also have the GameData objet as their owner.
// GameData.cs
public void SetOwner(object newOwner)
Calling SetOwner(), which then calls the same method on the various lists, should be one on during StartActions(), something to run after loading data. GameData.cs does this during StartActions(), which is called right after LoadState().