فهرست منبع

Added Rigidbody C++ component
SceneObjects can now contain more than one component of the same type
Retrieving components from SceneObjects can now be done by using base type instead of exact type

BearishSun 10 سال پیش
والد
کامیت
80503e959a

+ 18 - 3
BansheeCore/Include/BsCCollider.h

@@ -61,15 +61,21 @@ namespace BansheeEngine
 		/** @copydoc Collider::getRigidbody */
 		HRigidbody getRigidbody() const { return mParent; }
 
-		/** @copydoc Collider::setIsTrigger */
+		/** @copydoc Collider::onCollisionBegin */
 		Event<void(const CollisionData&)> onCollisionBegin;
 
-		/** @copydoc Collider::setIsTrigger */
+		/** @copydoc Collider::onCollisionStay */
 		Event<void(const CollisionData&)> onCollisionStay;
 
-		/** @copydoc Collider::setIsTrigger */
+		/** @copydoc Collider::onCollisionEnd */
 		Event<void(const CollisionData&)> onCollisionEnd;
 
+		/** @cond INTERNAL */
+
+		/** Changes the rigidbody parent of the collider. Meant to be called from the Rigidbody itself. */
+		void _setRigidbody(const HRigidbody& rigidbody);
+
+		/** @endcond */
 		/************************************************************************/
 		/* 						COMPONENT OVERRIDES                      		*/
 		/************************************************************************/
@@ -106,6 +112,15 @@ namespace BansheeEngine
 		/** Searches the parent scene object hierarchy to find a parent Rigidbody component. */
 		void updateParentRigidbody();
 
+		/** Triggered when the internal collider begins touching another object. */
+		void triggerOnCollisionBegin(const CollisionData& data);
+
+		/** Triggered when the internal collider continues touching another object. */
+		void triggerOnCollisionStay(const CollisionData& data);
+
+		/** Triggered when the internal collider ends touching another object. */
+		void triggerOnCollisionEnd(const CollisionData& data);
+
 		SPtr<Collider> mInternal;
 
 		UINT64 mLayer = 1;

+ 185 - 3
BansheeCore/Include/BsCRigidbody.h

@@ -5,6 +5,8 @@
 #include "BsCorePrerequisites.h"
 #include "BsRigidbody.h"
 #include "BsComponent.h"
+#include "BsVector3.h"
+#include "BsQuaternion.h"
 
 namespace BansheeEngine
 {
@@ -22,11 +24,160 @@ namespace BansheeEngine
 	public:
 		CRigidbody(const HSceneObject& parent);
 
-		// TODO
+		/** @copydoc Rigidbody::move */
+		inline void move(const Vector3& position);
+
+		/** @copydoc Rigidbody::rotate */
+		inline void rotate(const Quaternion& rotation);
+
+		/** @copydoc Rigidbody::setMass */
+		inline void setMass(float mass);
+
+		/** @copydoc Rigidbody::getMass */
+		float getMass() const { return mMass; };
+
+		/** @copydoc Rigidbody::setIsKinematic */
+		inline void setIsKinematic(bool kinematic);
+
+		/** @copydoc Rigidbody::getIsKinematic */
+		bool getIsKinematic() const { return mIsKinematic; }
+
+		/** @copydoc Rigidbody::isSleeping */
+		inline bool isSleeping() const;
+
+		/** @copydoc Rigidbody::sleep */
+		inline void sleep();
+
+		/** @copydoc Rigidbody::wakeUp */
+		inline void wakeUp();
+
+		/** @copydoc Rigidbody::setSleepThreshold */
+		inline void setSleepThreshold(float threshold);
+
+		/** @copydoc Rigidbody::getSleepThreshold */
+		float getSleepThreshold() const { return mSleepThreshold; }
+
+		/** @copydoc Rigidbody::setUseGravity */
+		inline void setUseGravity(bool gravity);
+
+		/** @copydoc Rigidbody::getUseGravity */
+		bool getUseGravity() const { return mUseGravity; }
+
+		/** @copydoc Rigidbody::setVelocity */
+		inline void setVelocity(const Vector3& velocity);
+
+		/** @copydoc Rigidbody::getVelocity */
+		inline Vector3 getVelocity() const;
+
+		/** @copydoc Rigidbody::setAngularVelocity */
+		inline void setAngularVelocity(const Vector3& velocity);
+
+		/** @copydoc Rigidbody::getAngularVelocity */
+		inline Vector3 getAngularVelocity() const;
+
+		/** @copydoc Rigidbody::setDrag */
+		inline void setDrag(float drag);
+
+		/** @copydoc Rigidbody::getDrag */
+		float getDrag() const { return mLinearDrag; }
+
+		/** @copydoc Rigidbody::setAngularDrag */
+		inline void setAngularDrag(float drag);
+
+		/** @copydoc Rigidbody::getAngularDrag */
+		float getAngularDrag() const { return mAngularDrag; }
+
+		/** @copydoc Rigidbody::setInertiaTensor */
+		inline void setInertiaTensor(const Vector3& tensor);
+
+		/** @copydoc Rigidbody::getInertiaTensor */
+		inline Vector3 getInertiaTensor() const;
+
+		/** @copydoc Rigidbody::setMaxAngularVelocity */
+		inline void setMaxAngularVelocity(float maxVelocity);
+
+		/** @copydoc Rigidbody::getMaxAngularVelocity */
+		float getMaxAngularVelocity() const { return mMaxAngularVelocity; }
+
+		/** @copydoc Rigidbody::setCenterOfMass */
+		inline void setCenterOfMass(const Vector3& position, const Quaternion& rotation);
+
+		/** @copydoc Rigidbody::getCenterOfMassPosition */
+		inline  Vector3 getCenterOfMassPosition() const;
+
+		/** @copydoc Rigidbody::getCenterOfMassRotation */
+		inline Quaternion getCenterOfMassRotation() const;
+
+		/** @copydoc Rigidbody::setPositionSolverCount */
+		inline void setPositionSolverCount(UINT32 count);
+
+		/** @copydoc Rigidbody::getPositionSolverCount */
+		UINT32 getPositionSolverCount() const { return mPositionSolverCount; }
+
+		/** @copydoc Rigidbody::setVelocitySolverCount */
+		inline void setVelocitySolverCount(UINT32 count);
+
+		/** @copydoc Rigidbody::getVelocitySolverCount */
+		UINT32 getVelocitySolverCount() const { return mVelocitySolverCount; }
+
+		/** @copydoc Rigidbody::setFlags */
+		inline void setFlags(Rigidbody::Flag flags);
+
+		/** @copydoc Rigidbody::getFlags */
+		Rigidbody::Flag getFlags() const { return mFlags; }
+
+		/** @copydoc Rigidbody::addForce */
+		inline void addForce(const Vector3& force, ForceMode mode = ForceMode::Force);
+
+		/** @copydoc Rigidbody::addTorque */
+		inline void addTorque(const Vector3& torque, ForceMode mode = ForceMode::Force);
+
+		/** @copydoc Rigidbody::addForceAtPoint */
+		inline void addForceAtPoint(const Vector3& force, const Vector3& position,
+			PointForceMode mode = PointForceMode::Force);
+
+		/** @copydoc Rigidbody::getVelocityAtPoint */
+		inline Vector3 getVelocityAtPoint(const Vector3& point) const;
+
+		/** @copydoc Rigidbody::onCollisionBegin */
+		Event<void(const CollisionData&)> onCollisionBegin;
+
+		/** @copydoc Rigidbody::onCollisionStay */
+		Event<void(const CollisionData&)> onCollisionStay;
+
+		/** @copydoc Rigidbody::onCollisionEnd */
+		Event<void(const CollisionData&)> onCollisionEnd;
+
+		/** @cond INTERNAL */
+
+		/** Returns the Rigidbody implementation wrapped by this component. */
 		SPtr<Rigidbody> _getInternal() const { return mInternal; }
-		void _updateMassDistribution() { }
+
+		/** @copydoc Rigidbody::_updateMassDistribution */
+		inline void _updateMassDistribution();
+
+		/** @endcond */
 	protected:
-		SPtr<Rigidbody> mInternal;
+		/** 
+		 * Searches child scene objects for Collider components and attaches them to the rigidbody. Make sure to call
+		 * clearChildColliders() if you need to clear old colliders first. 
+		 */
+		void updateChildColliders();
+
+		/** Unregisters all child colliders from the Rigidbody. */
+		void clearChildColliders();
+
+		/** Checks if the rigidbody is nested under another rigidbody, and throws out a warning if so. */
+		void checkForNestedRigibody();
+
+		/** Triggered when the internal rigidbody begins touching another object. */
+		void triggerOnCollisionBegin(const CollisionData& data);
+
+		/** Triggered when the internal rigidbody continues touching another object. */
+		void triggerOnCollisionStay(const CollisionData& data);
+
+		/** Triggered when the internal rigidbody ends touching another object. */
+		void triggerOnCollisionEnd(const CollisionData& data);
 
 		/************************************************************************/
 		/* 						COMPONENT OVERRIDES                      		*/
@@ -34,6 +185,37 @@ namespace BansheeEngine
 	protected:
 		friend class SceneObject;
 
+		/** @copydoc Component::onInitialized() */
+		void onInitialized() override;
+
+		/** @copydoc Component::onDestroyed() */
+		void onDestroyed() override;
+
+		/** @copydoc Component::onDisabled() */
+		void onDisabled() override;
+
+		/** @copydoc Component::onEnabled() */
+		void onEnabled() override;
+
+		/** @copydoc Component::onTransformChanged() */
+		void onTransformChanged(TransformChangedFlags flags) override;
+
+		SPtr<Rigidbody> mInternal;
+
+		UINT32 mPositionSolverCount = 4;
+		UINT32 mVelocitySolverCount = 1;
+		Rigidbody::Flag mFlags = (Rigidbody::Flag)((UINT32)Rigidbody::Flag::AutoTensors | (UINT32)Rigidbody::Flag::AutoMass);
+		Vector3 mCMassPosition;
+		Quaternion mCMassRotation;
+		Vector3 mIntertiaTensor;
+		float mMass = 0.0f;
+		float mMaxAngularVelocity = 1.0f;
+		float mLinearDrag = 0.0f;
+		float mAngularDrag = 0.0f;
+		float mSleepThreshold = 0.0f;
+		bool mUseGravity = true;
+		bool mIsKinematic = false;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 26 - 2
BansheeCore/Include/BsCRigidbodyRTTI.h

@@ -17,11 +17,35 @@ namespace BansheeEngine
 	class BS_CORE_EXPORT CRigidbodyRTTI : public RTTIType<CRigidbody, Component, CRigidbodyRTTI>
 	{
 	private:
-
+		BS_PLAIN_MEMBER(mPositionSolverCount);
+		BS_PLAIN_MEMBER(mVelocitySolverCount);
+		BS_PLAIN_MEMBER(mFlags);
+		BS_PLAIN_MEMBER(mCMassPosition);
+		BS_PLAIN_MEMBER(mCMassRotation);
+		BS_PLAIN_MEMBER(mIntertiaTensor);
+		BS_PLAIN_MEMBER(mMass);
+		BS_PLAIN_MEMBER(mMaxAngularVelocity);
+		BS_PLAIN_MEMBER(mLinearDrag);
+		BS_PLAIN_MEMBER(mAngularDrag);
+		BS_PLAIN_MEMBER(mSleepThreshold);
+		BS_PLAIN_MEMBER(mUseGravity);
+		BS_PLAIN_MEMBER(mIsKinematic);
 	public:
 		CRigidbodyRTTI()
 		{
-
+			BS_ADD_PLAIN_FIELD(mPositionSolverCount, 0);
+			BS_ADD_PLAIN_FIELD(mVelocitySolverCount, 1);
+			BS_ADD_PLAIN_FIELD(mFlags, 2);
+			BS_ADD_PLAIN_FIELD(mCMassPosition, 3);
+			BS_ADD_PLAIN_FIELD(mCMassRotation, 4);
+			BS_ADD_PLAIN_FIELD(mIntertiaTensor, 5);
+			BS_ADD_PLAIN_FIELD(mMass, 6);
+			BS_ADD_PLAIN_FIELD(mMaxAngularVelocity, 7);
+			BS_ADD_PLAIN_FIELD(mLinearDrag, 8);
+			BS_ADD_PLAIN_FIELD(mAngularDrag, 9);
+			BS_ADD_PLAIN_FIELD(mSleepThreshold, 10);
+			BS_ADD_PLAIN_FIELD(mUseGravity, 11);
+			BS_ADD_PLAIN_FIELD(mIsKinematic, 12);
 		}
 
 		const String& getRTTIName() override

+ 0 - 3
BansheeCore/Include/BsCollider.h

@@ -30,9 +30,6 @@ namespace BansheeEngine
 		inline void setMaterial(const HPhysicsMaterial& material);
 		inline HPhysicsMaterial getMaterial() const;
 
-		inline void setIsActive(bool value);
-		inline bool getIsActive() const;
-
 		inline void setContactOffset(float value);
 		inline float getContactOffset();
 

+ 1 - 0
BansheeCore/Include/BsComponent.h

@@ -101,6 +101,7 @@ namespace BansheeEngine
 
 	protected:
 		HSceneObject mParent;
+		HComponent mThisHandle;
 		TransformChangedFlags mNotifyFlags;
 
 		/************************************************************************/

+ 0 - 4
BansheeCore/Include/BsFCollider.h

@@ -30,9 +30,6 @@ namespace BansheeEngine
 		virtual void setMaterial(const HPhysicsMaterial& material);
 		virtual HPhysicsMaterial getMaterial() const { return mMaterial; }
 
-		virtual void setIsActive(bool value);
-		virtual bool getIsActive() const { return mIsActive; }
-
 		virtual void setContactOffset(float value) = 0;
 		virtual float getContactOffset() const = 0;
 
@@ -42,7 +39,6 @@ namespace BansheeEngine
 		virtual void setLayer(UINT64 layer);
 		virtual UINT64 getLayer() const { return mLayer; }
 	protected:
-		bool mIsActive = true;
 		UINT64 mLayer = 1;
 		float mMass = 1.0f;
 

+ 2 - 5
BansheeCore/Include/BsRigidbody.h

@@ -79,7 +79,7 @@ namespace BansheeEngine
 
 		virtual void setCenterOfMass(const Vector3& position, const Quaternion& rotation) = 0;
 		virtual Vector3 getCenterOfMassPosition() const = 0;
-		virtual Quaternion getCenterOfMassRotatation() const = 0;
+		virtual Quaternion getCenterOfMassRotation() const = 0;
 
 		virtual void setPositionSolverCount(UINT32 count) = 0;
 		virtual UINT32 getPositionSolverCount() const = 0;
@@ -87,9 +87,6 @@ namespace BansheeEngine
 		virtual void setVelocitySolverCount(UINT32 count) = 0;
 		virtual UINT32 getVelocitySolverCount() const = 0;
 
-		virtual void setIsActive(bool value);
-		virtual bool getIsActive() const { return mIsActive; }
-
 		virtual void setFlags(Flag flags);
 		virtual Flag getFlags() const { return mFlags; }
 
@@ -111,13 +108,13 @@ namespace BansheeEngine
 		void _setPriority(UINT32 priority);
 		void _setPhysicsId(UINT32 id) { mPhysicsId = id; }
 		void _setTransform(const Vector3& position, const Quaternion& rotation);
+		void _detachColliders();
 	protected:
 		friend class FCollider;
 
 		virtual void addCollider(FCollider* collider);
 		virtual void removeCollider(FCollider* collider);
 
-		bool mIsActive = true;
 		Flag mFlags = Flag::None;
 		UINT32 mPriority = 0;
 		UINT32 mPhysicsId = 0;

+ 39 - 15
BansheeCore/Include/BsSceneObject.h

@@ -497,15 +497,10 @@ namespace BansheeEngine
 				std::forward<Args>(args)...),
 				&bs_delete<T>, StdAlloc<T>());
 
-			for (auto& component : mComponents)
-			{
-				if (component->typeEquals(*gameObject))
-					return component; // Component of this type already exists
-			}
-
 			GameObjectHandle<T> newComponent =
 				GameObjectManager::instance().registerObject(gameObject);
 
+			newComponent->mThisHandle = newComponent;
 			mComponents.push_back(newComponent);
 
 			if (isInstantiated())
@@ -521,7 +516,8 @@ namespace BansheeEngine
 		}
 
 		/**
-		 * Searches for a component with the specific type and returns the first one it finds. 
+		 * Searches for a component with the specific type and returns the first one it finds. Will also return components
+		 * derived from the type.
 		 * 			
 		 * @tparam	typename T	Type of the component.
 		 * @return				Component if found, nullptr otherwise.
@@ -536,11 +532,38 @@ namespace BansheeEngine
 			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
 				"Specified type is not a valid Component.");
 
-			return static_object_cast<T>(getComponent(T::getRTTIStatic()->getRTTIId()));
+			return static_object_cast<T>(getComponent(T::getRTTIStatic()));
 		}
 
 		/**
-		 * Checks if the current object contains the specified component
+		 * Returns all components with the specific type. Will also return components derived from the type.
+		 * 			
+		 * @tparam	typename T	Type of the component.
+		 * @return				Array of found components.
+		 *
+		 * @note	
+		 * Don't call this too often as it is relatively slow. It is more efficient to call it once and store the result 
+		 * for further use.
+		 */
+		template <typename T>
+		Vector<GameObjectHandle<T>> getComponents()
+		{
+			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
+				"Specified type is not a valid Component.");
+
+			Vector<GameObjectHandle<T>> output;
+
+			for (auto entry : mComponents)
+			{
+				if (entry->getRTTI()->isDerivedFrom(T::getRTTIStatic()))
+					output.push_back(entry);
+			}
+
+			return output;
+		}
+
+		/**
+		 * Checks if the current object contains the specified component or components derived from the provided type.
 		 * 			 			
 		 * @tparam	typename T	Type of the component.
 		 * @return				True if component exists on the object.
@@ -553,9 +576,9 @@ namespace BansheeEngine
 			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
 				"Specified type is not a valid Component.");
 
-			for (auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
+			for (auto entry : mComponents)
 			{
-				if ((*iter)->getRTTI()->getRTTIId() == T::getRTTIStatic()->getRTTIId())
+				if (entry->getRTTI()->isDerivedFrom(T::getRTTIStatic()))
 					return true;
 			}
 
@@ -563,19 +586,20 @@ namespace BansheeEngine
 		}
 
 		/**
-		 * Searches for a component with the specified type id and returns the first one it finds.
+		 * Searches for a component with the specified type and returns the first one it finds. Will also return components
+		 * derived from the type.
 		 * 			
-		 * @param[in]	typeId	Identifier for the type.
+		 * @param[in]	type	RTTI information for the type.
 		 * @return				Component if found, nullptr otherwise.
 		 *
 		 * @note	
 		 * Don't call this too often as it is relatively slow. It is more efficient to call it once and store the result
 		 * for further use.
 		 */
-		HComponent getComponent(UINT32 typeId) const;
+		HComponent getComponent(RTTITypeBase* type) const;
 
 		/**
-		 * Removes the component from this object, and deallocates it.
+		 * Removes the component from this object, and deallocates it. 
 		 *
 		 * @param[in]	component	The component to destroy.
 		 * @param[in]	immediate	If true, the component will be deallocated and become unusable right away. Otherwise 

+ 39 - 1
BansheeCore/Source/BsCCollider.cpp

@@ -6,6 +6,8 @@
 #include "BsPhysics.h"
 #include "BsCColliderRTTI.h"
 
+using namespace std::placeholders;
+
 namespace BansheeEngine
 {
 	CCollider::CCollider(const HSceneObject& parent)
@@ -121,11 +123,32 @@ namespace BansheeEngine
 			updateTransform();
 	}
 
+	void CCollider::_setRigidbody(const HRigidbody& rigidbody)
+	{
+		SPtr<Rigidbody> rigidBodyPtr;
+
+		if (rigidbody != nullptr)
+			rigidBodyPtr = rigidbody->_getInternal();
+
+		if (mInternal == nullptr)
+		{
+			mInternal->setRigidbody(rigidBodyPtr);
+			mParent = rigidbody;
+		}
+	}
+
 	void CCollider::restoreInternal()
 	{
-		if(mInternal == nullptr)
+		if (mInternal == nullptr)
+		{
 			mInternal = createInternal();
 
+			mInternal->onCollisionBegin.connect(std::bind(&CCollider::triggerOnCollisionBegin, this, _1));
+			mInternal->onCollisionStay.connect(std::bind(&CCollider::triggerOnCollisionStay, this, _1));
+			mInternal->onCollisionEnd.connect(std::bind(&CCollider::triggerOnCollisionEnd, this, _1));
+		}
+
+		// Note: Merge into one call to avoid many virtual function calls
 		mInternal->setIsTrigger(mIsTrigger);
 		mInternal->setMass(mMass);
 		mInternal->setMaterial(mMaterial);
@@ -197,6 +220,21 @@ namespace BansheeEngine
 		}
 	}
 
+	void CCollider::triggerOnCollisionBegin(const CollisionData& data)
+	{
+		onCollisionBegin(data);
+	}
+
+	void CCollider::triggerOnCollisionStay(const CollisionData& data)
+	{
+		onCollisionStay(data);
+	}
+
+	void CCollider::triggerOnCollisionEnd(const CollisionData& data)
+	{
+		onCollisionEnd(data);
+	}
+
 	RTTITypeBase* CCollider::getRTTIStatic()
 	{
 		return CColliderRTTI::instance();

+ 366 - 0
BansheeCore/Source/BsCRigidbody.cpp

@@ -2,8 +2,11 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsCRigidbody.h"
 #include "BsSceneObject.h"
+#include "BsCCollider.h"
 #include "BsCRigidbodyRTTI.h"
 
+using namespace std::placeholders;
+
 namespace BansheeEngine
 {
 	CRigidbody::CRigidbody(const HSceneObject& parent)
@@ -12,6 +15,369 @@ namespace BansheeEngine
 		setName("Rigidbody");
 	}
 
+	void CRigidbody::move(const Vector3& position)
+	{
+		if (mInternal != nullptr)
+			mInternal->move(position);
+	}
+
+	void CRigidbody::rotate(const Quaternion& rotation)
+	{
+		if (mInternal != nullptr)
+			mInternal->rotate(rotation);
+	}
+
+	void CRigidbody::setMass(float mass)
+	{
+		mMass = mass;
+
+		if(mInternal != nullptr)
+			mInternal->setMass(mass);
+	}
+
+	void CRigidbody::setIsKinematic(bool kinematic)
+	{
+		mIsKinematic = kinematic;
+		
+		if (mInternal != nullptr)
+			mInternal->setIsKinematic(kinematic);
+	}
+
+	bool CRigidbody::isSleeping() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->isSleeping();
+
+		return true;
+	}
+
+	void CRigidbody::sleep()
+	{
+		if (mInternal != nullptr)
+			return mInternal->sleep();
+	}
+
+	void CRigidbody::wakeUp()
+	{
+		if (mInternal != nullptr)
+			return mInternal->wakeUp();
+	}
+
+	void CRigidbody::setSleepThreshold(float threshold)
+	{
+		mSleepThreshold = threshold;
+
+		if (mInternal != nullptr)
+			mInternal->setSleepThreshold(threshold);
+	}
+
+	void CRigidbody::setUseGravity(bool gravity)
+	{
+		mUseGravity = gravity;
+
+		if (mInternal != nullptr)
+			mInternal->setUseGravity(gravity);
+	}
+
+	void CRigidbody::setVelocity(const Vector3& velocity)
+	{
+		if (mInternal != nullptr)
+			mInternal->setVelocity(velocity);
+	}
+
+	Vector3 CRigidbody::getVelocity() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getVelocity();
+
+		return Vector3::ZERO;
+	}
+
+	void CRigidbody::setAngularVelocity(const Vector3& velocity)
+	{
+		if (mInternal != nullptr)
+			mInternal->setAngularVelocity(velocity);
+	}
+
+	inline Vector3 CRigidbody::getAngularVelocity() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getAngularVelocity();
+
+		return Vector3::ZERO;
+	}
+
+	void CRigidbody::setDrag(float drag)
+	{
+		mLinearDrag = drag;
+
+		if (mInternal != nullptr)
+			mInternal->setDrag(drag);
+	}
+
+	void CRigidbody::setAngularDrag(float drag)
+	{
+		mAngularDrag = drag;
+
+		if (mInternal != nullptr)
+			mInternal->setAngularDrag(drag);
+	}
+
+	void CRigidbody::setInertiaTensor(const Vector3& tensor)
+	{
+		mIntertiaTensor = tensor;
+
+		if (mInternal != nullptr)
+			mInternal->setInertiaTensor(tensor);
+	}
+
+	Vector3 CRigidbody::getInertiaTensor() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getInertiaTensor();
+
+		return Vector3::ZERO;
+	}
+
+	void CRigidbody::setMaxAngularVelocity(float maxVelocity)
+	{
+		mMaxAngularVelocity = maxVelocity;
+
+		if (mInternal != nullptr)
+			mInternal->setMaxAngularVelocity(maxVelocity);
+	}
+
+	void CRigidbody::setCenterOfMass(const Vector3& position, const Quaternion& rotation)
+	{
+		mCMassPosition = position;
+		mCMassRotation = rotation;
+
+		if (mInternal != nullptr)
+			mInternal->setCenterOfMass(position, rotation);
+	}
+
+	Vector3 CRigidbody::getCenterOfMassPosition() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getCenterOfMassPosition();
+
+		return Vector3::ZERO;
+	}
+
+	Quaternion CRigidbody::getCenterOfMassRotation() const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getCenterOfMassRotation();
+
+		return Quaternion::IDENTITY;
+	}
+
+	void CRigidbody::setPositionSolverCount(UINT32 count)
+	{
+		mPositionSolverCount = count;
+
+		if (mInternal != nullptr)
+			mInternal->setPositionSolverCount(count);
+	}
+
+	void CRigidbody::setVelocitySolverCount(UINT32 count)
+	{
+		mVelocitySolverCount = count;
+
+		if (mInternal != nullptr)
+			mInternal->setVelocitySolverCount(count);
+	}
+
+	void CRigidbody::setFlags(Rigidbody::Flag flags)
+	{
+		mFlags = flags;
+
+		if (mInternal != nullptr)
+			mInternal->setFlags(flags);
+	}
+
+	void CRigidbody::addForce(const Vector3& force, ForceMode mode)
+	{
+		if (mInternal != nullptr)
+			mInternal->addForce(force, mode);
+	}
+
+	void CRigidbody::addTorque(const Vector3& torque, ForceMode mode)
+	{
+		if (mInternal != nullptr)
+			mInternal->addTorque(torque, mode);
+	}
+
+	void CRigidbody::addForceAtPoint(const Vector3& force, const Vector3& position, PointForceMode mode)
+	{
+		if (mInternal != nullptr)
+			mInternal->addForceAtPoint(force, position, mode);
+	}
+
+	Vector3 CRigidbody::getVelocityAtPoint(const Vector3& point) const
+	{
+		if (mInternal != nullptr)
+			return mInternal->getVelocityAtPoint(point);
+
+		return Vector3::ZERO;
+	}
+
+	void CRigidbody::_updateMassDistribution()
+	{
+		if (mInternal != nullptr)
+			return mInternal->_updateMassDistribution();
+	}
+
+	void CRigidbody::updateChildColliders()
+	{
+		Stack<HSceneObject> todo;
+		todo.push(SO());
+
+		while(!todo.empty())
+		{
+			HSceneObject currentSO = todo.top();
+			todo.pop();
+
+			if(currentSO->hasComponent<CCollider>())
+			{
+				Vector<HCollider> colliders = currentSO->getComponents<CCollider>();
+				
+				for (auto& entry : colliders)
+					entry->_setRigidbody(mThisHandle);
+			}
+
+			UINT32 childCount = currentSO->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = currentSO->getChild(i);
+
+				if (child->hasComponent<CRigidbody>())
+					continue;
+
+				todo.push(child);
+			}
+		}
+	}
+
+	void CRigidbody::clearChildColliders()
+	{
+		if (mInternal == nullptr)
+			return;
+
+		mInternal->_detachColliders();
+	}
+
+	void CRigidbody::checkForNestedRigibody()
+	{
+		HSceneObject currentSO = SO()->getParent();
+
+		while(currentSO != nullptr)
+		{
+			if(currentSO->hasComponent<CRigidbody>())
+			{
+				LOGWRN("Nested Rigidbodies detected. This will result in inconsistent transformations. To parent one " \
+					"Rigidbody to another move its colliders to the new parent, but remove the Rigidbody component.");
+				return;
+			}
+
+			currentSO = currentSO->getParent();
+		}
+	}
+
+	void CRigidbody::triggerOnCollisionBegin(const CollisionData& data)
+	{
+		onCollisionBegin(data);
+	}
+
+	void CRigidbody::triggerOnCollisionStay(const CollisionData& data)
+	{
+		onCollisionStay(data);
+	}
+
+	void CRigidbody::triggerOnCollisionEnd(const CollisionData& data)
+	{
+		onCollisionEnd(data);
+	}
+
+	void CRigidbody::onInitialized()
+	{
+
+	}
+
+	void CRigidbody::onDestroyed()
+	{
+		clearChildColliders();
+		mInternal = nullptr;
+	}
+
+	void CRigidbody::onDisabled()
+	{
+		clearChildColliders();
+		mInternal = nullptr;
+	}
+
+	void CRigidbody::onEnabled()
+	{
+		mInternal = Rigidbody::create(SO());
+
+		updateChildColliders();
+
+#if BS_DEBUG_MODE
+		checkForNestedRigibody();
+#endif
+
+		mInternal->onCollisionBegin.connect(std::bind(&CRigidbody::triggerOnCollisionBegin, this, _1));
+		mInternal->onCollisionStay.connect(std::bind(&CRigidbody::triggerOnCollisionStay, this, _1));
+		mInternal->onCollisionEnd.connect(std::bind(&CRigidbody::triggerOnCollisionEnd, this, _1));
+
+		mInternal->setTransform(SO()->getWorldPosition(), SO()->getWorldRotation());
+
+		// Note: Merge into one call to avoid many virtual function calls
+		mInternal->setPositionSolverCount(mPositionSolverCount);
+		mInternal->setVelocitySolverCount(mVelocitySolverCount);
+		mInternal->setMaxAngularVelocity(mMaxAngularVelocity);
+		mInternal->setDrag(mLinearDrag);
+		mInternal->setAngularDrag(mAngularDrag);
+		mInternal->setSleepThreshold(mSleepThreshold);
+		mInternal->setUseGravity(mUseGravity);
+		mInternal->setIsKinematic(mIsKinematic);
+
+		if(((UINT32)mFlags & (UINT32)Rigidbody::Flag::AutoTensors) == 0)
+		{
+			mInternal->setCenterOfMass(mCMassPosition, mCMassRotation);
+			mInternal->setInertiaTensor(mIntertiaTensor);
+			mInternal->setMass(mMass);
+		}
+		else
+		{
+			if (((UINT32)mFlags & (UINT32)Rigidbody::Flag::AutoMass) == 0)
+				mInternal->setMass(mMass);
+
+			mInternal->_updateMassDistribution();
+		}
+	}
+
+	void CRigidbody::onTransformChanged(TransformChangedFlags flags)
+	{
+		if (!SO()->getActive())
+			return;
+
+		if((flags & TCF_Parent) != 0)
+		{
+			clearChildColliders();
+			updateChildColliders();
+
+			if (((UINT32)mFlags & (UINT32)Rigidbody::Flag::AutoTensors) != 0)
+				mInternal->_updateMassDistribution();
+
+#if BS_DEBUG_MODE
+			checkForNestedRigibody();
+#endif
+		}
+
+		mInternal->setTransform(SO()->getWorldPosition(), SO()->getWorldRotation());
+	}
+
 	RTTITypeBase* CRigidbody::getRTTIStatic()
 	{
 		return CRigidbodyRTTI::instance();

+ 0 - 10
BansheeCore/Source/BsCollider.cpp

@@ -58,16 +58,6 @@ namespace BansheeEngine
 		return mInternal->getMaterial();
 	}
 
-	void Collider::setIsActive(bool value)
-	{
-		mInternal->setIsActive(value);
-	}
-
-	bool Collider::getIsActive() const
-	{
-		return mInternal->getIsActive();
-	}
-
 	void Collider::setContactOffset(float value)
 	{
 		mInternal->setContactOffset(value);

+ 0 - 5
BansheeCore/Source/BsFCollider.cpp

@@ -28,11 +28,6 @@ namespace BansheeEngine
 		mMaterial = material;
 	}
 
-	void FCollider::setIsActive(bool value)
-	{
-		mIsActive = value;
-	}
-
 	void FCollider::setLayer(UINT64 layer)
 	{
 		mLayer = layer;

+ 510 - 531
BansheeCore/Source/BsPrefabDiff.cpp

@@ -1,534 +1,513 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsPrefabDiff.h"
-#include "BsPrefabDiffRTTI.h"
-#include "BsSceneObject.h"
-#include "BsMemorySerializer.h"
-#include "BsBinarySerializer.h"
-#include "BsBinaryDiff.h"
-
-namespace BansheeEngine
-{
-	RTTITypeBase* PrefabComponentDiff::getRTTIStatic()
-	{
-		return PrefabComponentDiffRTTI::instance();
-	}
-
-	RTTITypeBase* PrefabComponentDiff::getRTTI() const
-	{
-		return PrefabComponentDiff::getRTTIStatic();
-	}
-
-	RTTITypeBase* PrefabObjectDiff::getRTTIStatic()
-	{
-		return PrefabObjectDiffRTTI::instance();
-	}
-
-	RTTITypeBase* PrefabObjectDiff::getRTTI() const
-	{
-		return PrefabObjectDiff::getRTTIStatic();
-	}
-
-	SPtr<PrefabDiff> PrefabDiff::create(const HSceneObject& prefab, const HSceneObject& instance)
-	{
-		if (prefab->mPrefabLinkUUID != instance->mPrefabLinkUUID)
-			return nullptr;
-
-		// Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it
-		// would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now.
-
-		// Rename instance objects so they share the same IDs as the prefab objects (if they link IDs match). This allows
-		// game object handle diff to work properly, because otherwise handles that point to same objects would be 
-		// marked as different because the instance IDs of the two objects don't match (since one is in prefab and one
-		// in instance).
-		Vector<RenamedGameObject> renamedObjects;
-		renameInstanceIds(prefab, instance, renamedObjects);
-
-		SPtr<PrefabDiff> output = bs_shared_ptr_new<PrefabDiff>();
-		output->mRoot = generateDiff(prefab, instance);
-
-		restoreInstanceIds(renamedObjects);
-
-		return output;
-	}
-
-	void PrefabDiff::apply(const HSceneObject& object)
-	{
-		if (mRoot == nullptr)
-			return;
-
-		GameObjectManager::instance().startDeserialization();
-		applyDiff(mRoot, object);
-		GameObjectManager::instance().endDeserialization();
-	}
-
-	void PrefabDiff::applyDiff(const SPtr<PrefabObjectDiff>& diff, const HSceneObject& object)
-	{
-		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Name) != 0)
-			object->setName(diff->name);
-
-		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Position) != 0)
-			object->setPosition(diff->position);
-
-		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Rotation) != 0)
-			object->setRotation(diff->rotation);
-
-		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Scale) != 0)
-			object->setScale(diff->scale);
-
-		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Active) != 0)
-			object->setActive(diff->isActive);
-
-		// Note: It is important to remove objects and components first, before adding them.
-		//		 Some systems rely on the fact that applyDiff added components/objects are 
-		//       always at the end.
-		const Vector<HComponent>& components = object->getComponents();
-		for (auto& removedId : diff->removedComponents)
-		{
-			for (auto component : components)
-			{
-				if (removedId == component->getLinkId())
-				{
-					component->destroy();
-					break;
-				}
-			}
-		}
-
-		for (auto& removedId : diff->removedChildren)
-		{
-			UINT32 childCount = object->getNumChildren();
-			for (UINT32 i = 0; i < childCount; i++)
-			{
-				HSceneObject child = object->getChild(i);
-				if (removedId == child->getLinkId())
-				{
-					child->destroy();
-					break;
-				}
-			}
-		}
-
-		for (auto& addedComponentData : diff->addedComponents)
-		{
-			BinarySerializer bs;
-			SPtr<Component> component = std::static_pointer_cast<Component>(bs._decodeIntermediate(addedComponentData));
-
-			// It's possible the prefab added the same component that is in the diff, in which case we replace the existing
-			// component with the new one
-			bool foundExisting = false;
-			for (auto& entry : components)
-			{
-				if (entry->typeEquals(*component))
-				{
-					GameObjectInstanceDataPtr instanceData = entry->_getInstanceData();
-					entry->destroy(true);
-
-					component->_setInstanceData(instanceData);
-					object->addComponentInternal(component);
-
-					foundExisting = true;
-					break;
-				}
-			}
-
-			if (!foundExisting)
-			{
-				object->addComponentInternal(component);
-			}			
-		}
-
-		for (auto& addedChildData : diff->addedChildren)
-		{
-			BinarySerializer bs;
-			SPtr<SceneObject> sceneObject = std::static_pointer_cast<SceneObject>(bs._decodeIntermediate(addedChildData));
-			sceneObject->setParent(object);
-			sceneObject->_instantiate();
-		}
-
-		for (auto& componentDiff : diff->componentDiffs)
-		{
-			for (auto& component : components)
-			{
-				if (componentDiff->id == component->getLinkId())
-				{
-					IDiff& diffHandler = component->getRTTI()->getDiffHandler();
-					diffHandler.applyDiff(component.getInternalPtr(), componentDiff->data);
-					break;
-				}
-			}
-		}
-
-		for (auto& childDiff : diff->childDiffs)
-		{
-			UINT32 childCount = object->getNumChildren();
-			for (UINT32 i = 0; i < childCount; i++)
-			{
-				HSceneObject child = object->getChild(i);
-				if (childDiff->id == child->getLinkId())
-				{
-					applyDiff(childDiff, child);
-					break;
-				}
-			}
-		}
-	}
-
-	SPtr<PrefabObjectDiff> PrefabDiff::generateDiff(const HSceneObject& prefab, const HSceneObject& instance)
-	{
-		SPtr<PrefabObjectDiff> output;
-
-		if (prefab->getName() != instance->getName())
-		{
-			if (output == nullptr)
-				output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-			output->name = instance->getName();
-			output->soFlags |= (UINT32)SceneObjectDiffFlags::Name;
-		}
-
-		if (prefab->getPosition() != instance->getPosition())
-		{
-			if (output == nullptr)
-				output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-			output->position = instance->getPosition();
-			output->soFlags |= (UINT32)SceneObjectDiffFlags::Position;
-		}
-
-		if (prefab->getRotation() != instance->getRotation())
-		{
-			if (output == nullptr)
-				output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-			output->rotation = instance->getRotation();
-			output->soFlags |= (UINT32)SceneObjectDiffFlags::Rotation;
-		}
-
-		if (prefab->getScale() != instance->getScale())
-		{
-			if (output == nullptr)
-				output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-			output->scale = instance->getScale();
-			output->soFlags |= (UINT32)SceneObjectDiffFlags::Scale;
-		}
-
-		if (prefab->getActive() != instance->getActive())
-		{
-			if (output == nullptr)
-				output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-			output->isActive = instance->getActive();
-			output->soFlags |= (UINT32)SceneObjectDiffFlags::Active;
-		}
-
-		UINT32 prefabChildCount = prefab->getNumChildren();
-		UINT32 instanceChildCount = instance->getNumChildren();
-
-		// Find modified and removed children
-		for (UINT32 i = 0; i < prefabChildCount; i++)
-		{
-			HSceneObject prefabChild = prefab->getChild(i);
-
-			SPtr<PrefabObjectDiff> childDiff;
-			bool foundMatching = false;
-			for (UINT32 j = 0; j < instanceChildCount; j++)
-			{
-				HSceneObject instanceChild = instance->getChild(j);
-
-				if (prefabChild->getLinkId() == instanceChild->getLinkId())
-				{
-					if (instanceChild->mPrefabLinkUUID.empty())
-						childDiff = generateDiff(prefabChild, instanceChild);
-
-					foundMatching = true;
-					break;
-				}
-			}
-
-			if (foundMatching)
-			{
-				if (childDiff != nullptr)
-				{
-					if (output == nullptr)
-						output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-					output->childDiffs.push_back(childDiff);
-				}
-			}
-			else
-			{
-				if (output == nullptr)
-					output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-				output->removedChildren.push_back(prefabChild->getLinkId());
-			}	
-		}
-
-		// Find added children
-		for (UINT32 i = 0; i < instanceChildCount; i++)
-		{
-			HSceneObject instanceChild = instance->getChild(i);
-
-			if (instanceChild->hasFlag(SOF_DontSave))
-				continue;
-
-			bool foundMatching = false;
-			if (instanceChild->getLinkId() != -1)
-			{
-				for (UINT32 j = 0; j < prefabChildCount; j++)
-				{
-					HSceneObject prefabChild = prefab->getChild(j);
-
-					if (prefabChild->getLinkId() == instanceChild->getLinkId())
-					{
-						foundMatching = true;
-						break;
-					}
-				}
-			}
-
-			if (!foundMatching)
-			{
-				BinarySerializer bs;
-				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceChild.get());
-
-				if (output == nullptr)
-					output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-				output->addedChildren.push_back(obj);
-			}
-		}
-
-		const Vector<HComponent>& prefabComponents = prefab->getComponents();
-		const Vector<HComponent>& instanceComponents = instance->getComponents();
-
-		UINT32 prefabComponentCount = (UINT32)prefabComponents.size();
-		UINT32 instanceComponentCount = (UINT32)instanceComponents.size();
-
-		// Find modified and removed components
-		for (UINT32 i = 0; i < prefabComponentCount; i++)
-		{
-			HComponent prefabComponent = prefabComponents[i];
-
-			SPtr<PrefabComponentDiff> childDiff;
-			bool foundMatching = false;
-			for (UINT32 j = 0; j < instanceComponentCount; j++)
-			{
-				HComponent instanceComponent = instanceComponents[j];
-
-				if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
-				{
-					BinarySerializer bs;
-					SPtr<SerializedObject> encodedPrefab = bs._encodeIntermediate(prefabComponent.get());
-					SPtr<SerializedObject> encodedInstance = bs._encodeIntermediate(instanceComponent.get());
-
-					IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler();
-					SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance);
-
-					if (diff != nullptr)
-					{
-						childDiff = bs_shared_ptr_new<PrefabComponentDiff>();
-						childDiff->id = prefabComponent->getLinkId();
-						childDiff->data = diff;
-					}
-
-					foundMatching = true;
-					break;
-				}
-			}
-
-			if (foundMatching)
-			{
-				if (childDiff != nullptr)
-				{
-					if (output == nullptr)
-						output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-					output->componentDiffs.push_back(childDiff);
-				}
-			}
-			else
-			{
-				if (output == nullptr)
-					output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-				output->removedComponents.push_back(prefabComponent->getLinkId());
-			}
-		}
-
-		// Find added components
-		for (UINT32 i = 0; i < instanceComponentCount; i++)
-		{
-			HComponent instanceComponent = instanceComponents[i];
-
-			bool foundMatching = false;
-			if (instanceComponent->getLinkId() != -1)
-			{
-				for (UINT32 j = 0; j < prefabComponentCount; j++)
-				{
-					HComponent prefabComponent = prefabComponents[j];
-
-					if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
-					{
-						foundMatching = true;
-						break;
-					}
-				}
-			}
-
-			if (!foundMatching)
-			{
-				BinarySerializer bs;
-				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceComponent.get());
-
-				if (output == nullptr)
-					output = bs_shared_ptr_new<PrefabObjectDiff>();
-
-				output->addedComponents.push_back(obj);
-			}
-		}
-
-		if (output != nullptr)
-			output->id = instance->getLinkId();
-
-		return output;
-	}
-
-	void PrefabDiff::renameInstanceIds(const HSceneObject& prefab, const HSceneObject& instance, Vector<RenamedGameObject>& output)
-	{
-		UnorderedMap<String, UnorderedMap<UINT32, UINT64>> linkToInstanceId;
-
-		struct StackEntry
-		{
-			HSceneObject so;
-			String uuid;
-		};
-
-		// When renaming it is important to rename the prefab and not the instance, since the diff will otherwise
-		// contain prefab's IDs, but will be used for the instance.
-
-		Stack<StackEntry> todo;
-		todo.push({ instance, "root" });
-
-		while (!todo.empty())
-		{
-			StackEntry current = todo.top();
-			todo.pop();
-
-			String childParentUUID;
-			if (current.so->mPrefabLinkUUID.empty())
-				childParentUUID = current.uuid;
-			else
-				childParentUUID = current.so->mPrefabLinkUUID;
-
-			UnorderedMap<UINT32, UINT64>& idMap = linkToInstanceId[childParentUUID];
-
-			const Vector<HComponent>& components = current.so->getComponents();
-			for (auto& component : components)
-			{
-				if (component->getLinkId() != (UINT32)-1)
-					idMap[component->getLinkId()] = component->getInstanceId();
-			}
-
-			UINT32 numChildren = current.so->getNumChildren();
-			for (UINT32 i = 0; i < numChildren; i++)
-			{
-				HSceneObject child = current.so->getChild(i);
-
-				if (child->getLinkId() != (UINT32)-1)
-					idMap[child->getLinkId()] = child->getInstanceId();
-
-				todo.push({ child, childParentUUID });
-			}
-		}
-
-		// Root has link ID from its parent so we handle it separately
-		{
-			output.push_back(RenamedGameObject());
-			RenamedGameObject& renamedGO = output.back();
-			renamedGO.instanceData = instance->mInstanceData;
-			renamedGO.originalId = instance->getInstanceId();
-
-			prefab->mInstanceData->mInstanceId = instance->getInstanceId();
-		}
-
-		todo.push({ prefab, "root" });
-		while (!todo.empty())
-		{
-			StackEntry current = todo.top();
-			todo.pop();
-
-			String childParentUUID;
-			if (current.so->mPrefabLinkUUID.empty())
-				childParentUUID = current.uuid;
-			else
-				childParentUUID = current.so->mPrefabLinkUUID;
-
-			auto iterFind = linkToInstanceId.find(childParentUUID);
-			if (iterFind != linkToInstanceId.end())
-			{
-				UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
-
-				const Vector<HComponent>& components = current.so->getComponents();
-				for (auto& component : components)
-				{
-					auto iterFind2 = idMap.find(component->getLinkId());
-					if (iterFind2 != idMap.end())
-					{
-						output.push_back(RenamedGameObject());
-						RenamedGameObject& renamedGO = output.back();
-						renamedGO.instanceData = component->mInstanceData;
-						renamedGO.originalId = component->getInstanceId();
-
-						component->mInstanceData->mInstanceId = iterFind2->second;
-					}
-				}
-			}
-
-			UINT32 numChildren = current.so->getNumChildren();
-			for (UINT32 i = 0; i < numChildren; i++)
-			{
-				HSceneObject child = current.so->getChild(i);
-
-				if (iterFind != linkToInstanceId.end())
-				{
-					if (child->getLinkId() != -1)
-					{
-						UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
-
-						auto iterFind2 = idMap.find(child->getLinkId());
-						if (iterFind2 != idMap.end())
-						{
-							output.push_back(RenamedGameObject());
-							RenamedGameObject& renamedGO = output.back();
-							renamedGO.instanceData = child->mInstanceData;
-							renamedGO.originalId = child->getInstanceId();
-
-							child->mInstanceData->mInstanceId = iterFind2->second;
-						}
-					}
-				}
-
-				todo.push({ child, childParentUUID });
-			}
-		}
-	}
-
-	void PrefabDiff::restoreInstanceIds(const Vector<RenamedGameObject>& renamedObjects)
-	{
-		for (auto& renamedGO : renamedObjects)
-			renamedGO.instanceData->mInstanceId = renamedGO.originalId;
-	}
-
-	RTTITypeBase* PrefabDiff::getRTTIStatic()
-	{
-		return PrefabDiffRTTI::instance();
-	}
-
-	RTTITypeBase* PrefabDiff::getRTTI() const
-	{
-		return PrefabDiff::getRTTIStatic();
-	}
+#include "BsPrefabDiff.h"
+#include "BsPrefabDiffRTTI.h"
+#include "BsSceneObject.h"
+#include "BsMemorySerializer.h"
+#include "BsBinarySerializer.h"
+#include "BsBinaryDiff.h"
+
+namespace BansheeEngine
+{
+	RTTITypeBase* PrefabComponentDiff::getRTTIStatic()
+	{
+		return PrefabComponentDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabComponentDiff::getRTTI() const
+	{
+		return PrefabComponentDiff::getRTTIStatic();
+	}
+
+	RTTITypeBase* PrefabObjectDiff::getRTTIStatic()
+	{
+		return PrefabObjectDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabObjectDiff::getRTTI() const
+	{
+		return PrefabObjectDiff::getRTTIStatic();
+	}
+
+	SPtr<PrefabDiff> PrefabDiff::create(const HSceneObject& prefab, const HSceneObject& instance)
+	{
+		if (prefab->mPrefabLinkUUID != instance->mPrefabLinkUUID)
+			return nullptr;
+
+		// Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it
+		// would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now.
+
+		// Rename instance objects so they share the same IDs as the prefab objects (if they link IDs match). This allows
+		// game object handle diff to work properly, because otherwise handles that point to same objects would be 
+		// marked as different because the instance IDs of the two objects don't match (since one is in prefab and one
+		// in instance).
+		Vector<RenamedGameObject> renamedObjects;
+		renameInstanceIds(prefab, instance, renamedObjects);
+
+		SPtr<PrefabDiff> output = bs_shared_ptr_new<PrefabDiff>();
+		output->mRoot = generateDiff(prefab, instance);
+
+		restoreInstanceIds(renamedObjects);
+
+		return output;
+	}
+
+	void PrefabDiff::apply(const HSceneObject& object)
+	{
+		if (mRoot == nullptr)
+			return;
+
+		GameObjectManager::instance().startDeserialization();
+		applyDiff(mRoot, object);
+		GameObjectManager::instance().endDeserialization();
+	}
+
+	void PrefabDiff::applyDiff(const SPtr<PrefabObjectDiff>& diff, const HSceneObject& object)
+	{
+		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Name) != 0)
+			object->setName(diff->name);
+
+		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Position) != 0)
+			object->setPosition(diff->position);
+
+		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Rotation) != 0)
+			object->setRotation(diff->rotation);
+
+		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Scale) != 0)
+			object->setScale(diff->scale);
+
+		if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Active) != 0)
+			object->setActive(diff->isActive);
+
+		// Note: It is important to remove objects and components first, before adding them.
+		//		 Some systems rely on the fact that applyDiff added components/objects are 
+		//       always at the end.
+		const Vector<HComponent>& components = object->getComponents();
+		for (auto& removedId : diff->removedComponents)
+		{
+			for (auto component : components)
+			{
+				if (removedId == component->getLinkId())
+				{
+					component->destroy();
+					break;
+				}
+			}
+		}
+
+		for (auto& removedId : diff->removedChildren)
+		{
+			UINT32 childCount = object->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = object->getChild(i);
+				if (removedId == child->getLinkId())
+				{
+					child->destroy();
+					break;
+				}
+			}
+		}
+
+		for (auto& addedComponentData : diff->addedComponents)
+		{
+			BinarySerializer bs;
+			SPtr<Component> component = std::static_pointer_cast<Component>(bs._decodeIntermediate(addedComponentData));
+
+			object->addComponentInternal(component);
+		}
+
+		for (auto& addedChildData : diff->addedChildren)
+		{
+			BinarySerializer bs;
+			SPtr<SceneObject> sceneObject = std::static_pointer_cast<SceneObject>(bs._decodeIntermediate(addedChildData));
+			sceneObject->setParent(object);
+			sceneObject->_instantiate();
+		}
+
+		for (auto& componentDiff : diff->componentDiffs)
+		{
+			for (auto& component : components)
+			{
+				if (componentDiff->id == component->getLinkId())
+				{
+					IDiff& diffHandler = component->getRTTI()->getDiffHandler();
+					diffHandler.applyDiff(component.getInternalPtr(), componentDiff->data);
+					break;
+				}
+			}
+		}
+
+		for (auto& childDiff : diff->childDiffs)
+		{
+			UINT32 childCount = object->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = object->getChild(i);
+				if (childDiff->id == child->getLinkId())
+				{
+					applyDiff(childDiff, child);
+					break;
+				}
+			}
+		}
+	}
+
+	SPtr<PrefabObjectDiff> PrefabDiff::generateDiff(const HSceneObject& prefab, const HSceneObject& instance)
+	{
+		SPtr<PrefabObjectDiff> output;
+
+		if (prefab->getName() != instance->getName())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+			output->name = instance->getName();
+			output->soFlags |= (UINT32)SceneObjectDiffFlags::Name;
+		}
+
+		if (prefab->getPosition() != instance->getPosition())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+			output->position = instance->getPosition();
+			output->soFlags |= (UINT32)SceneObjectDiffFlags::Position;
+		}
+
+		if (prefab->getRotation() != instance->getRotation())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+			output->rotation = instance->getRotation();
+			output->soFlags |= (UINT32)SceneObjectDiffFlags::Rotation;
+		}
+
+		if (prefab->getScale() != instance->getScale())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+			output->scale = instance->getScale();
+			output->soFlags |= (UINT32)SceneObjectDiffFlags::Scale;
+		}
+
+		if (prefab->getActive() != instance->getActive())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+			output->isActive = instance->getActive();
+			output->soFlags |= (UINT32)SceneObjectDiffFlags::Active;
+		}
+
+		UINT32 prefabChildCount = prefab->getNumChildren();
+		UINT32 instanceChildCount = instance->getNumChildren();
+
+		// Find modified and removed children
+		for (UINT32 i = 0; i < prefabChildCount; i++)
+		{
+			HSceneObject prefabChild = prefab->getChild(i);
+
+			SPtr<PrefabObjectDiff> childDiff;
+			bool foundMatching = false;
+			for (UINT32 j = 0; j < instanceChildCount; j++)
+			{
+				HSceneObject instanceChild = instance->getChild(j);
+
+				if (prefabChild->getLinkId() == instanceChild->getLinkId())
+				{
+					if (instanceChild->mPrefabLinkUUID.empty())
+						childDiff = generateDiff(prefabChild, instanceChild);
+
+					foundMatching = true;
+					break;
+				}
+			}
+
+			if (foundMatching)
+			{
+				if (childDiff != nullptr)
+				{
+					if (output == nullptr)
+						output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+					output->childDiffs.push_back(childDiff);
+				}
+			}
+			else
+			{
+				if (output == nullptr)
+					output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+				output->removedChildren.push_back(prefabChild->getLinkId());
+			}	
+		}
+
+		// Find added children
+		for (UINT32 i = 0; i < instanceChildCount; i++)
+		{
+			HSceneObject instanceChild = instance->getChild(i);
+
+			if (instanceChild->hasFlag(SOF_DontSave))
+				continue;
+
+			bool foundMatching = false;
+			if (instanceChild->getLinkId() != -1)
+			{
+				for (UINT32 j = 0; j < prefabChildCount; j++)
+				{
+					HSceneObject prefabChild = prefab->getChild(j);
+
+					if (prefabChild->getLinkId() == instanceChild->getLinkId())
+					{
+						foundMatching = true;
+						break;
+					}
+				}
+			}
+
+			if (!foundMatching)
+			{
+				BinarySerializer bs;
+				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceChild.get());
+
+				if (output == nullptr)
+					output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+				output->addedChildren.push_back(obj);
+			}
+		}
+
+		const Vector<HComponent>& prefabComponents = prefab->getComponents();
+		const Vector<HComponent>& instanceComponents = instance->getComponents();
+
+		UINT32 prefabComponentCount = (UINT32)prefabComponents.size();
+		UINT32 instanceComponentCount = (UINT32)instanceComponents.size();
+
+		// Find modified and removed components
+		for (UINT32 i = 0; i < prefabComponentCount; i++)
+		{
+			HComponent prefabComponent = prefabComponents[i];
+
+			SPtr<PrefabComponentDiff> childDiff;
+			bool foundMatching = false;
+			for (UINT32 j = 0; j < instanceComponentCount; j++)
+			{
+				HComponent instanceComponent = instanceComponents[j];
+
+				if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
+				{
+					BinarySerializer bs;
+					SPtr<SerializedObject> encodedPrefab = bs._encodeIntermediate(prefabComponent.get());
+					SPtr<SerializedObject> encodedInstance = bs._encodeIntermediate(instanceComponent.get());
+
+					IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler();
+					SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance);
+
+					if (diff != nullptr)
+					{
+						childDiff = bs_shared_ptr_new<PrefabComponentDiff>();
+						childDiff->id = prefabComponent->getLinkId();
+						childDiff->data = diff;
+					}
+
+					foundMatching = true;
+					break;
+				}
+			}
+
+			if (foundMatching)
+			{
+				if (childDiff != nullptr)
+				{
+					if (output == nullptr)
+						output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+					output->componentDiffs.push_back(childDiff);
+				}
+			}
+			else
+			{
+				if (output == nullptr)
+					output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+				output->removedComponents.push_back(prefabComponent->getLinkId());
+			}
+		}
+
+		// Find added components
+		for (UINT32 i = 0; i < instanceComponentCount; i++)
+		{
+			HComponent instanceComponent = instanceComponents[i];
+
+			bool foundMatching = false;
+			if (instanceComponent->getLinkId() != -1)
+			{
+				for (UINT32 j = 0; j < prefabComponentCount; j++)
+				{
+					HComponent prefabComponent = prefabComponents[j];
+
+					if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
+					{
+						foundMatching = true;
+						break;
+					}
+				}
+			}
+
+			if (!foundMatching)
+			{
+				BinarySerializer bs;
+				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceComponent.get());
+
+				if (output == nullptr)
+					output = bs_shared_ptr_new<PrefabObjectDiff>();
+
+				output->addedComponents.push_back(obj);
+			}
+		}
+
+		if (output != nullptr)
+			output->id = instance->getLinkId();
+
+		return output;
+	}
+
+	void PrefabDiff::renameInstanceIds(const HSceneObject& prefab, const HSceneObject& instance, Vector<RenamedGameObject>& output)
+	{
+		UnorderedMap<String, UnorderedMap<UINT32, UINT64>> linkToInstanceId;
+
+		struct StackEntry
+		{
+			HSceneObject so;
+			String uuid;
+		};
+
+		// When renaming it is important to rename the prefab and not the instance, since the diff will otherwise
+		// contain prefab's IDs, but will be used for the instance.
+
+		Stack<StackEntry> todo;
+		todo.push({ instance, "root" });
+
+		while (!todo.empty())
+		{
+			StackEntry current = todo.top();
+			todo.pop();
+
+			String childParentUUID;
+			if (current.so->mPrefabLinkUUID.empty())
+				childParentUUID = current.uuid;
+			else
+				childParentUUID = current.so->mPrefabLinkUUID;
+
+			UnorderedMap<UINT32, UINT64>& idMap = linkToInstanceId[childParentUUID];
+
+			const Vector<HComponent>& components = current.so->getComponents();
+			for (auto& component : components)
+			{
+				if (component->getLinkId() != (UINT32)-1)
+					idMap[component->getLinkId()] = component->getInstanceId();
+			}
+
+			UINT32 numChildren = current.so->getNumChildren();
+			for (UINT32 i = 0; i < numChildren; i++)
+			{
+				HSceneObject child = current.so->getChild(i);
+
+				if (child->getLinkId() != (UINT32)-1)
+					idMap[child->getLinkId()] = child->getInstanceId();
+
+				todo.push({ child, childParentUUID });
+			}
+		}
+
+		// Root has link ID from its parent so we handle it separately
+		{
+			output.push_back(RenamedGameObject());
+			RenamedGameObject& renamedGO = output.back();
+			renamedGO.instanceData = instance->mInstanceData;
+			renamedGO.originalId = instance->getInstanceId();
+
+			prefab->mInstanceData->mInstanceId = instance->getInstanceId();
+		}
+
+		todo.push({ prefab, "root" });
+		while (!todo.empty())
+		{
+			StackEntry current = todo.top();
+			todo.pop();
+
+			String childParentUUID;
+			if (current.so->mPrefabLinkUUID.empty())
+				childParentUUID = current.uuid;
+			else
+				childParentUUID = current.so->mPrefabLinkUUID;
+
+			auto iterFind = linkToInstanceId.find(childParentUUID);
+			if (iterFind != linkToInstanceId.end())
+			{
+				UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
+
+				const Vector<HComponent>& components = current.so->getComponents();
+				for (auto& component : components)
+				{
+					auto iterFind2 = idMap.find(component->getLinkId());
+					if (iterFind2 != idMap.end())
+					{
+						output.push_back(RenamedGameObject());
+						RenamedGameObject& renamedGO = output.back();
+						renamedGO.instanceData = component->mInstanceData;
+						renamedGO.originalId = component->getInstanceId();
+
+						component->mInstanceData->mInstanceId = iterFind2->second;
+					}
+				}
+			}
+
+			UINT32 numChildren = current.so->getNumChildren();
+			for (UINT32 i = 0; i < numChildren; i++)
+			{
+				HSceneObject child = current.so->getChild(i);
+
+				if (iterFind != linkToInstanceId.end())
+				{
+					if (child->getLinkId() != -1)
+					{
+						UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
+
+						auto iterFind2 = idMap.find(child->getLinkId());
+						if (iterFind2 != idMap.end())
+						{
+							output.push_back(RenamedGameObject());
+							RenamedGameObject& renamedGO = output.back();
+							renamedGO.instanceData = child->mInstanceData;
+							renamedGO.originalId = child->getInstanceId();
+
+							child->mInstanceData->mInstanceId = iterFind2->second;
+						}
+					}
+				}
+
+				todo.push({ child, childParentUUID });
+			}
+		}
+	}
+
+	void PrefabDiff::restoreInstanceIds(const Vector<RenamedGameObject>& renamedObjects)
+	{
+		for (auto& renamedGO : renamedObjects)
+			renamedGO.instanceData->mInstanceId = renamedGO.originalId;
+	}
+
+	RTTITypeBase* PrefabDiff::getRTTIStatic()
+	{
+		return PrefabDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabDiff::getRTTI() const
+	{
+		return PrefabDiff::getRTTIStatic();
+	}
 }

+ 4 - 2
BansheeCore/Source/BsRigidbody.cpp

@@ -1,5 +1,6 @@
 #include "BsRigidbody.h"
 #include "BsPhysics.h"
+#include "BsFCollider.h"
 #include "BsSceneObject.h"
 #include "BsUtility.h"
 
@@ -30,9 +31,10 @@ namespace BansheeEngine
 			mColliders.erase(iterFind);
 	}
 
-	void Rigidbody::setIsActive(bool value)
+	void Rigidbody::_detachColliders()
 	{
-		mIsActive = value;
+		while (mColliders.size() > 0)
+			mColliders[0]->setRigidbody(nullptr);
 	}
 
 	void Rigidbody::setFlags(Flag flags)

+ 6 - 4
BansheeCore/Source/BsSceneObject.cpp

@@ -689,12 +689,12 @@ namespace BansheeEngine
 		return cloneObj->mThisHandle;
 	}
 
-	HComponent SceneObject::getComponent(UINT32 typeId) const
+	HComponent SceneObject::getComponent(RTTITypeBase* type) const
 	{
-		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
+		for(auto& entry : mComponents)
 		{
-			if((*iter)->getRTTI()->getRTTIId() == typeId)
-				return *iter;
+			if(entry->getRTTI()->isDerivedFrom(type))
+				return entry;
 		}
 
 		return HComponent();
@@ -750,6 +750,8 @@ namespace BansheeEngine
 	{
 		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
 		newComponent->mParent = mThisHandle;
+		newComponent->mThisHandle = newComponent;
+
 		mComponents.push_back(newComponent);
 	}
 

+ 0 - 1
BansheePhysX/Include/BsFPhysXCollider.h

@@ -48,7 +48,6 @@ namespace BansheeEngine
 
 		void setRigidbody(const SPtr<Rigidbody>& rigidbody) override;
 		void setMaterial(const HPhysicsMaterial& material) override;
-		void setIsActive(bool value) override;
 		void setLayer(UINT64 layer) override;
 
 		physx::PxShape* _getShape() const { return mShape; }

+ 1 - 3
BansheePhysX/Include/BsPhysXRigidbody.h

@@ -59,7 +59,7 @@ namespace BansheeEngine
 
 		void setCenterOfMass(const Vector3& position, const Quaternion& rotation) override;
 		Vector3 getCenterOfMassPosition() const override;
-		Quaternion getCenterOfMassRotatation() const override;
+		Quaternion getCenterOfMassRotation() const override;
 
 		void setPositionSolverCount(UINT32 count) override;
 		UINT32 getPositionSolverCount() const override;
@@ -67,8 +67,6 @@ namespace BansheeEngine
 		void setVelocitySolverCount(UINT32 count) override;
 		UINT32 getVelocitySolverCount() const override;
 
-		void setIsActive(bool value) override;
-
 		void addForce(const Vector3& force, ForceMode mode = ForceMode::Force) override;
 		void addTorque(const Vector3& torque, ForceMode mode = ForceMode::Force) override;
 		void addForceAtPoint(const Vector3& force, const Vector3& position,

+ 0 - 16
BansheePhysX/Source/BsFPhysXCollider.cpp

@@ -135,22 +135,6 @@ namespace BansheeEngine
 		mShape->setMaterials(materials, sizeof(materials));
 	}
 
-	void FPhysXCollider::setIsActive(bool value)
-	{
-		// Note: A different option might be just to fully destroy the shape & actor when disabled (might result in better
-		//       performance if a lot of disabled actors in scene).
-
-		if (!value)
-			mShape->setFlags((PxShapeFlags)0);
-		else
-		{
-			mShape->setFlag(PxShapeFlag::eSCENE_QUERY_SHAPE, true);
-			setIsTrigger(mIsTrigger);
-		}
-
-		FCollider::setIsActive(value);
-	}
-
 	void FPhysXCollider::setLayer(UINT64 layer)
 	{
 		PxFilterData data;

+ 1 - 6
BansheePhysX/Source/BsPhysXRigidbody.cpp

@@ -235,7 +235,7 @@ namespace BansheeEngine
 		return fromPxVector(cMassTfrm.p);
 	}
 
-	Quaternion PhysXRigidbody::getCenterOfMassRotatation() const
+	Quaternion PhysXRigidbody::getCenterOfMassRotation() const
 	{
 		PxTransform cMassTfrm = mInternal->getCMassLocalPose();
 		return fromPxQuaternion(cMassTfrm.q);
@@ -269,11 +269,6 @@ namespace BansheeEngine
 		return velCount;
 	}
 
-	void PhysXRigidbody::setIsActive(bool value)
-	{
-		// TODO
-	}
-
 	void PhysXRigidbody::addForce(const Vector3& force, ForceMode mode)
 	{
 		mInternal->addForce(toPxVector(force), toPxForceMode(mode));

+ 1 - 1
MBansheeEditor/EditorApplication.cs

@@ -589,7 +589,7 @@ namespace BansheeEditor
             sceneDirty = dirty;
             SetStatusScene(Scene.ActiveSceneName, dirty);
 
-            if (!dirty)
+            if (!dirty && Scene.ActiveSceneUUID != null)
                 persistentData.dirtyResources.Remove(Scene.ActiveSceneUUID);
         }
 

+ 794 - 797
MBansheeEditor/UnitTests.cs

@@ -1,799 +1,796 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-using BansheeEngine;
-using DebugUnit = System.Diagnostics.Debug;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Contains various managed unit tests.
-    /// </summary>
-    class UnitTests
-    {
-        /// <summary>
-        /// Triggers an exception when a unit test condition fails.
-        /// </summary>
-        /// <param name="success">True if the unit test condition succeeded, false otherwise.</param>
-        static void Assert(bool success)
-        {
-            if (!success)
-                throw new InvalidOperationException("Unit test failed.");
-        }
-
-        /// <summary>
-        /// Tests managed object serialization and deserialization.
-        /// </summary>
-        static void UnitTest1_ManagedSerialization()
-        {
-            SceneObject otherSO = new SceneObject("OtherSO");
-            UT1_Component2 dbgComponent2 = otherSO.AddComponent<UT1_Component2>();
-            dbgComponent2.a2 = 33;
-
-            SceneObject so = new SceneObject("TestSO");
-            UT1_Component1 dbgComponent = so.AddComponent<UT1_Component1>();
-
-            dbgComponent.a = 5;
-            dbgComponent.b = "SomeTestVal";
-            dbgComponent.complex.someValue = 19;
-            dbgComponent.complex.anotherValue = "AnotherValue";
-            dbgComponent.complex2.someValue2 = 21;
-            dbgComponent.complex2.anotherValue2 = "AnotherValue2";
-
-            dbgComponent.arrA = new int[5];
-            dbgComponent.arrA[4] = 5;
-            dbgComponent.arrB = new string[5];
-            dbgComponent.arrB[4] = "ArrAnotherValue";
-            dbgComponent.arrComplex = new UT1_SerzObj[5];
-            dbgComponent.arrComplex[4].someValue = 99;
-            dbgComponent.arrComplex[4].anotherValue = "ArrComplexAnotherValue";
-            dbgComponent.arrComplex2 = new UT1_SerzCls[5];
-            dbgComponent.arrComplex2[4] = new UT1_SerzCls();
-            dbgComponent.arrComplex2[4].someValue2 = 101;
-            dbgComponent.arrComplex2[4].anotherValue2 = "ArrComplex2AnotherValue";
-
-            dbgComponent.listA = new List<int>();
-            dbgComponent.listA.Add(5);
-            dbgComponent.listB = new List<string>();
-            dbgComponent.listB.Add("ListAnotherValue");
-            dbgComponent.listB.Add(null);
-            dbgComponent.listComplex = new List<UT1_SerzObj>();
-            dbgComponent.listComplex.Add(new UT1_SerzObj());
-            dbgComponent.listComplex.Add(new UT1_SerzObj(99, "ListComplexAnotherValue"));
-            dbgComponent.listComplex2 = new List<UT1_SerzCls>();
-            dbgComponent.listComplex2.Add(new UT1_SerzCls());
-            dbgComponent.listComplex2[0].someValue2 = 101;
-            dbgComponent.listComplex2[0].anotherValue2 = "ListComplexAnotherValue";
-            dbgComponent.listComplex2.Add(null);
-
-            dbgComponent.dictA = new Dictionary<int, string>();
-            dbgComponent.dictA[5] = "value";
-            dbgComponent.dictA[10] = "anotherValue";
-
-            dbgComponent.dictB = new Dictionary<string, UT1_SerzObj>();
-            dbgComponent.dictB["key1"] = new UT1_SerzObj(99, "DictComplexValue");
-
-            dbgComponent.otherComponent = dbgComponent2;
-            dbgComponent.otherSO = otherSO;
-
-            Internal_UT1_GameObjectClone(so);
-
-            System.Diagnostics.Debug.Assert(so.GetNumChildren() == 1);
-
-            for (int i = 0; i < so.GetNumChildren(); i++)
-            {
-                SceneObject childSO = so.GetChild(i);
-
-                UT1_Component1 otherComponent = childSO.GetComponent<UT1_Component1>();
-                DebugUnit.Assert(otherComponent.a == 5);
-                DebugUnit.Assert(otherComponent.b == "SomeTestVal");
-                DebugUnit.Assert(otherComponent.complex.someValue == 19);
-                DebugUnit.Assert(otherComponent.complex2.anotherValue2 == "AnotherValue2");
-
-                DebugUnit.Assert(otherComponent.arrA[4] == 5);
-                DebugUnit.Assert(otherComponent.arrB[4] == "ArrAnotherValue");
-                DebugUnit.Assert(otherComponent.arrComplex[4].someValue == 99);
-                DebugUnit.Assert(otherComponent.arrComplex2[4].anotherValue2 == "ArrComplex2AnotherValue");
-
-                DebugUnit.Assert(otherComponent.listA[0] == 5);
-                DebugUnit.Assert(otherComponent.listB[0] == "ListAnotherValue");
-                DebugUnit.Assert(otherComponent.listComplex[1].someValue == 99);
-                DebugUnit.Assert(otherComponent.listComplex2[0].anotherValue2 == "ListComplexAnotherValue");
-            }
-
-            so.Destroy();
-            otherSO.Destroy();
-        }
-
-        /// <summary>
-        /// Tests serializable properties used for inspection.
-        /// </summary>
-        static void UnitTest2_SerializableProperties()
-        {
-            SerializableObject obj = new SerializableObject(typeof(UT1_SerzCls), new UT1_SerzCls());
-
-            SerializableProperty prop = obj.Fields[0].GetProperty();
-            prop.SetValue(33);
-            DebugUnit.Assert(prop.GetValue<int>() == 33);
-
-            SerializableProperty prop2 = obj.Fields[2].GetProperty();
-
-            UT1_SerzCls child = new UT1_SerzCls();
-            child.anotherValue2 = "potato";
-            prop2.SetValue<UT1_SerzCls>(child);
-
-            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>() != null);
-            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>().anotherValue2 == "potato");
-        }
-
-        /// <summary>
-        /// Tests managed diff creation used by prefabs.
-        /// </summary>
-        static void UnitTest3_ManagedDiff()
-        {
-            UT_DiffObj original = new UT_DiffObj();
-            UT_DiffObj modified = new UT_DiffObj();
-
-            modified.plain2 = "banana";
-            modified.complex = new UT_DiffChildObj();
-            modified.complex2 = null;
-            modified.complex3.plain2 = "tomato";
-
-            modified.arrPlain1 = new[] {-1, -2, -3, -4};
-            modified.arrPlain2[2] = "cherry";
-            modified.arrComplex = new UT_DiffChildObj[3];
-            modified.arrComplex2[0].plain1 = -10;
-
-            modified.listPlain1[0] = -20;
-            modified.listPlain2 = new List<string>();
-            modified.listComplex = new List<UT_DiffChildObj>();
-            modified.listComplex.Add(new UT_DiffChildObj());
-            modified.listComplex2[1].plain2 = "orange";
-
-            modified.dictPlain1.Remove(20);
-            modified.dictPlain1[-30] = -30;
-            modified.dictComplex = new Dictionary<int, UT_DiffChildObj>();
-            modified.dictComplex[-40] = new UT_DiffChildObj();
-            modified.dictComplex2[31].plain1 = -50;
-
-            Internal_UT3_GenerateDiff(original, modified);
-            Internal_UT3_ApplyDiff(original);
-
-            DebugUnit.Assert(original.plain1 == modified.plain1);
-            DebugUnit.Assert(original.plain2 == modified.plain2);
-            DebugUnit.Assert(original.complex.plain2 == modified.complex.plain2);
-            DebugUnit.Assert(original.complex2 == modified.complex2);
-            DebugUnit.Assert(original.complex3.plain2 == modified.complex3.plain2);
-
-            DebugUnit.Assert(original.arrPlain1.Length == modified.arrPlain1.Length);
-            for (int i = 0; i < original.arrPlain1.Length; i++)
-                DebugUnit.Assert(original.arrPlain1[i] == modified.arrPlain1[i]);
-
-            for (int i = 0; i < original.arrPlain2.Length; i++)
-                DebugUnit.Assert(original.arrPlain2[i] == modified.arrPlain2[i]);
-
-            for (int i = 0; i < original.arrComplex.Length; i++)
-                DebugUnit.Assert(original.arrComplex[i] == modified.arrComplex[i]);
-
-            DebugUnit.Assert(original.arrComplex2[0].plain1 == modified.arrComplex2[0].plain1);
-
-            for (int i = 0; i < original.listPlain1.Count; i++)
-                DebugUnit.Assert(original.listPlain1[i] == modified.listPlain1[i]);
-
-            DebugUnit.Assert(original.listPlain2.Count == modified.listPlain2.Count);
-
-            for (int i = 0; i < original.listComplex.Count; i++)
-                DebugUnit.Assert(original.listComplex[i].plain1 == modified.listComplex[i].plain1);
-
-            DebugUnit.Assert(original.listComplex2[1].plain2 == modified.listComplex2[1].plain2);
-
-            foreach (var entry in modified.dictPlain1)
-            {
-                if (!original.dictPlain1.ContainsKey(entry.Key))
-                    DebugUnit.Assert(false);
-
-                DebugUnit.Assert(entry.Value == original.dictPlain1[entry.Key]);
-            }
-
-            foreach (var entry in modified.dictPlain2)
-            {
-                if (!original.dictPlain2.ContainsKey(entry.Key))
-                    DebugUnit.Assert(false);
-
-                DebugUnit.Assert(entry.Value == original.dictPlain2[entry.Key]);
-            }
-
-            foreach (var entry in modified.dictComplex)
-            {
-                if (!original.dictComplex.ContainsKey(entry.Key))
-                    DebugUnit.Assert(false);
-
-                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex[entry.Key].plain1);
-            }
-
-            foreach (var entry in modified.dictComplex2)
-            {
-                if (!original.dictComplex2.ContainsKey(entry.Key))
-                    DebugUnit.Assert(false);
-
-                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex2[entry.Key].plain1);
-            }
-        }
-
-        /// <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;
-            }
-
-            Action PrintSceneState = () =>
-            {
-                SceneObject root = Scene.Root;
-
-                Stack<SceneObject> todo = new Stack<SceneObject>();
-                todo.Push(root);
-
-                StringBuilder output = new StringBuilder();
-                while (todo.Count > 0)
-                {
-                    SceneObject so = todo.Pop();
-
-                    int numChildren = so.GetNumChildren();
-                    for (int i = numChildren - 1; i >= 0; i--)
-                    {
-                        SceneObject child = so.GetChild(i);
-
-                        output.AppendLine(child.Name);
-                        todo.Push(child);
-                    }  
-                }
-
-                Debug.Log(output);
-            };
-
-            // Disabled because it's a slow test, enable only when relevant (or when a build machine is set up)
-            return;
-
-            string oldScene = Scene.ActiveSceneUUID;
-            Scene.Clear();
-
-            try
-            {
-                // Simple scene save & load
-                {
-                    {
-                        // unitTest4Scene_0.prefab:
-                        // so0 (Comp1)
-                        //  - so0_0
-                        //  - so0_1 (Comp1)
-                        //    - so0_1_0 (Comp1)
-                        // so1 (Comp2)
-                        //  - so1_0
-
-                        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.LocalPosition = new Vector3(10.0f, 15.0f, 20.0f);
-                        so0_1.LocalPosition = new Vector3(1.0f, 2.0f, 3.0f);
-                        so1_0.LocalPosition = new Vector3(0, 123.0f, 0.0f);
-
-                        UT1_Component1 comp0 = so0.AddComponent<UT1_Component1>();
-                        UT1_Component2 comp1 = so1.AddComponent<UT1_Component2>();
-                        UT1_Component1 comp1_1 = so0_1.AddComponent<UT1_Component1>();
-                        UT1_Component1 comp0_1_0 = so0_1_0.AddComponent<UT1_Component1>();
-
-                        comp0.otherSO = so0_1_0;
-                        comp0.otherComponent = comp1;
-
-                        comp1_1.b = "originalValue2";
-
-                        comp0_1_0.b = "testValue";
-                        comp0_1_0.otherSO = so0;
-                        comp0_1_0.otherComponent2 = comp0;
-
-                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
-                    }
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_0.prefab");
-
-                        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 so0_1_0 = so0_1.FindChild("so0_1_0", false);
-
-                        Assert(so0_0 != null);
-                        Assert(so0_1 != null);
-                        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>();
-
-                        Assert(comp0 != null);
-                        Assert(comp1 != null);
-                        Assert(comp0_1_0 != null);
-                        Assert(comp0_1_0.b == "testValue");
-                        Assert(comp0.otherSO == so0_1_0);
-                        Assert(comp0.otherComponent == comp1);
-                        Assert(comp0_1_0.otherSO == so0);
-                        Assert(comp0_1_0.otherComponent2 == comp0);
-                    }
-                }
-
-                Debug.Log("Passed stage 1");
-
-                // Load & save a scene that contains a prefab and references its objects
-                {
-                    {
-                        // unitTest4Scene_1.prefab:
-                        // parentSO0
-                        //  - [unitTest4Scene_0.prefab]
-                        // parentSO1
-                        //  - parentSO1_0 (Comp1)
-
-                        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;
-                        parentSO0.LocalPosition = new Vector3(50.0f, 50.0f, 50.0f);
-
-                        UT1_Component1 parentComp1_0 = parentSO1_0.AddComponent<UT1_Component1>();
-
-                        Prefab scene0Prefab = ProjectLibrary.Load<Prefab>("unitTest4Scene_0.prefab");
-                        SceneObject prefabInstance = scene0Prefab.Instantiate();
-                        prefabInstance.Parent = parentSO0;
-                        prefabInstance.LocalPosition = Vector3.Zero;
-
-                        SceneObject so0 = prefabInstance.FindChild("so0", false);
-                        SceneObject so1 = prefabInstance.FindChild("so1", false);
-                        SceneObject so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.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.prefab");
-                    }
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
-
-                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
-                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
-                        SceneObject parentSO1_0 = parentSO1.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", false);
-                        SceneObject so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-
-                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
-
-                        Assert(parentComp1_0.otherSO == so1_0);
-                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
-                    }
-                }
-
-                Debug.Log("Passed stage 2");
-
-                // Modify prefab, reload the scene and ensure it is updated with modified prefab
-                {
-                    {
-                        // unitTest4Scene_0.prefab:
-                        // so0
-                        //  - so0_1 (Comp1)
-                        //    - so0_1_0 (Comp1)
-                        // so1 (Comp1, Comp2)
-                        //  - so1_0
-                        //  - so1_1
-
-                        Scene.Load("unitTest4Scene_0.prefab");
-
-                        SceneObject sceneRoot = Scene.Root;
-                        SceneObject so0 = sceneRoot.FindChild("so0", false);
-                        SceneObject so0_0 = so0.FindChild("so0_0", false);
-                        SceneObject so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1 = sceneRoot.FindChild("so1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-
-                        SceneObject so1_1 = new SceneObject("so1_1");
-                        so1_1.Parent = so1;
-
-                        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.LocalPosition = new Vector3(0, 999.0f, 0.0f);
-
-                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
-                    }
-
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
-
-                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
-                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
-                        SceneObject parentSO1_0 = parentSO1.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 so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-                        SceneObject so1_1 = so1.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>();
-
-                        Assert(parentComp1_0.otherSO == so1_0);
-                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
-                        Assert(so1_1 != null);
-                        Assert(so0_0 == null);
-                        Assert(comp0 == null);
-                        Assert(comp0_1_0.otherSO == so1_1);
-                        Assert(comp0_1_0.otherComponent2 == comp1);
-                        Assert(comp0_1_0.a == 123);
-                        Assert(comp0_1_0.b == "modifiedValue");
-                        Assert(comp1.otherSO == so1_0);
-                        Assert(comp1.otherComponent2 == comp0_1_0);
-                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
-                    }
-                }
-
-                Debug.Log("Passed stage 3");
-
-                // 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
-                    {
-                        // unitTest4Scene_2.prefab:
-                        // parent2SO0
-                        //  - [unitTest4Scene_0.prefab]
-                        // parent2SO1
-                        //  - parent2SO1_0 (Comp1)
-
-                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
-                        // so0 (Comp1(INSTANCE))
-                        //  - so0_0 (INSTANCE)
-                        //  - so0_1 (Comp1)
-                        //    - so0_1_0 (Comp1)
-                        // so1 (Comp2)
-                        //  - so1_0
-
-                        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.prefab");
-                        SceneObject prefabInstance = scene0Prefab.Instantiate();
-                        prefabInstance.Parent = parent2SO0;
-
-                        SceneObject so0 = prefabInstance.FindChild("so0", false);
-                        SceneObject so1 = prefabInstance.FindChild("so1_modified", false);
-
-                        SceneObject so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so1_1 = so1.FindChild("so1_1", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-
-                        UT1_Component2 comp1 = so1.GetComponent<UT1_Component2>();
-                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
-                        UT1_Component1 comp0_1 = so0_1.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";
-
-                        comp0_1.b = "instanceValue2";
-
-                        EditorApplication.SaveScene("unitTest4Scene_2.prefab");
-                    }
-
-                    Debug.Log("Passed stage 4.1");
-
-                    // Reload the scene and ensure instance modifications remain
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");
-
-                        SceneObject root = Scene.Root;
-                        SceneObject parent2SO0 = root.FindChild("parent2SO0", false);
-                        SceneObject parent2SO1 = root.FindChild("parent2SO1", false);
-                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);
-
-                        SceneObject prefabInstance = parent2SO0.GetChild(0);
-
-                        SceneObject so0 = prefabInstance.FindChild("so0", false);
-                        SceneObject so1 = prefabInstance.FindChild("so1_modified", 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 so1_1 = so1.FindChild("so1_1", false);
-                        SceneObject so0_1_0 = so0_1.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>();
-                        UT1_Component1 comp0_1 = so0_1.GetComponent<UT1_Component1>();
-
-                        Assert(so0_0 != null);
-                        Assert(comp0 != null);
-                        Assert(so1_1 == null);
-                        Assert(comp11 == null);
-
-                        Assert(comp0.otherSO == so0_1_0);
-                        Assert(comp0.otherComponent == comp1);
-
-                        Assert(parentComp1_0.otherSO == so1_0);
-                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
-
-                        Debug.Log(comp0_1_0.otherSO == null);
-                        if (comp0_1_0.otherSO != null)
-                            Debug.Log(comp0_1_0.otherSO.InstanceId + " - " + parent2SO1_0.InstanceId);
-
-                        Assert(comp0_1_0.otherSO == parent2SO1_0);
-                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
-                        Assert(comp0_1_0.b == "instanceValue");
-
-                        Assert(comp0_1.b == "instanceValue2");
-                    }
-
-                    Debug.Log("Passed stage 4.2");
-
-                    // Load original scene and ensure instance modifications didn't influence it
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
-
-                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
-                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
-                        SceneObject parentSO1_0 = parentSO1.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 so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-                        SceneObject so1_1 = so1.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>();
-                        UT1_Component1 comp0_1 = so0_1.GetComponent<UT1_Component1>();
-
-                        Assert(parentComp1_0.otherSO == so1_0);
-                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
-                        Assert(so1_1 != null);
-                        Assert(so0_0 == null);
-                        Assert(comp0 == null);
-                        Assert(comp0_1_0.otherSO == so1_1);
-                        Assert(comp0_1_0.otherComponent2 == comp1);
-                        Assert(comp0_1_0.a == 123);
-                        Assert(comp0_1_0.b == "modifiedValue");
-                        Assert(comp1.otherSO == so1_0);
-                        Assert(comp1.otherComponent2 == comp0_1_0);
-                        Assert(comp0_1.b == "originalValue2");
-                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
-                    }
-
-                    Debug.Log("Passed stage 4.3");
-
-                    // Modify prefab and ensure both prefab and instance modifications remain
-                    {
-                        // unitTest4Scene_0.prefab:
-                        // so0 (Comp1)
-                        //  - so0_1
-                        //    - so0_1_0 (Comp1)
-                        // so1 (Comp1, Comp2)
-                        //  - so1_1
-                        //  - so1_2 (Comp1)
-
-                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
-                        // so0 (Comp1)
-                        //  - so0_0
-                        //  - so0_1 (Comp1)
-                        //    - so0_1_0 (Comp1)
-                        // so1 (Comp2)
-                        //  - so1_2 (Comp1)
-
-                        Scene.Load("unitTest4Scene_0.prefab");
-
-                        SceneObject sceneRoot = Scene.Root;
-                        SceneObject so0 = sceneRoot.FindChild("so0", false);
-                        SceneObject so0_1 = so0.FindChild("so0_1", false);
-                        SceneObject so1 = sceneRoot.FindChild("so1_modified", false);
-                        SceneObject so1_0 = so1.FindChild("so1_0", false);
-                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
-
-                        SceneObject so1_2 = new SceneObject("so1_2");
-                        so1_2.Parent = so1;
-
-                        so0.AddComponent<UT1_Component1>();
-                        so0_1.RemoveComponent<UT1_Component1>();
-                        so1_0.Destroy();
-
-                        UT1_Component1 comp3 = so1_2.AddComponent<UT1_Component1>();
-                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
-                        comp0_1_0.b = "modifiedValueAgain";
-                        so1.Name = "so1_modifiedAgain";
-
-                        comp3.otherSO = so0_1;
-                        comp3.otherComponent2 = comp0_1_0;
-
-                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
-                    }
-
-                    Debug.Log("Passed stage 4.4");
-
-                    // Reload the scene and ensure both instance and prefab modifications remain
-                    {
-                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");
-
-                        SceneObject root = Scene.Root;
-                        SceneObject parent2SO0 = root.FindChild("parent2SO0", false);
-                        SceneObject parent2SO1 = root.FindChild("parent2SO1", false);
-                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);
-
-                        SceneObject prefabInstance = parent2SO0.GetChild(0);
-
-                        SceneObject so0 = prefabInstance.FindChild("so0", false);
-                        SceneObject so1 = prefabInstance.FindChild("so1_modifiedAgain", 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 so1_1 = so1.FindChild("so1_1", false);
-                        SceneObject so1_2 = so1.FindChild("so1_2", false);
-                        SceneObject so0_1_0 = so0_1.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>();
-                        UT1_Component1 comp3 = so1_2.AddComponent<UT1_Component1>();
-
-                        // Check instance modifications (they should override any prefab modifications)
-                        Assert(so0_0 != null);
-                        Assert(comp0 != null);
-                        Assert(so1_1 == null);
-                        Assert(comp11 == null);
-
-                        Assert(comp0.otherSO == so0_1_0);
-                        Assert(comp0.otherComponent == comp1);
-
-                        Assert(parentComp1_0.otherSO == so1_0);
-                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
-
-                        Assert(comp0_1_0.otherSO == parent2SO1_0);
-                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
-                        Assert(comp0_1_0.b == "instanceValue");
-
-                        // Check prefab modifications
-                        Assert(so1_0 == null);
-                        Assert(so1.Name == "so1_modifiedAgain");
-                        Assert(comp3.otherSO == so0_1);
-                        Assert(comp3.otherComponent2 == comp0_1_0);
-                    }
-
-                    Debug.Log("Passed stage 4.5");
-                }
-            }
-            catch
-            {
-                PrintSceneState();
-
-                throw;
-            }
-            finally
-            {
-                
-
-                if (!string.IsNullOrEmpty(oldScene))
-                    Scene.Load(ProjectLibrary.GetPath(oldScene));
-                else
-                    Scene.Clear();
-
-                ProjectLibrary.Delete("unitTest4Scene_0.prefab");
-                ProjectLibrary.Delete("unitTest4Scene_1.prefab");
-                ProjectLibrary.Delete("unitTest4Scene_2.prefab");
-            }
-        }
-
-        /// <summary>
-        /// Runs all tests.
-        /// </summary>
-        static void RunTests()
-        {
-            UnitTest1_ManagedSerialization();
-            UnitTest2_SerializableProperties();
-            UnitTest3_ManagedDiff();
-            UnitTest4_Prefabs();
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_UT1_GameObjectClone(SceneObject so);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_UT3_GenerateDiff(UT_DiffObj oldObj, UT_DiffObj newObj);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_UT3_ApplyDiff(UT_DiffObj obj);
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+using DebugUnit = System.Diagnostics.Debug;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Contains various managed unit tests.
+    /// </summary>
+    class UnitTests
+    {
+        /// <summary>
+        /// Triggers an exception when a unit test condition fails.
+        /// </summary>
+        /// <param name="success">True if the unit test condition succeeded, false otherwise.</param>
+        static void Assert(bool success)
+        {
+            if (!success)
+                throw new InvalidOperationException("Unit test failed.");
+        }
+
+        /// <summary>
+        /// Tests managed object serialization and deserialization.
+        /// </summary>
+        static void UnitTest1_ManagedSerialization()
+        {
+            SceneObject otherSO = new SceneObject("OtherSO");
+            UT1_Component2 dbgComponent2 = otherSO.AddComponent<UT1_Component2>();
+            dbgComponent2.a2 = 33;
+
+            SceneObject so = new SceneObject("TestSO");
+            UT1_Component1 dbgComponent = so.AddComponent<UT1_Component1>();
+
+            dbgComponent.a = 5;
+            dbgComponent.b = "SomeTestVal";
+            dbgComponent.complex.someValue = 19;
+            dbgComponent.complex.anotherValue = "AnotherValue";
+            dbgComponent.complex2.someValue2 = 21;
+            dbgComponent.complex2.anotherValue2 = "AnotherValue2";
+
+            dbgComponent.arrA = new int[5];
+            dbgComponent.arrA[4] = 5;
+            dbgComponent.arrB = new string[5];
+            dbgComponent.arrB[4] = "ArrAnotherValue";
+            dbgComponent.arrComplex = new UT1_SerzObj[5];
+            dbgComponent.arrComplex[4].someValue = 99;
+            dbgComponent.arrComplex[4].anotherValue = "ArrComplexAnotherValue";
+            dbgComponent.arrComplex2 = new UT1_SerzCls[5];
+            dbgComponent.arrComplex2[4] = new UT1_SerzCls();
+            dbgComponent.arrComplex2[4].someValue2 = 101;
+            dbgComponent.arrComplex2[4].anotherValue2 = "ArrComplex2AnotherValue";
+
+            dbgComponent.listA = new List<int>();
+            dbgComponent.listA.Add(5);
+            dbgComponent.listB = new List<string>();
+            dbgComponent.listB.Add("ListAnotherValue");
+            dbgComponent.listB.Add(null);
+            dbgComponent.listComplex = new List<UT1_SerzObj>();
+            dbgComponent.listComplex.Add(new UT1_SerzObj());
+            dbgComponent.listComplex.Add(new UT1_SerzObj(99, "ListComplexAnotherValue"));
+            dbgComponent.listComplex2 = new List<UT1_SerzCls>();
+            dbgComponent.listComplex2.Add(new UT1_SerzCls());
+            dbgComponent.listComplex2[0].someValue2 = 101;
+            dbgComponent.listComplex2[0].anotherValue2 = "ListComplexAnotherValue";
+            dbgComponent.listComplex2.Add(null);
+
+            dbgComponent.dictA = new Dictionary<int, string>();
+            dbgComponent.dictA[5] = "value";
+            dbgComponent.dictA[10] = "anotherValue";
+
+            dbgComponent.dictB = new Dictionary<string, UT1_SerzObj>();
+            dbgComponent.dictB["key1"] = new UT1_SerzObj(99, "DictComplexValue");
+
+            dbgComponent.otherComponent = dbgComponent2;
+            dbgComponent.otherSO = otherSO;
+
+            Internal_UT1_GameObjectClone(so);
+
+            System.Diagnostics.Debug.Assert(so.GetNumChildren() == 1);
+
+            for (int i = 0; i < so.GetNumChildren(); i++)
+            {
+                SceneObject childSO = so.GetChild(i);
+
+                UT1_Component1 otherComponent = childSO.GetComponent<UT1_Component1>();
+                DebugUnit.Assert(otherComponent.a == 5);
+                DebugUnit.Assert(otherComponent.b == "SomeTestVal");
+                DebugUnit.Assert(otherComponent.complex.someValue == 19);
+                DebugUnit.Assert(otherComponent.complex2.anotherValue2 == "AnotherValue2");
+
+                DebugUnit.Assert(otherComponent.arrA[4] == 5);
+                DebugUnit.Assert(otherComponent.arrB[4] == "ArrAnotherValue");
+                DebugUnit.Assert(otherComponent.arrComplex[4].someValue == 99);
+                DebugUnit.Assert(otherComponent.arrComplex2[4].anotherValue2 == "ArrComplex2AnotherValue");
+
+                DebugUnit.Assert(otherComponent.listA[0] == 5);
+                DebugUnit.Assert(otherComponent.listB[0] == "ListAnotherValue");
+                DebugUnit.Assert(otherComponent.listComplex[1].someValue == 99);
+                DebugUnit.Assert(otherComponent.listComplex2[0].anotherValue2 == "ListComplexAnotherValue");
+            }
+
+            so.Destroy();
+            otherSO.Destroy();
+        }
+
+        /// <summary>
+        /// Tests serializable properties used for inspection.
+        /// </summary>
+        static void UnitTest2_SerializableProperties()
+        {
+            SerializableObject obj = new SerializableObject(typeof(UT1_SerzCls), new UT1_SerzCls());
+
+            SerializableProperty prop = obj.Fields[0].GetProperty();
+            prop.SetValue(33);
+            DebugUnit.Assert(prop.GetValue<int>() == 33);
+
+            SerializableProperty prop2 = obj.Fields[2].GetProperty();
+
+            UT1_SerzCls child = new UT1_SerzCls();
+            child.anotherValue2 = "potato";
+            prop2.SetValue<UT1_SerzCls>(child);
+
+            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>() != null);
+            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>().anotherValue2 == "potato");
+        }
+
+        /// <summary>
+        /// Tests managed diff creation used by prefabs.
+        /// </summary>
+        static void UnitTest3_ManagedDiff()
+        {
+            UT_DiffObj original = new UT_DiffObj();
+            UT_DiffObj modified = new UT_DiffObj();
+
+            modified.plain2 = "banana";
+            modified.complex = new UT_DiffChildObj();
+            modified.complex2 = null;
+            modified.complex3.plain2 = "tomato";
+
+            modified.arrPlain1 = new[] {-1, -2, -3, -4};
+            modified.arrPlain2[2] = "cherry";
+            modified.arrComplex = new UT_DiffChildObj[3];
+            modified.arrComplex2[0].plain1 = -10;
+
+            modified.listPlain1[0] = -20;
+            modified.listPlain2 = new List<string>();
+            modified.listComplex = new List<UT_DiffChildObj>();
+            modified.listComplex.Add(new UT_DiffChildObj());
+            modified.listComplex2[1].plain2 = "orange";
+
+            modified.dictPlain1.Remove(20);
+            modified.dictPlain1[-30] = -30;
+            modified.dictComplex = new Dictionary<int, UT_DiffChildObj>();
+            modified.dictComplex[-40] = new UT_DiffChildObj();
+            modified.dictComplex2[31].plain1 = -50;
+
+            Internal_UT3_GenerateDiff(original, modified);
+            Internal_UT3_ApplyDiff(original);
+
+            DebugUnit.Assert(original.plain1 == modified.plain1);
+            DebugUnit.Assert(original.plain2 == modified.plain2);
+            DebugUnit.Assert(original.complex.plain2 == modified.complex.plain2);
+            DebugUnit.Assert(original.complex2 == modified.complex2);
+            DebugUnit.Assert(original.complex3.plain2 == modified.complex3.plain2);
+
+            DebugUnit.Assert(original.arrPlain1.Length == modified.arrPlain1.Length);
+            for (int i = 0; i < original.arrPlain1.Length; i++)
+                DebugUnit.Assert(original.arrPlain1[i] == modified.arrPlain1[i]);
+
+            for (int i = 0; i < original.arrPlain2.Length; i++)
+                DebugUnit.Assert(original.arrPlain2[i] == modified.arrPlain2[i]);
+
+            for (int i = 0; i < original.arrComplex.Length; i++)
+                DebugUnit.Assert(original.arrComplex[i] == modified.arrComplex[i]);
+
+            DebugUnit.Assert(original.arrComplex2[0].plain1 == modified.arrComplex2[0].plain1);
+
+            for (int i = 0; i < original.listPlain1.Count; i++)
+                DebugUnit.Assert(original.listPlain1[i] == modified.listPlain1[i]);
+
+            DebugUnit.Assert(original.listPlain2.Count == modified.listPlain2.Count);
+
+            for (int i = 0; i < original.listComplex.Count; i++)
+                DebugUnit.Assert(original.listComplex[i].plain1 == modified.listComplex[i].plain1);
+
+            DebugUnit.Assert(original.listComplex2[1].plain2 == modified.listComplex2[1].plain2);
+
+            foreach (var entry in modified.dictPlain1)
+            {
+                if (!original.dictPlain1.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value == original.dictPlain1[entry.Key]);
+            }
+
+            foreach (var entry in modified.dictPlain2)
+            {
+                if (!original.dictPlain2.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value == original.dictPlain2[entry.Key]);
+            }
+
+            foreach (var entry in modified.dictComplex)
+            {
+                if (!original.dictComplex.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex[entry.Key].plain1);
+            }
+
+            foreach (var entry in modified.dictComplex2)
+            {
+                if (!original.dictComplex2.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex2[entry.Key].plain1);
+            }
+        }
+
+        /// <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;
+            }
+
+            Action PrintSceneState = () =>
+            {
+                SceneObject root = Scene.Root;
+
+                Stack<SceneObject> todo = new Stack<SceneObject>();
+                todo.Push(root);
+
+                StringBuilder output = new StringBuilder();
+                while (todo.Count > 0)
+                {
+                    SceneObject so = todo.Pop();
+
+                    int numChildren = so.GetNumChildren();
+                    for (int i = numChildren - 1; i >= 0; i--)
+                    {
+                        SceneObject child = so.GetChild(i);
+
+                        output.AppendLine(child.Name);
+                        todo.Push(child);
+                    }  
+                }
+
+                Debug.Log(output);
+            };
+
+            // Disabled because it's a slow test, enable only when relevant (or when a build machine is set up)
+            return;
+
+            string oldScene = Scene.ActiveSceneUUID;
+            Scene.Clear();
+
+            try
+            {
+                // Simple scene save & load
+                {
+                    {
+                        // unitTest4Scene_0.prefab:
+                        // so0 (Comp1)
+                        //  - so0_0
+                        //  - so0_1 (Comp1)
+                        //    - so0_1_0 (Comp1)
+                        // so1 (Comp2)
+                        //  - so1_0
+
+                        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.LocalPosition = new Vector3(10.0f, 15.0f, 20.0f);
+                        so0_1.LocalPosition = new Vector3(1.0f, 2.0f, 3.0f);
+                        so1_0.LocalPosition = new Vector3(0, 123.0f, 0.0f);
+
+                        UT1_Component1 comp0 = so0.AddComponent<UT1_Component1>();
+                        UT1_Component2 comp1 = so1.AddComponent<UT1_Component2>();
+                        UT1_Component1 comp1_1 = so0_1.AddComponent<UT1_Component1>();
+                        UT1_Component1 comp0_1_0 = so0_1_0.AddComponent<UT1_Component1>();
+
+                        comp0.otherSO = so0_1_0;
+                        comp0.otherComponent = comp1;
+
+                        comp1_1.b = "originalValue2";
+
+                        comp0_1_0.b = "testValue";
+                        comp0_1_0.otherSO = so0;
+                        comp0_1_0.otherComponent2 = comp0;
+
+                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
+                    }
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_0.prefab");
+
+                        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 so0_1_0 = so0_1.FindChild("so0_1_0", false);
+
+                        Assert(so0_0 != null);
+                        Assert(so0_1 != null);
+                        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>();
+
+                        Assert(comp0 != null);
+                        Assert(comp1 != null);
+                        Assert(comp0_1_0 != null);
+                        Assert(comp0_1_0.b == "testValue");
+                        Assert(comp0.otherSO == so0_1_0);
+                        Assert(comp0.otherComponent == comp1);
+                        Assert(comp0_1_0.otherSO == so0);
+                        Assert(comp0_1_0.otherComponent2 == comp0);
+                    }
+                }
+
+                Debug.Log("Passed stage 1");
+
+                // Load & save a scene that contains a prefab and references its objects
+                {
+                    {
+                        // unitTest4Scene_1.prefab:
+                        // parentSO0
+                        //  - [unitTest4Scene_0.prefab]
+                        // parentSO1
+                        //  - parentSO1_0 (Comp1)
+
+                        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;
+                        parentSO0.LocalPosition = new Vector3(50.0f, 50.0f, 50.0f);
+
+                        UT1_Component1 parentComp1_0 = parentSO1_0.AddComponent<UT1_Component1>();
+
+                        Prefab scene0Prefab = ProjectLibrary.Load<Prefab>("unitTest4Scene_0.prefab");
+                        SceneObject prefabInstance = scene0Prefab.Instantiate();
+                        prefabInstance.Parent = parentSO0;
+                        prefabInstance.LocalPosition = Vector3.Zero;
+
+                        SceneObject so0 = prefabInstance.FindChild("so0", false);
+                        SceneObject so1 = prefabInstance.FindChild("so1", false);
+                        SceneObject so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.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.prefab");
+                    }
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
+
+                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
+                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
+                        SceneObject parentSO1_0 = parentSO1.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", false);
+                        SceneObject so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+
+                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+
+                        Assert(parentComp1_0.otherSO == so1_0);
+                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+                    }
+                }
+
+                Debug.Log("Passed stage 2");
+
+                // Modify prefab, reload the scene and ensure it is updated with modified prefab
+                {
+                    {
+                        // unitTest4Scene_0.prefab:
+                        // so0
+                        //  - so0_1 (Comp1)
+                        //    - so0_1_0 (Comp1)
+                        // so1 (Comp1, Comp2)
+                        //  - so1_0
+                        //  - so1_1
+
+                        Scene.Load("unitTest4Scene_0.prefab");
+
+                        SceneObject sceneRoot = Scene.Root;
+                        SceneObject so0 = sceneRoot.FindChild("so0", false);
+                        SceneObject so0_0 = so0.FindChild("so0_0", false);
+                        SceneObject so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1 = sceneRoot.FindChild("so1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+
+                        SceneObject so1_1 = new SceneObject("so1_1");
+                        so1_1.Parent = so1;
+
+                        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.LocalPosition = new Vector3(0, 999.0f, 0.0f);
+
+                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
+                    }
+
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
+
+                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
+                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
+                        SceneObject parentSO1_0 = parentSO1.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 so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+                        SceneObject so1_1 = so1.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>();
+
+                        Assert(parentComp1_0.otherSO == so1_0);
+                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+                        Assert(so1_1 != null);
+                        Assert(so0_0 == null);
+                        Assert(comp0 == null);
+                        Assert(comp0_1_0.otherSO == so1_1);
+                        Assert(comp0_1_0.otherComponent2 == comp1);
+                        Assert(comp0_1_0.a == 123);
+                        Assert(comp0_1_0.b == "modifiedValue");
+                        Assert(comp1.otherSO == so1_0);
+                        Assert(comp1.otherComponent2 == comp0_1_0);
+                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
+                    }
+                }
+
+                Debug.Log("Passed stage 3");
+
+                // 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
+                    {
+                        // unitTest4Scene_2.prefab:
+                        // parent2SO0
+                        //  - [unitTest4Scene_0.prefab]
+                        // parent2SO1
+                        //  - parent2SO1_0 (Comp1)
+
+                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
+                        // so0 (Comp1(INSTANCE))
+                        //  - so0_0 (INSTANCE)
+                        //  - so0_1 (Comp1)
+                        //    - so0_1_0 (Comp1)
+                        // so1 (Comp2)
+                        //  - so1_0
+
+                        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.prefab");
+                        SceneObject prefabInstance = scene0Prefab.Instantiate();
+                        prefabInstance.Parent = parent2SO0;
+
+                        SceneObject so0 = prefabInstance.FindChild("so0", false);
+                        SceneObject so1 = prefabInstance.FindChild("so1_modified", false);
+
+                        SceneObject so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so1_1 = so1.FindChild("so1_1", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+
+                        UT1_Component2 comp1 = so1.GetComponent<UT1_Component2>();
+                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+                        UT1_Component1 comp0_1 = so0_1.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";
+
+                        comp0_1.b = "instanceValue2";
+
+                        EditorApplication.SaveScene("unitTest4Scene_2.prefab");
+                    }
+
+                    Debug.Log("Passed stage 4.1");
+
+                    // Reload the scene and ensure instance modifications remain
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");
+
+                        SceneObject root = Scene.Root;
+                        SceneObject parent2SO0 = root.FindChild("parent2SO0", false);
+                        SceneObject parent2SO1 = root.FindChild("parent2SO1", false);
+                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);
+
+                        SceneObject prefabInstance = parent2SO0.GetChild(0);
+
+                        SceneObject so0 = prefabInstance.FindChild("so0", false);
+                        SceneObject so1 = prefabInstance.FindChild("so1_modified", 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 so1_1 = so1.FindChild("so1_1", false);
+                        SceneObject so0_1_0 = so0_1.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>();
+                        UT1_Component1 comp0_1 = so0_1.GetComponent<UT1_Component1>();
+
+                        Assert(so0_0 != null);
+                        Assert(comp0 != null);
+                        Assert(so1_1 == null);
+                        Assert(comp11 == null);
+
+                        Assert(comp0.otherSO == so0_1_0);
+                        Assert(comp0.otherComponent == comp1);
+
+                        Assert(parentComp1_0.otherSO == so1_0);
+                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+
+                        Assert(comp0_1_0.otherSO == parent2SO1_0);
+                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
+                        Assert(comp0_1_0.b == "instanceValue");
+
+                        Assert(comp0_1.b == "instanceValue2");
+                    }
+
+                    Debug.Log("Passed stage 4.2");
+
+                    // Load original scene and ensure instance modifications didn't influence it
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");
+
+                        SceneObject parentSO0 = Scene.Root.FindChild("parentSO0", false);
+                        SceneObject parentSO1 = Scene.Root.FindChild("parentSO1", false);
+                        SceneObject parentSO1_0 = parentSO1.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 so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+                        SceneObject so1_1 = so1.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>();
+                        UT1_Component1 comp0_1 = so0_1.GetComponent<UT1_Component1>();
+
+                        Assert(parentComp1_0.otherSO == so1_0);
+                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+                        Assert(so1_1 != null);
+                        Assert(so0_0 == null);
+                        Assert(comp0 == null);
+                        Assert(comp0_1_0.otherSO == so1_1);
+                        Assert(comp0_1_0.otherComponent2 == comp1);
+                        Assert(comp0_1_0.a == 123);
+                        Assert(comp0_1_0.b == "modifiedValue");
+                        Assert(comp1.otherSO == so1_0);
+                        Assert(comp1.otherComponent2 == comp0_1_0);
+                        Assert(comp0_1.b == "originalValue2");
+                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
+                    }
+
+                    Debug.Log("Passed stage 4.3");
+
+                    // Modify prefab and ensure both prefab and instance modifications remain
+                    {
+                        // unitTest4Scene_0.prefab:
+                        // so0 (Comp2)
+                        //  - so0_1
+                        //    - so0_1_0 (Comp1)
+                        // so1 (Comp1, Comp2)
+                        //  - so1_1
+                        //  - so1_2 (Comp1)
+
+                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
+                        // so0 (Comp1)
+                        //  - so0_0
+                        //  - so0_1 (Comp1)
+                        //    - so0_1_0 (Comp1)
+                        // so1 (Comp2)
+                        //  - so1_2 (Comp1)
+
+                        Scene.Load("unitTest4Scene_0.prefab");
+
+                        SceneObject sceneRoot = Scene.Root;
+                        SceneObject so0 = sceneRoot.FindChild("so0", false);
+                        SceneObject so0_1 = so0.FindChild("so0_1", false);
+                        SceneObject so1 = sceneRoot.FindChild("so1_modified", false);
+                        SceneObject so1_0 = so1.FindChild("so1_0", false);
+                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);
+
+                        SceneObject so1_2 = new SceneObject("so1_2");
+                        so1_2.Parent = so1;
+
+                        so0.AddComponent<UT1_Component2>();
+                        so0_1.RemoveComponent<UT1_Component1>();
+                        so1_0.Destroy();
+
+                        UT1_Component1 comp3 = so1_2.AddComponent<UT1_Component1>();
+                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent<UT1_Component1>();
+                        comp0_1_0.b = "modifiedValueAgain";
+                        so1.Name = "so1_modifiedAgain";
+
+                        comp3.otherSO = so0_1;
+                        comp3.otherComponent2 = comp0_1_0;
+
+                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
+                    }
+
+                    Debug.Log("Passed stage 4.4");
+
+                    // Reload the scene and ensure both instance and prefab modifications remain
+                    {
+                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");
+
+                        SceneObject root = Scene.Root;
+                        SceneObject parent2SO0 = root.FindChild("parent2SO0", false);
+                        SceneObject parent2SO1 = root.FindChild("parent2SO1", false);
+                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);
+
+                        SceneObject prefabInstance = parent2SO0.GetChild(0);
+
+                        SceneObject so0 = prefabInstance.FindChild("so0", false);
+                        SceneObject so1 = prefabInstance.FindChild("so1_modifiedAgain", 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 so1_1 = so1.FindChild("so1_1", false);
+                        SceneObject so1_2 = so1.FindChild("so1_2", false);
+                        SceneObject so0_1_0 = so0_1.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>();
+                        UT1_Component1 comp3 = so1_2.GetComponent<UT1_Component1>();
+
+                        // Check instance modifications (they should override any prefab modifications)
+                        Assert(so0_0 != null);
+                        Assert(comp0 != null);
+                        Assert(so1_1 == null);
+                        Assert(comp11 == null);
+
+                        Assert(comp0.otherSO == so0_1_0);
+                        Assert(comp0.otherComponent == comp1);
+
+                        Assert(parentComp1_0.otherSO == so1_0);
+                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
+
+                        Assert(comp0_1_0.otherSO == parent2SO1_0);
+                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
+                        Assert(comp0_1_0.b == "instanceValue");
+
+                        // Check prefab modifications
+                        Assert(so1_0 == null);
+                        Assert(so1.Name == "so1_modifiedAgain");
+
+                        Assert(comp3.otherSO == so0_1);
+                        Assert(comp3.otherComponent2 == comp0_1_0);
+                    }
+
+                    Debug.Log("Passed stage 4.5");
+                }
+            }
+            catch
+            {
+                PrintSceneState();
+
+                throw;
+            }
+            finally
+            {
+                
+
+                if (!string.IsNullOrEmpty(oldScene))
+                    Scene.Load(ProjectLibrary.GetPath(oldScene));
+                else
+                    Scene.Clear();
+
+                ProjectLibrary.Delete("unitTest4Scene_0.prefab");
+                ProjectLibrary.Delete("unitTest4Scene_1.prefab");
+                ProjectLibrary.Delete("unitTest4Scene_2.prefab");
+            }
+        }
+
+        /// <summary>
+        /// Runs all tests.
+        /// </summary>
+        static void RunTests()
+        {
+            UnitTest1_ManagedSerialization();
+            UnitTest2_SerializableProperties();
+            UnitTest3_ManagedDiff();
+            UnitTest4_Prefabs();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT1_GameObjectClone(SceneObject so);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT3_GenerateDiff(UT_DiffObj oldObj, UT_DiffObj newObj);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT3_ApplyDiff(UT_DiffObj obj);
+    }
+}

+ 17 - 0
MBansheeEngine/Component.cs

@@ -39,6 +39,17 @@ namespace BansheeEngine
             get { return Internal_GetSceneObject(mCachedPtr); }
         }
 
+        /// <summary>
+        /// Destroys the component, removing it from its scene object and stopping component updates.
+        /// </summary>
+        /// <param name="immediate">If true the component will be fully destroyed immediately. This means that objects
+        ///                         that are still referencing this component might fail. Normally destruction is delayed
+        ///                         until the end of the frame to give other objects a chance to stop using it.</param>
+        public void Destroy(bool immediate = false)
+        {
+            Internal_Destroy(mCachedPtr, immediate);
+        }
+
         /// <summary>
         /// Calculates bounds of the visible content for this component.
         /// </summary>
@@ -64,10 +75,16 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern Component[] Internal_GetComponents(SceneObject parent);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern Component[] Internal_GetComponentsPerType(SceneObject parent, Type type);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern Component Internal_RemoveComponent(SceneObject parent, Type type);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern SceneObject Internal_GetSceneObject(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Destroy(IntPtr nativeInstance, bool immediate);
     }
 }

+ 22 - 10
MBansheeEngine/SceneObject.cs

@@ -264,15 +264,28 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Searches for a component of a specific type.
+        /// Searches for a component of a specific type. If there are multiple components matching the type, only the first
+        /// one found is returned.
         /// </summary>
-        /// <typeparam name="T">Type of the component to search for.</typeparam>
+        /// <typeparam name="T">Type of the component to search for. Includes any components derived from the type.
+        /// </typeparam>
         /// <returns>Component instance if found, null otherwise.</returns>
         public T GetComponent<T>() where T : Component
         {
             return (T)Component.Internal_GetComponent(this, typeof(T));
         }
 
+        /// <summary>
+        /// Searches for all components of a specific type. 
+        /// </summary>
+        /// <typeparam name="T">Type of the component to search for. Includes any components derived from the type.
+        /// </typeparam>
+        /// <returns>All components matching the specified type.</returns>
+        public Component[] GetComponents<T>() where T : Component
+        {
+            return Component.Internal_GetComponentsPerType(this, typeof(T));
+        }
+
         /// <summary>
         /// Returns a list of all components attached to this object.
         /// </summary>
@@ -283,18 +296,20 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Removes a component from the scene object.
+        /// Removes a component from the scene object. If there are multiple components matching the type, only the first
+        /// one found is removed.
         /// </summary>
-        /// <typeparam name="T">Type of the component to remove.</typeparam>
+        /// <typeparam name="T">Type of the component to remove. Includes any components derived from the type.</typeparam>
         public void RemoveComponent<T>() where T : Component
         {
             Component.Internal_RemoveComponent(this, typeof(T));
         }
 
         /// <summary>
-        /// Removes a component from the scene object.
+        /// Removes a component from the scene object. If there are multiple components matching the type, only the first
+        /// one found is removed.
         /// </summary>
-        /// <param name="type">Type of the component to remove.</param>
+        /// <param name="type">Type of the component to remove. Includes any components derived from the type.</param>
         public void RemoveComponent(Type type)
         {
             Component.Internal_RemoveComponent(this, type);
@@ -427,7 +442,7 @@ namespace BansheeEngine
         /// </summary>
         /// <param name="immediate">If true the scene object will be fully destroyed immediately. This means that objects
         ///                         that are still referencing this scene object might fail. Normally destruction is delayed
-        ///                         until the end of the frame to give other object's a chance to stop using it.</param>
+        ///                         until the end of the frame to give other objects a chance to stop using it.</param>
         public void Destroy(bool immediate = false)
         {
             Internal_Destroy(mCachedPtr, immediate);
@@ -466,9 +481,6 @@ namespace BansheeEngine
         [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);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_GetPosition(IntPtr nativeInstance, out Vector3 value);
 

+ 2 - 0
SBansheeEngine/Include/BsScriptComponent.h

@@ -69,7 +69,9 @@ namespace BansheeEngine
 		static MonoObject* internal_addComponent(MonoObject* parentSceneObject, MonoReflectionType* type);
 		static MonoObject* internal_getComponent(MonoObject* parentSceneObject, MonoReflectionType* type);
 		static MonoArray* internal_getComponents(MonoObject* parentSceneObject);
+		static MonoArray* internal_getComponentsPerType(MonoObject* parentSceneObject, MonoReflectionType* type);
 		static void internal_removeComponent(MonoObject* parentSceneObject, MonoReflectionType* type);
 		static MonoObject* internal_getSceneObject(ScriptComponent* nativeInstance);
+		static void internal_destroy(ScriptComponent* nativeInstance, bool immediate);
 	};
 }

+ 65 - 18
SBansheeEngine/Source/BsScriptComponent.cpp

@@ -30,8 +30,10 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_AddComponent", &ScriptComponent::internal_addComponent);
 		metaData.scriptClass->addInternalCall("Internal_GetComponent", &ScriptComponent::internal_getComponent);
 		metaData.scriptClass->addInternalCall("Internal_GetComponents", &ScriptComponent::internal_getComponents);
+		metaData.scriptClass->addInternalCall("Internal_GetComponentsPerType", &ScriptComponent::internal_getComponentsPerType);
 		metaData.scriptClass->addInternalCall("Internal_RemoveComponent", &ScriptComponent::internal_removeComponent);
 		metaData.scriptClass->addInternalCall("Internal_GetSceneObject", &ScriptComponent::internal_getSceneObject);
+		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptComponent::internal_destroy);
 	}
 
 	MonoObject* ScriptComponent::internal_addComponent(MonoObject* parentSceneObject, MonoReflectionType* type)
@@ -42,22 +44,7 @@ namespace BansheeEngine
 		if (checkIfDestroyed(so))
 			return nullptr;
 
-		const Vector<HComponent>& mComponents = so->getComponents();
-		for (auto& component : mComponents)
-		{
-			if (component->getTypeId() == TID_ManagedComponent)
-			{
-				GameObjectHandle<ManagedComponent> managedComponent = static_object_cast<ManagedComponent>(component);
-
-				if (managedComponent->getRuntimeType() == type)
-				{
-					return managedComponent->getManagedInstance();
-				}
-			}
-		}
-
 		GameObjectHandle<ManagedComponent> mc = so->addComponent<ManagedComponent>(type);
-
 		return mc->getManagedInstance();
 	}
 
@@ -69,6 +56,9 @@ namespace BansheeEngine
 		if (checkIfDestroyed(so))
 			return nullptr;
 
+		MonoType* baseType = mono_reflection_type_get_type(type);
+		::MonoClass* baseClass = mono_type_get_class(baseType);
+
 		const Vector<HComponent>& mComponents = so->getComponents();
 		for(auto& component : mComponents)
 		{
@@ -76,7 +66,11 @@ namespace BansheeEngine
 			{
 				GameObjectHandle<ManagedComponent> managedComponent = static_object_cast<ManagedComponent>(component);
 
-				if(managedComponent->getRuntimeType() == type)
+				MonoReflectionType* componentReflType = managedComponent->getRuntimeType();
+				MonoType* componentType = mono_reflection_type_get_type(componentReflType);
+				::MonoClass* componentClass = mono_type_get_class(componentType);
+				
+				if(mono_class_is_subclass_of(componentClass, baseClass, true))
 				{
 					return managedComponent->getManagedInstance();
 				}
@@ -86,6 +80,47 @@ namespace BansheeEngine
 		return nullptr;
 	}
 
+	MonoArray* ScriptComponent::internal_getComponentsPerType(MonoObject* parentSceneObject, MonoReflectionType* type)
+	{
+		ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(parentSceneObject);
+		HSceneObject so = static_object_cast<SceneObject>(scriptSO->getNativeHandle());
+
+		MonoType* baseType = mono_reflection_type_get_type(type);
+		::MonoClass* baseClass = mono_type_get_class(baseType);
+
+		Vector<MonoObject*> managedComponents;
+
+		if (!checkIfDestroyed(so))
+		{
+			const Vector<HComponent>& mComponents = so->getComponents();
+			for (auto& component : mComponents)
+			{
+				if (component->getTypeId() == TID_ManagedComponent)
+				{
+					GameObjectHandle<ManagedComponent> managedComponent = static_object_cast<ManagedComponent>(component);
+
+					MonoReflectionType* componentReflType = managedComponent->getRuntimeType();
+					MonoType* componentType = mono_reflection_type_get_type(componentReflType);
+					::MonoClass* componentClass = mono_type_get_class(componentType);
+
+					if (mono_class_is_subclass_of(componentClass, baseClass, true))
+						managedComponents.push_back(managedComponent->getManagedInstance());
+				}
+			}
+		}
+
+		MonoArray* componentArray = mono_array_new(MonoManager::instance().getDomain(),
+			metaData.scriptClass->_getInternalClass(), (UINT32)managedComponents.size());
+
+		for (UINT32 i = 0; i < (UINT32)managedComponents.size(); i++)
+		{
+			void* elemAddr = mono_array_addr_with_size(componentArray, sizeof(MonoObject*), i);
+			memcpy(elemAddr, &managedComponents[i], sizeof(MonoObject*));
+		}
+
+		return componentArray;
+	}
+
 	MonoArray* ScriptComponent::internal_getComponents(MonoObject* parentSceneObject)
 	{
 		ScriptSceneObject* scriptSO = ScriptSceneObject::toNative(parentSceneObject);
@@ -127,7 +162,9 @@ namespace BansheeEngine
 		if (checkIfDestroyed(so))
 			return;
 
-		// We only allow single component per type
+		MonoType* baseType = mono_reflection_type_get_type(type);
+		::MonoClass* baseClass = mono_type_get_class(baseType);
+
 		const Vector<HComponent>& mComponents = so->getComponents();
 		for(auto& component : mComponents)
 		{
@@ -135,7 +172,11 @@ namespace BansheeEngine
 			{
 				GameObjectHandle<ManagedComponent> managedComponent = static_object_cast<ManagedComponent>(component);
 
-				if(managedComponent->getRuntimeType() == type)
+				MonoReflectionType* componentReflType = managedComponent->getRuntimeType();
+				MonoType* componentType = mono_reflection_type_get_type(componentReflType);
+				::MonoClass* componentClass = mono_type_get_class(componentType);
+
+				if (mono_class_is_subclass_of(componentClass, baseClass, true))
 				{
 					managedComponent->destroy();
 					return;
@@ -159,6 +200,12 @@ namespace BansheeEngine
 		return scriptSO->getManagedInstance();
 	}
 
+	void ScriptComponent::internal_destroy(ScriptComponent* nativeInstance, bool immediate)
+	{
+		if (!checkIfDestroyed(nativeInstance->mManagedComponent))
+			nativeInstance->mManagedComponent->destroy(immediate);
+	}
+
 	bool ScriptComponent::checkIfDestroyed(const GameObjectHandleBase& handle)
 	{
 		if (handle.isDestroyed())

+ 0 - 4
SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -9,10 +9,6 @@
 #include "BsMonoManager.h"
 #include "BsSceneObject.h"
 #include "BsMonoUtil.h"
-#include "BsScriptPrefab.h"
-#include "BsPrefab.h"
-#include "BsPrefabUtility.h"
-#include "BsResources.h"
 
 namespace BansheeEngine
 {