From ea0a837f01886562b925d7f36de54d0ce29d77da Mon Sep 17 00:00:00 2001 From: bid-soft Date: Mon, 12 Feb 2024 23:45:01 +0100 Subject: [PATCH 1/5] new RenderManager.SpawnSafe for better icons --- JotunnLib/Managers/RenderManager.cs | 62 ++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/JotunnLib/Managers/RenderManager.cs b/JotunnLib/Managers/RenderManager.cs index 30219baf6..40ab30bfc 100644 --- a/JotunnLib/Managers/RenderManager.cs +++ b/JotunnLib/Managers/RenderManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -315,38 +315,53 @@ private static bool IsVisualComponent(Component component) /// private static RenderObject SpawnSafe(RenderRequest request) { - GameObject prefab = request.Target; - - // map prefab GameObjects to the instantiated GameObjects - Dictionary realToClone = new Dictionary(); - GameObject spawn = SpawnOnlyTransformsClone(prefab, null, realToClone); - - foreach (var pair in realToClone) - { - CopyVisualComponents(pair.Key, pair.Value, realToClone); - } - + // create temporary parent and clone target as child + GameObject parent = new GameObject(); + parent.SetActive(false); + GameObject spawn = Object.Instantiate(request.Target, parent.transform); + + // delete problematic components Monobehavours + parent.GetComponentsInChildren(true) + .Where(monoBehaviour => monoBehaviour != null && monoBehaviour.gameObject != null) + .OrderBy(monoBehaviour => monoBehaviour is CharacterDrop ? 0 : 1) // Order by whether it's a CharacterDrop, as other scripts depend on it + .ToList().ForEach(monoBehaviour => Object.DestroyImmediate(monoBehaviour)); + + parent.GetComponentsInChildren(true) + .Where(animator => animator != null && animator.gameObject != null) + .ToList() + .ForEach(animator => Object.DestroyImmediate(animator)); + + SetLayerRecursive(spawn); + spawn.transform.SetParent(null); + Object.DestroyImmediate(parent); spawn.transform.position = Vector3.zero; spawn.transform.rotation = request.Rotation; - spawn.name = prefab.name; + spawn.name = request.Target.name; // calculate visual center Vector3 min = new Vector3(1000f, 1000f, 1000f); Vector3 max = new Vector3(-1000f, -1000f, -1000f); - foreach (Renderer meshRenderer in spawn.GetComponentsInChildren()) - { - min = Vector3.Min(min, meshRenderer.bounds.min); - max = Vector3.Max(max, meshRenderer.bounds.max); - } + spawn.GetComponentsInChildren().ToList() + .Where(renderer => renderer is MeshRenderer || renderer is SkinnedMeshRenderer) + .ToList().ForEach(renderer => + { + min = Vector3.Min(min, renderer.bounds.min); + max = Vector3.Max(max, renderer.bounds.max); + }); // center the prefab spawn.transform.position = -(min + max) / 2f; + Vector3 size = new Vector3( Mathf.Abs(min.x) + Mathf.Abs(max.x), Mathf.Abs(min.y) + Mathf.Abs(max.y), Mathf.Abs(min.z) + Mathf.Abs(max.z)); + // simulate particle effects + foreach (ParticleSystem particleSystem in spawn.GetComponentsInChildren(true)) + particleSystem.Simulate(request.ParticleSimulationTime); + // just in case it doesn't gets deleted properly later TimedDestruction timedDestruction = spawn.AddComponent(); timedDestruction.Trigger(1f); @@ -378,6 +393,12 @@ private static GameObject SpawnOnlyTransformsClone(GameObject prefab, Transform return clone; } + private static void SetLayerRecursive(GameObject root) + { + root.layer = Layer; + foreach (Transform child in root.transform) SetLayerRecursive(child.gameObject); + } + private static Transform MapRealBoneToClonedBone(Dictionary resolver, Transform transform) { if (transform) @@ -523,6 +544,11 @@ public RenderRequest(GameObject target) { Target = target; } + + /// + /// Simulates the particle effects for this amount of seconds. 0 for no particles. + /// + public float ParticleSimulationTime { get; set; } = 2f; } } } From 9e4a1c68541efb8551da8d6758272a5ea390d6c6 Mon Sep 17 00:00:00 2001 From: bid-soft Date: Mon, 19 Feb 2024 23:15:45 +0100 Subject: [PATCH 2/5] changed linq to foreach, iterate through all Components, build DAG, particle simulation time: default=5, <0 is not particles, renamed SpawnSafe to SpawnRenderClone, deleted dead code --- JotunnLib/Managers/RenderManager.cs | 218 ++++++++++++++++++---------- 1 file changed, 139 insertions(+), 79 deletions(-) diff --git a/JotunnLib/Managers/RenderManager.cs b/JotunnLib/Managers/RenderManager.cs index 40ab30bfc..802dec6d8 100644 --- a/JotunnLib/Managers/RenderManager.cs +++ b/JotunnLib/Managers/RenderManager.cs @@ -182,7 +182,9 @@ public Sprite Render(RenderRequest renderRequest) SetupRendering(); } - RenderObject spawned = SpawnSafe(renderRequest); + RenderObject spawned = SpawnRenderClone(renderRequest); + // no safe spawn possible + if (spawned == null) return null; Sprite rendered = RenderSprite(spawned); if (renderRequest.UseCache) @@ -308,28 +310,41 @@ private static bool IsVisualComponent(Component component) return component is Renderer || component is MeshFilter; } + private static bool IsRenderComponent(Component component) + { + return component is MeshRenderer || component is SkinnedMeshRenderer || component is MeshFilter || component is Transform; + } + + private static bool IsParticleComponent(Component component) + { + return component is ParticleSystemRenderer || component is ParticleSystem; + } + /// /// Spawn a prefab without any Components except visuals. Also prevents calling Awake methods of the prefab. /// /// /// - private static RenderObject SpawnSafe(RenderRequest request) + private static RenderObject SpawnRenderClone(RenderRequest request) { // create temporary parent and clone target as child GameObject parent = new GameObject(); parent.SetActive(false); GameObject spawn = Object.Instantiate(request.Target, parent.transform); - // delete problematic components Monobehavours - parent.GetComponentsInChildren(true) - .Where(monoBehaviour => monoBehaviour != null && monoBehaviour.gameObject != null) - .OrderBy(monoBehaviour => monoBehaviour is CharacterDrop ? 0 : 1) // Order by whether it's a CharacterDrop, as other scripts depend on it - .ToList().ForEach(monoBehaviour => Object.DestroyImmediate(monoBehaviour)); + // build dag of coomponent dependencies + DAG dag = new DAG(parent.transform); - parent.GetComponentsInChildren(true) - .Where(animator => animator != null && animator.gameObject != null) - .ToList() - .ForEach(animator => Object.DestroyImmediate(animator)); + List componets = dag.TopologicalSort(); + // can not be spawned safe + if (dag.hasCycle) return null; + + // delete all components except visuals and transforms + foreach (var component in componets) + { + if (!(IsRenderComponent(component) || (request.ParticleSimulationTime >= 0 && IsParticleComponent(component)))) + Object.DestroyImmediate(component); + } SetLayerRecursive(spawn); spawn.transform.SetParent(null); @@ -342,13 +357,14 @@ private static RenderObject SpawnSafe(RenderRequest request) Vector3 min = new Vector3(1000f, 1000f, 1000f); Vector3 max = new Vector3(-1000f, -1000f, -1000f); - spawn.GetComponentsInChildren().ToList() - .Where(renderer => renderer is MeshRenderer || renderer is SkinnedMeshRenderer) - .ToList().ForEach(renderer => + foreach (Renderer renderer in spawn.GetComponentsInChildren()) + { + if (renderer is MeshRenderer || renderer is SkinnedMeshRenderer) { min = Vector3.Min(min, renderer.bounds.min); max = Vector3.Max(max, renderer.bounds.max); - }); + } + } // center the prefab spawn.transform.position = -(min + max) / 2f; @@ -358,9 +374,11 @@ private static RenderObject SpawnSafe(RenderRequest request) Mathf.Abs(min.y) + Mathf.Abs(max.y), Mathf.Abs(min.z) + Mathf.Abs(max.z)); - // simulate particle effects + // simulate particle effects (in case of simulation time less than 0, components have already been destroyed) foreach (ParticleSystem particleSystem in spawn.GetComponentsInChildren(true)) + { particleSystem.Simulate(request.ParticleSimulationTime); + } // just in case it doesn't gets deleted properly later TimedDestruction timedDestruction = spawn.AddComponent(); @@ -372,71 +390,12 @@ private static RenderObject SpawnSafe(RenderRequest request) }; } - private static GameObject SpawnOnlyTransformsClone(GameObject prefab, Transform parent, Dictionary realToClone) - { - GameObject clone = new GameObject(); - clone.gameObject.layer = Layer; - clone.gameObject.SetActive(prefab.activeSelf); - clone.name = prefab.name; - clone.transform.SetParent(parent); - clone.transform.localPosition = prefab.transform.localPosition; - clone.transform.localRotation = prefab.transform.localRotation; - clone.transform.localScale = prefab.transform.localScale; - - realToClone.Add(prefab, clone); - - for (int i = 0; i < prefab.transform.childCount; i++) - { - SpawnOnlyTransformsClone(prefab.transform.GetChild(i).gameObject, clone.transform, realToClone); - } - - return clone; - } - private static void SetLayerRecursive(GameObject root) { root.layer = Layer; - foreach (Transform child in root.transform) SetLayerRecursive(child.gameObject); - } - - private static Transform MapRealBoneToClonedBone(Dictionary resolver, Transform transform) - { - if (transform) + foreach (Transform child in root.transform) { - return resolver[transform.gameObject].transform; - } - - return transform; - } - - private static void CopyVisualComponents(GameObject prefab, GameObject clone, Dictionary resolver) - { - foreach (MeshFilter meshFilter in prefab.GetComponents()) - { - clone.gameObject.AddComponentCopy(meshFilter); - } - - foreach (Renderer renderer in prefab.GetComponents()) - { - Renderer clonedRenderer = (Renderer)clone.gameObject.AddComponentCopy(renderer); - - if (!(renderer is SkinnedMeshRenderer skinnedMeshRenderer)) - { - continue; - } - - SkinnedMeshRenderer clonedSkinnedMeshRenderer = (SkinnedMeshRenderer)clonedRenderer; - - if (skinnedMeshRenderer.rootBone != null) - { - Transform[] bones = skinnedMeshRenderer.bones.Select(t => MapRealBoneToClonedBone(resolver, t)).ToArray(); - clonedSkinnedMeshRenderer.bones = bones; - clonedSkinnedMeshRenderer.updateWhenOffscreen = true; - } - else - { - clonedSkinnedMeshRenderer.rootBone = null; - } + SetLayerRecursive(child.gameObject); } } @@ -546,9 +505,110 @@ public RenderRequest(GameObject target) } /// - /// Simulates the particle effects for this amount of seconds. 0 for no particles. + /// Simulates the particle effects for this amount of seconds. <0 for no particles. /// - public float ParticleSimulationTime { get; set; } = 2f; + public float ParticleSimulationTime { get; set; } = 5f; + } + } + + /// + /// Directed Acyclic Graph (DAG) class to resolve component dependencies + /// + public class DAG + { + /// + /// Are there cycles in the graph + /// + public bool hasCycle { get; private set; } + private readonly Transform initial; + private readonly Dictionary> nodes = new Dictionary>(); + private readonly HashSet visitedNodes = new HashSet(); + + /// + /// Constructor taking the component to resolve the dependencies for + /// + public DAG(Transform initial) + { + if (initial == null) return; + hasCycle = false; + this.initial = initial; + + Parse(initial); + } + + private void Parse(Component element) + { + if (element == null) return; + + visitedNodes.Add(element); + + if (!nodes.ContainsKey(element)) nodes.Add(element, new List()); + + foreach (Component component in element.gameObject.GetComponentsInChildren(true)) + { + // element already processed + if (visitedNodes.Contains(component)) continue; + + nodes[element].Add(component); + Parse(component); + } + } + + /// + /// Retrieves the components from DAG according to their dependencies in order to destroy the components in correct order + /// + /// List of + public List TopologicalSort() + { + List result = new List(); + HashSet visited = new HashSet(); + HashSet recursionStack = new HashSet(); + + foreach (var node in nodes.Keys) + { + if (!visited.Contains(node)) + { + if (!TopologicalSortUtil(node, visited, recursionStack, result)) + { + Logger.LogError($"Cycles detected in component dependencies for component {node}. Unable to determine deletion order for {initial}."); + return null; + } + } + } + + return result; } + + private bool TopologicalSortUtil(Component node, HashSet visited, HashSet recursionStack, List result) + { + if (recursionStack.Contains(node)) + { + // Cycle detected + hasCycle = true; + return false; + } + + if (!visited.Contains(node)) + { + visited.Add(node); + recursionStack.Add(node); + + foreach (var neighbor in nodes[node]) + { + if (!TopologicalSortUtil(neighbor, visited, recursionStack, result)) + { + return false; + } + } + + recursionStack.Remove(node); + result.Add(node); + } + + return true; + } + } + + } From 857199141e2bec74d94a422c1ac0f3e80a1f5d15 Mon Sep 17 00:00:00 2001 From: bid-soft Date: Mon, 19 Feb 2024 23:31:01 +0100 Subject: [PATCH 3/5] fixed typo --- JotunnLib/Managers/RenderManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/JotunnLib/Managers/RenderManager.cs b/JotunnLib/Managers/RenderManager.cs index 802dec6d8..383e51073 100644 --- a/JotunnLib/Managers/RenderManager.cs +++ b/JotunnLib/Managers/RenderManager.cs @@ -335,12 +335,12 @@ private static RenderObject SpawnRenderClone(RenderRequest request) // build dag of coomponent dependencies DAG dag = new DAG(parent.transform); - List componets = dag.TopologicalSort(); + List components = dag.TopologicalSort(); // can not be spawned safe if (dag.hasCycle) return null; // delete all components except visuals and transforms - foreach (var component in componets) + foreach (var component in components) { if (!(IsRenderComponent(component) || (request.ParticleSimulationTime >= 0 && IsParticleComponent(component)))) Object.DestroyImmediate(component); From 45a1a636bb0dc3b24a130379965172e3fdc4492a Mon Sep 17 00:00:00 2001 From: bid-soft Date: Thu, 22 Feb 2024 10:25:36 +0100 Subject: [PATCH 4/5] adopted deletion of components: only consider dependencies of one gameobject (not complete hierarch); consider dependencies based on RequireComponent --- JotunnLib/Managers/RenderManager.cs | 217 +++++++++++++--------------- 1 file changed, 104 insertions(+), 113 deletions(-) diff --git a/JotunnLib/Managers/RenderManager.cs b/JotunnLib/Managers/RenderManager.cs index 383e51073..27d5dedd6 100644 --- a/JotunnLib/Managers/RenderManager.cs +++ b/JotunnLib/Managers/RenderManager.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using BepInEx; using Jotunn.Utils; using UnityEngine; +using static Mono.Security.X509.X520; using Object = UnityEngine.Object; namespace Jotunn.Managers @@ -332,19 +334,12 @@ private static RenderObject SpawnRenderClone(RenderRequest request) parent.SetActive(false); GameObject spawn = Object.Instantiate(request.Target, parent.transform); - // build dag of coomponent dependencies - DAG dag = new DAG(parent.transform); - - List components = dag.TopologicalSort(); - // can not be spawned safe - if (dag.hasCycle) return null; - - // delete all components except visuals and transforms - foreach (var component in components) + // exception could happen e.g. if a renderer has a dependency to a script + if (!RecursivelyRemoveComponents(parent.transform, request)) { - if (!(IsRenderComponent(component) || (request.ParticleSimulationTime >= 0 && IsParticleComponent(component)))) - Object.DestroyImmediate(component); - } + Logger.LogWarning($"Sprite could not be rendered for {request.Target} due to components not being removed."); + return null; + } SetLayerRecursive(spawn); spawn.transform.SetParent(null); @@ -390,6 +385,102 @@ private static RenderObject SpawnRenderClone(RenderRequest request) }; } + /// + /// Recursively remove all components not needed for rendering in order of their dependencies. + /// + /// + /// + /// false if components could not be removed, true otherwise + private static bool RecursivelyRemoveComponents(Transform parentTransform, RenderRequest request) + { + GameObject go = parentTransform.gameObject; + + // remove components from all childs + for (int i = 0; i < parentTransform.childCount; i++) + { + RecursivelyRemoveComponents(parentTransform.GetChild(i), request); + } + + // remove components in order of its dependencies + List typesToRemove = GetTopolocialSort(parentTransform); + // cycles detected + if (typesToRemove == null) return false; + + foreach (Type type in typesToRemove) + { + foreach (Component componentToRemove in go.GetComponents(type)) + { + if (!(IsRenderComponent(componentToRemove) || (request.ParticleSimulationTime >= 0 && IsParticleComponent(componentToRemove)))) + { + try + { + Object.DestroyImmediate(componentToRemove); + } + // could happen if a component needed for rendering has a dependency to a script component + catch (Exception e) + { + Logger.LogError($"Component {componentToRemove} could not be removed. Exception caught: {e.Message}"); + return false; + } + } + } + } + + return true; + } + + /// + /// Topological sort (regarding dependencies between components) of all components of this transforms gameobject + /// + /// + /// A topologically sorted list of types. Null if cycles have been detected. + private static List GetTopolocialSort(Transform tranform) + { + List result = new List(); + HashSet visitedNodes = new HashSet(); + HashSet recursionStack = new HashSet(); + + foreach(Component component in tranform.gameObject.GetComponents()) + { + if (!TopologicalSortUtil(component.GetType(), visitedNodes, recursionStack, result)) + { + Logger.LogWarning($"Cycles detected in component dependencies for type {component.GetType()}. Unable to determine deletion order for {tranform}."); + return null; + } + } + + result.Reverse(); + return result; + } + + private static bool TopologicalSortUtil(Type node, HashSet visited, HashSet recursionStack, List result) + { + // Type already processed + if (visited.Contains(node)) return true; + + // Cycle detected + if (recursionStack.Contains(node)) return false; + recursionStack.Add(node); + + // Parse dependencies + IEnumerable requirements = node.GetCustomAttributes(); + foreach (RequireComponent requirement in requirements) + { + if (requirement.m_Type0 != null) + if (!TopologicalSortUtil(requirement.m_Type0, visited, recursionStack, result)) return false; + if (requirement.m_Type1 != null) + if (!TopologicalSortUtil(requirement.m_Type1, visited, recursionStack, result)) return false; + if (requirement.m_Type2 != null) + if (!TopologicalSortUtil(requirement.m_Type2, visited, recursionStack, result)) return false; + } + + recursionStack.Remove(node); + visited.Add(node); + result.Add(node); + + return true; + } + private static void SetLayerRecursive(GameObject root) { root.layer = Layer; @@ -505,110 +596,10 @@ public RenderRequest(GameObject target) } /// - /// Simulates the particle effects for this amount of seconds. <0 for no particles. + /// Simulates the particle effects for this amount of seconds. Less than 0 for no particles. /// public float ParticleSimulationTime { get; set; } = 5f; } } - /// - /// Directed Acyclic Graph (DAG) class to resolve component dependencies - /// - public class DAG - { - /// - /// Are there cycles in the graph - /// - public bool hasCycle { get; private set; } - private readonly Transform initial; - private readonly Dictionary> nodes = new Dictionary>(); - private readonly HashSet visitedNodes = new HashSet(); - - /// - /// Constructor taking the component to resolve the dependencies for - /// - public DAG(Transform initial) - { - if (initial == null) return; - hasCycle = false; - this.initial = initial; - - Parse(initial); - } - - private void Parse(Component element) - { - if (element == null) return; - - visitedNodes.Add(element); - - if (!nodes.ContainsKey(element)) nodes.Add(element, new List()); - - foreach (Component component in element.gameObject.GetComponentsInChildren(true)) - { - // element already processed - if (visitedNodes.Contains(component)) continue; - - nodes[element].Add(component); - Parse(component); - } - } - - /// - /// Retrieves the components from DAG according to their dependencies in order to destroy the components in correct order - /// - /// List of - public List TopologicalSort() - { - List result = new List(); - HashSet visited = new HashSet(); - HashSet recursionStack = new HashSet(); - - foreach (var node in nodes.Keys) - { - if (!visited.Contains(node)) - { - if (!TopologicalSortUtil(node, visited, recursionStack, result)) - { - Logger.LogError($"Cycles detected in component dependencies for component {node}. Unable to determine deletion order for {initial}."); - return null; - } - } - } - - return result; - } - - private bool TopologicalSortUtil(Component node, HashSet visited, HashSet recursionStack, List result) - { - if (recursionStack.Contains(node)) - { - // Cycle detected - hasCycle = true; - return false; - } - - if (!visited.Contains(node)) - { - visited.Add(node); - recursionStack.Add(node); - - foreach (var neighbor in nodes[node]) - { - if (!TopologicalSortUtil(neighbor, visited, recursionStack, result)) - { - return false; - } - } - - recursionStack.Remove(node); - result.Add(node); - } - - return true; - } - - } - - } From 7f67d6d0314723f3af0fc315011ae2e0606029b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 28 Feb 2024 13:07:13 +0100 Subject: [PATCH 5/5] chore: cleanup RenderManager --- JotunnLib/Managers/RenderManager.cs | 46 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/JotunnLib/Managers/RenderManager.cs b/JotunnLib/Managers/RenderManager.cs index 27d5dedd6..dd2867fda 100644 --- a/JotunnLib/Managers/RenderManager.cs +++ b/JotunnLib/Managers/RenderManager.cs @@ -7,7 +7,6 @@ using BepInEx; using Jotunn.Utils; using UnityEngine; -using static Mono.Security.X509.X520; using Object = UnityEngine.Object; namespace Jotunn.Managers @@ -23,6 +22,7 @@ public class RenderManager : IManager public static readonly Quaternion IsometricRotation = Quaternion.Euler(23, 51, 25.8f); private static RenderManager _instance; + private Sprite EmptySprite { get; } = Sprite.Create(Texture2D.whiteTexture, Rect.zero, Vector2.one); /// @@ -185,8 +185,13 @@ public Sprite Render(RenderRequest renderRequest) } RenderObject spawned = SpawnRenderClone(renderRequest); - // no safe spawn possible - if (spawned == null) return null; + + if (spawned == null) + { + // no safe spawn possible + return null; + } + Sprite rendered = RenderSprite(spawned); if (renderRequest.UseCache) @@ -339,7 +344,7 @@ private static RenderObject SpawnRenderClone(RenderRequest request) { Logger.LogWarning($"Sprite could not be rendered for {request.Target} due to components not being removed."); return null; - } + } SetLayerRecursive(spawn); spawn.transform.SetParent(null); @@ -403,8 +408,12 @@ private static bool RecursivelyRemoveComponents(Transform parentTransform, Rende // remove components in order of its dependencies List typesToRemove = GetTopolocialSort(parentTransform); - // cycles detected - if (typesToRemove == null) return false; + + if (typesToRemove == null) + { + // cycles detected + return false; + } foreach (Type type in typesToRemove) { @@ -416,16 +425,16 @@ private static bool RecursivelyRemoveComponents(Transform parentTransform, Rende { Object.DestroyImmediate(componentToRemove); } - // could happen if a component needed for rendering has a dependency to a script component catch (Exception e) { + // could happen if a component needed for rendering has a dependency to a script component Logger.LogError($"Component {componentToRemove} could not be removed. Exception caught: {e.Message}"); return false; } } } } - + return true; } @@ -440,7 +449,7 @@ private static List GetTopolocialSort(Transform tranform) HashSet visitedNodes = new HashSet(); HashSet recursionStack = new HashSet(); - foreach(Component component in tranform.gameObject.GetComponents()) + foreach (Component component in tranform.gameObject.GetComponents()) { if (!TopologicalSortUtil(component.GetType(), visitedNodes, recursionStack, result)) { @@ -448,7 +457,7 @@ private static List GetTopolocialSort(Transform tranform) return null; } } - + result.Reverse(); return result; } @@ -463,21 +472,21 @@ private static bool TopologicalSortUtil(Type node, HashSet visited, HashSe recursionStack.Add(node); // Parse dependencies - IEnumerable requirements = node.GetCustomAttributes(); + IEnumerable requirements = node.GetCustomAttributes(true); foreach (RequireComponent requirement in requirements) { - if (requirement.m_Type0 != null) - if (!TopologicalSortUtil(requirement.m_Type0, visited, recursionStack, result)) return false; - if (requirement.m_Type1 != null) - if (!TopologicalSortUtil(requirement.m_Type1, visited, recursionStack, result)) return false; - if (requirement.m_Type2 != null) - if (!TopologicalSortUtil(requirement.m_Type2, visited, recursionStack, result)) return false; + if (requirement.m_Type0 != null && !TopologicalSortUtil(requirement.m_Type0, visited, recursionStack, result)) + return false; + if (requirement.m_Type1 != null && !TopologicalSortUtil(requirement.m_Type1, visited, recursionStack, result)) + return false; + if (requirement.m_Type2 != null && !TopologicalSortUtil(requirement.m_Type2, visited, recursionStack, result)) + return false; } recursionStack.Remove(node); visited.Add(node); result.Add(node); - + return true; } @@ -601,5 +610,4 @@ public RenderRequest(GameObject target) public float ParticleSimulationTime { get; set; } = 5f; } } - }