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

Prefab fixes:
- When updating prefab instances in scene first record their diffs to ensure instance-specific data persists
- When generating managed diffs ignore non-serializable fields, and properly handle fields that contain no serialized data

BearishSun 9 лет назад
Родитель
Сommit
c2075062e0

+ 1 - 1
Source/MBansheeEditor/General/EditorApplication.cs

@@ -560,7 +560,7 @@ namespace BansheeEditor
             
             if (root != null)
             {
-                PrefabUtility.ApplyPrefab(root);
+                PrefabUtility.ApplyPrefab(root, false);
 
                 ProjectLibrary.Refresh(true);
                 SetSceneDirty(false);

+ 20 - 1
Source/MBansheeEditor/Utility/PrefabUtility.cs

@@ -34,13 +34,29 @@ namespace BansheeEditor
         /// is not a prefab instance nothing happens.
         /// </summary>
         /// <param name="obj">Prefab instance whose prefab to update.</param>
-        public static void ApplyPrefab(SceneObject obj)
+        /// <param name="refreshScene">If true, all prefab instances in the current scene will be updated so they consistent
+        ///                            with the newly saved data.</param>
+        public static void ApplyPrefab(SceneObject obj, bool refreshScene = true)
         {
             if (obj == null)
                 return;
 
+            if (refreshScene)
+            {
+                SceneObject root = Scene.Root;
+                if (root != null)
+                    Internal_RecordPrefabDiff(root.GetCachedPtr());
+            }
+
             IntPtr objPtr = obj.GetCachedPtr();
             Internal_ApplyPrefab(objPtr);
+
+            if (refreshScene)
+            {
+                SceneObject root = Scene.Root;
+                if (root != null)
+                    Internal_UpdateFromPrefab(root.GetCachedPtr());
+            }
         }
 
         /// <summary>
@@ -120,6 +136,9 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_ApplyPrefab(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RecordPrefabDiff(IntPtr nativeInstance);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_RevertPrefab(IntPtr nativeInstance);
 

+ 0 - 3
Source/MBansheeEditor/Window/MenuItems.cs

@@ -607,9 +607,6 @@ namespace BansheeEditor
                 return;
 
             PrefabUtility.ApplyPrefab(so);
-
-            // Refresh any prefab instances in the scene
-            PrefabUtility.UpdateFromPrefab(Scene.Root);
         }
 
         /// <summary>

+ 0 - 3
Source/MBansheeEditor/Windows/Inspector/InspectorWindow.cs

@@ -381,9 +381,6 @@ namespace BansheeEditor
                     btnApplyPrefab.OnClick += () =>
                     {
                         PrefabUtility.ApplyPrefab(activeSO);
-
-                        // Refresh any prefab instances in the scene
-                        PrefabUtility.UpdateFromPrefab(Scene.Root);
                     };
                     btnRevertPrefab.OnClick += () =>
                     {

+ 1 - 0
Source/SBansheeEditor/Include/BsScriptPrefabUtility.h

@@ -28,6 +28,7 @@ namespace BansheeEngine
 		static MonoObject* internal_getPrefabParent(ScriptSceneObject* nativeInstance);
 		static MonoString* internal_GetPrefabUUID(ScriptSceneObject* nativeInstance);
 		static void internal_UpdateFromPrefab(ScriptSceneObject* nativeInstance);
+		static void internal_RecordPrefabDiff(ScriptSceneObject* nativeInstance);
 
 		ScriptPrefabUtility(MonoObject* instance);
 	};

+ 11 - 1
Source/SBansheeEditor/Source/BsScriptPrefabUtility.cpp

@@ -26,6 +26,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetPrefabParent", &ScriptPrefabUtility::internal_getPrefabParent);
 		metaData.scriptClass->addInternalCall("Internal_GetPrefabUUID", &ScriptPrefabUtility::internal_GetPrefabUUID);
 		metaData.scriptClass->addInternalCall("Internal_UpdateFromPrefab", &ScriptPrefabUtility::internal_UpdateFromPrefab);
+		metaData.scriptClass->addInternalCall("Internal_RecordPrefabDiff", &ScriptPrefabUtility::internal_RecordPrefabDiff);
 	}
 
 	void ScriptPrefabUtility::internal_breakPrefab(ScriptSceneObject* nativeInstance)
@@ -76,7 +77,7 @@ namespace BansheeEngine
 		HSceneObject so = nativeInstance->getNativeSceneObject();
 		HSceneObject parent = so->getPrefabParent();
 
-		if(parent != nullptr)
+		if (parent != nullptr)
 		{
 			ScriptSceneObject* scriptParent = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(parent);
 			return scriptParent->getManagedInstance();
@@ -107,4 +108,13 @@ namespace BansheeEngine
 		HSceneObject so = nativeInstance->getNativeSceneObject();
 		PrefabUtility::updateFromPrefab(so);
 	}
+
+	void ScriptPrefabUtility::internal_RecordPrefabDiff(ScriptSceneObject* nativeInstance)
+	{
+		if (ScriptSceneObject::checkIfDestroyed(nativeInstance))
+			return;
+
+		HSceneObject so = nativeInstance->getNativeSceneObject();
+		PrefabUtility::recordPrefabDiff(so);
+	}
 }

+ 18 - 0
Source/SBansheeEngine/Source/BsManagedSerializableDiff.cpp

@@ -184,6 +184,9 @@ namespace BansheeEngine
 		{
 			for (auto& field : curObjInfo->mFields)
 			{
+				if (!field.second->isSerializable())
+					continue;
+
 				UINT32 fieldTypeId = field.second->mTypeInfo->getTypeId();
 
 				SPtr<ManagedSerializableFieldData> oldData = oldObj->getFieldData(field.second);
@@ -211,6 +214,21 @@ namespace BansheeEngine
 	{
 		bool isPrimitive = entryTypeId == TID_SerializableTypeInfoPrimitive || entryTypeId == TID_SerializableTypeInfoRef;
 
+		// It's possible the field data is null if the class structure changed (i.e. new field was added that is not present
+		// in serialized data). Check for this case first to ensure field data is valid for the remainder of the method.
+		if(oldData == nullptr)
+		{
+			if (newData == nullptr)
+				return nullptr;
+			else
+				return ModifiedEntry::create(newData);
+		}
+		else
+		{
+			if (newData == nullptr)
+				return nullptr;
+		}
+
 		SPtr<Modification> newMod = nullptr;
 		if (isPrimitive)
 		{