123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Photon.Pun
- {
- public static class NestedComponentUtilities
- {
- public static T EnsureRootComponentExists<T, NestedT>(this Transform transform)
- where T : Component
- where NestedT : Component
- {
- var root = GetParentComponent<NestedT>(transform);
- if (root)
- {
- var comp = root.GetComponent<T>();
- if (comp)
- return comp;
- return root.gameObject.AddComponent<T>();
- }
- return null;
- }
- #region GetComponent Replacements
- // Recycled collections
- private static Queue<Transform> nodesQueue = new Queue<Transform>();
- public static Dictionary<System.Type, ICollection> searchLists = new Dictionary<System.Type, ICollection>();
- private static Stack<Transform> nodeStack = new Stack<Transform>();
- /// <summary>
- /// Find T on supplied transform or any parent. Unlike GetComponentInParent, GameObjects do not need to be active to be found.
- /// </summary>
- public static T GetParentComponent<T>(this Transform t)
- where T : Component
- {
- T found = t.GetComponent<T>();
- if (found)
- return found;
- var par = t.parent;
- while (par)
- {
- found = par.GetComponent<T>();
- if (found)
- return found;
- par = par.parent;
- }
- return null;
- }
- /// <summary>
- /// Returns all T found between the child transform and its root. Order in List from child to parent, with the root/parent most being last.
- /// </summary>
- /// <param name="t"></param>
- /// <returns></returns>
- public static void GetNestedComponentsInParents<T>(this Transform t, List<T> list)
- where T : Component
- {
- list.Clear();
- while (t != null)
- {
- T obj = t.GetComponent<T>();
- if (obj)
- list.Add(obj);
- t = t.parent;
- }
- }
- public static T GetNestedComponentInChildren<T, NestedT>(this Transform t, bool includeInactive)
- where T : class
- where NestedT : class
- {
- // Look for the most obvious check first on the root.
- var found = t.GetComponent<T>();
- if (!ReferenceEquals(found, null))
- return found;
- // No root found, start testing layer by layer - root is the first layer. Add to queue.
- nodesQueue.Clear();
- nodesQueue.Enqueue(t);
- while (nodesQueue.Count > 0)
- {
- var node = nodesQueue.Dequeue();
- for (int c = 0, ccnt = node.childCount; c < ccnt; ++c)
- {
- var child = node.GetChild(c);
- // Ignore branches that are not active
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // Hit a nested node - don't search this node
- if (!ReferenceEquals(child.GetComponent<NestedT>(), null))
- continue;
- // see if what we are looking for is on this node
- found = child.GetComponent<T>();
- // Return if we found what we are looking for
- if (!ReferenceEquals(found, null))
- return found;
- // Add node to queue for next depth pass since nothing was found on this layer.
- nodesQueue.Enqueue(child);
- }
- }
- return found;
- }
- /// <summary>
- /// Same as GetComponentInParent, but will always include inactive objects in search.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="DontRecurseOnT"></typeparam>
- /// <param name="t"></param>
- /// <returns></returns>
- public static T GetNestedComponentInParent<T, NestedT>(this Transform t)
- where T : class
- where NestedT : class
- {
- T found = null;
- Transform node = t;
- do
- {
- found = node.GetComponent<T>();
- if (!ReferenceEquals(found, null))
- return found;
- // stop search on node with PV
- if (!ReferenceEquals(node.GetComponent<NestedT>(), null))
- return null;
- node = node.parent;
- }
- while (!ReferenceEquals(node, null));
- return null;
- }
- /// <summary>
- /// UNTESTED
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="StopSearchOnT"></typeparam>
- /// <param name="t"></param>
- /// <returns></returns>
- public static T GetNestedComponentInParents<T, NestedT>(this Transform t)
- where T : class
- where NestedT : class
- {
- // First try root
- var found = t.GetComponent<T>();
- if (!ReferenceEquals(found, null))
- return found;
- /// Get the reverse list of transforms climbing for start up to netobject
- var par = t.parent;
- while (!ReferenceEquals(par, null))
- {
- found = par.GetComponent<T>();
- if (!ReferenceEquals(found, null))
- return found;
- /// Stop climbing at the NetObj (this is how we detect nesting
- if (!ReferenceEquals(par.GetComponent<NestedT>(), null))
- return null;
- par = par.parent;
- };
- return null;
- }
- /// <summary>
- /// Finds components of type T on supplied transform, and every parent above that node, inclusively stopping on node StopSearchOnT component.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <typeparam name="StopSearchOnT"></typeparam>
- /// <param name="t"></param>
- /// <param name="list"></param>
- /// <returns></returns>
- public static void GetNestedComponentsInParents<T, NestedT>(this Transform t, List<T> list)
- where T : class
- where NestedT : class
- {
- // Get components on the starting node - this is a given.
- t.GetComponents(list);
- // If the starting node has the stop component, we are done.
- if (!ReferenceEquals(t.GetComponent<NestedT>(), null))
- return;
- var tnode = t.parent;
- // If there is no parent, we are done.
- if (ReferenceEquals(tnode, null))
- return;
- nodeStack.Clear();
- while (true)
- {
- // add new parent to stack
- nodeStack.Push(tnode);
- // if this node has the Stop, we are done recursing up.
- if (!ReferenceEquals(tnode.GetComponent<NestedT>(), null))
- break;
- // Get the next parent node and add it to the stack
- tnode = tnode.parent;
- // Stop recursing up if the parent is null
- if (ReferenceEquals(tnode, null))
- break;
- }
- if (nodeStack.Count == 0)
- return;
- System.Type type = typeof(T);
- // Acquire the right searchlist from our pool
- List<T> searchList;
- if (!searchLists.ContainsKey(type))
- {
- searchList = new List<T>();
- searchLists.Add(type, searchList);
- }
- else
- {
- searchList = searchLists[type] as List<T>;
- }
- // Reverse iterate the nodes found. This produces a GetComponentInParent that starts from the parent Stop down to the provided transform
- while (nodeStack.Count > 0)
- {
- var node = nodeStack.Pop();
- node.GetComponents(searchList);
- list.AddRange(searchList);
- }
- }
- /// <summary>
- /// Same as GetComponentsInChildren, but will not recurse into children with component of the DontRecurseOnT type. This allows nesting of PhotonViews/NetObjects to be respected.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="t"></param>
- /// <param name="list">Pass null and a reused list will be used. Consume immediately.</param>
- public static List<T> GetNestedComponentsInChildren<T, NestedT>(this Transform t, List<T> list, bool includeInactive = true)
- where T : class
- where NestedT : class
- {
- System.Type type = typeof(T);
- // Temp lists are also recycled. Get/Create a reusable List of this type.
- List<T> searchList;
- if (!searchLists.ContainsKey(type))
- searchLists.Add(type, searchList = new List<T>());
- else
- searchList = searchLists[type] as List<T>;
- nodesQueue.Clear();
- if (list == null)
- list = new List<T>();
- // Get components on starting transform - no exceptions
- t.GetComponents(list);
- // Add first layer of children to the queue for next layer processing.
- for (int i = 0, cnt = t.childCount; i < cnt; ++i)
- {
- var child = t.GetChild(i);
- // Ignore inactive nodes (optional)
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // ignore nested DontRecurseOnT
- if (!ReferenceEquals(child.GetComponent<NestedT>(), null))
- continue;
- nodesQueue.Enqueue(child);
- }
- // Recurse node layers
- while (nodesQueue.Count > 0)
- {
- var node = nodesQueue.Dequeue();
- // Add found components on this gameobject node
- node.GetComponents(searchList);
- list.AddRange(searchList);
- // Add children to the queue for next layer processing.
- for (int i = 0, cnt = node.childCount; i < cnt; ++i)
- {
- var child = node.GetChild(i);
- // Ignore inactive nodes (optional)
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // ignore nested NestedT
- if (!ReferenceEquals(child.GetComponent<NestedT>(), null))
- continue;
- nodesQueue.Enqueue(child);
- }
- }
- return list;
- }
- /// <summary>
- /// Same as GetComponentsInChildren, but will not recurse into children with component of the DontRecurseOnT type. This allows nesting of PhotonViews/NetObjects to be respected.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="t"></param>
- /// <param name="list">Pass null and a reused list will be used. Consume immediately.</param>
- public static List<T> GetNestedComponentsInChildren<T>(this Transform t, List<T> list, bool includeInactive = true, params System.Type[] stopOn)
- where T : class
- {
- System.Type type = typeof(T);
- // Temp lists are also recycled. Get/Create a reusable List of this type.
- List<T> searchList;
- if (!searchLists.ContainsKey(type))
- searchLists.Add(type, searchList = new List<T>());
- else
- searchList = searchLists[type] as List<T>;
- nodesQueue.Clear();
- // Get components on starting transform - no exceptions
- t.GetComponents(list);
- // Add first layer of children to the queue for next layer processing.
- for (int i = 0, cnt = t.childCount; i < cnt; ++i)
- {
- var child = t.GetChild(i);
- // Ignore inactive nodes (optional)
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // ignore nested DontRecurseOnT
- bool stopRecurse = false;
- for (int s = 0, scnt = stopOn.Length; s < scnt; ++s)
- {
- if (!ReferenceEquals(child.GetComponent(stopOn[s]), null))
- {
- stopRecurse = true;
- break;
- }
- }
- if (stopRecurse)
- continue;
- nodesQueue.Enqueue(child);
- }
- // Recurse node layers
- while (nodesQueue.Count > 0)
- {
- var node = nodesQueue.Dequeue();
- // Add found components on this gameobject node
- node.GetComponents(searchList);
- list.AddRange(searchList);
- // Add children to the queue for next layer processing.
- for (int i = 0, cnt = node.childCount; i < cnt; ++i)
- {
- var child = node.GetChild(i);
- // Ignore inactive nodes (optional)
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // ignore nested NestedT
- bool stopRecurse = false;
- for (int s = 0, scnt = stopOn.Length; s < scnt; ++s)
- {
- if (!ReferenceEquals(child.GetComponent(stopOn[s]), null))
- {
- stopRecurse = true;
- break;
- }
- }
- if (stopRecurse)
- continue;
- nodesQueue.Enqueue(child);
- }
- }
- return list;
- }
- /// <summary>
- /// Same as GetComponentsInChildren, but will not recurse into children with component of the NestedT type. This allows nesting of PhotonViews/NetObjects to be respected.
- /// </summary>
- /// <typeparam name="T">Cast found components to this type. Typically Component, but any other class/interface will work as long as they are assignable from SearchT.</typeparam>
- /// <typeparam name="SearchT">Find components of this class or interface type.</typeparam>
- /// <typeparam name="DontRecurseOnT"></typeparam>
- /// <param name="t"></param>
- /// <param name="includeInactive"></param>
- /// <param name="list"></param>
- /// <returns></returns>
- public static void GetNestedComponentsInChildren<T, SearchT, NestedT>(this Transform t, bool includeInactive, List<T> list)
- where T : class
- where SearchT : class
- {
- list.Clear();
- // If this is inactive, nothing will be found. Give up now if we are restricted to active.
- if (!includeInactive && !t.gameObject.activeSelf)
- return;
- System.Type searchType = typeof(SearchT);
- // Temp lists are also recycled. Get/Create a reusable List of this type.
- List<SearchT> searchList;
- if (!searchLists.ContainsKey(searchType))
- searchLists.Add(searchType, searchList = new List<SearchT>());
- else
- searchList = searchLists[searchType] as List<SearchT>;
- // Recurse child nodes one layer at a time. Using a Queue allows this to happen without a lot of work.
- nodesQueue.Clear();
- nodesQueue.Enqueue(t);
- while (nodesQueue.Count > 0)
- {
- var node = nodesQueue.Dequeue();
- // Add found components on this gameobject node
- searchList.Clear();
- node.GetComponents(searchList);
- foreach (var comp in searchList)
- {
- var casted = comp as T;
- if (!ReferenceEquals(casted, null))
- list.Add(casted);
- }
- // Add children to the queue for next layer processing.
- for (int i = 0, cnt = node.childCount; i < cnt; ++i)
- {
- var child = node.GetChild(i);
- // Ignore inactive nodes (optional)
- if (!includeInactive && !child.gameObject.activeSelf)
- continue;
- // ignore nested DontRecurseOnT
- if (!ReferenceEquals(child.GetComponent<NestedT>(), null))
- continue;
- nodesQueue.Enqueue(child);
- }
- }
- }
- #endregion
- }
- }
|