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

Added a way to specify application taskbar icon
More work on play-in-editor

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

+ 1 - 1
BansheeCore/Include/BsPixelData.h

@@ -302,7 +302,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Returns pixel color at the specified coordinates.
 		 */
-        Color getColorAt(UINT32 x, UINT32 y, UINT32 z = 0);
+		Color getColorAt(UINT32 x, UINT32 y, UINT32 z = 0) const;
 
 		/**
 		 * @brief	Sets the pixel color at the specified coordinates.

+ 10 - 0
BansheeCore/Include/BsPlatform.h

@@ -298,6 +298,16 @@ namespace BansheeEngine
 		 */
 		static void setCursor(PixelData& pixelData, const Vector2I& hotSpot);
 
+		/**
+		 * @brief	Sets an icon for the main application window.
+		 *
+		 * @param 	pixelData	Icon image data. This will be resized to the required icon size, depending on platform
+		 * 						implementation.
+		 *
+		 * @note	Thread safe.
+		 */
+		static void setIcon(const PixelData& pixelData);
+
 		/**
 		 * @brief	Sets custom caption non client areas for the specified window. Using custom client
 		 * 			areas will override window move/drag operation and trigger when user interacts

+ 1 - 1
BansheeCore/Include/Win32/BsWin32Platform.h

@@ -18,7 +18,7 @@ namespace BansheeEngine
 		 * @brief	Creates a new bitmap usable by various Win32 methods from the provided pixel data.
 		 *			Caller must ensure to call ::DeleteObject on the bitmap handle when finished.
 		 */
-		static HBITMAP createBitmap(const PixelDataPtr& pixelData, bool premultiplyAlpha);
+		static HBITMAP createBitmap(const PixelData& pixelData, bool premultiplyAlpha);
 
 		/**
 		 * @brief	Main message loop callback that processes messages received from windows.

+ 1 - 1
BansheeCore/Source/BsPixelData.cpp

@@ -70,7 +70,7 @@ namespace BansheeEngine
 		return rval;
 	}
 
-	Color PixelData::getColorAt(UINT32 x, UINT32 y, UINT32 z)
+	Color PixelData::getColorAt(UINT32 x, UINT32 y, UINT32 z) const
 	{
 		Color cv;
 

+ 36 - 52
BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -153,52 +153,8 @@ namespace BansheeEngine
 
 		mData->mUsingCustomCursor = true;
 
-		BITMAPV5HEADER bi;
-
-		ZeroMemory(&bi,sizeof(BITMAPV5HEADER));
-		bi.bV5Size = sizeof(BITMAPV5HEADER);
-		bi.bV5Width = pixelData.getWidth();
-		bi.bV5Height = pixelData.getHeight();
-		bi.bV5Planes = 1;
-		bi.bV5BitCount = 32;
-		bi.bV5Compression = BI_BITFIELDS;
-		bi.bV5RedMask   =  0x00FF0000;
-		bi.bV5GreenMask =  0x0000FF00;
-		bi.bV5BlueMask  =  0x000000FF;
-		bi.bV5AlphaMask =  0xFF000000; 
-
-		HDC hDC = GetDC(NULL);
-
-		void* data = nullptr;
-		HBITMAP hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&bi, DIB_RGB_COLORS, 
-			(void**)&data, NULL, (DWORD)0);
-
-		HDC hBitmapDC = CreateCompatibleDC(hDC); 
-		ReleaseDC(NULL, hDC);
-
-		// Create an empty mask bitmap.
-		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, NULL);
-
-		//Select the bitmaps to DC
-		HBITMAP hOldBitmap  = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
-
-		//Scan each pixel of the source bitmap and create the masks
-		Color pixel;
-		DWORD *dst = (DWORD*)data;
-		for(UINT32 y = 0; y < pixelData.getHeight(); ++y)
-		{
-			for(UINT32 x = 0; x < pixelData.getWidth(); ++x)
-			{
-				pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
-				*dst = pixel.getAsBGRA();
-
-				dst++;
-			}
-		}
-
-		SelectObject(hBitmapDC, hOldBitmap);
-
-		DeleteDC(hBitmapDC);
+		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false); 
+		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
 
 		ICONINFO iconinfo = {0};
 		iconinfo.fIcon = FALSE;
@@ -220,6 +176,34 @@ namespace BansheeEngine
 		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
 	}
 
+	void Platform::setIcon(const PixelData& pixelData)
+	{
+		PixelDataPtr resizedData = PixelData::create(32, 32, 1, PF_R8G8B8A8);
+		PixelUtil::scale(pixelData, *resizedData);
+
+		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false);
+		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
+
+		ICONINFO iconinfo = { 0 };
+		iconinfo.fIcon = TRUE;
+		iconinfo.xHotspot = 0;
+		iconinfo.yHotspot = 0;
+		iconinfo.hbmMask = hMonoBitmap;
+		iconinfo.hbmColor = hBitmap;
+
+		HICON icon = CreateIconIndirect(&iconinfo);
+
+		DeleteObject(hBitmap);
+		DeleteObject(hMonoBitmap);
+
+		// Make sure we notify the message loop to perform the actual cursor update
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+		
+		PostMessage((HWND)hwnd, WM_SETICON, WPARAM(ICON_BIG), (LPARAM)icon);
+	}
+
 	void Platform::setCaptionNonClientAreas(const RenderWindowCore& window, const Vector<Rect2I>& nonClientAreas)
 	{
 		BS_LOCK_MUTEX(mData->mSync);
@@ -473,14 +457,14 @@ namespace BansheeEngine
 		return false;
 	}
 
-	HBITMAP Win32Platform::createBitmap(const PixelDataPtr& pixelData, bool premultiplyAlpha)
+	HBITMAP Win32Platform::createBitmap(const PixelData& pixelData, bool premultiplyAlpha)
 	{
 		BITMAPINFO bi;
 
 		ZeroMemory(&bi, sizeof(BITMAPINFO));
 		bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-		bi.bmiHeader.biWidth = pixelData->getWidth();
-		bi.bmiHeader.biHeight = pixelData->getHeight();
+		bi.bmiHeader.biWidth = pixelData.getWidth();
+		bi.bmiHeader.biHeight = pixelData.getHeight();
 		bi.bmiHeader.biPlanes = 1;
 		bi.bmiHeader.biBitCount = 32;
 		bi.bmiHeader.biCompression = BI_RGB;
@@ -499,11 +483,11 @@ namespace BansheeEngine
 		//Scan each pixel of the source bitmap and create the masks
 		Color pixel;
 		DWORD *dst = (DWORD*)data;
-		for (UINT32 y = 0; y < pixelData->getHeight(); ++y)
+		for (UINT32 y = 0; y < pixelData.getHeight(); ++y)
 		{
-			for (UINT32 x = 0; x < pixelData->getWidth(); ++x)
+			for (UINT32 x = 0; x < pixelData.getWidth(); ++x)
 			{
-				pixel = pixelData->getColorAt(x, pixelData->getHeight() - y - 1);
+				pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
 
 				if (premultiplyAlpha)
 				{

+ 1 - 1
BansheeCore/Source/Win32/BsWin32Window.cpp

@@ -173,7 +173,7 @@ namespace BansheeEngine
 		// Set background, if any
 		if (desc.background != nullptr)
 		{
-			HBITMAP backgroundBitmap = Win32Platform::createBitmap(desc.background, true);
+			HBITMAP backgroundBitmap = Win32Platform::createBitmap(*desc.background, true);
 
 			HDC hdcScreen = GetDC(nullptr);
 			HDC hdcMem = CreateCompatibleDC(hdcScreen);

+ 11 - 0
BansheeEngine/Include/BsBuiltinResources.h

@@ -86,6 +86,11 @@ namespace BansheeEngine
 		 */
 		const PixelData& getCursorMoveLeftRight(Vector2I& hotSpot);
 
+		/**
+		 * @brief	Returns the default application icon.
+		 */
+		const PixelData& getBansheeIcon();
+
 		/**
 		 * @brief	Returns a shader used for rendering only a diffuse texture.
 		 */
@@ -126,6 +131,7 @@ namespace BansheeEngine
 		static const Path BuiltinDataFolder;
 		static const Path EngineSkinFolder;
 		static const Path EngineCursorFolder;
+		static const Path EngineIconFolder;
 		static const Path EngineShaderFolder;
 		static const Path EngineShaderIncludeFolder;
 		static const Path EngineMeshFolder;
@@ -171,6 +177,7 @@ namespace BansheeEngine
 		PixelDataPtr mCursorSizeNS;
 		PixelDataPtr mCursorSizeNWSE;
 		PixelDataPtr mCursorSizeWE;
+		PixelDataPtr mBansheeIcon;
 
 		HSpriteTexture mWhiteSpriteTexture;
 
@@ -183,11 +190,13 @@ namespace BansheeEngine
 
 		static const Path BuiltinRawDataFolder;
 		static const Path EngineRawCursorFolder;
+		static const Path EngineRawIconFolder;
 		static const Path EngineRawShaderFolder;
 		static const Path EngineRawShaderIncludeFolder;
 		static const Path EngineRawSkinFolder;
 
 		static const Path CursorFolder;
+		static const Path IconFolder;
 		static const Path ShaderFolder;
 		static const Path ShaderIncludeFolder;
 		static const Path SkinFolder;
@@ -289,6 +298,8 @@ namespace BansheeEngine
 		static const Vector2I CursorSizeNWSEHotspot;
 		static const Vector2I CursorSizeWEHotspot;
 
+		static const WString IconTex;
+
 		static const WString ShaderSpriteTextFile;
 		static const WString ShaderSpriteImageAlphaFile;
 		static const WString ShaderSpriteImageNoAlphaFile;

+ 1 - 0
BansheeEngine/Source/BsApplication.cpp

@@ -80,6 +80,7 @@ namespace BansheeEngine
 
 		Cursor::startUp();
 		Cursor::instance().setCursor(CursorType::Arrow);
+		Platform::setIcon(BuiltinResources::instance().getBansheeIcon());
 
 		SceneManager::instance().setMainRenderTarget(getPrimaryWindow());
 

+ 29 - 0
BansheeEngine/Source/BsBuiltinResources.cpp

@@ -46,6 +46,7 @@ namespace BansheeEngine
 	const WString BuiltinResources::GUISkinFile = L"GUISkin";
 
 	const Path BuiltinResources::CursorFolder = L"Cursors\\";
+	const Path BuiltinResources::IconFolder = L"Icons\\";
 	const Path BuiltinResources::ShaderFolder = L"Shaders\\";
 	const Path BuiltinResources::SkinFolder = L"Skin\\";
 	const Path BuiltinResources::ShaderIncludeFolder = L"Includes\\";
@@ -54,12 +55,14 @@ namespace BansheeEngine
 	const Path BuiltinResources::BuiltinRawDataFolder = RUNTIME_DATA_PATH + L"Raw\\Engine\\";
 	const Path BuiltinResources::EngineRawSkinFolder = BuiltinRawDataFolder + SkinFolder;
 	const Path BuiltinResources::EngineRawCursorFolder = BuiltinRawDataFolder + CursorFolder;
+	const Path BuiltinResources::EngineRawIconFolder = BuiltinRawDataFolder + IconFolder;
 	const Path BuiltinResources::EngineRawShaderFolder = BuiltinRawDataFolder + ShaderFolder;
 	const Path BuiltinResources::EngineRawShaderIncludeFolder = BuiltinRawDataFolder + ShaderIncludeFolder;
 
 	const Path BuiltinResources::BuiltinDataFolder = RUNTIME_DATA_PATH + L"Engine\\";
 	const Path BuiltinResources::EngineSkinFolder = BuiltinDataFolder + SkinFolder;
 	const Path BuiltinResources::EngineCursorFolder = BuiltinDataFolder + CursorFolder;
+	const Path BuiltinResources::EngineIconFolder = BuiltinDataFolder + IconFolder;
 	const Path BuiltinResources::EngineShaderFolder = BuiltinDataFolder + ShaderFolder;
 	const Path BuiltinResources::EngineShaderIncludeFolder = BuiltinDataFolder + ShaderIncludeFolder;
 	const Path BuiltinResources::EngineMeshFolder = BuiltinDataFolder + MeshFolder;
@@ -161,6 +164,12 @@ namespace BansheeEngine
 	const Vector2I BuiltinResources::CursorSizeNWSEHotspot = Vector2I(15, 15);
 	const Vector2I BuiltinResources::CursorSizeWEHotspot = Vector2I(15, 15);
 
+	/************************************************************************/
+	/* 							ICON TEXTURES					    		*/
+	/************************************************************************/
+
+	const WString BuiltinResources::IconTex = L"BansheeIcon.png";
+
 	/************************************************************************/
 	/* 									SHADERS                      		*/
 	/************************************************************************/
@@ -192,6 +201,7 @@ namespace BansheeEngine
 		mCursorSizeNS = nullptr;
 		mCursorSizeNWSE = nullptr;
 		mCursorSizeWE = nullptr;
+		mBansheeIcon = nullptr;
 	}
 
 	BuiltinResources::BuiltinResources()
@@ -274,12 +284,26 @@ namespace BansheeEngine
 		mCursorSizeWE = cursorSizeWETex->getProperties().allocateSubresourceBuffer(0);
 		cursorSizeWETex->readSubresource(gCoreAccessor(), 0, mCursorSizeWE);
 
+		/************************************************************************/
+		/* 								ICON		                     		*/
+		/************************************************************************/
+
+		Path cursorPath = FileSystem::getWorkingDirectoryPath();
+		cursorPath.append(EngineIconFolder);
+		cursorPath.append(IconTex + L".asset");
+
+		HTexture iconTex = gResources().load<Texture>(cursorPath);
+
+		mBansheeIcon = iconTex->getProperties().allocateSubresourceBuffer(0);
+		iconTex->readSubresource(gCoreAccessor(), 0, mBansheeIcon);
+
 		gCoreAccessor().submitToCoreThread(true);
 	}
 
 	void BuiltinResources::preprocess()
 	{
 		BuiltinResourcesHelper::importAssets(EngineRawCursorFolder, EngineCursorFolder, mResourceManifest);
+		BuiltinResourcesHelper::importAssets(EngineRawIconFolder, EngineIconFolder, mResourceManifest);
 		BuiltinResourcesHelper::importAssets(EngineRawShaderIncludeFolder, EngineShaderIncludeFolder, mResourceManifest); // Hidden dependency: Includes must be imported before shaders
 		BuiltinResourcesHelper::importAssets(EngineRawShaderFolder, EngineShaderFolder, mResourceManifest);
 		BuiltinResourcesHelper::importAssets(EngineRawSkinFolder, EngineSkinFolder, mResourceManifest);
@@ -891,6 +915,11 @@ namespace BansheeEngine
 		return *mCursorArrowLeftRight.get();
 	}
 
+	const PixelData& BuiltinResources::getBansheeIcon()
+	{
+		return *mBansheeIcon.get();
+	}
+
 	PixelDataPtr BuiltinResources::getSplashScreen()
 	{
 		Path splashScreenPath = BuiltinDataFolder + (WString(SplashScreenName) + L".asset");

+ 6 - 2
BansheeEngine/Source/BsSceneManager.cpp

@@ -138,8 +138,12 @@ namespace BansheeEngine
 		
 		mMainRT = rt;
 
-		auto& rtProps = rt->getProperties();
-		float aspect = rtProps.getWidth() / (float)rtProps.getHeight();
+		float aspect = 1.0f;
+		if (rt != nullptr)
+		{
+			auto& rtProps = rt->getProperties();
+			aspect = rtProps.getWidth() / (float)rtProps.getHeight();
+		}
 
 		for (auto& entry : mMainCameras)
 		{

+ 2 - 0
Game/Source/Main.cpp

@@ -28,6 +28,8 @@ int CALLBACK WinMain(
 
 		Application::startUp(renderWindowDesc, renderAPI);
 
+		// TODO - Apply icon
+
 		// TODO - If on first run start in fullscreen (perhaps Build settings controlled) at desktop resolution
 		const VideoModeInfo& videoModeInfo = RenderAPI::getVideoModeInfo();
 		const VideoOutputInfo& primaryMonitorInfo = videoModeInfo.getOutputInfo(0);

+ 5 - 0
MBansheeEditor/GameWindow.cs

@@ -98,6 +98,11 @@ namespace BansheeEditor
             UpdateRenderTexture(Width, Height);
         }
 
+        private void OnDestroy()
+        {
+            EditorApplication.MainRenderTarget = null;
+        }
+
         /// <summary>
         /// Creates or rebuilds the main render texture. Should be called at least once before using the
         /// game window. Should be called whenever the window is resized.

+ 18 - 0
MBansheeEngine/Component.cs

@@ -6,6 +6,24 @@ namespace BansheeEngine
     /// <summary>
     /// Base class for all components. Components represent primary logic elements in the scene. They are attached to 
     /// scene objects.
+    ///
+    /// Implementations of <see cref="Component"/> 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 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 or paused.
+    /// void OnDisable() - Called whenever a component is disabled. This includes destruction where it is called before 
+    ///                    OnDestroy. Only called when the game is playing or paused.
+    /// void OnDestroy() - Called before the component is destroyed. Destruction is usually delayed until the end of the 
+    ///                    current frame unless specified otherwise in a call to Destroy. Only called when the game is 
+    ///                    playing or paused.
+    /// void OnReset() - Called when script assemblies have been refreshed or when the component is initialized. During
+    ///                  initialization it is called after OnInitialize but before OnEnable. Only relevant in editor. Only 
+    ///                  called when the game is playing or paused.
+    ///
+    /// You can also make these callbacks trigger when the game is stopped/paused by using the <see cref="RunInEditor"/>
+    /// attribute on the component.
     /// </summary>
     public class Component : GameObject
     {

+ 5 - 0
SBansheeEngine/Include/BsManagedComponent.h

@@ -70,6 +70,11 @@ namespace BansheeEngine
 		 */
 		void restore(MonoObject* instance, const ComponentBackupData& data, bool missingType);
 
+		/**
+		 * @brief	Triggers the managed OnInitialize callback.
+		 */
+		void triggerOnInitialize();
+
 		/**
 		 * @brief	Triggers the managed OnReset callback.
 		 */

+ 11 - 0
SBansheeEngine/Include/BsScriptGameObjectManager.h

@@ -83,6 +83,11 @@ namespace BansheeEngine
 		 */
 		ScriptGameObjectBase* getScriptGameObject(UINT64 instanceId) const;
 
+		/**
+		 * @brief	Notifies the managed that the OnInitialize method on a managed component has been called.
+		 */
+		void notifyComponentInitialized(UINT64 instanceId);
+
 		/**
 		 * @brief	Destroys and unregisters the specified SceneObject interop object.
 		 */
@@ -93,6 +98,11 @@ namespace BansheeEngine
 		 */
 		void destroyScriptComponent(ScriptComponent* component);
 
+		/**
+		 * @brief	Triggers OnInitialize methods on all uninitialized managed components.
+		 */
+		void sendComponentInitializeEvents();
+
 	private:
 		/**
 		 * @brief	Triggers OnReset methods on all registered managed components.
@@ -108,6 +118,7 @@ namespace BansheeEngine
 
 		UnorderedMap<UINT64, ScriptComponent*> mScriptComponents;
 		UnorderedMap<UINT64, ScriptSceneObject*> mScriptSceneObjects;
+		UnorderedMap<UINT64, ScriptComponent*> mUninitializedScriptComponents;
 
 		HEvent mOnAssemblyReloadDoneConn;
 		HEvent onGameObjectDestroyedConn;

+ 20 - 14
SBansheeEngine/Source/BsManagedComponent.cpp

@@ -237,11 +237,26 @@ namespace BansheeEngine
 		}
 	}
 
+	void ManagedComponent::triggerOnInitialize()
+	{
+		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Playing || mRunInEditor)
+		{
+			if (mOnInitializedThunk != nullptr)
+			{
+				// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
+				// for some extra speed.
+				MonoUtil::invokeThunk(mOnInitializedThunk, mManagedInstance);
+			}
+
+			ScriptGameObjectManager::instance().notifyComponentInitialized(getInstanceId());
+		}
+	}
+
 	void ManagedComponent::triggerOnReset()
 	{
 		assert(mManagedInstance != nullptr);
 
-		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Playing || mRunInEditor)
+		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Stopped || mRunInEditor)
 		{
 			if (mRequiresReset && mOnResetThunk != nullptr)
 			{
@@ -301,16 +316,7 @@ namespace BansheeEngine
 			mSerializedObjectData = nullptr;
 		}
 
-		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Playing || mRunInEditor)
-		{
-			if (mOnInitializedThunk != nullptr)
-			{
-				// Note: Not calling virtual methods. Can be easily done if needed but for now doing this
-				// for some extra speed.
-				MonoUtil::invokeThunk(mOnInitializedThunk, mManagedInstance);
-			}
-		}
-
+		triggerOnInitialize();
 		triggerOnReset();
 	}
 
@@ -318,7 +324,7 @@ namespace BansheeEngine
 	{
 		assert(mManagedInstance != nullptr);
 
-		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Playing || mRunInEditor)
+		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Stopped || mRunInEditor)
 		{
 			if (mOnDestroyThunk != nullptr)
 			{
@@ -334,7 +340,7 @@ namespace BansheeEngine
 
 	void ManagedComponent::onEnabled()
 	{
-		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Playing && !mRunInEditor)
+		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Stopped && !mRunInEditor)
 			return;
 
 		assert(mManagedInstance != nullptr);
@@ -349,7 +355,7 @@ namespace BansheeEngine
 
 	void ManagedComponent::onDisabled()
 	{
-		if (PlayInEditorManager::instance().getState() != PlayInEditorState::Playing && !mRunInEditor)
+		if (PlayInEditorManager::instance().getState() == PlayInEditorState::Stopped && !mRunInEditor)
 			return;
 
 		assert(mManagedInstance != nullptr);

+ 54 - 4
SBansheeEngine/Source/BsPlayInEditorManager.cpp

@@ -1,4 +1,6 @@
 #include "BsPlayInEditorManager.h"
+#include "BsScriptGameObjectManager.h"
+#include "BsTime.h"
 
 namespace BansheeEngine
 {
@@ -8,17 +10,65 @@ namespace BansheeEngine
 
 	void PlayInEditorManager::setState(PlayInEditorState state)
 	{
-		// TODO
-		//  - make sure to reset the clock when starting/stopping
+		if (mState == state)
+			return;
+
+		PlayInEditorState oldState = mState;
+		mState = state;
+
+		switch (state)
+		{
+		case PlayInEditorState::Stopped:
+		{
+			mFrameStepActive = false;
+			mPausableTime = 0.0f;
+			// TODO - Load saved scene from prefab
+		}
+			break;
+		case PlayInEditorState::Playing:
+		{
+			if (oldState == PlayInEditorState::Paused)
+			{
+				ScriptGameObjectManager::instance().sendComponentInitializeEvents();
+			}
+			else // Was stopped
+			{
+				// TODO - Save current scene in a prefab
+			}
+		}
+			break;
+		case PlayInEditorState::Paused:
+		{
+			mFrameStepActive = false;
+			if (oldState == PlayInEditorState::Stopped)
+			{
+				// TODO - Save current scene in a prefab
+			}
+		}
+			break;
+		default:
+			break;
+		}		
 	}
 
 	void PlayInEditorManager::frameStep()
 	{
-		// TODO
+		switch (mState)
+		{
+		case PlayInEditorState::Stopped:
+		case PlayInEditorState::Paused:
+			setState(PlayInEditorState::Playing);
+			break;
+		}
+
+		mFrameStepActive = true;
 	}
 
 	void PlayInEditorManager::update()
 	{
-		// TODO
+		if (mState == PlayInEditorState::Playing)
+			mPausableTime += gTime().getFrameDelta();
+
+		// TODO - disable framestep once a frame has passed
 	}
 }

+ 24 - 1
SBansheeEngine/Source/BsScriptGameObjectManager.cpp

@@ -78,7 +78,10 @@ namespace BansheeEngine
 
 		ScriptComponent* nativeInstance = new (bs_alloc<ScriptComponent>()) ScriptComponent(existingInstance);
 		nativeInstance->setNativeHandle(component);
-		mScriptComponents[component->getInstanceId()] = nativeInstance;
+
+		UINT64 instanceId = component->getInstanceId();
+		mScriptComponents[instanceId] = nativeInstance;
+		mUninitializedScriptComponents[instanceId] = nativeInstance;
 
 		return nativeInstance;
 	}
@@ -144,10 +147,16 @@ namespace BansheeEngine
 	{
 		UINT64 instanceId = component->getNativeHandle().getInstanceId();
 		mScriptComponents.erase(instanceId);
+		mUninitializedScriptComponents(instanceId);
 
 		bs_delete(component);
 	}
 
+	void ScriptGameObjectManager::notifyComponentInitialized(UINT64 instanceId)
+	{
+		mUninitializedScriptComponents.erase(instanceId);
+	}
+
 	void ScriptGameObjectManager::sendComponentResetEvents()
 	{
 		for (auto& scriptObjectEntry : mScriptComponents)
@@ -159,6 +168,20 @@ namespace BansheeEngine
 		}
 	}
 
+	void ScriptGameObjectManager::sendComponentInitializeEvents()
+	{
+		// Need to make a copy since successful calls will remove entries from mScriptComponents
+		UnorderedMap<UINT64, ScriptComponent*> componentsToInitialize = mScriptComponents;
+
+		for (auto& scriptObjectEntry : componentsToInitialize)
+		{
+			ScriptComponent* scriptComponent = scriptObjectEntry.second;
+			HManagedComponent component = scriptComponent->getNativeHandle();
+
+			component->triggerOnInitialize();
+		}
+	}
+
 	void ScriptGameObjectManager::onGameObjectDestroyed(const HGameObject& go)
 	{
 		UINT64 instanceId = go.getInstanceId();

+ 0 - 27
TODO.txt

@@ -95,33 +95,6 @@ Build system
 	 - Similar to project/editor settings this can contain user data as needed
   - Game.exe shouldn't need to include importer .dlls
 
-----------------------------------------------------------------------
-Game window
- - Main camera
-  - Allow camera to be marked with "Main" toggle
-  - When Game window is created search the scene for such a camera
-   - Same needs to happen whenever a new scene is loaded
-  - Create a render target similar to scene window and render to it if the camera is found
-   - Otherwise output a message that main camera is missing
- - Extend Time class (C++ and C#)
-  - Current time and frame idx should be renamed with Real prefix
-  - New time and frame idx should be added that do not tick when paused
- - EditorApplication Play/Pause/Step
-  - When game is paused non-real time doesn't tick, and component OnInitialize/Update/OnDestroy methods aren't called
-   - Some components might rely on these being called when they're added/removed from the scene. Perhaps add
-     a separate set of initialize/destroy methods for internal only use that get called regardless?
-   - Internal (editor) components either ignore this rule, or use the internal-only versions exclusively
-   - When play is hit:
-     - Current scene data is serialized into a Prefab stored in memory
-	 - Time starts ticking and initialize/update/destroy methods start to get called normally
-   - When pause is hit:
-     - Time stops ticking and so do components method calls
-   - When stop is hit:
-     - Time stops ticking and so do components method calls
-	 - Current scene is unloaded and prefab from memory is loaded
-   - When frame-step is hit:
-     - Time and component updates are unpaused for a single frame and then paused again
-
 /*********************************************************************/
 /************************ LESS IMPORTANT *****************************/
 /*********************************************************************/