Просмотр исходного кода

Managed SceneObject instances are now kept alive until underlying scene object is destroyed

BearishSun 10 лет назад
Родитель
Сommit
2fd1e02aad

+ 5 - 0
BansheeCore/Include/BsGameObjectManager.h

@@ -81,6 +81,11 @@ namespace BansheeEngine
 		 */
 		void destroyQueuedObjects();
 
+		/**
+		 * @brief	Triggered when a game object is being destroyed.
+		 */
+		Event<void(const HGameObject&)> onDestroyed;
+
 		/************************************************************************/
 		/* 							DESERIALIZATION                      		*/
 		/************************************************************************/

+ 2 - 0
BansheeCore/Source/BsGameObjectManager.cpp

@@ -82,6 +82,8 @@ namespace BansheeEngine
 	void GameObjectManager::unregisterObject(const GameObjectHandleBase& object)
 	{
 		mObjects.erase(object->getInstanceId());
+
+		onDestroyed(object);
 	}
 
 	void GameObjectManager::startDeserialization()

+ 23 - 4
SBansheeEngine/Include/BsScriptGameObjectManager.h

@@ -48,7 +48,7 @@ namespace BansheeEngine
 		ScriptSceneObject* createScriptSceneObject(MonoObject* existingInstance, const HSceneObject& sceneObject);
 
 		/**
-		 * @brief	Connects an existing managed Component instance with the native Component by creating
+		 * @brief	Connects an existing managed ManagedComponent instance with the native ManagedComponent by creating
 		 *			the interop object. Throws an exception if the interop object already exists.
 		 */
 		ScriptComponent* createScriptComponent(MonoObject* existingInstance, const GameObjectHandle<ManagedComponent>& component);
@@ -71,6 +71,12 @@ namespace BansheeEngine
 		 */
 		ScriptSceneObject* getScriptSceneObject(const HSceneObject& sceneObject) const;
 
+		/**
+		 * @brief	Attempts to find the interop object for a managed scene object with the specified instance ID. 
+		 * 			If one cannot be found null is returned.
+		 */
+		ScriptSceneObject* getScriptSceneObject(UINT64 instanceId) const;
+
 		/**
 		 * @brief	Attempts to find the interop object for a GameObject with the specified instance ID. 
 		 *			If one cannot be found null is returned.
@@ -78,9 +84,14 @@ namespace BansheeEngine
 		ScriptGameObjectBase* getScriptGameObject(UINT64 instanceId) const;
 
 		/**
-		 * @brief	Destroys and unregisters the specified GameObject interop object.
+		 * @brief	Destroys and unregisters the specified SceneObject interop object.
+		 */
+		void destroyScriptSceneObject(ScriptSceneObject* sceneObject);
+
+		/**
+		 * @brief	Destroys and unregisters the specified ManagedComponent interop object.
 		 */
-		void destroyScriptGameObject(ScriptGameObjectBase* gameObject);
+		void destroyScriptComponent(ScriptComponent* component);
 
 	private:
 		/**
@@ -90,7 +101,15 @@ namespace BansheeEngine
 		 */
 		void sendComponentResetEvents();
 
-		UnorderedMap<UINT64, ScriptGameObjectEntry> mScriptGameObjects;
+		/**
+		 * @brief	Triggered when the any game object is destroyed.
+		 */
+		void onGameObjectDestroyed(const HGameObject& go);
+
+		UnorderedMap<UINT64, ScriptComponent*> mScriptComponents;
+		UnorderedMap<UINT64, ScriptSceneObject*> mScriptSceneObjects;
+
 		HEvent mOnAssemblyReloadDoneConn;
+		HEvent onGameObjectDestroyedConn;
 	};
 }

+ 11 - 0
SBansheeEngine/Include/BsScriptSceneObject.h

@@ -45,7 +45,18 @@ namespace BansheeEngine
 		 */
 		void _onManagedInstanceDeleted() override;
 
+		/**
+		 * @copydoc	ScriptObjectBase::_createManagedInstance
+		 */
+		MonoObject* _createManagedInstance(bool construct) override;
+
+		/**
+		 * @brief	Triggered by the script game object manager when the handle this object is referencing is destroyed.
+		 */
+		void _notifyDestroyed();
+
 		HSceneObject mSceneObject;
+		uint32_t mManagedHandle;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/

+ 1 - 1
SBansheeEngine/Source/BsScriptComponent.cpp

@@ -201,7 +201,7 @@ namespace BansheeEngine
 		// is still kept during assembly refresh. Such components shouldn't be restored
 		// so we delete them.
 		if (!mRefreshInProgress || mManagedComponent.isDestroyed(true))
-			ScriptGameObjectManager::instance().destroyScriptGameObject(this);
+			ScriptGameObjectManager::instance().destroyScriptComponent(this);
 	}
 
 	void ScriptComponent::setNativeHandle(const HGameObject& gameObject)

+ 71 - 32
SBansheeEngine/Source/BsScriptGameObjectManager.cpp

@@ -2,6 +2,7 @@
 #include "BsScriptGameObject.h"
 #include "BsScriptComponent.h"
 #include "BsScriptSceneObject.h"
+#include "BsGameObjectManager.h"
 #include "BsGameObject.h"
 #include "BsComponent.h"
 #include "BsManagedComponent.h"
@@ -12,6 +13,8 @@
 #include "BsScriptAssemblyManager.h"
 #include "BsScriptObjectManager.h"
 
+using namespace std::placeholders;
+
 namespace BansheeEngine
 {
 	ScriptGameObjectManager::ScriptGameObjectEntry::ScriptGameObjectEntry()
@@ -27,11 +30,15 @@ namespace BansheeEngine
 		// Calls OnReset on all components after assembly reload happens
 		mOnAssemblyReloadDoneConn = ScriptObjectManager::instance().onRefreshComplete.connect(
 			std::bind(&ScriptGameObjectManager::sendComponentResetEvents, this));
+
+		onGameObjectDestroyedConn = GameObjectManager::instance().onDestroyed.connect(
+			std::bind(&ScriptGameObjectManager::onGameObjectDestroyed, this, _1));
 	}
 
 	ScriptGameObjectManager::~ScriptGameObjectManager()
 	{
 		mOnAssemblyReloadDoneConn.disconnect();
+		onGameObjectDestroyedConn.disconnect();
 	}
 
 	ScriptSceneObject* ScriptGameObjectManager::getOrCreateScriptSceneObject(const HSceneObject& sceneObject)
@@ -53,81 +60,113 @@ namespace BansheeEngine
 
 	ScriptSceneObject* ScriptGameObjectManager::createScriptSceneObject(MonoObject* existingInstance, const HSceneObject& sceneObject)
 	{
-		ScriptGameObjectBase* go = getScriptGameObject(sceneObject.getInstanceId());
-		if (go != nullptr)
-		{
-			BS_EXCEPT(InvalidStateException, "Script component for this SceneObject already exists.");
-		}
+		ScriptSceneObject* so = getScriptSceneObject(sceneObject);
+		if (so != nullptr)
+			BS_EXCEPT(InvalidStateException, "Script object for this SceneObject already exists.");
 
 		ScriptSceneObject* nativeInstance = new (bs_alloc<ScriptSceneObject>()) ScriptSceneObject(existingInstance, sceneObject);
-		mScriptGameObjects[sceneObject.getInstanceId()] = ScriptGameObjectEntry(nativeInstance, false);
+		mScriptSceneObjects[sceneObject.getInstanceId()] = nativeInstance;
 
 		return nativeInstance;
 	}
 
 	ScriptComponent* ScriptGameObjectManager::createScriptComponent(MonoObject* existingInstance, const GameObjectHandle<ManagedComponent>& component)
 	{
-		ScriptGameObjectBase* go = getScriptGameObject(component.getInstanceId());
-		if (go != nullptr)
-		{
-			BS_EXCEPT(InvalidStateException, "Script component for this Component already exists.");
-		}
+		ScriptGameObjectBase* comp = getScriptComponent(component.getInstanceId());
+		if (comp != nullptr)
+			BS_EXCEPT(InvalidStateException, "Script object for this Component already exists.");
 
 		ScriptComponent* nativeInstance = new (bs_alloc<ScriptComponent>()) ScriptComponent(existingInstance);
 		nativeInstance->setNativeHandle(component);
-		mScriptGameObjects[component->getInstanceId()] = ScriptGameObjectEntry(nativeInstance, true);
+		mScriptComponents[component->getInstanceId()] = nativeInstance;
 
 		return nativeInstance;
 	}
 
 	ScriptComponent* ScriptGameObjectManager::getScriptComponent(const GameObjectHandle<ManagedComponent>& component) const
 	{
-		ScriptGameObjectBase* go = getScriptGameObject(component.getInstanceId());
-		return static_cast<ScriptComponent*>(go);
+		auto findIter = mScriptComponents.find(component.getInstanceId());
+		if (findIter != mScriptComponents.end())
+			return findIter->second;
+
+		return nullptr;
 	}
 
 	ScriptComponent* ScriptGameObjectManager::getScriptComponent(UINT64 instanceId) const
 	{
-		ScriptGameObjectBase* go = getScriptGameObject(instanceId);
-		return static_cast<ScriptComponent*>(go);
+		auto findIter = mScriptComponents.find(instanceId);
+		if (findIter != mScriptComponents.end())
+			return findIter->second;
+
+		return nullptr;
 	}
 
 	ScriptSceneObject* ScriptGameObjectManager::getScriptSceneObject(const HSceneObject& sceneObject) const
 	{
-		ScriptGameObjectBase* go = getScriptGameObject(sceneObject.getInstanceId());
-		return static_cast<ScriptSceneObject*>(go);
+		auto findIter = mScriptSceneObjects.find(sceneObject.getInstanceId());
+		if (findIter != mScriptSceneObjects.end())
+			return findIter->second;
+
+		return nullptr;
+	}
+
+	ScriptSceneObject* ScriptGameObjectManager::getScriptSceneObject(UINT64 instanceId) const
+	{
+		auto findIter = mScriptSceneObjects.find(instanceId);
+		if (findIter != mScriptSceneObjects.end())
+			return findIter->second;
+
+		return nullptr;
 	}
 
 	ScriptGameObjectBase* ScriptGameObjectManager::getScriptGameObject(UINT64 instanceId) const
 	{
-		auto findIter = mScriptGameObjects.find(instanceId);
-		if (findIter != mScriptGameObjects.end())
-			return static_cast<ScriptSceneObject*>(findIter->second.instance);
+		auto findIter = mScriptSceneObjects.find(instanceId);
+		if (findIter != mScriptSceneObjects.end())
+			return findIter->second;
+
+		auto findIter2 = mScriptComponents.find(instanceId);
+		if (findIter2 != mScriptComponents.end())
+			return findIter2->second;
 
 		return nullptr;
 	}
 
-	void ScriptGameObjectManager::destroyScriptGameObject(ScriptGameObjectBase* gameObject)
+	void ScriptGameObjectManager::destroyScriptSceneObject(ScriptSceneObject* sceneObject)
 	{
-		UINT64 instanceId = gameObject->getNativeHandle().getInstanceId();
-		mScriptGameObjects.erase(instanceId);
+		UINT64 instanceId = sceneObject->getNativeHandle().getInstanceId();
+		mScriptSceneObjects.erase(instanceId);
 
-		bs_delete(gameObject);
+		bs_delete(sceneObject);
 	}
 
-	void ScriptGameObjectManager::sendComponentResetEvents()
+	void ScriptGameObjectManager::destroyScriptComponent(ScriptComponent* component)
 	{
-		for (auto& scriptObjectEntry : mScriptGameObjects)
-		{
-			const ScriptGameObjectEntry& entry = scriptObjectEntry.second;
+		UINT64 instanceId = component->getNativeHandle().getInstanceId();
+		mScriptComponents.erase(instanceId);
 
-			if (!entry.isComponent)
-				continue;
+		bs_delete(component);
+	}
 
-			ScriptComponent* scriptComponent = static_cast<ScriptComponent*>(entry.instance);
+	void ScriptGameObjectManager::sendComponentResetEvents()
+	{
+		for (auto& scriptObjectEntry : mScriptComponents)
+		{
+			ScriptComponent* scriptComponent = scriptObjectEntry.second;
 			HManagedComponent component = scriptComponent->getNativeHandle();
 
 			component->triggerOnReset();
 		}
 	}
+
+	void ScriptGameObjectManager::onGameObjectDestroyed(const HGameObject& go)
+	{
+		UINT64 instanceId = go.getInstanceId();
+
+		ScriptSceneObject* so = getScriptSceneObject(instanceId);
+		if (so == nullptr)
+			return;
+
+		so->_notifyDestroyed();
+	}
 }

+ 21 - 2
SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -17,7 +17,7 @@ namespace BansheeEngine
 	ScriptSceneObject::ScriptSceneObject(MonoObject* instance, const HSceneObject& sceneObject)
 		:ScriptObject(instance), mSceneObject(sceneObject)
 	{
-
+		mManagedHandle = mono_gchandle_new(instance, false);
 	}
 
 	void ScriptSceneObject::initRuntimeData()
@@ -343,7 +343,26 @@ namespace BansheeEngine
 		mManagedInstance = nullptr;
 
 		if (!mRefreshInProgress)
-			ScriptGameObjectManager::instance().destroyScriptGameObject(this);
+			ScriptGameObjectManager::instance().destroyScriptSceneObject(this);
+		else
+		{
+			mono_gchandle_free(mManagedHandle);
+			mManagedHandle = 0;
+		}
+	}
+
+	MonoObject* ScriptSceneObject::_createManagedInstance(bool construct)
+	{
+		MonoObject* managedInstance = metaData.scriptClass->createInstance(construct);
+		mManagedHandle = mono_gchandle_new(managedInstance, false);
+
+		return managedInstance;
+	}
+
+	void ScriptSceneObject::_notifyDestroyed()
+	{
+		mono_gchandle_free(mManagedHandle);
+		mManagedHandle = 0;
 	}
 
 	void ScriptSceneObject::setNativeHandle(const HGameObject& gameObject)