π Observables
What are observables
Observables are values that let you subscribe to changes being made. It returns the old and the new value so you can apply for example a text animation when the amount of coins in the player inventory changes.
There are 3 kinds of observables:
ObservableValue<T>
ObservableCollection<T>
ObservableDictionary<TKey, TValue>
The values get serialized in the same way normal values would, so they do not have an extra storage overhead, but do have a slight performance overhead.
The build-in SaveData is using Observables for any custom systems to subscribe to, and it is advised to do the same as it gives the added benefits to listen to changes from anywhere.
ObservableValue
These are the most common use case and can be used for any systems to listen to changes.
public struct ObservableValue<T>
{
public T Value;
public bool HasValue;
public event ValueChangedEventHandler<ObservableValue<T>, T, T> ValueChanged;
}
Subscribe to the ValueChanged event and get a result of the ObservableValue itself, the old and the new value, like this example:
public ObservableValue<int> Coins;
protected void Start()
{
Coins.ValueChanged += OnCoinValueChanged;
}
private void OnCoinValueChanged(ObservableValue<int> sender, int oldValue, int newValue)
{
}
Reading the ObservableValue
does not need a cast and can directly be used. However when writing it you will have to use the .Value
:
ObservableValue<int> lives = 10;
int livesRemaining = lives;
lives.Value -= 2;
ObservableCollection
Collection is basically a list with the added option to Move
an item from one spot to another to reorganize the list.
public struct ObservableCollection<T> : Collection<T>
{
// All functionality from Collection<T>, plus the Move method:
public void Move(int oldIndex, int newIndex);
public event CollectionChangedEventHandler<ObservableCollection<T>, NotifyCollectionChangedEventArgs<T>> CollectionChanged;
}
public enum NotifyCollectionChangedAction
{
Add,
Remove,
Change,
Reset
}
public class NotifyCollectionChangedEventArgs<T> : EventArgs
{
public NotifyCollectionChangedAction Action;
public int Index;
public T OldValue;
public T NewValue;
}
This example shows how to subscribe to a ObservableCollection
and how to use the return arguments from the callback:
public ObservableCollection<Vector3> ItemLocations;
protected void Start()
{
ItemLocations.CollectionChanged += OnItemLocationsChanged;
}
private void OnItemLocationsChanged(ObservableCollection<Vector3> sender, NotifyCollectionChangedEventArgs<Guid> e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
int itemAddedIndex = e.Index;
Vector3 addedValue = e.NewValue;
break;
case NotifyCollectionChangedAction.Remove:
int itemRemovedIndex = e.Index;
Vector3 removedValue = e.OldValue;
break;
case NotifyCollectionChangedAction.Change:
int itemChangedIndex = e.Index;
Vector3 previousValue = e.OldValue;
Vector3 newValue = e.NewValue;
break;
case NotifyCollectionChangedAction.Clear:
// The list has been cleared and is now empty.
break;
}
}
ObservableDictionary
The ObservableDictionary
is the same as the normal Dictionary
, with the added option to subscribe to changes.
public struct ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
// All functionality from Dictionary<TKey, TValue>
public event DictionaryChangedEventHandler<ObservableDictionary<TKey, TValue>, NotifyDictionaryChangedEventArgs<TKey, TValue>> DictionaryChanged;
}
public enum NotifyDictionaryChangedAction
{
Add,
Remove,
Change,
Reset
}
public class NotifyDictionaryChangedEventArgs<TKey, TValue> : EventArgs
{
public NotifyDictionaryChangedAction Action;
public TKey Key;
public TValue OldValue;
public TValue NewValue;
}
This example shows how to subscribe to a ObservableDictionary
and how to use the return arguments from the callback:
public ObservableDictionary<Guid, int> CollectedCharacters;
protected void Start()
{
CollectedCharacters.DictionaryChanged += OnCollectedCharactersChanged;
}
private void OnCollectedCharactersChanged(ObservableDictionary<Guid, int> sender, NotifyDictionaryChangedEventArgs<TKey, TValue> e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Guid itemAddedKey = e.Key;
int addedValue = e.NewValue;
break;
case NotifyCollectionChangedAction.Remove:
Guid itemRemovedKey = e.Key;
int removedValue = e.OldValue;
break;
case NotifyCollectionChangedAction.Change:
Guid itemChangedKey = e.Key;
int previousValue = e.OldValue;
int newValue = e.NewValue;
break;
case NotifyCollectionChangedAction.Clear:
// The dictionary has been cleared and is now empty.
break;
}
}