Browse Source

Native components can now be in paused or stopped state, in an attempt to unify native/managed component logic

BearishSun 8 years ago
parent
commit
d8e62567db

+ 65 - 11
Source/BansheeCore/Include/BsComponent.h

@@ -12,7 +12,34 @@ namespace bs
 	 *  @{
 	 */
 
-	/** Components represent primary logic elements in the scene. They are attached to scene objects. */
+	/** Flags that control behavior of a Component. */
+	enum class ComponentFlag
+	{
+		AlwaysRun = 1 /**< Ensures that scene manager cannot pause or stop component callbacks from executing. Off by default. */
+	};
+
+	typedef Flags<ComponentFlag> ComponentFlags;
+	BS_FLAGS_OPERATORS(ComponentFlag)
+
+	/** 
+	 * Components represent primary logic elements in the scene. They are attached to scene objects. 
+	 *
+	 * You should implement some or all of update/onCreated/onInitialized/onEnabled/onDisabled/onTransformChanged/
+	 * onDestroyed methods to implement the relevant component logic. Avoid putting logic in constructors or destructors.
+	 *
+	 * Components can be in different states. These states control which of the events listed above trigger:
+	 *  - Running - Scene manager is sending out events.  
+	 *  - Paused - Scene manager is sending out all events except per-frame update(). 
+	 *	- Stopped - Scene manager is not sending out events except for onCreated/onDestroyed.
+	 *	
+	 * These states can be changed globally though SceneManager and affect all components. Individual components can
+	 * override these states in two ways:
+	 *  - Set the ComponentFlag::AlwaysRun to true and the component will always stay in Running state, regardless of
+	 *    state set in SceneManager. This flag should be set in constructor and not change during component lifetime.
+	 *  - If the component's parent SceneObject is inactive (SceneObject::setActive(false)), or any of his parents are
+	 *    inactive, then the component is considered to be in Stopped state, regardless whether the ComponentFlag::AlwaysRun
+	 *    flag is set or not.
+	 **/
 	class BS_CORE_EXPORT Component : public GameObject
 	{
 	public:
@@ -25,11 +52,7 @@ namespace bs
 		/**	Returns a handle to this object. */
 		HComponent getHandle() const { return mThisHandle; }
 
-		/**
-		 * Called once per frame on all components.
-		 * 			
-		 * @note	Internal method.
-		 */
+		/** Called once per frame. Only called if the component is in Running state. */
 		virtual void update() { }
 
 		/**
@@ -76,30 +99,59 @@ namespace bs
 
 		/** @} */
 	protected:
+		friend class SceneManager;
 		friend class SceneObject;
 		friend class SceneObjectRTTI;
 
 		Component(const HSceneObject& parent);
 		virtual ~Component();
 
-		/**	Called when the component is ready to be initialized. */
+		/** Called once when the component has been created. Called regardless of the state the component is in. */
+		virtual void onCreated() {}
+
+		/**	
+		 * Called once when the component first leaves the Stopped state. This includes component creation if requirements
+		 * for leaving Stopped state are met, in which case it is called after onCreated. 
+		 */
 		virtual void onInitialized() {}
 
-		/**	Called just before the component is destroyed. */
+		/**	Called once just before the component is destroyed. Called regardless of the state the component is in. */
 		virtual void onDestroyed() {}
 
-		/**	Called just before the component is deactivated or destroyed. */
+		/**	
+		 * Called every time a component is placed into the Stopped state. This includes component destruction if component
+		 * wasn't already in Stopped state during destruction. When called during destruction it is called before
+		 * onDestroyed. 
+		 */
 		virtual void onDisabled() {}
 
-		/**	Called when the component is activated or created. */
+		/**	
+		 * Called every time a component leaves the Stopped state. This includes component creation if requirements
+		 * for leaving the Stopped state are met. When called during creation it is called after onInitialized. 
+		 */
 		virtual void onEnabled() {}
 
-		/** Called when the component's parent scene object has changed. */
+		/** 
+		 * Called when the component's parent scene object has changed. Not called if the component is in Stopped state. 
+		 * Also only called if necessary notify flags are set via _setNotifyFlags().
+		 */
 		virtual void onTransformChanged(TransformChangedFlags flags) { }
 
 		/** Checks whether the component wants to received the specified transform changed message. */
 		bool supportsNotify(TransformChangedFlags flags) const { return (mNotifyFlags & flags) != 0; }
 
+		/** Enables or disabled a flag controlling component's behaviour. */
+		void setFlag(ComponentFlag flag, bool enabled) { enabled ? mFlags.set(flag) : mFlags.unset(flag); }
+
+		/** Checks if the component has a certain flag enabled. */
+		bool hasFlag(ComponentFlag flag) const { return mFlags.isSet(flag); }
+
+		/** Sets an index that uniquely identifies a component with the SceneManager. */
+		void setSceneManagerIdx(UINT32 idx) { mSceneManagerIdx = idx; }
+
+		/** Returns an index that unique identifies a component with the SceneManager. */
+		UINT32 getSceneManagerIdx() const { return mSceneManagerIdx; }
+
 		/**
 		 * Destroys this component.
 		 *
@@ -116,6 +168,8 @@ namespace bs
 	protected:
 		HComponent mThisHandle;
 		TransformChangedFlags mNotifyFlags;
+		ComponentFlags mFlags;
+		UINT32 mSceneManagerIdx;
 
 	private:
 		HSceneObject mParent;

+ 47 - 0
Source/BansheeCore/Include/BsSceneManager.h

@@ -51,6 +51,14 @@ namespace bs
 		HSceneObject sceneObject;
 	};
 
+	/** Possible states components can be in. Controls which component callbacks are triggered. */
+	enum class ComponentState
+	{
+		Running, /**< All components callbacks are being triggered normally. */
+		Paused, /**< All component callbacks except update are being triggered normally. */
+		Stopped /**< No component callbacks are being triggered. */
+	};
+
 	/** Manages active SceneObjects and provides ways for querying and updating them or their components. */
 	class BS_CORE_EXPORT SceneManager : public Module<SceneManager>
 	{
@@ -68,6 +76,15 @@ namespace bs
 		 */
 		void clearScene(bool forceAll = false);
 
+		/** 
+		 * Changes the component state that globally determines which component callbacks are activated. Only affects
+		 * components that don't have the ComponentFlag::AlwaysRun flag set. 
+		 */
+		void setComponentState(ComponentState state);
+
+		/** Checks are the components currently in the Running state. */
+		bool isRunning() const { return mComponentState == ComponentState::Running; }
+
 		/** Returns all cameras in the scene. */
 		const Map<Camera*, SceneCameraData>& getAllCameras() const { return mCameras; }
 
@@ -116,6 +133,24 @@ namespace bs
 		/** Updates dirty transforms on any core objects that may be tied with scene objects. */
 		void _updateCoreObjectTransforms();
 
+		/** Notifies the manager that a new component has just been created. The manager triggers necessary callbacks. */
+		void _notifyComponentCreated(const HComponent& component, bool parentActive);
+
+		/** 
+		 * Notifies the manager that a scene object the component belongs to was activated. The manager triggers necessary
+		 * callbacks. 
+		 */
+		void _notifyComponentActivated(const HComponent& component, bool triggerEvent);
+
+		/** 
+		 * Notifies the manager that a scene object the component belongs to was deactivated. The manager triggers necessary
+		 * callbacks. 
+		 */
+		void _notifyComponentDeactivated(const HComponent& component, bool triggerEvent);
+
+		/** Notifies the manager that a component is about to be destroyed. The manager triggers necessary callbacks. */
+		void _notifyComponentDestroyed(const HComponent& component);
+
 	protected:
 		friend class SceneObject;
 
@@ -135,6 +170,12 @@ namespace bs
 		/**	Callback that is triggered when the main render target size is changed. */
 		void onMainRenderTargetResized();
 
+		/** Removes a component from the active component list. */
+		void removeFromActiveList(const HComponent& component);
+
+		/** Removes a component from the inactive component list. */
+		void removeFromInactiveList(const HComponent& component);
+
 	protected:
 		HSceneObject mRootNode;
 
@@ -144,8 +185,14 @@ namespace bs
 		Map<Renderable*, SceneRenderableData> mRenderables;
 		Map<Light*, SceneLightData> mLights;
 
+		Vector<HComponent> mActiveComponents;
+		Vector<HComponent> mInactiveComponents;
+		Vector<HComponent> mUnintializedComponents;
+
 		SPtr<RenderTarget> mMainRT;
 		HEvent mMainRTResizedConn;
+
+		ComponentState mComponentState = ComponentState::Running;
 	};
 
 	/**	Provides easy access to the SceneManager. */

+ 7 - 12
Source/BansheeCore/Include/BsSceneObject.h

@@ -513,18 +513,7 @@ namespace bs
 			GameObjectHandle<T> newComponent =
 				GameObjectManager::instance().registerObject(gameObject);
 
-			newComponent->mThisHandle = newComponent;
-			mComponents.push_back(newComponent);
-
-			if (isInstantiated())
-			{
-				newComponent->_instantiate();
-				newComponent->onInitialized();
-
-				if (getActive())
-					newComponent->onEnabled();
-			}
-
+			addAndInitializeComponent(newComponent);
 			return newComponent;
 		}
 
@@ -654,6 +643,12 @@ namespace bs
 		/**	Adds the component to the internal component array. */
 		void addComponentInternal(const SPtr<Component> component);
 
+		/**	Adds the component to the internal component array, and initializes it. */
+		void addAndInitializeComponent(const HComponent& component);
+
+		/**	Adds the component to the internal component array, and initializes it. */
+		void addAndInitializeComponent(const SPtr<Component> component);
+
 		Vector<HComponent> mComponents;
 
 		/************************************************************************/

+ 1 - 0
Source/BansheeCore/Source/BsCRenderable.cpp

@@ -15,6 +15,7 @@ namespace bs
 		:Component(parent)
 	{
 		setName("Renderable");
+		setFlag(ComponentFlag::AlwaysRun, true);
 	}
 
 	void CRenderable::setMesh(HMesh mesh)

+ 2 - 2
Source/BansheeCore/Source/BsComponent.cpp

@@ -7,11 +7,11 @@
 namespace bs
 {
 	Component::Component()
-		:mNotifyFlags(TCF_None)
+		:mNotifyFlags(TCF_None), mSceneManagerIdx(-1)
 	{ }
 
 	Component::Component(const HSceneObject& parent)
-		:mNotifyFlags(TCF_None), mParent(parent)
+		:mNotifyFlags(TCF_None), mParent(parent), mSceneManagerIdx(-1)
 	{
 		setName("Component");
 	}

+ 6 - 2
Source/BansheeCore/Source/BsPrefabDiff.cpp

@@ -6,6 +6,7 @@
 #include "BsMemorySerializer.h"
 #include "BsBinarySerializer.h"
 #include "BsBinaryDiff.h"
+#include "BsSceneManager.h"
 
 namespace bs
 {
@@ -114,7 +115,7 @@ namespace bs
 			BinarySerializer bs;
 			SPtr<Component> component = std::static_pointer_cast<Component>(bs._decodeFromIntermediate(addedComponentData));
 
-			object->addComponentInternal(component);
+			object->addAndInitializeComponent(component);
 		}
 
 		for (auto& addedChildData : diff->addedChildren)
@@ -122,6 +123,9 @@ namespace bs
 			BinarySerializer bs;
 			SPtr<SceneObject> sceneObject = std::static_pointer_cast<SceneObject>(bs._decodeFromIntermediate(addedChildData));
 			sceneObject->setParent(object);
+
+			if(object->isInstantiated())
+				sceneObject->_instantiate();
 		}
 
 		for (auto& componentDiff : diff->componentDiffs)
@@ -509,4 +513,4 @@ namespace bs
 	{
 		return PrefabDiff::getRTTIStatic();
 	}
-}
+}

+ 219 - 14
Source/BansheeCore/Source/BsSceneManager.cpp

@@ -229,30 +229,235 @@ namespace bs
 		}
 	}
 
-	void SceneManager::_update()
+	void SceneManager::setComponentState(ComponentState state)
 	{
-		Stack<HSceneObject> todo;
-		todo.push(mRootNode);
+		if (mComponentState == state)
+			return;
 
-		while(!todo.empty())
+		// Wake up all components with onInitialize/onEnable events if moving to running or paused state
+		if(state == ComponentState::Running || state == ComponentState::Paused)
 		{
-			HSceneObject currentGO = todo.top();
-			todo.pop();
+			if(mComponentState == ComponentState::Stopped)
+			{
+				// Trigger enable on all components that don't have AlwaysRun flag (at this point those will be all
+				// inactive components that have active scene object parents)
+				for(auto& entry : mInactiveComponents)
+				{
+					if (entry->sceneObject()->getActive())
+						entry->onEnabled();
+				}
+
+				// Initialize and enable uninitialized components
+				for(auto& entry : mUnintializedComponents)
+				{
+					entry->onInitialized();
+
+					if (entry->sceneObject()->getActive())
+					{
+						entry->onEnabled();
+
+						UINT32 idx = (UINT32)mActiveComponents.size();
+						mActiveComponents.push_back(entry);
+
+						entry->setSceneManagerIdx(idx);
+					}
+					else
+					{
+						UINT32 idx = (UINT32)mInactiveComponents.size();
+						mInactiveComponents.push_back(entry);
+
+						entry->setSceneManagerIdx(idx);
+					}
+				}
+
+				mUnintializedComponents.clear();
+			}
+		}
 
-			if (!currentGO->getActive(true))
-				continue;
-			                  
-			const Vector<HComponent>& components = currentGO->getComponents();
+		// Start updates on all active components
+		if (state == ComponentState::Running)
+		{
+			// Move from inactive to active list
+			for(INT32 i = 0; i < (INT32)mInactiveComponents.size(); i++)
+			{
+				HComponent component = mInactiveComponents[i];
+				if (!component->sceneObject()->getActive())
+					continue;
+				
+				removeFromInactiveList(component);
+				i--; // Keep the same index next iteration to process the component we just swapped
+
+				UINT32 activeIdx = (UINT32)mActiveComponents.size();
+				mActiveComponents.push_back(component);
 
-			for(auto iter = components.begin(); iter != components.end(); ++iter)
+				component->setSceneManagerIdx(activeIdx);
+			}
+		}
+		// Stop updates on all active components
+		else if(state == ComponentState::Paused || state == ComponentState::Stopped)
+		{
+			// Trigger onDisable events if stopping
+			if (state == ComponentState::Stopped)
 			{
-				(*iter)->update();
+				for (INT32 i = 0; i < (INT32)mActiveComponents.size(); i++)
+				{
+					HComponent component = mActiveComponents[i];
+
+					bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+					if (alwaysRun)
+						continue;
+
+					component->onDisabled();
+				}
 			}
 
-			for(UINT32 i = 0; i < currentGO->getNumChildren(); i++)
-				todo.push(currentGO->getChild(i));
+			// Move from active to inactive list
+			for (INT32 i = 0; i < (INT32)mActiveComponents.size(); i++)
+			{
+				HComponent component = mActiveComponents[i];
+
+				bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+				if (alwaysRun)
+					continue;
+
+				removeFromActiveList(component);
+				i--; // Keep the same index next iteration to process the component we just swapped
+
+				UINT32 inactiveIdx = (UINT32)mInactiveComponents.size();
+				mInactiveComponents.push_back(component);
+
+				component->setSceneManagerIdx(inactiveIdx);
+			}
 		}
 
+		mComponentState = state;
+	}
+
+	void SceneManager::_notifyComponentCreated(const HComponent& component, bool parentActive)
+	{
+		component->onCreated();
+		
+		bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+		if(alwaysRun || mComponentState != ComponentState::Stopped)
+		{
+			component->onInitialized();
+
+			if (parentActive)
+			{
+				component->onEnabled();
+
+				UINT32 idx = (UINT32)mActiveComponents.size();
+				mActiveComponents.push_back(component);
+
+				component->setSceneManagerIdx(idx);
+			}
+			else
+			{
+				UINT32 idx = (UINT32)mInactiveComponents.size();
+				mInactiveComponents.push_back(component);
+
+				component->setSceneManagerIdx(idx);
+			}
+		}
+		else // Stopped
+		{
+			mUnintializedComponents.push_back(component);
+		}
+	}
+
+	void SceneManager::_notifyComponentActivated(const HComponent& component, bool triggerEvent)
+	{
+		bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+
+		if (alwaysRun || mComponentState == ComponentState::Running || mComponentState == ComponentState::Paused)
+		{
+			if (triggerEvent)
+				component->onEnabled();
+
+			removeFromInactiveList(component);
+
+			UINT32 activeIdx = (UINT32)mActiveComponents.size();
+			mActiveComponents.push_back(component);
+
+			component->setSceneManagerIdx(activeIdx);
+		}
+	}
+
+	void SceneManager::_notifyComponentDeactivated(const HComponent& component, bool triggerEvent)
+	{
+		bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+
+		if (alwaysRun || mComponentState == ComponentState::Running || mComponentState == ComponentState::Paused)
+		{
+			if (triggerEvent)
+				component->onDisabled();
+
+			removeFromActiveList(component);
+
+			UINT32 inactiveIdx = (UINT32)mInactiveComponents.size();
+			mInactiveComponents.push_back(component);
+
+			component->setSceneManagerIdx(inactiveIdx);
+		}
+	}
+
+	void SceneManager::_notifyComponentDestroyed(const HComponent& component)
+	{
+		bool alwaysRun = component->hasFlag(ComponentFlag::AlwaysRun);
+		bool isActive = component->sceneObject()->getActive() && (alwaysRun || mComponentState == ComponentState::Running);
+		bool isEnabled = component->sceneObject()->getActive() && (alwaysRun || mComponentState != ComponentState::Stopped);
+
+		if (isActive)
+			removeFromActiveList(component);
+		else
+			removeFromInactiveList(component);
+
+		if (isEnabled)
+			component->onDisabled();
+		
+		component->onDestroyed();
+	}
+
+	void SceneManager::removeFromActiveList(const HComponent& component)
+	{
+		UINT32 activeIdx = component->getSceneManagerIdx();
+		UINT32 lastActiveIdx = mActiveComponents.back()->getSceneManagerIdx();
+
+		assert(mActiveComponents[activeIdx] == component);
+
+		if (activeIdx != lastActiveIdx)
+		{
+			std::swap(mActiveComponents[activeIdx], mActiveComponents[lastActiveIdx]);
+			mActiveComponents[activeIdx]->setSceneManagerIdx(activeIdx);
+		}
+
+		mActiveComponents.erase(mActiveComponents.end() - 1);
+	}
+
+	void SceneManager::removeFromInactiveList(const HComponent& component)
+	{
+		UINT32 inactiveIdx = component->getSceneManagerIdx();
+		UINT32 lastInactiveIdx = mInactiveComponents.back()->getSceneManagerIdx();
+
+		assert(mInactiveComponents[inactiveIdx] == component);
+
+		if (inactiveIdx != lastInactiveIdx)
+		{
+			std::swap(mInactiveComponents[inactiveIdx], mInactiveComponents[lastInactiveIdx]);
+			mInactiveComponents[inactiveIdx]->setSceneManagerIdx(inactiveIdx);
+		}
+
+		mInactiveComponents.erase(mInactiveComponents.end() - 1);
+	}
+
+	void SceneManager::_update()
+	{
+		// Note: Eventually perform updates based on component types and/or on component priority. Right now we just
+		// iterate in an undefined order, but it wouldn't be hard to change it.
+
+		for (auto& entry : mActiveComponents)
+			entry->update();
+
 		GameObjectManager::instance().destroyQueuedObjects();
 	}
 

+ 29 - 30
Source/BansheeCore/Source/BsSceneObject.cpp

@@ -93,12 +93,7 @@ namespace bs
 				component->_setIsDestroyed();
 
 				if (isInstantiated())
-				{
-					if (getActive())
-						component->onDisabled();
-
-					component->onDestroyed();
-				}
+					gSceneManager()._notifyComponentDestroyed(component);
 
 				component->destroyInternal(component, true);
 				mComponents.erase(mComponents.end() - 1);
@@ -221,12 +216,7 @@ namespace bs
 		std::function<void(SceneObject*)> triggerEventsRecursive = [&](SceneObject* obj)
 		{
 			for (auto& component : obj->mComponents)
-			{
-				component->onInitialized();
-
-				if (obj->getActive())
-					component->onEnabled();
-			}
+				gSceneManager()._notifyComponentCreated(component, obj->getActive());
 
 			for (auto& child : obj->mChildren)
 			{
@@ -449,7 +439,11 @@ namespace bs
 		for(auto& entry : mComponents)
 		{
 			if (entry->supportsNotify(flags))
-				entry->onTransformChanged(flags);
+			{
+				bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun);
+				if(alwaysRun || gSceneManager().isRunning())
+					entry->onTransformChanged(flags);
+			}
 		}
 
 		for (auto& entry : mChildren)
@@ -665,12 +659,12 @@ namespace bs
 				if (activeHierarchy)
 				{
 					for (auto& component : mComponents)
-						component->onEnabled();
+						gSceneManager()._notifyComponentActivated(component, triggerEvents);
 				}
 				else
 				{
 					for (auto& component : mComponents)
-						component->onDisabled();
+						gSceneManager()._notifyComponentDeactivated(component, triggerEvents);
 				}
 			}
 		}
@@ -741,12 +735,7 @@ namespace bs
 			(*iter)->_setIsDestroyed();
 
 			if (isInstantiated())
-			{
-				if (getActive())
-					component->onDisabled();
-
-				(*iter)->onDestroyed();
-			}
+				gSceneManager()._notifyComponentDestroyed(*iter);
 			
 			(*iter)->destroyInternal(*iter, immediate);
 			mComponents.erase(iter);
@@ -784,30 +773,40 @@ namespace bs
 
 		SPtr<Component> componentPtr = std::static_pointer_cast<Component>(newObj);
 		HComponent newComponent = GameObjectManager::instance().registerObject(componentPtr);
+		newComponent->mParent = mThisHandle;
 
+		addAndInitializeComponent(newComponent);
+		return newComponent;
+	}
+
+	void SceneObject::addComponentInternal(const SPtr<Component> component)
+	{
+		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
 		newComponent->mParent = mThisHandle;
 		newComponent->mThisHandle = newComponent;
+
 		mComponents.push_back(newComponent);
+	}
+
+	void SceneObject::addAndInitializeComponent(const HComponent& component)
+	{
+		component->mThisHandle = component;
+		mComponents.push_back(component);
 
 		if (isInstantiated())
 		{
-			newComponent->_instantiate();
-			newComponent->onInitialized();
+			component->_instantiate();
 
-			if (getActive())
-				newComponent->onEnabled();
+			gSceneManager()._notifyComponentCreated(component, getActive());
 		}
-
-		return newComponent;
 	}
 
-	void SceneObject::addComponentInternal(const SPtr<Component> component)
+	void SceneObject::addAndInitializeComponent(const SPtr<Component> component)
 	{
 		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
 		newComponent->mParent = mThisHandle;
-		newComponent->mThisHandle = newComponent;
 
-		mComponents.push_back(newComponent);
+		addAndInitializeComponent(newComponent);
 	}
 
 	RTTITypeBase* SceneObject::getRTTIStatic()

+ 5 - 2
Source/MBansheeEngine/Scene/Component.cs

@@ -103,7 +103,9 @@ namespace BansheeEngine
     ///
     /// Implementations of <see cref="ManagedComponent"/> can implement a set of callbacks that will be called by the
     /// runtime at specified occassions:
-    /// void OnInitialize() - Called once when the component is instantiated. Only called when the game is playing.
+    /// void OnCreate() - Called once when the component is instantiated. 
+    /// void OnInitialize() - Called once when the component is first enabled. In case this is during instantiation, it is
+    ///                       called after OnCreate. Only called when the game is playing.
     /// void OnUpdate() - Called every frame while the game is running and the component is enabled.
     /// void OnEnable() - Called whenever a component is enabled, or instantiated as enabled in which case it is called 
     ///                   after OnInitialize. Only called when the game is playing.
@@ -115,7 +117,8 @@ namespace BansheeEngine
     ///                  initialization it is called after OnInitialize but before OnEnable. Only relevant in editor.
     /// void OnTransformChanged(TransformChangedFlags) - Called when the transform of the owning scene object changes.
     ///                                                  When and if this gets triggered depends on 
-    ///                                                  <see cref="Component.NotifyFlags"/>.
+    ///                                                  <see cref="Component.NotifyFlags"/>. Only called while game is
+    ///                                                  playing.
     ///
     /// You can also make these callbacks trigger when the game is stopped/paused by using the <see cref="RunInEditor"/>
     /// attribute on the component.

+ 6 - 1
Source/SBansheeEngine/Include/BsManagedComponent.h

@@ -84,6 +84,7 @@ namespace bs
 		 */
 		void initialize(MonoObject* object);
 
+		typedef void(__stdcall *OnCreatedThunkDef) (MonoObject*, MonoException**);
 		typedef void(__stdcall *OnInitializedThunkDef) (MonoObject*, MonoException**);
 		typedef void(__stdcall *OnUpdateThunkDef) (MonoObject*, MonoException**);
 		typedef void(__stdcall *OnDestroyedThunkDef) (MonoObject*, MonoException**);
@@ -107,6 +108,7 @@ namespace bs
 		SPtr<ManagedSerializableObject> mSerializedObjectData;
 		SPtr<ManagedSerializableObjectInfo> mObjInfo; // Transient
 
+		OnCreatedThunkDef mOnCreatedThunk;
 		OnInitializedThunkDef mOnInitializedThunk;
 		OnUpdateThunkDef mOnUpdateThunk;
 		OnResetThunkDef mOnResetThunk;
@@ -129,6 +131,9 @@ namespace bs
 		/** @copydoc Component::_instantiate */
 		void _instantiate() override;
 
+		/** @copydoc Component::onCreated */
+		void onCreated() override;
+
 		/** @copydoc Component::onInitialized */
 		void onInitialized() override;
 
@@ -160,7 +165,7 @@ namespace bs
 	public:
 		friend class ManagedComponentRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 
 	protected:
 		ManagedComponent(); // Serialization only

+ 31 - 7
Source/SBansheeEngine/Source/BsManagedComponent.cpp

@@ -18,16 +18,17 @@ namespace bs
 {
 	ManagedComponent::ManagedComponent()
 		: mManagedInstance(nullptr), mManagedClass(nullptr), mRuntimeType(nullptr), mManagedHandle(0), mRunInEditor(false)
-		, mRequiresReset(true), mMissingType(false), mOnInitializedThunk(nullptr), mOnUpdateThunk(nullptr)
-		, mOnResetThunk(nullptr), mOnDestroyThunk(nullptr), mOnDisabledThunk(nullptr), mOnEnabledThunk(nullptr)
-		, mOnTransformChangedThunk(nullptr), mCalculateBoundsMethod(nullptr)
+		, mRequiresReset(true), mMissingType(false), mOnCreatedThunk(nullptr), mOnInitializedThunk(nullptr)
+		, mOnUpdateThunk(nullptr), mOnResetThunk(nullptr), mOnDestroyThunk(nullptr), mOnDisabledThunk(nullptr)
+		, mOnEnabledThunk(nullptr), mOnTransformChangedThunk(nullptr), mCalculateBoundsMethod(nullptr)
 	{ }
 
 	ManagedComponent::ManagedComponent(const HSceneObject& parent, MonoReflectionType* runtimeType)
 		: Component(parent), mManagedInstance(nullptr), mManagedClass(nullptr), mRuntimeType(runtimeType)
-		, mManagedHandle(0), mRunInEditor(false), mRequiresReset(true), mMissingType(false), mOnInitializedThunk(nullptr)
-		, mOnUpdateThunk(nullptr), mOnResetThunk(nullptr), mOnDestroyThunk(nullptr), mOnDisabledThunk(nullptr)
-		, mOnEnabledThunk(nullptr), mOnTransformChangedThunk(nullptr), mCalculateBoundsMethod(nullptr)
+		, mManagedHandle(0), mRunInEditor(false), mRequiresReset(true), mMissingType(false), mOnCreatedThunk(nullptr)
+		, mOnInitializedThunk(nullptr), mOnUpdateThunk(nullptr), mOnResetThunk(nullptr), mOnDestroyThunk(nullptr)
+		, mOnDisabledThunk(nullptr), mOnEnabledThunk(nullptr), mOnTransformChangedThunk(nullptr)
+		, mCalculateBoundsMethod(nullptr)
 	{
 		MonoUtil::getClassName(mRuntimeType, mNamespace, mTypeName);
 		setName(mTypeName);
@@ -87,6 +88,7 @@ namespace bs
 
 			mManagedClass = nullptr;
 			mRuntimeType = nullptr;
+			mOnCreatedThunk = nullptr;
 			mOnInitializedThunk = nullptr;
 			mOnUpdateThunk = nullptr;
 			mOnDestroyThunk = nullptr;
@@ -145,6 +147,7 @@ namespace bs
 			mManagedClass = MonoManager::instance().findClass(monoClass);
 		}
 
+		mOnCreatedThunk = nullptr;
 		mOnInitializedThunk = nullptr;
 		mOnUpdateThunk = nullptr;
 		mOnResetThunk = nullptr;
@@ -156,6 +159,13 @@ namespace bs
 
 		while(mManagedClass != nullptr)
 		{
+			if (mOnCreatedThunk == nullptr)
+			{
+				MonoMethod* onCreatedMethod = mManagedClass->getMethod("OnCreate", 0);
+				if (onCreatedMethod != nullptr)
+					mOnCreatedThunk = (OnInitializedThunkDef)onCreatedMethod->getThunk();
+			}
+
 			if (mOnInitializedThunk == nullptr)
 			{
 				MonoMethod* onInitializedMethod = mManagedClass->getMethod("OnInitialize", 0);
@@ -362,7 +372,7 @@ namespace bs
 		ScriptGameObjectManager::instance().createManagedScriptComponent(mManagedInstance, componentHandle);
 	}
 
-	void ManagedComponent::onInitialized()
+	void ManagedComponent::onCreated()
 	{
 		assert(mManagedInstance != nullptr);
 
@@ -372,6 +382,20 @@ namespace bs
 			mSerializedObjectData = nullptr;
 		}
 
+		if (mOnCreatedThunk != nullptr)
+		{
+			// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
+			// for some extra speed.
+			MonoUtil::invokeThunk(mOnCreatedThunk, mManagedInstance);
+		}
+
+		triggerOnReset();
+	}
+
+	void ManagedComponent::onInitialized()
+	{
+		assert(mManagedInstance != nullptr);
+
 		triggerOnInitialize();
 		triggerOnReset();
 	}