فهرست منبع

Added a way to find scene objects by name
Added a way to retrieve managed root scene object
Added unit test for scene save/load (WIP)

BearishSun 10 سال پیش
والد
کامیت
059d757ac5

+ 22 - 0
BansheeCore/Include/BsSceneObject.h

@@ -413,6 +413,28 @@ namespace BansheeEngine
 		 */
 		UINT32 getNumChildren() const { return (UINT32)mChildren.size(); }
 
+		/**
+		 * @brief	Searches the child objects for an object matching the specified name.
+		 *
+		 * @param	name		Name of the object to locate.
+		 * @param	recursive	If true all descendants of the scene object will be searched, 
+		 *						otherwise only immediate children.
+		 *
+		 * @returns	First found scene object, or empty handle if none found.
+		 */
+		HSceneObject findChild(const String& name, bool recursive = true);
+
+		/**
+		 * @brief	Searches the child objects for objects matching the specified name.
+		 *
+		 * @param	name		Name of the objects to locate.
+		 * @param	recursive	If true all descendants of the scene object will be searched, 
+		 *						otherwise only immediate children.
+		 *
+		 * @returns	All scene objects matching the specified name.
+		 */
+		Vector<HSceneObject> findChildren(const String& name, bool recursive = true);
+
 		/**
 		 * @brief	Enables or disables this object. Disabled objects also implicitly disable
 		 *			all their child objects. No components on the disabled object are updated.

+ 45 - 0
BansheeCore/Source/BsSceneObject.cpp

@@ -551,6 +551,51 @@ namespace BansheeEngine
 		}
 	}
 
+	HSceneObject SceneObject::findChild(const String& name, bool recursive)
+	{
+		for (auto& child : mChildren)
+		{
+			if (child->getName() == name)
+				return child;
+		}
+
+		if (recursive)
+		{
+			for (auto& child : mChildren)
+			{
+				HSceneObject foundObject = child->findChild(name, true);
+				if (foundObject != nullptr)
+					return foundObject;
+			}
+		}
+
+		return HSceneObject();
+	}
+
+	Vector<HSceneObject> SceneObject::findChildren(const String& name, bool recursive)
+	{
+		std::function<void(const HSceneObject&, Vector<HSceneObject>&)> findChildrenInternal = 
+			[&](const HSceneObject& so, Vector<HSceneObject>& output)
+		{
+			for (auto& child : so->mChildren)
+			{
+				if (child->getName() == name)
+					output.push_back(child);
+			}
+
+			if (recursive)
+			{
+				for (auto& child : so->mChildren)
+					findChildrenInternal(child, output);
+			}
+		};
+
+		Vector<HSceneObject> output;
+		findChildrenInternal(mThisHandle, output);
+
+		return output;
+	}
+
 	void SceneObject::setActive(bool active)
 	{
 		mActiveSelf = active;

+ 1 - 1
MBansheeEditor/EditorApplication.cs

@@ -282,7 +282,7 @@ namespace BansheeEditor
         /// Saves the currently loaded scene to the specified path.
         /// </summary>
         /// <param name="path">Path relative to the resource folder. This can be the path to the existing scene
-        ///                    prefab it just needs updating. </param>
+        ///                    prefab if it just needs updating. </param>
         public static void SaveScene(string path)
         {
             Internal_SaveScene(path);

+ 1 - 0
MBansheeEditor/UnitTestTypes.cs

@@ -27,6 +27,7 @@ namespace BansheeEditor
         public Dictionary<string, UT1_SerzObj> dictB;
 
         public UT1_Component2 otherComponent;
+        public UT1_Component1 otherComponent2;
         public SceneObject otherSO;
     }
 

+ 307 - 0
MBansheeEditor/UnitTests.cs

@@ -213,6 +213,312 @@ namespace BansheeEditor
             }
         }
 
+        /// <summary>
+        /// Tests saving, loading and updating of prefabs.
+        /// </summary>
+        private static void UnitTest4_Prefabs()
+        {
+            if (EditorApplication.IsProjectLoaded)
+            {
+                Debug.LogWarning("Skipping unit test as no project is loaded.");
+                return;
+            }
+
+            if (EditorApplication.IsSceneModified())
+            {
+                Debug.LogWarning("Cannot perform unit test as the current scene is modified.");
+                return;
+            }
+
+            string oldScene = Scene.ActiveSceneUUID;
+            Scene.Clear();
+
+            // Simple scene save & load
+            {
+                {
+                    SceneObject so0 = new SceneObject("so0");
+                    SceneObject so1 = new SceneObject("so1");
+                    SceneObject so0_0 = new SceneObject("so0_0");
+                    SceneObject so0_1 = new SceneObject("so0_1");
+                    SceneObject so1_0 = new SceneObject("so1_0");
+                    SceneObject so0_1_0 = new SceneObject("so0_1_0");
+
+                    so0_0.Parent = so0;
+                    so0_1.Parent = so0;
+                    so1_0.Parent = so1;
+                    so0_1_0.Parent = so0_1;
+
+                    so0_1_0.Position = new Vector3(10.0f, 15.0f, 20.0f);
+                    so0_1.Position = new Vector3(1.0f, 2.0f, 3.0f);
+                    so1_0.Position = new Vector3(0, 123.0f, 0.0f);
+
+                    UT1_Component1 comp0 = so0.AddComponent<UT1_Component1>();
+                    UT1_Component2 comp1 = so1.AddComponent<UT1_Component2>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.AddComponent<UT1_Component1>();
+
+                    comp0.otherSO = so0_1_0;
+                    comp0.otherComponent = comp1;
+
+                    comp0_1_0.b = "testValue";
+                    comp0_1_0.otherSO = so0;
+                    comp0_1_0.otherComponent2 = comp0;
+
+                    EditorApplication.SaveScene("unitTest4Scene_0");
+                }
+                {
+                    EditorApplication.LoadScene("unitTest4Scene_0");
+
+                    SceneObject sceneRoot = Scene.Root;
+                    SceneObject so0 = sceneRoot.FindChild("so0", false);
+                    SceneObject so1 = sceneRoot.FindChild("so1", false);
+                    SceneObject so0_0 = so0.FindChild("so0_0", false);
+                    SceneObject so0_1 = so0.FindChild("so0_1", false);
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+
+                    DebugUnit.Assert(so0_0 != null);
+                    DebugUnit.Assert(so0_1 != null);
+                    DebugUnit.Assert(so0_1_0 != null);
+
+                    UT1_Component1 comp0 = so0.GetComponent<UT1_Component1>();
+                    UT1_Component2 comp1 = so1.GetComponent<UT1_Component2>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    DebugUnit.Assert(comp0 != null);
+                    DebugUnit.Assert(comp1 != null);
+                    DebugUnit.Assert(comp0_1_0 != null);
+                    DebugUnit.Assert(comp0_1_0.b == "testValue");
+                    DebugUnit.Assert(comp0.otherSO == so0_1_0);
+                    DebugUnit.Assert(comp0.otherComponent == comp1);
+                    DebugUnit.Assert(comp0_1_0.otherSO == so0);
+                    DebugUnit.Assert(comp0_1_0.otherComponent2 == comp0);
+                }
+            }
+
+            // Load & save a scene that contains a prefab and references its objects
+            {
+                {
+                    Scene.Clear();
+
+                    SceneObject parentSO0 = new SceneObject("parentSO0", false);
+                    SceneObject parentSO1 = new SceneObject("parentSO1", false);
+                    SceneObject parentSO1_0 = new SceneObject("parentSO1_0", false);
+
+                    parentSO1_0.Parent = parentSO1;
+
+                    UT1_Component1 parentComp1_0 = parentSO1_0.AddComponent<UT1_Component1>();
+
+                    Prefab scene0Prefab = ProjectLibrary.Load<Prefab>("unitTest4Scene_0");
+                    SceneObject prefabInstance = scene0Prefab.Instantiate();
+                    prefabInstance.Parent = parentSO0;
+
+                    SceneObject so1 = prefabInstance.FindChild("so1", false);
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    parentComp1_0.otherSO = so1_0;
+                    parentComp1_0.otherComponent2 = comp0_1_0;
+
+                    EditorApplication.SaveScene("unitTest4Scene_1");
+                }
+                {
+                    EditorApplication.LoadScene("unitTest4Scene_1");
+
+                    SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
+                    SceneObject parentSO1_0 = parentSO0.FindChild("parentSO1_0", false);
+
+                    UT1_Component1 parentComp1_0 = parentSO1_0.GetComponent<UT1_Component1>();
+
+                    SceneObject prefabInstance = parentSO0.GetChild(0);
+                    SceneObject so1 = prefabInstance.FindChild("so1");
+                    SceneObject so1_0 = so1.FindChild("so1_0");
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0");
+
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    DebugUnit.Assert(parentComp1_0.otherSO == so1_0);
+                    DebugUnit.Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+                }
+            }
+
+            // Modify prefab, reload the scene and ensure it is updated with modified prefab
+            {
+                {
+                    Scene.Load("unitTest4Scene_0");
+
+                    SceneObject sceneRoot = Scene.Root;
+                    SceneObject so0 = sceneRoot.FindChild("so0", false);
+                    SceneObject so0_0 = so0.FindChild("so0_0", false);
+                    SceneObject so1 = sceneRoot.FindChild("so1", false);
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+
+                    SceneObject so1_1 = new SceneObject("so1_1");
+                    so1_1.Parent = so1_0;
+
+                    so0.RemoveComponent<UT1_Component1>();
+                    UT1_Component1 comp1 = so1.AddComponent<UT1_Component1>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    so0_0.Destroy();
+
+                    comp1.otherSO = so1_0;
+                    comp1.otherComponent2 = comp0_1_0;
+
+                    comp0_1_0.otherSO = so1_1;
+                    comp0_1_0.otherComponent2 = comp1;
+                    comp0_1_0.a = 123;
+                    comp0_1_0.b = "modifiedValue";
+
+                    so1.Name = "so1_modified";
+                    so1.Position = new Vector3(0, 999.0f, 0.0f);
+
+                    EditorApplication.SaveScene("unitTest4Scene_0");
+                }
+
+                {
+                    EditorApplication.LoadScene("unitTest4Scene_1");
+
+                    SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
+                    SceneObject parentSO1_0 = parentSO0.FindChild("parentSO1_0", false);
+
+                    UT1_Component1 parentComp1_0 = parentSO1_0.GetComponent<UT1_Component1>();
+
+                    SceneObject prefabInstance = parentSO0.GetChild(0);
+                    SceneObject so0 = prefabInstance.FindChild("so0", false);
+                    SceneObject so1 = prefabInstance.FindChild("so1_modified", false);
+                    SceneObject so0_0 = so0.FindChild("so0_0", false);
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+                    SceneObject so1_1 = so1_0.FindChild("so1_1", false);
+
+                    UT1_Component1 comp0 = so0.GetComponent<UT1_Component1>();
+                    UT1_Component1 comp1 = so1.GetComponent<UT1_Component1>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    DebugUnit.Assert(parentComp1_0.otherSO == so1_0);
+                    DebugUnit.Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+                    DebugUnit.Assert(so1_1 != null);
+                    DebugUnit.Assert(so0_0 == null);
+                    DebugUnit.Assert(comp0 == null);
+                    DebugUnit.Assert(comp0_1_0.otherSO == so1_1);
+                    DebugUnit.Assert(comp0_1_0.otherComponent2 == comp1);
+                    DebugUnit.Assert(comp1.otherSO == so1_0);
+                    DebugUnit.Assert(comp1.otherComponent2 == comp0_1_0);
+                    DebugUnit.Assert(MathEx.ApproxEquals(so1.Position.y, 999.0f));
+                }
+            }
+
+            // Make instance specific changes to the prefab, modify the prefab itself and ensure
+            // both changes persist
+            {
+                // Create new scene referencing the prefab and make instance modifications
+                {
+                    Scene.Clear();
+
+                    SceneObject parent2SO0 = new SceneObject("parent2SO0");
+                    SceneObject parent2SO1 = new SceneObject("parent2SO1");
+                    SceneObject parent2SO1_0 = new SceneObject("parent2SO1_0");
+
+                    parent2SO1_0.Parent = parent2SO1;
+
+                    UT1_Component1 parentComp1_0 = parent2SO1_0.AddComponent<UT1_Component1>();
+
+                    Prefab scene0Prefab = ProjectLibrary.Load<Prefab>("unitTest4Scene_0");
+                    SceneObject prefabInstance = scene0Prefab.Instantiate();
+                    prefabInstance.Parent = parent2SO0;
+
+                    SceneObject so0 = prefabInstance.FindChild("so0", false);
+                    SceneObject so1 = prefabInstance.FindChild("so1", false);
+                    
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so1_1 = so1_0.FindChild("so1_1", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+
+                    UT1_Component2 comp1 = so1.GetComponent<UT1_Component2>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    SceneObject so0_0 = new SceneObject("so0_0");
+                    so0_0.Parent = so0;
+                    UT1_Component1 comp0 = so0.AddComponent<UT1_Component1>();
+
+                    so1.RemoveComponent<UT1_Component1>();
+                    so1_1.Destroy();
+
+                    comp0.otherSO = so0_1_0;
+                    comp0.otherComponent = comp1;
+
+                    parentComp1_0.otherSO = so1_0;
+                    parentComp1_0.otherComponent2 = comp0_1_0;
+
+                    comp0_1_0.otherSO = parent2SO1_0;
+                    comp0_1_0.otherComponent2 = parentComp1_0;
+                    comp0_1_0.b = "instanceValue";
+
+                    EditorApplication.SaveScene("unitTest4Scene_2");
+                }
+
+                // Reload the scene and ensure instance modifications remain
+                {
+                    EditorApplication.LoadScene("unitTest4Scene_2");
+
+                    SceneObject root = Scene.Root;
+                    SceneObject parent2SO0 = root.FindChild("parent2SO0", false);
+                    SceneObject parent2SO1 = root.FindChild("parent2SO1", false);
+                    SceneObject parent2SO1_0 = root.FindChild("parent2SO1_0", false);
+
+                    SceneObject prefabInstance = parent2SO0.GetChild(0);
+
+                    SceneObject so0 = prefabInstance.FindChild("so0", false);
+                    SceneObject so1 = prefabInstance.FindChild("so1", false);
+                    SceneObject so0_0 = so0.FindChild("so0_0", false);
+                    SceneObject so1_0 = so1.FindChild("so1_0", false);
+                    SceneObject so1_1 = so1_0.FindChild("so1_1", false);
+                    SceneObject so0_1_0 = so1_0.FindChild("so0_1_0", false);
+
+                    UT1_Component1 parentComp1_0 = parent2SO1_0.GetComponent<UT1_Component1>();
+                    UT1_Component1 comp0 = so0.GetComponent<UT1_Component1>();
+                    UT1_Component2 comp1 = so1.GetComponent<UT1_Component2>();
+                    UT1_Component1 comp11 = so1.GetComponent<UT1_Component1>();
+                    UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                    DebugUnit.Assert(so0_0 != null);
+                    DebugUnit.Assert(comp0 != null);
+                    DebugUnit.Assert(so1_1 == null);
+                    DebugUnit.Assert(comp11 == null);
+
+                    DebugUnit.Assert(comp0.otherSO == so0_1_0);
+                    DebugUnit.Assert(comp0.otherComponent == comp1);
+
+                    DebugUnit.Assert(parentComp1_0.otherSO == so1_0);
+                    DebugUnit.Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+
+                    DebugUnit.Assert(comp0_1_0.otherSO == parent2SO1_0);
+                    DebugUnit.Assert(comp0_1_0.otherComponent2 == parentComp1_0);
+                    DebugUnit.Assert(comp0_1_0.b == "instanceValue");
+                }
+
+                // Load original scene and ensure instance modifications didn't influence it
+                {
+                    // TODO
+                }
+
+                // Modify prefab and ensure both prefab and instance modifications remain
+                {
+                    // TODO
+                }
+            }
+
+            if (!string.IsNullOrEmpty(oldScene))
+                Scene.Load(ProjectLibrary.GetPath(oldScene));
+            else
+                Scene.Clear();
+
+            ProjectLibrary.Delete("unitTest4Scene_0");
+        }
+
         /// <summary>
         /// Runs all tests.
         /// </summary>
@@ -221,6 +527,7 @@ namespace BansheeEditor
             UnitTest1_ManagedSerialization();
             UnitTest2_SerializableProperties();
             UnitTest3_ManagedDiff();
+            UnitTest4_Prefabs();
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 12 - 0
MBansheeEngine/Math/MathEx.cs

@@ -561,5 +561,17 @@ namespace BansheeEngine
 
             return new Degree(angleVal);
         }
+
+        /// <summary>
+        /// Compares two floating point numbers with an error margin.
+        /// </summary>
+        /// <param name="a">First number to compare.</param>
+        /// <param name="b">Second number to compare.</param>
+        /// <param name="epsilon">Error margin within which the numbers should be considered equal.</param>
+        /// <returns>True if equal, false otherwise.</returns>
+        public static bool ApproxEquals(float a, float b, float epsilon = 1.192092896e-07F)
+        {
+            return Abs(b - a) <= epsilon;
+        }
     }
 }

+ 11 - 0
MBansheeEngine/Scene.cs

@@ -20,6 +20,14 @@ namespace BansheeEngine
         private static string activeSceneName = "Unnamed";
         private static string activeSceneUUID = "";
 
+        /// <summary>
+        /// Returns the root scene object for the current scene.
+        /// </summary>
+        public static SceneObject Root
+        {
+            get { return Internal_GetRoot(); }
+        }
+
         /// <summary>
         /// Clears all scene objects from the current scene.
         /// </summary>
@@ -49,6 +57,9 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Prefab Internal_LoadScene(string path);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SceneObject Internal_GetRoot();
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_ClearScene();
     }

+ 30 - 0
MBansheeEngine/SceneObject.cs

@@ -319,6 +319,30 @@ namespace BansheeEngine
             return Internal_GetChild(mCachedPtr, idx);
         }
 
+        /// <summary>
+        /// Searches the child objects for an object matching the specified name.
+        /// </summary>
+        /// <param name="name">Name of the object to locate.</param>
+        /// <param name="recursive">If true all descendants of the scene object will be searched, otherwise only immediate 
+        ///                         children.</param>
+        /// <returns>First found scene object, or empty handle if none found.</returns>
+        public SceneObject FindChild(string name, bool recursive = true)
+        {
+            return Internal_FindChild(mCachedPtr, name, recursive);
+        }
+
+        /// <summary>
+        /// Searches the child objects for objects matching the specified name.
+        /// </summary>
+        /// <param name="name">Name of the objects to locate.</param>
+        /// <param name="recursive">If true all descendants of the scene object will be searched, otherwise only immediate 
+        ///                         children.</param>
+        /// <returns>All scene objects matching the specified name.</returns>
+        public SceneObject[] FindChildren(string name, bool recursive = true)
+        {
+            return Internal_FindChildren(mCachedPtr, name, recursive);
+        }
+
         /// <summary>
         /// Orients the object so it is looking at the provided location.
         /// </summary>
@@ -430,6 +454,12 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern SceneObject Internal_GetChild(IntPtr nativeInstance, int idx);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SceneObject Internal_FindChild(IntPtr nativeInstance, string name, bool recursive);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SceneObject[] Internal_FindChildren(IntPtr nativeInstance, string name, bool recursive);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Prefab Internal_GetPrefab(IntPtr nativeInstance);
 

+ 1 - 0
SBansheeEngine/Include/BsScriptScene.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static MonoObject* internal_LoadScene(MonoString* path);
+		static MonoObject* internal_GetRoot();
 		static void internal_ClearScene();
 	};
 }

+ 2 - 0
SBansheeEngine/Include/BsScriptSceneObject.h

@@ -73,6 +73,8 @@ namespace BansheeEngine
 
 		static void internal_getNumChildren(ScriptSceneObject* nativeInstance, UINT32* value);
 		static MonoObject* internal_getChild(ScriptSceneObject* nativeInstance, UINT32 idx);
+		static MonoObject* internal_findChild(ScriptSceneObject* nativeInstance, MonoString* name, bool recursive);
+		static MonoArray* internal_findChildren(ScriptSceneObject* nativeInstance, MonoString* name, bool recursive);
 
 		static void internal_getPosition(ScriptSceneObject* nativeInstance, Vector3* value);
 		static void internal_getLocalPosition(ScriptSceneObject* nativeInstance, Vector3* value);

+ 11 - 0
SBansheeEngine/Source/BsScriptScene.cpp

@@ -8,9 +8,11 @@
 #include "BsPrefab.h"
 #include "BsApplication.h"
 #include "BsSceneObject.h"
+#include "BsScriptGameObjectManager.h"
 #include "BsGameResourceManager.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptPrefab.h"
+#include "BsScriptSceneObject.h"
 
 namespace BansheeEngine
 {
@@ -21,6 +23,7 @@ namespace BansheeEngine
 	void ScriptScene::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_LoadScene", &ScriptScene::internal_LoadScene);
+		metaData.scriptClass->addInternalCall("Internal_GetRoot", &ScriptScene::internal_GetRoot);
 		metaData.scriptClass->addInternalCall("Internal_ClearScene", &ScriptScene::internal_ClearScene);
 	}
 
@@ -57,6 +60,14 @@ namespace BansheeEngine
 		}
 	}
 
+	MonoObject* ScriptScene::internal_GetRoot()
+	{
+		HSceneObject root = SceneManager::instance().getRootNode();
+
+		ScriptSceneObject* scriptRoot = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(root);
+		return scriptRoot->getManagedInstance();
+	}
+
 	void ScriptScene::internal_ClearScene()
 	{
 		gSceneManager().clearScene();

+ 43 - 3
SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -31,6 +31,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetParent", &ScriptSceneObject::internal_setParent);
 		metaData.scriptClass->addInternalCall("Internal_GetNumChildren", &ScriptSceneObject::internal_getNumChildren);
 		metaData.scriptClass->addInternalCall("Internal_GetChild", &ScriptSceneObject::internal_getChild);
+		metaData.scriptClass->addInternalCall("Internal_FindChild", &ScriptSceneObject::internal_findChild);
+		metaData.scriptClass->addInternalCall("Internal_FindChildren", &ScriptSceneObject::internal_findChildren);
 
 		metaData.scriptClass->addInternalCall("Internal_GetPosition", &ScriptSceneObject::internal_getPosition);
 		metaData.scriptClass->addInternalCall("Internal_GetLocalPosition", &ScriptSceneObject::internal_getLocalPosition);
@@ -148,13 +150,51 @@ namespace BansheeEngine
 		}
 
 		HSceneObject childSO = nativeInstance->mSceneObject->getChild(idx);
-		ScriptSceneObject* childScriptSO = ScriptGameObjectManager::instance().getScriptSceneObject(childSO);
-		if(childScriptSO == nullptr)
-			childScriptSO = ScriptGameObjectManager::instance().createScriptSceneObject(childSO);
+		ScriptSceneObject* childScriptSO = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(childSO);
 
 		return childScriptSO->getManagedInstance();
 	}
 
+	MonoObject* ScriptSceneObject::internal_findChild(ScriptSceneObject* nativeInstance, MonoString* name, bool recursive)
+	{
+		if (checkIfDestroyed(nativeInstance))
+			return nullptr;
+
+		String nativeName = MonoUtil::monoToString(name);
+		HSceneObject child = nativeInstance->getNativeSceneObject()->findChild(nativeName, recursive);
+
+		if (child == nullptr)
+			return nullptr;
+
+		ScriptSceneObject* scriptChild = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(child);
+		return scriptChild->getManagedInstance();
+	}
+
+	MonoArray* ScriptSceneObject::internal_findChildren(ScriptSceneObject* nativeInstance, MonoString* name, bool recursive)
+	{
+		if (checkIfDestroyed(nativeInstance))
+		{
+			ScriptArray emptyArray = ScriptArray::create<ScriptSceneObject>(0);
+			return emptyArray.getInternal();
+		}
+
+		String nativeName = MonoUtil::monoToString(name);
+		Vector<HSceneObject> children = nativeInstance->getNativeSceneObject()->findChildren(nativeName, recursive);
+
+		UINT32 numChildren = (UINT32)children.size();
+		ScriptArray output = ScriptArray::create<ScriptSceneObject>(numChildren);
+
+		for (UINT32 i = 0; i < numChildren; i++)
+		{
+			HSceneObject child = children[i];
+			ScriptSceneObject* scriptChild = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(child);
+
+			output.set(i, scriptChild->getManagedInstance());
+		}
+
+		return output.getInternal();
+	}
+
 	void ScriptSceneObject::internal_getPosition(ScriptSceneObject* nativeInstance, Vector3* value)
 	{
 		if (!checkIfDestroyed(nativeInstance))