Skip to content

Instantly share code, notes, and snippets.

@malj
Created March 19, 2023 09:16
Show Gist options
  • Save malj/905ebcc8dbedaddcfe2bb15907bbc884 to your computer and use it in GitHub Desktop.
Save malj/905ebcc8dbedaddcfe2bb15907bbc884 to your computer and use it in GitHub Desktop.
Unity MonoBehaviour ECS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/// <summary>
/// Enumerable component cache with methods for querying components based on their types.
/// </summary>
class World : IEnumerable<MonoBehaviour>
{
public static readonly World Default = new();
readonly Dictionary<Type, HashSet<MonoBehaviour>> cache = new();
public IEnumerator<MonoBehaviour> GetEnumerator() =>
cache.SelectMany(entry => entry.Value).Distinct().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public event Action<MonoBehaviour> Added;
public event Action<MonoBehaviour> Removed;
public bool Add(MonoBehaviour component)
{
var added = false;
if (component && !component.destroyCancellationToken.IsCancellationRequested)
{
foreach (var key in GetKeys(component))
{
if (!cache.TryGetValue(key, out var components))
{
components = cache[key] = new();
}
if (components.Add(component))
{
added = true;
}
}
if (added)
{
var registration = component.destroyCancellationToken.Register(() => Remove(component));
Removed += OnComponentRemoved;
void OnComponentRemoved(MonoBehaviour removedComponent)
{
if (component == removedComponent)
{
Removed -= OnComponentRemoved;
registration.Dispose();
}
}
Added?.Invoke(component);
}
}
return added;
}
public bool Remove(MonoBehaviour component)
{
var removed = false;
if (component)
{
foreach (var key in GetKeys(component))
{
if (cache[key].Remove(component))
{
removed = true;
if (cache[key].Count == 0)
{
cache.Remove(key);
}
}
}
if (removed)
{
Removed?.Invoke(component);
}
}
return removed;
}
public void Clear()
{
var components = Removed != null ? this.ToList() : null;
cache.Clear();
components?.ForEach(Removed);
}
static IEnumerable<Type> GetKeys(MonoBehaviour component)
{
var type = component.GetType();
while (type != typeof(MonoBehaviour))
{
yield return type;
foreach (var interfaceType in type.GetInterfaces())
{
yield return interfaceType;
}
type = type.BaseType;
}
}
public IEnumerable<T> Query<T>(bool includeDisabled = false)
where T : class =>
cache.TryGetValue(typeof(T), out var components)
? components.Where(component => includeDisabled || component.enabled).Cast<T>()
: Enumerable.Empty<T>();
public IEnumerable<(T1, T2)> Query<T1, T2>(bool includeDisabled = false)
where T1 : class
where T2 : class =>
from x in Query<T1>(includeDisabled)
join y in Query<T2>(includeDisabled) on (x as MonoBehaviour).gameObject equals (y as MonoBehaviour).gameObject
select (x, y);
public IEnumerable<(T1, T2, T3)> Query<T1, T2, T3>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class =>
from xs in Query<T1, T2>(includeDisabled)
join y in Query<T3>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, y);
public IEnumerable<(T1, T2, T3, T4)> Query<T1, T2, T3, T4>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class =>
from xs in Query<T1, T2, T3>(includeDisabled)
join y in Query<T4>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, y);
public IEnumerable<(T1, T2, T3, T4, T5)> Query<T1, T2, T3, T4, T5>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class =>
from xs in Query<T1, T2, T3, T4>(includeDisabled)
join y in Query<T5>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6)> Query<T1, T2, T3, T4, T5, T6>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class =>
from xs in Query<T1, T2, T3, T4, T5>(includeDisabled)
join y in Query<T6>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7)> Query<T1, T2, T3, T4, T5, T6, T7>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6>(includeDisabled)
join y in Query<T7>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7, T8)> Query<T1, T2, T3, T4, T5, T6, T7, T8>(
bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6, T7>(includeDisabled)
join y in Query<T8>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, xs.Item7, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> Query<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6, T7, T8>(includeDisabled)
join y in Query<T9>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, xs.Item7, xs.Item8, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> Query<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6, T7, T8, T9>(includeDisabled)
join y in Query<T10>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, xs.Item7, xs.Item8, xs.Item9, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)> Query<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
T11>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(includeDisabled)
join y in Query<T11>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, xs.Item7, xs.Item8, xs.Item9, xs.Item10, y);
public IEnumerable<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)> Query<T1, T2, T3, T4, T5, T6, T7, T8, T9,
T10, T11, T12>(bool includeDisabled = false)
where T1 : class
where T2 : class
where T3 : class
where T4 : class
where T5 : class
where T6 : class
where T7 : class
where T8 : class
where T9 : class
where T10 : class
where T11 : class
where T12 : class =>
from xs in Query<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(includeDisabled)
join y in Query<T12>(includeDisabled) on (xs.Item1 as MonoBehaviour).gameObject equals (y as MonoBehaviour)
.gameObject
select (xs.Item1, xs.Item2, xs.Item3, xs.Item4, xs.Item5, xs.Item6, xs.Item7, xs.Item8, xs.Item9, xs.Item10,
xs.Item11, y);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment