/// <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> /// Searches the scene object hierarchy to find a property at the given path. /// </summary> /// <param name="root">Root scene object to which the path is relative to.</param> /// <param name="path">Path to the property, where each element of the path is separated with "/". /// /// Path elements prefixed with "!" signify names of child scene objects (first one relative to /// <paramref name="root"/>. Name of the root element should not be included in the path. /// /// Path element prefixed with ":" signify names of components. If a path doesn't have a /// component element, it is assumed the field is relative to the scene object itself (only /// "Position", "Rotation" and "Scale" fields are supported in such case). Only one component /// path element per path is allowed. /// /// Path entries with no prefix are considered regular script object fields. Each path must have /// at least one such entry. /// /// A field path can be followed by an indexer [n] where n is a zero-based index. Such paths /// are assumed to be referencing an index within an array or a list. /// /// A field path can also be followed by a suffix (after the indexer, if any) separated from the /// path name with ".". This suffix is not parsed internally, but will be returned as /// <paramref name="suffix"/>. /// /// Path examples: /// :MyComponent/myInt (path to myInt variable on a component attached to the root object) /// :MyComponent/myArray[0] (path to first element of myArray on the same component as above) /// !childSO/:MyComponent/myInt (path to myInt variable on a child scene object) /// !childSO/Position (path to the scene object position) /// :MyComponent/myVector.z (path to the z component of myVector on the root object) /// </param> /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param> /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise /// null.</returns> internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix) { suffix = null; if (string.IsNullOrEmpty(path) || root == null) { return(null); } string trimmedPath = path.Trim('/'); string[] entries = trimmedPath.Split('/'); // Find scene object referenced by the path SceneObject so = root; int pathIdx = 0; for (; pathIdx < entries.Length; pathIdx++) { string entry = entries[pathIdx]; if (string.IsNullOrEmpty(entry)) { continue; } // Not a scene object, break if (entry[0] != '!') { break; } string childName = entry.Substring(1, entry.Length - 1); so = so.FindChild(childName); if (so == null) { break; } } // Child scene object couldn't be found if (so == null) { return(null); } // Path too short, no field entry if (pathIdx >= entries.Length) { return(null); } // Check if path is referencing a component, and if so find it Component component = null; { string entry = entries[pathIdx]; if (entry[0] == ':') { string componentName = entry.Substring(1, entry.Length - 1); Component[] components = so.GetComponents(); component = Array.Find(components, x => x.GetType().Name == componentName); // Cannot find component with specified type if (component == null) { return(null); } } } // Look for a field within a component if (component != null) { pathIdx++; if (pathIdx >= entries.Length) { return(null); } SerializableObject componentObj = new SerializableObject(component); StringBuilder pathBuilder = new StringBuilder(); for (; pathIdx < entries.Length - 1; pathIdx++) { pathBuilder.Append(entries[pathIdx] + "/"); } // Check last path entry for suffix and remove it int suffixIdx = entries[pathIdx].LastIndexOf("."); if (suffixIdx != -1) { string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx); suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx); pathBuilder.Append(entryNoSuffix); } else { pathBuilder.Append(entries[pathIdx]); } return(componentObj.FindProperty(pathBuilder.ToString())); } else // Field is one of the builtin ones on the SceneObject itself { if ((pathIdx + 1) < entries.Length) { return(null); } string entry = entries[pathIdx]; if (entry == "Position") { SerializableProperty property = new SerializableProperty( SerializableProperty.FieldType.Vector3, typeof(Vector3), () => so.LocalPosition, (x) => so.LocalPosition = (Vector3)x); return(property); } else if (entry == "Rotation") { SerializableProperty property = new SerializableProperty( SerializableProperty.FieldType.Vector3, typeof(Vector3), () => so.LocalRotation.ToEuler(), (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x)); return(property); } else if (entry == "Scale") { SerializableProperty property = new SerializableProperty( SerializableProperty.FieldType.Vector3, typeof(Vector3), () => so.LocalScale, (x) => so.LocalScale = (Vector3)x); return(property); } return(null); } }