Skip to content

Instantly share code, notes, and snippets.

@der-hugo
Last active September 6, 2022 11:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save der-hugo/4f676ede0d4a43bc187690ec3269cd9b to your computer and use it in GitHub Desktop.
Save der-hugo/4f676ede0d4a43bc187690ec3269cd9b to your computer and use it in GitHub Desktop.
An observable list for Unity
public abstract class ObservedList { }
[Serializable]
public class ObservedList<T> : ObservedList, IList<T>
{
public delegate void ChangedDelegate(int index, T oldValue, T newValue);
// In a bit of a dirty hack the editor scripts is looking for this field
// with exactly this name "_list"
[SerializeField] private List<T> _list = new List<T>();
// NOTE: I changed the signature to provide a bit more information
// now it returns index, oldValue, newValue
public event ChangedDelegate Changed;
public event Action Updated;
public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
_list.Add(item);
Updated?.Invoke();
}
public void Clear()
{
_list.Clear();
Updated?.Invoke();
}
public bool Contains(T item)
{
return _list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
var output = _list.Remove(item);
Updated?.Invoke();
return output;
}
public int Count => _list.Count;
public bool IsReadOnly => false;
public int IndexOf(T item)
{
return _list.IndexOf(item);
}
public void InsertRange(int index, IEnumerable<T> collection)
{
_list.InsertRange(index, collection);
Updated?.Invoke();
}
public void AddRange(IEnumerable<T> collection)
{
_list.AddRange(collection);
Updated?.Invoke();
}
public void RemoveAll(Predicate<T> predicate)
{
_list.RemoveAll(predicate);
Updated?.Invoke();
}
public void RemoveRange(int index, int count)
{
_list.RemoveRange(index, count);
Updated?.Invoke();
}
public void Insert(int index, T item)
{
_list.Insert(index, item);
Updated?.Invoke();
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
Updated?.Invoke();
}
public T this[int index]
{
get { return _list[index]; }
set
{
var oldValue = _list[index];
_list[index] = value;
Changed?.Invoke(index, oldValue, value);
// I would also call the generic one here
Updated?.Invoke();
}
}
}
[CustomPropertyDrawer(typeof(ObservedList), true)]
public class ObservedListDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var list = property.FindPropertyRelative("_list");
EditorGUI.PropertyField(position, list, label, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var list = property.FindPropertyRelative("_list");
return EditorGUI.GetPropertyHeight(list, label, true);
}
}
@usernameHed
Copy link

I get an error in unity 2022:
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.PropertyHandler.GetHeight (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren) (at <7d481861b3d34328a9633ade22dcedd6>:0)

I think the hack "_list" return null:
var list = property.FindPropertyRelative("_list");

Where shoud I search to find the new way to get the "list" name ?

@der-hugo
Copy link
Author

der-hugo commented Sep 6, 2022

I get an error in unity 2022: NullReferenceException: Object reference not set to an instance of an object UnityEditor.PropertyHandler.GetHeight (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren) (at <7d481861b3d34328a9633ade22dcedd6>:0)

I think the hack "_list" return null: var list = property.FindPropertyRelative("_list");

Where shoud I search to find the new way to get the "list" name ?

Hey there, tbh I don't remember why I did this here ^^

The issue here is that the custom editor is only for the base type ObservableList which doesn't have any serialized property called _list (which btw is not a hack but the normal way of accessing those) - only the generic implementation ObservableList<T> has this.

As said I don't really remember and not sure if serialization / editor rules maybe changed since then.


Though I just tested and imported both into Unity 2021.3.8f1 (Latest LTS) and it works exactly as expected .. maybe stick to the LTS until the pre-release sorts out its bugs ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment