├── Library
├── BuildPlayer.prefs
├── TagManager.asset
├── TimeManager.asset
├── AudioManager.asset
├── EditorSettings.asset
├── InputManager.asset
├── NetworkManager.asset
├── TextSceneBuildSettings.txt
├── DynamicsManager.asset
├── ProjectSettings.asset
├── QualitySettings.asset
└── InspectorExpandedItems.asset
├── Assets
├── Prefabs.meta
├── Scenes.meta
├── Scripts.meta
├── Materials.meta
├── Scenes
│ ├── spheres.txt.meta
│ ├── scenedemo.txt.meta
│ ├── sceneinscene.txt.meta
│ ├── otherscenedemo.txt.meta
│ ├── sceneinscene.txt
│ ├── spheres.txt
│ ├── otherscenedemo.txt
│ └── scenedemo.txt
├── Scripts
│ ├── Editor.meta
│ ├── General.meta
│ ├── TextScene.meta
│ ├── Editor
│ │ ├── TextScene.meta
│ │ ├── EditorHelper.cs.meta
│ │ ├── MaterialPreserver.cs.meta
│ │ ├── PlayerPrefsTools.cs.meta
│ │ ├── TextScene
│ │ │ ├── TextScene.cs.meta
│ │ │ ├── TextSceneMenu.cs.meta
│ │ │ ├── TextSceneWindow.cs.meta
│ │ │ ├── TextSceneHierarchy.cs.meta
│ │ │ ├── TextSceneInspector.cs.meta
│ │ │ ├── TextSceneMonitor.cs.meta
│ │ │ ├── TextSceneSerializer.cs.meta
│ │ │ ├── TextSceneDeserializer.cs.meta
│ │ │ ├── TextSceneInspector.cs
│ │ │ ├── TextSceneMenu.cs
│ │ │ ├── TextSceneWindow.cs
│ │ │ ├── TextScene.cs
│ │ │ ├── TextSceneMonitor.cs
│ │ │ ├── TextSceneSerializer.cs
│ │ │ ├── TextSceneHierarchy.cs
│ │ │ └── TextSceneDeserializer.cs
│ │ ├── PlayerPrefsTools.cs
│ │ ├── EditorHelper.cs
│ │ └── MaterialPreserver.cs
│ ├── General
│ │ ├── Helper.cs.meta
│ │ ├── MyColor.cs.meta
│ │ ├── MyColor.cs
│ │ └── Helper.cs
│ └── TextScene
│ │ ├── TextSceneLinkTest.cs.meta
│ │ ├── TextSceneObject.cs.meta
│ │ ├── TextSceneMonitorRelauncher.cs.meta
│ │ ├── TextSceneObject.cs
│ │ ├── TextSceneLinkTest.cs
│ │ └── TextSceneMonitorRelauncher.cs
├── Prefabs
│ ├── Capsules.prefab.meta
│ └── Capsules.prefab
└── Materials
│ ├── CustomMaterial.mat.meta
│ └── CustomMaterial.mat
├── .gitignore
├── LICENSE.txt
└── README.txt
/Library/BuildPlayer.prefs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Assets/Prefabs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: f9aceb4c7b17849b0b1ea6a9ee704464
3 |
--------------------------------------------------------------------------------
/Assets/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: a85f79dc4975844e38bc1b03e5837a31
3 |
--------------------------------------------------------------------------------
/Assets/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 06cd262a3aab2934e86944baea9b58c1
3 |
--------------------------------------------------------------------------------
/Assets/Materials.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: f175a448cb5f5455eb2c0ea6593c86f8
3 |
--------------------------------------------------------------------------------
/Assets/Scenes/spheres.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 66152891b922a419fbdab4bf019657d1
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: afd829c1ab38a4946b1fffa8b71fed30
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/General.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 7d2f5950c41a5754c8b4eb7f8145fb46
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: c78afe397e8b040d9baa371f39350c4d
3 |
--------------------------------------------------------------------------------
/Assets/Prefabs/Capsules.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: f0529f741bc2540eea3ec100e446c639
3 |
--------------------------------------------------------------------------------
/Assets/Scenes/scenedemo.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 10bff0427622240a28d6a168b67a6a81
3 |
--------------------------------------------------------------------------------
/Assets/Scenes/sceneinscene.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 8a1bf9ef9a41f4f42b7d260235032519
3 |
--------------------------------------------------------------------------------
/Assets/Materials/CustomMaterial.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: e9b851d350fc14be09ce3ba99babfe07
3 |
--------------------------------------------------------------------------------
/Assets/Scenes/otherscenedemo.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 7adf941b4440fba40afcdc066c2e50ce
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 3e07b0048c56b43faaf6056fb6b8db22
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/General/Helper.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 63b50f93355a73344ad0be84b399071d
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/General/MyColor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: ae2ab01f0b9e6ce409b9a290498f0780
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/EditorHelper.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 02200f17fae8c4694ae4a37813bc4f14
3 |
--------------------------------------------------------------------------------
/Library/TagManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/TagManager.asset
--------------------------------------------------------------------------------
/Library/TimeManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/TimeManager.asset
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/MaterialPreserver.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 6f4d5df8c6294084aa522fe7249211ea
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/PlayerPrefsTools.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: d8d13231945febb4a972f449203951fe
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextScene.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 169a73f779578455ebbfa89344781a2e
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneLinkTest.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 1d92b3e6c83a2b5429f7d7e6b7d0a98d
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneObject.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: dc824b060b5be4dff8dd18f2173bef36
3 |
--------------------------------------------------------------------------------
/Library/AudioManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/AudioManager.asset
--------------------------------------------------------------------------------
/Library/EditorSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/EditorSettings.asset
--------------------------------------------------------------------------------
/Library/InputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/InputManager.asset
--------------------------------------------------------------------------------
/Library/NetworkManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/NetworkManager.asset
--------------------------------------------------------------------------------
/Library/TextSceneBuildSettings.txt:
--------------------------------------------------------------------------------
1 | scene Assets/Scenes/scenedemo.txt
2 | scene Assets/Scenes/otherscenedemo.txt
3 |
--------------------------------------------------------------------------------
/Assets/Prefabs/Capsules.prefab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Assets/Prefabs/Capsules.prefab
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneMenu.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 7b150f4ca1761492c9c99014f1eb7fee
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneWindow.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 3f8256cb1bcff4dac99b9a9a464cb1ee
3 |
--------------------------------------------------------------------------------
/Library/DynamicsManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/DynamicsManager.asset
--------------------------------------------------------------------------------
/Library/ProjectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/ProjectSettings.asset
--------------------------------------------------------------------------------
/Library/QualitySettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/QualitySettings.asset
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneHierarchy.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: a66cdc3d14ce0e14f904d737ab12c97b
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneInspector.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 62a67117b823d44dfafbc8fffe6c3be8
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneMonitor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 9057b6c053eda4099a7725025081c63e
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneSerializer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: fb8e4ac47f8154837ba188ea2c243689
3 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneMonitorRelauncher.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: b32c77ef5241944bdb27cfefa659c883
3 |
--------------------------------------------------------------------------------
/Assets/Materials/CustomMaterial.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Assets/Materials/CustomMaterial.mat
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneDeserializer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 1
2 | guid: 6907f6bbc4ac5401ab36d66f467ba935
3 |
--------------------------------------------------------------------------------
/Library/InspectorExpandedItems.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terravision/UnityTextScene/HEAD/Library/InspectorExpandedItems.asset
--------------------------------------------------------------------------------
/Assets/Scripts/General/MyColor.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using System.Collections;
8 |
9 | public class MyColor : MonoBehaviour {
10 |
11 | public Color color = Color.red;
12 |
13 | // Use this for initialization
14 | void Start () {
15 | renderer.material.color = color;
16 | }
17 |
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneObject.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// TODO: It should not be necessary to hold a reference to the actual asset, path and/or GUID
6 | /// should be sufficient. This way it shouldn't be included in a final build as a
7 | /// dependency.
8 |
9 | using UnityEngine;
10 |
11 | public class TextSceneObject : MonoBehaviour
12 | {
13 | public TextAsset textScene;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/PlayerPrefsTools.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using UnityEditor;
8 |
9 | public class PlayerPrefsTools
10 | {
11 | [MenuItem("Tools/PlayerPrefsTools/Delete All")]
12 | public static void DeleteAll()
13 | {
14 | if (EditorUtility.DisplayDialog("Warning", "This will clear the playerprefs! Are you sure?", "Yes", "No"))
15 | PlayerPrefs.DeleteAll();
16 | }
17 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Standard
2 | .DS_Store
3 |
4 | # Project specific
5 | obj
6 | Temp
7 | Build
8 | TempScenes
9 | *.csproj
10 | *.sln
11 | *.suo
12 | *.pidb
13 | *.userprefs
14 | Library/AssetServerCacheV3
15 | Library/AssetVersioning.db
16 | Library/BuildSettings.asset
17 | Library/EditorBuildSettings.asset
18 | Library/FailedAssetImports.txt
19 | Library/MonoManager.asset
20 | Library/ProjectUsesCompressTexturesOnImport
21 | Library/ScriptAssemblies
22 | Library/ScriptMapper
23 | Library/assetDatabase3
24 | Library/cache
25 | Library/expandedItems
26 | Library/guidmapper
27 | Library/metadata
28 | Library/unity default resources
29 | Library/unity editor resources
30 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneLinkTest.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 |
8 | class TextSceneLinkTest : MonoBehaviour
9 | {
10 | public Material[] materialList;
11 | public GameObject goLink;
12 | public Transform transformLink;
13 | public GameObject prefabLink;
14 | public BoxCollider colliderLink;
15 |
16 | public string nextScene;
17 |
18 | protected void OnGUI()
19 | {
20 | if (GUI.Button(new Rect(20.0f, 20.0f, 100.0f, 20.0f), "Change scene"))
21 | {
22 | if (nextScene != null && nextScene.Length > 0)
23 | Application.LoadLevel(nextScene);
24 | else
25 | Application.LoadLevel(Application.loadedLevelName);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 TerraVision AS
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneInspector.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEditor;
7 | using UnityEngine;
8 |
9 | [CustomEditor(typeof(TextSceneObject))]
10 | public class TextSceneInspector : Editor
11 | {
12 | public override void OnInspectorGUI()
13 | {
14 | TextSceneObject tso = target as TextSceneObject;
15 |
16 | EditorGUILayout.BeginVertical();
17 |
18 | GUILayout.Label("External scene");
19 |
20 | EditorGUILayout.BeginHorizontal ();
21 | GUILayout.Label("Open scene: ");
22 |
23 | if (GUILayout.Button(tso.textScene.name))
24 | {
25 | if (EditorUtility.DisplayDialog("Open scenefile?", "Do you want to close the current scene and open the scene pointed to by this TextSceneObject?", "Yes", "No"))
26 | {
27 | TextSceneDeserializer.LoadSafe(EditorHelper.GetProjectFolder() + AssetDatabase.GetAssetPath(tso.textScene));
28 | GUIUtility.ExitGUI();
29 | }
30 | }
31 |
32 | EditorGUILayout.EndHorizontal ();
33 | EditorGUILayout.EndVertical();
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/EditorHelper.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using UnityEditor;
8 | using System.Reflection;
9 | using System;
10 |
11 |
12 | public class EditorHelper
13 | {
14 | public static void ClearLog()
15 | {
16 | Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
17 |
18 | Type type = assembly.GetType("UnityEditor.LogEntries");
19 | MethodInfo method = type.GetMethod("Clear");
20 | method.Invoke(new object(), null);
21 | }
22 |
23 | public static string GetProjectFolder()
24 | {
25 | string dataPath = Application.dataPath;
26 |
27 | return dataPath.Substring(0, dataPath.LastIndexOf('/')+1);
28 | }
29 |
30 | public static System.Object[] ParamList(params System.Object[] paramList)
31 | {
32 | return paramList;
33 | }
34 |
35 | private static MethodInfo textContentMethod;
36 |
37 | public static GUIContent TextContent(string text)
38 | {
39 | if (textContentMethod == null)
40 | textContentMethod = typeof(EditorGUIUtility).GetMethod("TextContent", BindingFlags.Static | BindingFlags.NonPublic);
41 |
42 | return textContentMethod.Invoke(null, EditorHelper.ParamList(text)) as GUIContent;
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/Assets/Scripts/TextScene/TextSceneMonitorRelauncher.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using System.Reflection;
8 |
9 | ///
10 | /// The sole purpose of this component is to re-launch the monitor we use to detect file changes
11 | /// etc in the editor.
12 | ///
13 | public class TextSceneMonitorRelauncher : MonoBehaviour
14 | {
15 | void OnDisable()
16 | {
17 | string projectPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/'));
18 |
19 | string assemblyPath = projectPath + "/Library/ScriptAssemblies/Assembly - CSharp - Editor.dll";
20 |
21 | Debug.Log("Loading assembly to launch TextSceneMonitor: " + assemblyPath);
22 |
23 | Assembly editorAssembly = Assembly.LoadFile(assemblyPath);
24 |
25 | System.Type tsm = editorAssembly.GetType("TextSceneMonitor");
26 |
27 | if (tsm != null)
28 | {
29 |
30 |
31 | MethodInfo mi = tsm.GetMethod("MonitorUpdate", BindingFlags.Static | BindingFlags.Public);
32 |
33 | if (mi != null)
34 | {
35 | mi.Invoke(null, null);
36 | }
37 | else
38 | Debug.LogError("Unable to find method: MonitorUpdate");
39 | }
40 | else
41 | Debug.LogError("Unable to find type: TextSceneMonitor");
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneMenu.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEditor;
7 | using UnityEngine;
8 |
9 | ///
10 | /// All TextScene menu items.
11 | ///
12 | public class TextSceneMenu
13 | {
14 | [MenuItem("TextScene/Hierarchy")]
15 | public static void Hierarchy()
16 | {
17 | TextSceneHierarchy.CreateHierarchy();
18 | }
19 |
20 | [MenuItem("TextScene/Load")]
21 | public static void Load()
22 | {
23 | TextSceneDeserializer.Load();
24 | }
25 |
26 | [MenuItem("TextScene/Save as")]
27 | public static void SaveAs()
28 | {
29 | TextSceneSerializer.SaveAs();
30 | }
31 |
32 | [MenuItem("TextScene/Save")]
33 | public static void SaveCurrent()
34 | {
35 | TextSceneSerializer.SaveCurrent();
36 | }
37 |
38 | [MenuItem ("TextScene/Build/Add current to build")]
39 | public static void AddCurrentToBuild()
40 | {
41 | TextScene.AddCurrentSceneToBuild();
42 | }
43 |
44 | [MenuItem ("TextScene/Build/Open Window")]
45 | public static void OpenWindow()
46 | {
47 | TextSceneWindow.Create();
48 | }
49 |
50 | [MenuItem ("Assets/TextScene Open")]
51 | public static void LoadContext()
52 | {
53 | TextSceneDeserializer.LoadContext();
54 | }
55 |
56 | [MenuItem ("Assets/TextScene add to build")]
57 | public static void AddContext()
58 | {
59 | TextScene.AddSelectedSceneToBuild();
60 | }
61 |
62 | [MenuItem ("TextScene/Warnings/Toggle Default Hierarchy")]
63 | public static void ToggleDefaultHierarchyWarning()
64 | {
65 | int currentValue = PlayerPrefs.GetInt("ShowDefaultHierarchyWarning", 1);
66 |
67 | if (currentValue == 0)
68 | {
69 | PlayerPrefs.SetInt("ShowDefaultHierarchyWarning", 1);
70 | EditorUtility.DisplayDialog("Enabled warning", "Default hierarchy warning has been enabled", "OK");
71 | }
72 | else
73 | {
74 | if (EditorUtility.DisplayDialog("Disable warning", "Are you sure you want to disable the default hierarchy warning? It is recommended to leave this warning on.", "Disable it", "Keep it on"))
75 | PlayerPrefs.SetInt("ShowDefaultHierarchyWarning", 0);
76 | }
77 | }
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/MaterialPreserver.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using UnityEditor;
8 | using System.IO;
9 |
10 | public class MaterialPreserver : AssetPostprocessor
11 | {
12 | public Material OnAssignMaterialModel(Material material, Renderer renderer)
13 | {
14 | string subfolderName = assetPath.Substring(0, assetPath.LastIndexOf("/")) + "/Materials";
15 | string materialFileName = "/" + material.name + ".mat";
16 |
17 | if (material.name == null || material.name.Length == 0)
18 | {
19 | subfolderName = "Assets/Materials";
20 | materialFileName = "/Unnamed.mat";
21 | }
22 |
23 | string assetMaterialDirectory = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("/")) + "/" + subfolderName;
24 |
25 | //Debug.Log(assetMaterialDirectory);
26 |
27 | if (!Directory.Exists(assetMaterialDirectory))
28 | {
29 | Debug.Log("Creating directory for material (" + material.name + "): " + assetMaterialDirectory);
30 | Directory.CreateDirectory(assetMaterialDirectory);
31 | }
32 |
33 |
34 | string materialAssetPath = subfolderName + materialFileName;
35 |
36 | //Use the existing material if there is one. If you want to regenerate the material,
37 | //delete the file itself.
38 | Material existing = AssetDatabase.LoadAssetAtPath(materialAssetPath, typeof(Material)) as Material;
39 |
40 | if (existing != null)
41 | {
42 | Debug.Log("Material (" + material.name + ") already exists, using that one");
43 | return existing;
44 | }
45 |
46 |
47 |
48 |
49 | Debug.Log("Material (" + material.name + ") does not exist, creating new one at " + materialAssetPath);
50 |
51 |
52 | // Create a new material asset using the specular shader
53 | // but otherwise the default values from the model
54 | material.shader = Shader.Find("Specular");
55 | AssetDatabase.CreateAsset(material, materialAssetPath);
56 | return material;
57 | }
58 | }
--------------------------------------------------------------------------------
/Assets/Scenes/sceneinscene.txt:
--------------------------------------------------------------------------------
1 | prefab Capsules_instance
2 | assetpath Assets/Prefabs/Capsules.prefab, f0529f741bc2540eea3ec100e446c639
3 | -7.25459 -8.03027 -0.12661
4 | 0.46448 0.00000 0.00000 0.88558
5 | 1.76387 1.76387 1.76387
6 |
7 | gameobject ScaledSphere
8 | tag Untagged layer 0
9 | 8.81933 0.00000 6.95787
10 | 0.00000 0.00000 0.00000 1.00000
11 | 1.73578 5.00000 1.73578
12 | children 0
13 | components 3
14 | UnityEngine.MeshFilter 1
15 | property sharedMesh builtinmesh UnityEngine.Mesh = Sphere
16 | UnityEngine.SphereCollider 3
17 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
18 | property radius primitive System.Single = 0.5000001
19 | property isTrigger primitive System.Boolean = False
20 | UnityEngine.MeshRenderer 5
21 | property castShadows primitive System.Boolean = True
22 | property receiveShadows primitive System.Boolean = True
23 | property sharedMaterials array UnityEngine.Material[] = 1
24 | asset UnityEngine.Material = Assets/Materials/CustomMaterial.mat, CustomMaterial, e9b851d350fc14be09ce3ba99babfe07
25 | property lightmapIndex primitive System.Int32 = -1
26 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
27 |
28 | gameobject Sphere
29 | tag Untagged layer 0
30 | 0.00000 0.00000 4.27119
31 | 0.00000 0.00000 0.00000 1.00000
32 | 2.96577 2.96577 2.96577
33 | children 0
34 | components 3
35 | UnityEngine.MeshFilter 1
36 | property sharedMesh builtinmesh UnityEngine.Mesh = Sphere
37 | UnityEngine.SphereCollider 3
38 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
39 | property radius primitive System.Single = 0.5000001
40 | property isTrigger primitive System.Boolean = False
41 | UnityEngine.MeshRenderer 5
42 | property castShadows primitive System.Boolean = True
43 | property receiveShadows primitive System.Boolean = True
44 | property sharedMaterials array UnityEngine.Material[] = 1
45 | asset UnityEngine.Material = Assets/Materials/CustomMaterial.mat, CustomMaterial, e9b851d350fc14be09ce3ba99babfe07
46 | property lightmapIndex primitive System.Int32 = -1
47 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
48 |
49 | textscene spheres
50 | assetpath Assets/Scenes/spheres.txt, 66152891b922a419fbdab4bf019657d1
51 | 0.00000 0.00000 0.00000
52 | 0.00000 0.00000 0.00000 1.00000
53 | 1.00000 1.00000 1.00000
54 |
55 |
--------------------------------------------------------------------------------
/Assets/Scenes/spheres.txt:
--------------------------------------------------------------------------------
1 | gameobject Sphere
2 | tag Untagged layer 0
3 | 0.00000 0.00000 0.00000
4 | 0.00000 0.00000 0.00000 1.00000
5 | 2.96577 2.96577 2.96577
6 | children 0
7 | components 3
8 | UnityEngine.MeshFilter 1
9 | property sharedMesh builtinmesh UnityEngine.Mesh = Sphere
10 | UnityEngine.SphereCollider 3
11 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
12 | property radius primitive System.Single = 0.5000001
13 | property isTrigger primitive System.Boolean = False
14 | UnityEngine.MeshRenderer 5
15 | property castShadows primitive System.Boolean = True
16 | property receiveShadows primitive System.Boolean = True
17 | property sharedMaterials array UnityEngine.Material[] = 1
18 | builtinmaterial UnityEngine.Material = Default-Diffuse
19 | property lightmapIndex primitive System.Int32 = -1
20 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
21 |
22 | gameobject Sphere
23 | tag Untagged layer 0
24 | -4.66703 -7.40282 6.95787
25 | 0.00000 0.00000 0.00000 1.00000
26 | 1.73578 1.73578 1.73578
27 | children 0
28 | components 3
29 | UnityEngine.MeshFilter 1
30 | property sharedMesh builtinmesh UnityEngine.Mesh = Sphere
31 | UnityEngine.SphereCollider 3
32 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
33 | property radius primitive System.Single = 0.5000001
34 | property isTrigger primitive System.Boolean = False
35 | UnityEngine.MeshRenderer 5
36 | property castShadows primitive System.Boolean = True
37 | property receiveShadows primitive System.Boolean = True
38 | property sharedMaterials array UnityEngine.Material[] = 1
39 | builtinmaterial UnityEngine.Material = Default-Diffuse
40 | property lightmapIndex primitive System.Int32 = -1
41 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
42 |
43 | gameobject UniqueName
44 | tag Untagged layer 0
45 | 4.67459 0.00000 6.95787
46 | 0.00000 0.00000 0.00000 1.00000
47 | 1.73578 1.73578 1.73578
48 | children 0
49 | components 3
50 | UnityEngine.MeshFilter 1
51 | property sharedMesh builtinmesh UnityEngine.Mesh = Sphere
52 | UnityEngine.SphereCollider 3
53 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
54 | property radius primitive System.Single = 0.5000001
55 | property isTrigger primitive System.Boolean = False
56 | UnityEngine.MeshRenderer 5
57 | property castShadows primitive System.Boolean = True
58 | property receiveShadows primitive System.Boolean = True
59 | property sharedMaterials array UnityEngine.Material[] = 1
60 | builtinmaterial UnityEngine.Material = Default-Diffuse
61 | property lightmapIndex primitive System.Int32 = -1
62 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
63 |
64 |
--------------------------------------------------------------------------------
/Assets/Scenes/otherscenedemo.txt:
--------------------------------------------------------------------------------
1 | gameobject Cube
2 | tag Untagged layer 0
3 | 0.00000 -2.48280 0.00000
4 | 0.00000 0.00000 0.00000 1.00000
5 | 1.00000 1.00000 1.00000
6 | children 0
7 | components 3
8 | UnityEngine.MeshFilter 1
9 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
10 | UnityEngine.BoxCollider 4
11 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
12 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
13 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
14 | property isTrigger primitive System.Boolean = False
15 | UnityEngine.MeshRenderer 5
16 | property castShadows primitive System.Boolean = True
17 | property receiveShadows primitive System.Boolean = True
18 | property sharedMaterials array UnityEngine.Material[] = 1
19 | builtinmaterial UnityEngine.Material = Default-Diffuse
20 | property lightmapIndex primitive System.Int32 = -1
21 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
22 |
23 | gameobject Cube
24 | tag Untagged layer 0
25 | 3.00000 -2.48280 2.00000
26 | 0.00000 0.00000 0.00000 1.00000
27 | 1.00000 1.00000 1.00000
28 | children 1
29 | gameobject Cube
30 | tag Untagged layer 0
31 | -10.00000 6.00000 0.00000
32 | 0.00000 0.00000 0.00000 1.00000
33 | 1.00000 1.00000 1.00000
34 | children 0
35 | components 3
36 | UnityEngine.MeshFilter 1
37 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
38 | UnityEngine.BoxCollider 4
39 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
40 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
41 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
42 | property isTrigger primitive System.Boolean = False
43 | UnityEngine.MeshRenderer 5
44 | property castShadows primitive System.Boolean = True
45 | property receiveShadows primitive System.Boolean = True
46 | property sharedMaterials array UnityEngine.Material[] = 1
47 | asset UnityEngine.Material = Assets/Materials/CustomMaterial.mat, CustomMaterial, e9b851d350fc14be09ce3ba99babfe07
48 | property lightmapIndex primitive System.Int32 = -1
49 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
50 | components 3
51 | UnityEngine.MeshFilter 1
52 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
53 | UnityEngine.BoxCollider 4
54 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
55 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
56 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
57 | property isTrigger primitive System.Boolean = False
58 | UnityEngine.MeshRenderer 5
59 | property castShadows primitive System.Boolean = True
60 | property receiveShadows primitive System.Boolean = True
61 | property sharedMaterials array UnityEngine.Material[] = 1
62 | builtinmaterial UnityEngine.Material = Default-Diffuse
63 | property lightmapIndex primitive System.Int32 = -1
64 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
65 |
66 | gameobject Directional light
67 | tag Untagged layer 0
68 | 740.81120 66.71914 1054.85100
69 | 0.37889 0.00000 0.00000 0.92544
70 | 1.00000 1.00000 1.00000
71 | children 0
72 | components 1
73 | UnityEngine.Light 12
74 | property type primitive UnityEngine.LightType = Directional
75 | property color primitive UnityEngine.Color = (1.000, 1.000, 1.000, 1.000)
76 | property intensity primitive System.Single = 0.5
77 | property shadows primitive UnityEngine.LightShadows = None
78 | property shadowStrength primitive System.Single = 0.8
79 | property shadowConstantBias primitive System.Single = 0.07
80 | property shadowObjectSizeBias primitive System.Single = 0.01
81 | property attenuate primitive System.Boolean = True
82 | property range primitive System.Single = 10
83 | property spotAngle primitive System.Single = 30
84 | property renderMode primitive UnityEngine.LightRenderMode = Auto
85 | property cullingMask primitive System.Int32 = -1
86 |
87 | gameobject Links
88 | tag Untagged layer 0
89 | 8.54124 9.88922 -6.45947
90 | 0.00000 0.00000 0.00000 1.00000
91 | 1.00000 1.00000 1.00000
92 | children 0
93 | components 1
94 | TextSceneLinkTest 4
95 | field materialList array UnityEngine.Material[] = 0
96 | field prefabLink asset UnityEngine.GameObject = Assets/Prefabs/Capsules.prefab, Capsules, f0529f741bc2540eea3ec100e446c639
97 | field colliderLink scenelink UnityEngine.BoxCollider = /Cube/Cube
98 | field nextScene primitive System.String = scenedemo
99 |
100 | gameobject Main Camera
101 | tag MainCamera layer 0
102 | 0.00000 1.00000 -10.00000
103 | 0.00000 0.00000 0.00000 1.00000
104 | 1.00000 1.00000 1.00000
105 | children 0
106 | components 3
107 | UnityEngine.Camera 15
108 | property fov primitive System.Single = 60
109 | property near primitive System.Single = 0.3
110 | property far primitive System.Single = 1000
111 | property fieldOfView primitive System.Single = 60
112 | property nearClipPlane primitive System.Single = 0.3
113 | property farClipPlane primitive System.Single = 1000
114 | property orthographicSize primitive System.Single = 100
115 | property orthographic primitive System.Boolean = False
116 | property isOrthoGraphic primitive System.Boolean = False
117 | property depth primitive System.Single = -1
118 | property cullingMask primitive System.Int32 = -1
119 | property backgroundColor primitive UnityEngine.Color = (0.192, 0.302, 0.475, 0.020)
120 | property rect primitive UnityEngine.Rect = (0.00, 0.00, 1.00, 1.00)
121 | property clearFlags primitive UnityEngine.CameraClearFlags = Skybox
122 | property depthTextureMode primitive UnityEngine.DepthTextureMode = None
123 | UnityEngine.GUILayer 0
124 | UnityEngine.AudioListener 1
125 | property velocityUpdateMode primitive UnityEngine.AudioVelocityUpdateMode = Dynamic
126 |
127 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneWindow.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// TODO: *Make this look less crap.
6 | /// *Rename to something better, probably including "BuildSettings".
7 | /// *Check for built-in build settings window and alert user if it is up
8 | /// (similar to TextSceneHierarchy whining if the built-in is up).
9 |
10 | using UnityEngine;
11 | using UnityEditor;
12 |
13 | using System.IO;
14 | using System.Collections.Generic;
15 | using System;
16 | using System.Text;
17 |
18 |
19 | ///
20 | /// Editor window currently only holding the build settings and build options.
21 | ///
22 | public class TextSceneWindow : EditorWindow
23 | {
24 | public static void Create ()
25 | {
26 | // Get existing open window or if none, make a new one:
27 | TextSceneWindow window = (TextSceneWindow)EditorWindow.GetWindow (typeof (TextSceneWindow));
28 |
29 | window.title = "TextScene Build Settings";
30 | window.Show ();
31 | window.LoadSettings();
32 | }
33 |
34 | double nextBuildSettingsCheck = 0.0;
35 | DateTime loadedBuildSettingsTime;
36 |
37 | List scenes = new List();
38 |
39 | Vector2 scroll = Vector2.zero;
40 |
41 | void LoadSettings()
42 | {
43 | scenes = TextScene.ReadScenes();
44 |
45 | loadedBuildSettingsTime = TextScene.BuildSettingsDate();
46 | }
47 |
48 | void OnGUI ()
49 | {
50 | GUILayout.BeginVertical();
51 |
52 | scroll = GUILayout.BeginScrollView(scroll);
53 |
54 | GUILayout.Label("Scenes to build");
55 |
56 | foreach(string scene in scenes)
57 | {
58 | EditorGUILayout.BeginHorizontal();
59 |
60 | if (GUILayout.Button(scene))
61 | {
62 | TextSceneDeserializer.LoadSafe(EditorHelper.GetProjectFolder() + scene);
63 | GUIUtility.ExitGUI();
64 | return;
65 | }
66 |
67 |
68 | if (GUILayout.Button("Remove", GUILayout.MaxWidth(60)))
69 | {
70 | if (EditorUtility.DisplayDialog("Remove scene", "Are you sure you want to remove this scene from the build settings?", "Yes", "No"))
71 | {
72 | TextScene.RemoveSceneFromBuild(scene);
73 | LoadSettings();
74 | }
75 | }
76 |
77 | if (GUILayout.Button("+", GUILayout.MaxWidth(20)))
78 | {
79 | TextScene.MoveScenePosition(scene, 1);
80 | LoadSettings();
81 | }
82 |
83 | if (GUILayout.Button("-", GUILayout.MaxWidth(20)))
84 | {
85 | TextScene.MoveScenePosition(scene, -1);
86 | LoadSettings();
87 | }
88 |
89 | GUILayout.EndHorizontal();
90 | }
91 |
92 | if (scenes.Count > 0)
93 | {
94 | EditorGUILayout.Separator();
95 | EditorGUILayout.BeginVertical(GUILayout.MaxWidth(150));
96 |
97 | if (GUILayout.Button("Build Temp"))
98 | {
99 | TextScene.BuildTempScenes();
100 | GUIUtility.ExitGUI();
101 | return;
102 | }
103 |
104 | if (GUILayout.Button("Build Streamed Web"))
105 | {
106 | TextScene.Build(BuildTarget.WebPlayerStreamed);
107 | GUIUtility.ExitGUI();
108 | return;
109 | }
110 |
111 | if (GUILayout.Button("Build Web"))
112 | {
113 | TextScene.Build(BuildTarget.WebPlayer);
114 | GUIUtility.ExitGUI();
115 | return;
116 | }
117 |
118 | if (GUILayout.Button("Build OSX"))
119 | {
120 | TextScene.Build(BuildTarget.StandaloneOSXUniversal);
121 | GUIUtility.ExitGUI();
122 | return;
123 | }
124 |
125 | if (GUILayout.Button("Build Windows"))
126 | {
127 | TextScene.Build(BuildTarget.StandaloneWindows);
128 | GUIUtility.ExitGUI();
129 | return;
130 | }
131 |
132 | EditorGUILayout.EndVertical();
133 | }
134 | else
135 | {
136 | GUILayout.Label("Add scenes via the TextScene menu item to enable build");
137 | }
138 |
139 | EditorGUILayout.Separator();
140 |
141 | if (GUILayout.Button("Add current", GUILayout.MaxWidth(100)))
142 | {
143 | TextScene.AddCurrentSceneToBuild();
144 | LoadSettings();
145 | }
146 |
147 | EditorGUILayout.Separator();
148 |
149 | if (GUILayout.Button("Validate Settings", GUILayout.MaxWidth(100)))
150 | {
151 | List invalidScenes;
152 |
153 | if (TextScene.ValidateBuildSettings(out invalidScenes))
154 | EditorUtility.DisplayDialog("Valid settings", "The build settings seem valid enough", "OK");
155 | else
156 | {
157 | StringBuilder sb = new StringBuilder();
158 |
159 | sb.Append("There were errors in validation: \n");
160 |
161 | foreach(string scene in invalidScenes)
162 | {
163 | sb.Append(" ");
164 | sb.Append(scene);
165 | sb.Append('\n');
166 | }
167 |
168 | sb.Append("Try running 'Build Temp' to fix them up, or inspect the console for further hints.");
169 |
170 | EditorUtility.DisplayDialog("Validation failed", sb.ToString(), "OK");
171 | }
172 | }
173 |
174 | GUILayout.EndScrollView();
175 |
176 | GUILayout.EndVertical();
177 | }
178 |
179 | void OnHierarchyChange()
180 | {
181 | //TODO/FIXME: Detect this and set TextSceneMonitor to dirty so we can warn users if they try to
182 | // load a different scene.
183 |
184 | //UPDATE: Tried to go around this by using 'SaveIfUserWantsTo' where possible.
185 | }
186 |
187 | void OnInspectorUpdate()
188 | {
189 | //TODO FIXME HACK: I can't currently see how to make the instance survive a play/stop session,
190 | // so we just make sure it is constantly requested (and renewed if necessary).
191 | //UPDATE: Moved this to a possibly even worse hack, but at least it works without needing to have
192 | // this editor window active.
193 | //TextSceneMonitor.MonitorUpdate();
194 |
195 |
196 | if (EditorApplication.timeSinceStartup > nextBuildSettingsCheck)
197 | {
198 | nextBuildSettingsCheck = EditorApplication.timeSinceStartup + 2.0f;
199 |
200 | DateTime buildSettingsTime = TextScene.BuildSettingsDate();
201 |
202 | if (buildSettingsTime > loadedBuildSettingsTime)
203 | {
204 | LoadSettings();
205 | loadedBuildSettingsTime = buildSettingsTime;
206 | }
207 | }
208 | }
209 | }
--------------------------------------------------------------------------------
/Assets/Scenes/scenedemo.txt:
--------------------------------------------------------------------------------
1 | prefab Capsule
2 | assetpath Assets/Prefabs/Capsules.prefab, f0529f741bc2540eea3ec100e446c639
3 | 3.32136 0.00000 0.00000
4 | 0.00000 0.00000 -0.35426 0.93515
5 | 1.00000 1.00000 1.00000
6 |
7 | gameobject Cube
8 | tag Untagged layer 0
9 | 0.00000 -2.48280 0.00000
10 | 0.00000 0.00000 0.00000 1.00000
11 | 1.00000 1.00000 1.00000
12 | children 0
13 | components 3
14 | UnityEngine.MeshFilter 1
15 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
16 | UnityEngine.BoxCollider 4
17 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
18 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
19 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
20 | property isTrigger primitive System.Boolean = False
21 | UnityEngine.MeshRenderer 5
22 | property castShadows primitive System.Boolean = True
23 | property receiveShadows primitive System.Boolean = True
24 | property sharedMaterials array UnityEngine.Material[] = 1
25 | builtinmaterial UnityEngine.Material = Default-Diffuse
26 | property lightmapIndex primitive System.Int32 = -1
27 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
28 |
29 | gameobject Cube
30 | tag Untagged layer 0
31 | 3.00000 -2.48280 2.00000
32 | 0.00000 0.00000 0.00000 1.00000
33 | 1.00000 1.00000 1.00000
34 | children 1
35 | gameobject Cube
36 | tag Untagged layer 0
37 | -10.00000 6.00000 0.00000
38 | 0.00000 0.00000 0.00000 1.00000
39 | 1.00000 1.00000 1.00000
40 | children 0
41 | components 3
42 | UnityEngine.MeshFilter 1
43 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
44 | UnityEngine.BoxCollider 4
45 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
46 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
47 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
48 | property isTrigger primitive System.Boolean = False
49 | UnityEngine.MeshRenderer 5
50 | property castShadows primitive System.Boolean = True
51 | property receiveShadows primitive System.Boolean = True
52 | property sharedMaterials array UnityEngine.Material[] = 1
53 | asset UnityEngine.Material = Assets/Materials/CustomMaterial.mat, CustomMaterial, e9b851d350fc14be09ce3ba99babfe07
54 | property lightmapIndex primitive System.Int32 = -1
55 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
56 | components 3
57 | UnityEngine.MeshFilter 1
58 | property sharedMesh builtinmesh UnityEngine.Mesh = Cube
59 | UnityEngine.BoxCollider 4
60 | property center primitive UnityEngine.Vector3 = (0.0, 0.0, 0.0)
61 | property size primitive UnityEngine.Vector3 = (1.0, 1.0, 1.0)
62 | property extents primitive UnityEngine.Vector3 = (0.5, 0.5, 0.5)
63 | property isTrigger primitive System.Boolean = False
64 | UnityEngine.MeshRenderer 5
65 | property castShadows primitive System.Boolean = True
66 | property receiveShadows primitive System.Boolean = True
67 | property sharedMaterials array UnityEngine.Material[] = 1
68 | builtinmaterial UnityEngine.Material = Default-Diffuse
69 | property lightmapIndex primitive System.Int32 = -1
70 | property lightmapTilingOffset primitive UnityEngine.Vector4 = (1.0, 1.0, 0.0, 0.0)
71 |
72 | gameobject Directional light
73 | tag Untagged layer 0
74 | 740.81120 66.71914 1054.85100
75 | 0.37889 0.00000 0.00000 0.92544
76 | 1.00000 1.00000 1.00000
77 | children 0
78 | components 1
79 | UnityEngine.Light 12
80 | property type primitive UnityEngine.LightType = Directional
81 | property color primitive UnityEngine.Color = (1.000, 1.000, 1.000, 1.000)
82 | property intensity primitive System.Single = 0.5
83 | property shadows primitive UnityEngine.LightShadows = None
84 | property shadowStrength primitive System.Single = 0.8
85 | property shadowConstantBias primitive System.Single = 0.07
86 | property shadowObjectSizeBias primitive System.Single = 0.01
87 | property attenuate primitive System.Boolean = True
88 | property range primitive System.Single = 10
89 | property spotAngle primitive System.Single = 30
90 | property renderMode primitive UnityEngine.LightRenderMode = Auto
91 | property cullingMask primitive System.Int32 = -1
92 |
93 | gameobject Links
94 | tag Untagged layer 0
95 | 8.54124 9.88922 -6.45947
96 | 0.00000 0.00000 0.00000 1.00000
97 | 1.00000 1.00000 1.00000
98 | children 0
99 | components 1
100 | TextSceneLinkTest 6
101 | field materialList array UnityEngine.Material[] = 0
102 | field goLink scenelink UnityEngine.GameObject = /spheres/UniqueName
103 | field transformLink scenelink UnityEngine.Transform = /Capsule/Capsule
104 | field prefabLink asset UnityEngine.GameObject = Assets/Prefabs/Capsules.prefab, Capsules, f0529f741bc2540eea3ec100e446c639
105 | field colliderLink scenelink UnityEngine.BoxCollider = /Cube/Cube
106 | field nextScene primitive System.String = otherscenedemo
107 |
108 | gameobject Main Camera
109 | tag MainCamera layer 0
110 | 0.00000 1.00000 -10.00000
111 | 0.00000 0.00000 0.00000 1.00000
112 | 1.00000 1.00000 1.00000
113 | children 0
114 | components 3
115 | UnityEngine.Camera 15
116 | property fov primitive System.Single = 60
117 | property near primitive System.Single = 0.3
118 | property far primitive System.Single = 1000
119 | property fieldOfView primitive System.Single = 60
120 | property nearClipPlane primitive System.Single = 0.3
121 | property farClipPlane primitive System.Single = 1000
122 | property orthographicSize primitive System.Single = 100
123 | property orthographic primitive System.Boolean = False
124 | property isOrthoGraphic primitive System.Boolean = False
125 | property depth primitive System.Single = -1
126 | property cullingMask primitive System.Int32 = -1
127 | property backgroundColor primitive UnityEngine.Color = (0.192, 0.302, 0.475, 0.020)
128 | property rect primitive UnityEngine.Rect = (0.00, 0.00, 1.00, 1.00)
129 | property clearFlags primitive UnityEngine.CameraClearFlags = Skybox
130 | property depthTextureMode primitive UnityEngine.DepthTextureMode = None
131 | UnityEngine.GUILayer 0
132 | UnityEngine.AudioListener 1
133 | property velocityUpdateMode primitive UnityEngine.AudioVelocityUpdateMode = Dynamic
134 |
135 | textscene sceneinscene
136 | assetpath Assets/Scenes/sceneinscene.txt, 8a1bf9ef9a41f4f42b7d260235032519
137 | -7.54487 -4.46076 8.38350
138 | -0.60867 -0.12085 0.35437 0.69953
139 | 1.00000 1.00000 1.00000
140 |
141 | textscene spheres
142 | assetpath Assets/Scenes/spheres.txt, 66152891b922a419fbdab4bf019657d1
143 | 0.00000 0.00000 0.00000
144 | 0.00000 0.00000 0.00000 1.00000
145 | 1.00000 1.00000 1.00000
146 |
147 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | DISCLAIMER: Use this code at your own risk! It is WIP!
2 |
3 | ----What is this?
4 |
5 | The TextScene system is intended to be a complete replacement of the Unity built-in binary scene format. The reason to hack together and use such a format, is because it is very difficult, if not impossible, to merge scene conflicts in a team environment using a binary scene format. If you don't care about diffable scenes, you should avoid this (one-man teams will most likely not benefit at all from this, unless you desperately want scene history).
6 |
7 | If you decide to venture into the source code, be warned that it is poorly commented and work in process.
8 |
9 | ----How does this stuff work?
10 |
11 | When using TextScenes, you are highly advised against mixing them with binary Unity scenes. This is because the TextScene system does a few things in parallell with or re-implements (badly, mind you) certain functionality from the binary Unity scene handling. What immediately comes to mind is the Hierarchy View and the build process.
12 |
13 | While you will primarily work with text-based scene files, the binary unity scenes are still used as temporary files. The TextScene files are only used on save and load, and then saved to temporary unity scenes outside your assets folder. These files are also used when a player is built (this way scene dependencies should work as they do using the regular binary scene format).
14 |
15 | The TextScene format requires certain changes to the normal workflow in Unity. First of all, as far as I know, there are no useful hooks for the GUI save and load exposed to editor scripts, so you should no longer use Load or Save from the main menu (in fact, Save *should* work, as the TextSceneMonitor should pick that one up, but it is recommended to keep with the TextScene functionality). To open a TextScene, please use the menu TextScene or right-click a TextAsset and choose 'TextScene Open'. It is also possible to open TextScenes via the TextScene build settings window, or via the in-scene TextScenes' inspector (they have a component called TextSceneObject).
16 |
17 | When you are working on a TextScene, it is recommended to keep the TextSceneHierarchy view open at all times. This hierarchy has added functionality in order to prevent user mistakes, such as disabling children of TextSceneObjects. It also regularly pokes the TextSceneMonitor so it is able to keep monitoring our files.
18 |
19 | To get started, open the project. Go to the TextScene menu and choose "Hierarchy". You should now be told to close down the standard hierarchy, and it is advised to do so. You will also very likely be prompted with a file dialog. Click cancel and click the next dialog which tells you to save using the TextScene menu. When you intend to create new scenes, it is recommended to not click away the file dialog, but save to a TextScene to keep the TextSceneMonitor happy. However, now expand the "Scenes" folder in the Project view. Right-click 'scenedemo' and choose TextScene Open. You should now be looking at a scene containing a few primitives.
20 |
21 | As you can see, there are both blue and yellow colored objects in the hierarchy. The yellow-colored object is a TextSceneObject and it is in reality a link to another TextScene. If you open 'demoscene' in a text editor, you will see that the 'sceneinscene' object is only a transform and a pointer to a different TextScene. Select the yellow object in the hierarchy, and click "sceneinscene" on the button that should appear in the Inspector. Choose "yes", and you should now have navigated to the TextScene the link points at. Drag the MyColor-script from "Scripts/General" onto the ScaledSphere, set the color to something other than red or white, and save the scene. Go back to the main scene by right-clicking 'demoscene' in the Project view, and hit 'play'. If all goes well, it should now start up and the scaled sphere should turn into whatever color you set it to.
22 |
23 | If you expand the yellow objects, you will notice that the children are uneditable. This is to prevent any changes in the scene that will not persist when you save and load (there is no "apply" functionality for TextSceneObjects, unfortunately). Now, select the Capsule prefab. Notice that you can drag the root prefab and root TextSceneObject around in the hierarchy, but you cannot move children out, for the same save/load reason.
24 |
25 | At last, go to TextScene again and go to Build->Open Window. This is where you should create builds from. Hopefully, demoscene should be in the list. Feel free to build a player to verify that it actually works.
26 |
27 |
28 | ----What are the main elements?
29 |
30 | *TextSceneSerializer:
31 |
32 | This class writes the current scene to a human-readable, diffable textfile. If it fails, it will give the user feedback (hopefully) and not write anything to the actual file until the errors have been resolved.
33 |
34 | *TextSceneDeserializer:
35 |
36 | Reads a TextScene file and populates a scene (either a clean one, or loads a scene into an existing scene).
37 |
38 | *TextSceneMonitor:
39 |
40 | Is polled continously by the TextSceneHierarchy. This class monitors user actions and external file changes. When a scene is loaded, the TextSceneDeserializer notifies this class and tells it to create binary temporary representations of the scene (note: The implementation for this is extremely hacky, but works for now. It is related to what seems like a bug with the Unity API SaveScene/LoadScene, see code for details). These temporary scenes are standard Unity scenes, and they are also what we pass on to the player builder. These scenes are regularly monitored for changes, and this is currently how we try to detect if the user saved using built-in functionality. The binary temp scenes are stored outside the Assets folder, like this: /Project/TempScenes/somefilename.unity, in parallell with their TextScene counterpart, which is in /Project/Assets/somefilename.txt
41 |
42 | The TextSceneMonitor also monitors editor state changes. The only one we actually react upon, is whenever the user presses "play". The TextScene system will then validate any BuildSettings in order to make the in-editor play session as painless as possible. Keep in mind that we are using TextScenes which need to be converted to binary Unity scenes before they are usable, so part of the validation process is making sure these actually exist and are up to date.
43 |
44 | *TextSceneHierarchy
45 |
46 | A poor replacement of the built-in Scene Hierarchy. It has the most basic functionality, such as moving stuff around the tree, new color codes for TextSceneObjects, the ability to add TextScenes into scenes (can be thought of as a way of doing prefabs-in-prefabs - drag'n'drop a TextAsset containing a TextScene into the view pane, the result should be a yellow-colored object containing the other scene), instantiating objects by drag'n'drop from the Project View and script assignment.
47 |
48 | *TextSceneWindow (stupid name, this is in fact the build settings)
49 |
50 | Exposes TextScene build functionality to the user. Most of the functionality itself is implemented in TextScene (which basically is a utility class).
51 |
52 | *TextScene
53 |
54 | Utility class that most importantly has functionality for reading/writing TextSceneBuildSettings and creating builds.
55 |
56 |
57 | ----What are the main limitations and areas that desperately need work?
58 |
59 | In random order:
60 |
61 | *Does NOT work as-is with Unity3. Manual changes need to be applied manually for the time being. Inspect error console for hints.
62 |
63 | *Pro is required (at least for the build stuff, not sure about how/if the asset reference GUID resolves will break on Unity 'regular' or using the Asset Server - the current project has External VCS enabled).
64 |
65 | *Currently, it is not possible to save and load prefabs *with instance changes*. Such changes will simply be ignored when saving, so they are not preserved.
66 |
67 | *Certain Unity objects cannot be entirely reconstructed from scripts, for example the ellipsoid particle emitter. Such objects should be made into prefabs and dropped into the scene for TextScene compatibility.
68 |
69 | *The TextSceneHierarchy view has very limited functionality compared to the built in hiererchy. Multiple selection, keyboard shortcuts, object renaming is just a fraction of the missing features.
70 |
71 | *In-scene links (typically drag'n'drop links between components) require the linked object to have a unique path in the scene. The reason for this, is that the only reference stored in the TextScene is the full scene path of the object. Using InstanceID is a possible way to get around this, but is currently not implemented.
72 |
73 | *The textscene format currently lists the count of children, components, script members, array sizes etc in the format itself. This should be removed, as it makes hand-editing the scene files more difficult than it should be.
74 |
75 | *I have seen at a couple of occasions that Unity gives strange error messages regarding temp file overwrites. I have yet to figure out exactly why or when this happens, but I have not (yet) seen any loss of data - it happens to the temporary binary files, not the TextScenes themselves.
76 |
77 | *TextSceneObjects (scene-in-scene links) are not monitored for external changes.
78 |
79 | *Prefabs seem to not revert correctly. The reverts arent 'recursive', so if you instantiate a prefab, move it's instance child around, select the root instance again and click 'revert' - nothing happens. You have to select the child itself and 'revert' that one. Given that instance changes to prefabs isn't currently supported, changes like this will automatically revert themselves when you save and re-load a scene. You have been warned ;)
80 |
81 | *The context-menu item 'Disconnect' in the TextSceneHierarchy is badly implemented - it re-instantiates a new object and deletes the prefab instance, resulting in loss of in-scene links if any objects were pointing at the prefab instance.
82 |
83 | *Code cleanups. Also look for TODO/FIXME tags in the code.
84 |
85 | *Probably more.
86 |
87 | Good luck, and thanks for trying it out!
88 |
89 | ----About UnityTextScene
90 | UnityTextScene was an internal project at serious games studio TerraVision (http://www.terravision.no), which was decided to be shared with the Unity community, for further development.
91 |
92 |
--------------------------------------------------------------------------------
/Assets/Scripts/General/Helper.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Text;
10 |
11 | public class Helper
12 | {
13 | public static float SolveQuadricGetMax(float a, float b, float c)
14 | {
15 | float x1 = 0.0f;
16 | float x2 = 0.0f;
17 |
18 | SolveQuadric(a, b, c, ref x1, ref x2);
19 |
20 | return Mathf.Max(x1, x2);
21 | }
22 |
23 | public static int SolveQuadric(float a, float b, float c, ref float x1, ref float x2)
24 | {
25 | float discriminant = (b * b) - 4 * a * c;
26 |
27 | float denom = 2 * a;
28 |
29 | if (Mathf.Abs(discriminant) < 0.0001f)
30 | {
31 | x1 = -b / denom;
32 | x2 = x1;
33 | return 1;
34 | }
35 | else if (discriminant > 0.0f)
36 | {
37 | float sq = Mathf.Sqrt(discriminant);
38 |
39 | x1 = (-b + sq) / denom;
40 | x2 = (-b - sq) / denom;
41 |
42 | return 2;
43 | }
44 | else
45 | return 0;
46 |
47 | }
48 |
49 | public static T GetObjectOfType() where T : Component
50 | {
51 | UnityEngine.Object[] comps = GameObject.FindObjectsOfType(typeof(T));
52 |
53 | if (comps == null || comps.Length == 0)
54 | return default(T);
55 | else
56 | return comps[0] as T;
57 | }
58 |
59 | public static T GetObjectOfTypeByName(string name) where T : Component
60 | {
61 | UnityEngine.Object[] comps = GameObject.FindObjectsOfType(typeof(T));
62 |
63 | if (comps == null || comps.Length == 0)
64 | return default(T);
65 | else
66 | {
67 | for (int i = 0; i < comps.Length; i++)
68 | {
69 | if (comps[i].name == name)
70 | return comps[i] as T;
71 | }
72 | }
73 |
74 | return default(T);
75 | }
76 |
77 | public static T[] GetObjectsOfType() where T : Component
78 | {
79 | UnityEngine.Object[] comps = GameObject.FindObjectsOfType(typeof(T));
80 |
81 | if (comps == null || comps.Length == 0)
82 | return new T[0];
83 | else
84 | {
85 | T[] result = new T[comps.Length];
86 |
87 | for (int i = 0; i < comps.Length; i++)
88 | result[i] = comps[i] as T;
89 |
90 | return result;
91 | }
92 | }
93 |
94 | public static GameObject[] GetGameObjectsByName(string name)
95 | {
96 | List transforms = new List(GameObject.FindObjectsOfType(typeof(Transform)));
97 |
98 | return transforms.FindAll(t => t.name == name).ConvertAll(tr => (tr as Transform).gameObject).ToArray();
99 | }
100 |
101 | public static T[] GetGameObjectsByTypeAndName(string name) where T : Component
102 | {
103 | UnityEngine.Object[] comps = GameObject.FindObjectsOfType(typeof(T));
104 |
105 | if (comps == null || comps.Length == 0)
106 | return new T[0];
107 | else
108 | {
109 | List filtered = new List();
110 |
111 | for (int i = 0; i < comps.Length; i++)
112 | {
113 | if (comps[i].name == name)
114 | filtered.Add(comps[i] as T);
115 | }
116 |
117 | return filtered.ToArray();
118 | }
119 | }
120 |
121 | public static T AddComponent(GameObject go) where T : Component
122 | {
123 | if (go == null)
124 | return null;
125 |
126 | T comp = go.GetComponent(typeof(T)) as T;
127 |
128 | if (comp == null)
129 | comp = go.AddComponent(typeof(T)) as T;
130 |
131 | return comp;
132 | }
133 |
134 | public static T[] AddComponent(GameObject[] gameObjects) where T : Component
135 | {
136 | if (gameObjects == null)
137 | return new T[0];
138 |
139 | List added = new List();
140 |
141 | for (int i = 0; i < gameObjects.Length; i++)
142 | {
143 | T comp = gameObjects[i].AddComponent(typeof(T)) as T;
144 |
145 | added.Add(comp);
146 | }
147 |
148 | return added.ToArray();
149 | }
150 |
151 | public static void AddComponent(GameObject[] gameObjects, Type component)
152 | {
153 | if (gameObjects == null)
154 | return;
155 |
156 | for (int i = 0; i < gameObjects.Length; i++)
157 | gameObjects[i].AddComponent(component);
158 | }
159 |
160 | public static void AddComponent(T[] gameObjects) where T : Component where R : Component
161 | {
162 | if (gameObjects == null)
163 | return;
164 |
165 | for (int i = 0; i < gameObjects.Length; i++)
166 | gameObjects[i].gameObject.AddComponent(typeof(R));
167 | }
168 |
169 | public static T[] AddComponentToType(GameObject go) where T : Component where R : Component
170 | {
171 | if (go == null)
172 | return new T[0];
173 |
174 | List added = new List();
175 |
176 | R[] comp = Helper.GetComponentsInChildren(go);
177 |
178 | for(int i = 0; i < comp.Length; i++)
179 | {
180 | added.Add(comp[i].gameObject.AddComponent(typeof(T)) as T);
181 | }
182 |
183 | return added.ToArray();
184 | }
185 |
186 | ///
187 | /// Returns all components in a hierarchy until it hits the specified type.
188 | ///
189 | public static Component[] GetComponentsInChildrenAboveType(GameObject go) where T : Component
190 | {
191 | if (go.GetComponent() != null)
192 | return new Component[0];
193 |
194 |
195 | List components = new List();
196 |
197 | foreach (Transform child in go.transform)
198 | {
199 | components.AddRange(GetComponentsInChildrenAboveType(child.gameObject));
200 | }
201 |
202 | components.AddRange(go.GetComponents());
203 |
204 | return components.ToArray();
205 | }
206 |
207 | ///
208 | /// Returns components recursively "layers" deep. If you have a hierarchy
209 | /// of three parented colliders, and send in "2" for layers, you will get
210 | /// the two first components, but not the third.
211 | ///
212 | public static T[] GetComponentsInChildren(GameObject go, int layers) where T : Component
213 | {
214 | if (layers == 0)
215 | return new T[0];
216 |
217 | List components = new List();
218 |
219 | T[] layerComps = go.GetComponents();
220 |
221 | if (layerComps.Length > 0)
222 | {
223 | components.AddRange(layerComps);
224 | layers--;
225 | }
226 |
227 | foreach (Transform child in go.transform)
228 | {
229 | components.AddRange(GetComponentsInChildren(child.gameObject, layers));
230 | }
231 |
232 | return components.ToArray();
233 | }
234 |
235 | public static T GetComponentInChildren(GameObject go) where T : Component
236 | {
237 | return go.GetComponentInChildren(typeof(T)) as T;
238 | }
239 |
240 | public static T[] GetComponentsInChildren(GameObject go) where T : Component
241 | {
242 | if (go == null)
243 | return new T[0];
244 |
245 | Component[] comps = go.GetComponentsInChildren(typeof(T));
246 |
247 | if (comps == null || comps.Length == 0)
248 | return new T[0];
249 |
250 |
251 | T[] result = new T[comps.Length];
252 |
253 | for (int i = 0; i < comps.Length; i++)
254 | result[i] = comps[i] as T;
255 |
256 |
257 | //WTF FIXME: What's wrong with this one?
258 | //result = System.Array.ConvertAll(comps, comp => (T)comp);
259 |
260 | return result;
261 | }
262 |
263 | public static void SetLayerRecursively(GameObject go, int layer)
264 | {
265 | go.layer = layer;
266 |
267 | foreach(Transform child in go.transform)
268 | {
269 | SetLayerRecursively(child.gameObject, layer);
270 | }
271 | }
272 |
273 |
274 | public static void DestroyComponentsInChildren(GameObject go, bool immediate)
275 | {
276 | Component[] comps = go.GetComponentsInChildren(typeof(T));
277 |
278 | for (int i = 0; i < comps.Length; i++)
279 | {
280 | if (immediate)
281 | GameObject.DestroyImmediate(comps[i]);
282 | else
283 | GameObject.Destroy(comps[i]);
284 | }
285 | }
286 |
287 | public static void Destroy(GameObject[] gos)
288 | {
289 | if (gos == null)
290 | return;
291 |
292 | for (int i = 0; i < gos.Length; i++)
293 | {
294 | GameObject.Destroy(gos[i]);
295 | }
296 | }
297 |
298 | public static Bounds CalculateBounds(GameObject go)
299 | {
300 | Bounds b = new Bounds();
301 |
302 | Renderer[] renderers = Helper.GetComponentsInChildren(go);
303 |
304 | for (int i = 0; i < renderers.Length; i++)
305 | {
306 | if (i == 0)
307 | b = renderers[i].bounds;
308 | else
309 | b.Encapsulate(renderers[i].bounds);
310 | }
311 |
312 | return b;
313 | }
314 |
315 | public static T AddComponentToNamedObject(string name) where T : Component
316 | {
317 | GameObject go = GameObject.Find(name);
318 |
319 | if (go == null)
320 | return default(T);
321 |
322 | return go.AddComponent(typeof(T)) as T;
323 | }
324 |
325 | public static T[] AddComponentToNamedObjects(string name) where T : Component
326 | {
327 | UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(Transform));
328 |
329 | List filtered = new List();
330 |
331 | for (int i = 0; i < objects.Length; i++)
332 | {
333 | if (objects[i].name.Equals(name))
334 | filtered.Add((objects[i] as Transform).gameObject.AddComponent(typeof(T)) as T);
335 | }
336 |
337 | return filtered.ToArray();
338 | }
339 |
340 | public static T[] AddComponentToPartiallyNamedObjects(string partialName) where T : Component
341 | {
342 | UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(Transform));
343 |
344 | List filtered = new List();
345 |
346 | for (int i = 0; i < objects.Length; i++)
347 | {
348 | if (objects[i].name.Contains(partialName))
349 | filtered.Add((objects[i] as Transform).gameObject.AddComponent(typeof(T)) as T);
350 | }
351 |
352 | return filtered.ToArray();
353 | }
354 |
355 | public static T CreateObject(Vector3 position, Quaternion rotation, Vector3 scale) where T : Component
356 | {
357 | T instance = CreateObject();
358 | instance.transform.position = position;
359 | instance.transform.rotation = rotation;
360 | instance.transform.localScale = scale;
361 |
362 | return instance;
363 | }
364 |
365 | public static T CreateObject() where T : Component
366 | {
367 | GameObject go = new GameObject();
368 | go.name = typeof(T).ToString();
369 | return go.AddComponent(typeof(T)) as T;
370 | }
371 |
372 | public static GameObject[] FindObjectsWithPartialName(string partialName)
373 | {
374 | UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(Transform));
375 |
376 | List filtered = new List();
377 |
378 | for (int i = 0; i < objects.Length; i++)
379 | {
380 | if (objects[i].name.Contains(partialName))
381 | filtered.Add((objects[i] as Transform).gameObject);
382 | }
383 |
384 | return filtered.ToArray();
385 | }
386 |
387 | public static void DisableScriptsInScene() where T : MonoBehaviour
388 | {
389 | T[] comps = Helper.GetObjectsOfType();
390 |
391 | for(int i = 0; i < comps.Length; i++)
392 | comps[i].enabled = false;
393 | }
394 |
395 | public static GameObject FindOrFail(string name)
396 | {
397 | GameObject go = GameObject.Find(name);
398 |
399 | if (go == null)
400 | {
401 | Debug.LogError("Unable to find object: " + name);
402 | Debug.Break();
403 | }
404 |
405 | return go;
406 | }
407 |
408 | public static GameObject FindInChildren(GameObject go, string name)
409 | {
410 | foreach (Transform child in go.transform)
411 | {
412 | if (child.name == name)
413 | return child.gameObject;
414 |
415 | GameObject ret = FindInChildren(child.gameObject, name);
416 |
417 | if (ret != null)
418 | return ret;
419 | }
420 |
421 | return null;
422 | }
423 |
424 | public static string GetFullName(GameObject go)
425 | {
426 |
427 |
428 | Transform current = go.transform;
429 |
430 | List parentList = new List();
431 |
432 | while(current != null)
433 | {
434 | parentList.Add(current);
435 |
436 | current = current.parent;
437 | }
438 |
439 | parentList.Reverse();
440 |
441 | StringBuilder sb = new StringBuilder();
442 |
443 | foreach(Transform t in parentList)
444 | {
445 | sb.Append('/');
446 | sb.Append(t.name);
447 | }
448 |
449 | return sb.ToString();
450 | }
451 |
452 | public static GameObject[] FindGameObjectsFromFullName(string fullName)
453 | {
454 | Transform[] transforms = Helper.GetObjectsOfType();
455 |
456 |
457 | List matched = new List();
458 |
459 | foreach(Transform t in transforms)
460 | {
461 | if (t.parent == null)
462 | {
463 | //Debug.Log("Finding full: " + fullName);
464 | FindMatching(t, matched, fullName);
465 | }
466 | }
467 |
468 | return matched.ToArray();
469 | }
470 |
471 | private static void FindMatching(Transform t, List matched, string remaining)
472 | {
473 | string currentLevelName = remaining.Substring(1);
474 |
475 | int separatorIndex = currentLevelName.IndexOf('/');
476 |
477 | if (separatorIndex > 0)
478 | remaining = currentLevelName.Substring(separatorIndex);
479 | else
480 | remaining = "";
481 |
482 | if (separatorIndex > 0)
483 | currentLevelName = currentLevelName.Substring(0, separatorIndex);
484 |
485 | //Debug.Log("matching object: '" + currentLevelName + "' (" + remaining + ")");
486 |
487 | if (t.name == currentLevelName)
488 | {
489 | if (remaining.Length == 0)
490 | {
491 | //Debug.Log("MATCH");
492 | matched.Add(t.gameObject);
493 | }
494 | else
495 | {
496 | foreach(Transform child in t)
497 | {
498 | FindMatching(child, matched, remaining);
499 | }
500 | }
501 | }
502 | }
503 | }
504 |
505 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextScene.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// TODO: *Create function to validate all scenes in the build settings and opt the user to create
6 | /// temp scenes if they do not exist (typically fresh checkout where all text scenes are
7 | /// present, but no binary temp version - this will make the player fail in editor mode if
8 | /// the game needs to change level).
9 |
10 | using UnityEditor;
11 | using System.IO;
12 | using System.Collections.Generic;
13 | using UnityEngine;
14 | using System;
15 |
16 |
17 | ///
18 | /// Utility class currently handling custom build settings and a few
19 | /// functions for getting matching text/binary files based on path.
20 | ///
21 | public static class TextScene
22 | {
23 | private const string buildSettingsFile = "Library/TextSceneBuildSettings.txt";
24 |
25 | ///
26 | /// Returns the time the build settings file was last written to.
27 | ///
28 | public static DateTime BuildSettingsDate()
29 | {
30 | string fullPath = EditorHelper.GetProjectFolder() + buildSettingsFile;
31 |
32 | if (!File.Exists(fullPath))
33 | return new DateTime(1, 1, 1);
34 |
35 | return File.GetLastWriteTime(fullPath);
36 | }
37 |
38 | ///
39 | /// Takes a project-relatived temp-scene path and
40 | /// converts it to the matching TextScene path.
41 | ///
42 | public static string TempToTextSceneFile(string tempScene)
43 | {
44 | string textSceneFile = "Assets" + tempScene.Substring(tempScene.IndexOf('/'));
45 |
46 | textSceneFile = textSceneFile.Replace(".unity", ".txt");
47 |
48 | return textSceneFile;
49 | }
50 |
51 | ///
52 | /// Takes a project-relative TextScene filename and
53 | /// converts it to the matching binary temp file.
54 | ///
55 | public static string TextSceneToTempBinaryFile(string textScene)
56 | {
57 | string tempSceneFile = "TempScenes" + textScene.Substring(textScene.IndexOf('/'));
58 |
59 | tempSceneFile = tempSceneFile.Replace(".txt", ".unity");
60 |
61 | return tempSceneFile;
62 | }
63 |
64 | ///
65 | /// Reads in the current list of scenes registered in the custom
66 | /// TextScene buildsettings.
67 | ///
68 | public static List ReadScenes()
69 | {
70 | List sceneList = new List();
71 |
72 | string fullPath = EditorHelper.GetProjectFolder() + buildSettingsFile;
73 |
74 | if (!File.Exists(fullPath))
75 | return sceneList;
76 |
77 | StreamReader reader = File.OpenText(fullPath);
78 |
79 | while(!reader.EndOfStream)
80 | {
81 | string line = reader.ReadLine();
82 |
83 | string[] elements = line.Trim().Split();
84 |
85 | string key = "";
86 | string val = "";
87 |
88 | if (elements.Length >= 2)
89 | {
90 | key = elements[0];
91 | val = elements[1];
92 | }
93 |
94 | if (key == "scene")
95 | sceneList.Add(val);
96 | }
97 |
98 | reader.Close();
99 |
100 | return sceneList;
101 | }
102 |
103 | ///
104 | /// Writes a list of project-relative TextScene files to buildsettings. The
105 | /// corresponding binary unity scenes are stored in the Unity standard
106 | /// build-settings.
107 | ///
108 | private static void WriteBuildSettings(List sceneList)
109 | {
110 | StreamWriter writer = File.CreateText(EditorHelper.GetProjectFolder() + buildSettingsFile);
111 |
112 | foreach(string scene in sceneList)
113 | {
114 | writer.Write("scene " + scene + '\n');
115 | }
116 |
117 | writer.Close();
118 |
119 | //Also update the editor build settings
120 | List binaryScenes = new List();
121 |
122 | foreach(string scene in sceneList)
123 | {
124 | EditorBuildSettingsScene ebss = new EditorBuildSettingsScene();
125 |
126 | ebss.path = TextScene.TextSceneToTempBinaryFile(scene);
127 | ebss.enabled = true;
128 |
129 | binaryScenes.Add(ebss);
130 | }
131 |
132 | EditorBuildSettings.scenes = binaryScenes.ToArray();
133 | }
134 |
135 | ///
136 | /// Adds a project-relative TextScene to the TextScene buildsettings
137 | /// and the corresponding binary temp file is written to the standard
138 | /// Unity build settings.
139 | ///
140 | public static bool AddSceneToBuild(string scene)
141 | {
142 | string projectFolder = EditorHelper.GetProjectFolder();
143 |
144 | string fullScenePath = projectFolder + scene;
145 |
146 | //Make sure the scene file actually exists
147 | if (!File.Exists(fullScenePath))
148 | {
149 | EditorUtility.DisplayDialog("ERROR", "The text scene '" + scene + "' does not seem to exist!", "OK");
150 | return false;
151 | }
152 |
153 | List sceneList = ReadScenes();
154 |
155 | if (sceneList.Contains(scene))
156 | {
157 | EditorUtility.DisplayDialog("Already added", "This scene is already in the build list", "OK");
158 | return false;
159 | }
160 |
161 | Debug.Log("Added scene to build: " + scene);
162 |
163 | sceneList.Add(scene);
164 |
165 | WriteBuildSettings(sceneList);
166 |
167 | return true;
168 | }
169 |
170 | ///
171 | /// Removes a project-relative TextScene from the TextScene build-settings.
172 | /// Also updates the standard Unity build settings.
173 | ///
174 | public static void RemoveSceneFromBuild (string scene)
175 | {
176 | List sceneList = ReadScenes();
177 |
178 | sceneList.Remove(scene);
179 |
180 | WriteBuildSettings(sceneList);
181 | }
182 |
183 | ///
184 | /// Moves a scene up or down in the build settings list.
185 | ///
186 | public static void MoveScenePosition (string scene, int direction)
187 | {
188 | if (direction == 0)
189 | return;
190 |
191 | List sceneList = ReadScenes();
192 |
193 | int index = sceneList.IndexOf(scene);
194 |
195 | if (index < 0)
196 | return;
197 |
198 | if (direction > 0)
199 | {
200 | if (index+1 >= sceneList.Count)
201 | return;
202 |
203 | string nextEntry = sceneList[index+1];
204 |
205 | sceneList[index] = nextEntry;
206 | sceneList[index+1] = scene;
207 | }
208 | else if (direction < 0)
209 | {
210 | if (index-1 < 0)
211 | return;
212 |
213 | string prevEntry = sceneList[index-1];
214 |
215 | sceneList[index] = prevEntry;
216 | sceneList[index-1] = scene;
217 | }
218 |
219 |
220 | WriteBuildSettings(sceneList);
221 | }
222 |
223 | ///
224 | /// Adds the currently open scene to the TextScene buildsettings, and updates
225 | /// the standard Unity buildsettings with the corresponding temp binary scene file.
226 | /// User will be notified if the current scene is not saved or is not a temp binary
227 | /// file.
228 | ///
229 | public static void AddCurrentSceneToBuild()
230 | {
231 | string currentScene = EditorApplication.currentScene;
232 |
233 | if (currentScene.Length == 0)
234 | {
235 | EditorUtility.DisplayDialog("Unsaved", "The currently open scene is not saved. Please save it using the TextScene menu option and try again.", "OK");
236 | return;
237 | }
238 |
239 | if (currentScene.StartsWith("Assets/"))
240 | {
241 | EditorUtility.DisplayDialog("Invalid scene", "The currently open scene is not a TextScene. Please re-save using the TextScene menu options and try again", "OK");
242 | return;
243 | }
244 |
245 | //Debug.Log("Text scene file to save in build settings: " + textSceneFile);
246 |
247 | TextScene.AddSceneToBuild(TextScene.TempToTextSceneFile(EditorApplication.currentScene));
248 | }
249 |
250 | ///
251 | /// Adds the selected TextScene file to buildsettings. Also updates the standard Unity
252 | /// buildsettings.
253 | ///
254 | public static void AddSelectedSceneToBuild()
255 | {
256 | if (Selection.activeObject == null)
257 | {
258 | EditorUtility.DisplayDialog ("Nothing selected", "You need to select a text scene asset to add to build", "OK");
259 | return;
260 | }
261 |
262 | TextAsset asset = Selection.activeObject as TextAsset;
263 |
264 | string assetPath = "";
265 |
266 | if (asset != null)
267 | assetPath = AssetDatabase.GetAssetPath(asset);
268 |
269 | if (!assetPath.EndsWith(".txt"))
270 | EditorUtility.DisplayDialog ("Not a text file", "Text scenes can be TextAssets (*.txt)", "OK");
271 | else
272 | {
273 | TextScene.AddSceneToBuild(assetPath);
274 | }
275 | }
276 |
277 | ///
278 | /// Read and write scenes so unity build settings gets updated.
279 | ///
280 | private static void SyncBuildSettings()
281 | {
282 | WriteBuildSettings(ReadScenes());
283 | }
284 |
285 | //TODO: Finish up and make public!
286 | public static bool ValidateBuildSettings(out List invalidScenes)
287 | {
288 | SyncBuildSettings();
289 |
290 | List sceneList = ReadScenes();
291 |
292 | invalidScenes = new List();
293 |
294 | foreach (string scene in sceneList)
295 | {
296 | string absoluteTextScene = EditorHelper.GetProjectFolder() + scene;
297 |
298 | //Make sure the scene exists at all in a TextScene format
299 | if (!File.Exists(absoluteTextScene))
300 | {
301 | Debug.LogWarning("Scene does not exist: " + scene);
302 |
303 | EditorApplication.isPlaying = false;
304 |
305 | if (EditorUtility.DisplayDialog("Invalid scene", "The scene '" + scene + "' is listed in your build settings but it does not exist. Do you want to remove it from build settings?", "Yes", "No"))
306 | {
307 | TextScene.RemoveSceneFromBuild(scene);
308 | }
309 | else
310 | {
311 | invalidScenes.Add(scene);
312 | continue;
313 | }
314 | }
315 | else
316 | {
317 | //While the textscene might be present, we also need the binary temp file.
318 | //Make sure there is one up-to-date, if not, the user should be prompted
319 | //to generate one.
320 | string absoluteBinaryTempScene = EditorHelper.GetProjectFolder() + TextSceneToTempBinaryFile(scene);
321 |
322 | if (!File.Exists(absoluteBinaryTempScene))
323 | {
324 | Debug.LogWarning("Temp scene does not exist: " + absoluteBinaryTempScene);
325 |
326 | //EditorApplication.isPlaying = false;
327 | //if (EditorUtility.DisplayDialog("Missing temp file", "Missing temp file for '" + scene + "' - do you want to generate it now?", "Yes", "No"))
328 | // TextSceneDeserializer.LoadSafe(absoluteTextScene);
329 |
330 | invalidScenes.Add(scene);
331 | continue;
332 | }
333 | else
334 | {
335 | //Both files exist, but we also need to make sure the temp scene isn't outdated.
336 | DateTime textSceneTime = File.GetLastWriteTime(absoluteTextScene);
337 | DateTime binaryTempSceneTime = File.GetLastWriteTime(absoluteBinaryTempScene);
338 |
339 | if (textSceneTime > binaryTempSceneTime)
340 | {
341 | Debug.LogWarning("Temp scene for '" + scene + "' is outdated: " + binaryTempSceneTime + " is older than " + textSceneTime);
342 |
343 | //EditorApplication.isPlaying = false;
344 | //if (EditorUtility.DisplayDialog("Outdated temp file", "Outdated temp file for '" + scene + "' - do you want to update it now?", "Yes", "No"))
345 | // TextSceneDeserializer.LoadSafe(absoluteTextScene);
346 |
347 | invalidScenes.Add(scene);
348 | continue;
349 | }
350 | }
351 | }
352 | }
353 |
354 | return invalidScenes.Count == 0;
355 | }
356 |
357 | ///
358 | /// Builds a self-launching player based on the passed BuildTarget parameter. Will go through all
359 | /// scenes in the buildsettings list and make sure they have a temporary binary file to build. Will
360 | /// notify user if anything went wrong (such as empty build settings or unsupported build target).
361 | ///
362 | public static void Build(BuildTarget buildTarget)
363 | {
364 | string extension = "";
365 |
366 | if (buildTarget == BuildTarget.WebPlayer
367 | || buildTarget == BuildTarget.WebPlayerStreamed)
368 | extension = "unity3d";
369 | else if (buildTarget == BuildTarget.StandaloneWindows)
370 | extension = "exe";
371 | else if (buildTarget == BuildTarget.StandaloneOSXUniversal)
372 | extension = "app";
373 |
374 | if (extension.Length == 0)
375 | {
376 | EditorUtility.DisplayDialog("Build target not supported", "Build target not currently supported: " + buildTarget.ToString(), "OK");
377 | return;
378 | }
379 |
380 | string startPath = Application.dataPath;
381 |
382 | startPath = startPath.Substring(0, startPath.LastIndexOf('/')) + "/Build";
383 |
384 | if (!Directory.Exists(startPath))
385 | Directory.CreateDirectory(startPath);
386 |
387 | string path = EditorUtility.SaveFilePanel("Build target", startPath, "build", extension);
388 |
389 | if (path.Length == 0)
390 | return;
391 |
392 | new TextSceneBuilder(path, buildTarget);
393 | }
394 |
395 | public static void BuildTempScenes(List scenes)
396 | {
397 | new TextSceneBuilder(null, BuildTarget.PlayerDataFolderForDevelopment, scenes);
398 | }
399 |
400 | public static void BuildTempScenes()
401 | {
402 | //TODO: Get rid of unused parameters in constructor.
403 | new TextSceneBuilder(null, BuildTarget.PlayerDataFolderForDevelopment);
404 | }
405 | }
406 |
407 | class TextSceneBuilder
408 | {
409 | private List binarySceneList = new List();
410 | private Queue sceneQueue;
411 | private string sceneToLoad;
412 | private string buildPath;
413 | private BuildTarget buildTarget;
414 |
415 | public TextSceneBuilder(string buildPath, BuildTarget buildTarget) : this(buildPath, buildTarget, null)
416 | {
417 |
418 | }
419 |
420 | public TextSceneBuilder(string buildPath, BuildTarget buildTarget, List sceneList)
421 | {
422 | if (sceneList == null)
423 | sceneList = TextScene.ReadScenes();
424 |
425 | if (sceneList.Count == 0)
426 | {
427 | EditorUtility.DisplayDialog("No scenes", "No scenes have been added to the build settings. Please add some, and try to build again.", "OK");
428 | return;
429 | }
430 |
431 | this.sceneQueue = new Queue(sceneList);
432 | this.sceneToLoad = TextSceneMonitor.Instance.GetCurrentScene();
433 | this.buildPath = buildPath;
434 | this.buildTarget = buildTarget;
435 |
436 | if (EditorApplication.SaveCurrentSceneIfUserWantsTo())
437 | {
438 | TextSceneMonitor.Instance.SaveIfTempIsNewer();
439 | }
440 | else
441 | {
442 | Debug.Log("Cancelled build");
443 | return;
444 | }
445 |
446 | BuildNext();
447 | }
448 |
449 | private void BuildNext()
450 | {
451 | //TODO: Must be fixed up to work with callback - scene saving does not work very well if done
452 | // immediately after load, which is why we need to make this into a async operation
453 | // of some sort. See unity case 336621.
454 | //Run through all scenes and build them based on the human readable representation.
455 | if (sceneQueue.Count > 0)
456 | {
457 | string textScene = sceneQueue.Dequeue();
458 |
459 | Debug.Log("Building temp for: " + textScene);
460 |
461 | string result = TextSceneDeserializer.Load(EditorHelper.GetProjectFolder() + textScene, this.BuildNext);
462 |
463 | if (result.Length == 0)
464 | {
465 | EditorUtility.DisplayDialog("Scene does not exist", "Unable to find scene file: " + textScene + " Will be excluded from build", "OK");
466 | BuildNext();
467 | }
468 | else
469 | binarySceneList.Add(result);
470 | }
471 | else
472 | FinishBuild();
473 | }
474 |
475 | private void FinishBuild()
476 | {
477 | Debug.Log("Building " + binarySceneList.Count + " scenes: ");
478 |
479 | foreach(string scene in binarySceneList)
480 | {
481 | Debug.Log(scene);
482 | }
483 |
484 | if (buildPath != null)
485 | {
486 | BuildPipeline.BuildPlayer(binarySceneList.ToArray(), buildPath, buildTarget, BuildOptions.AutoRunPlayer);
487 | }
488 | else
489 | {
490 | Debug.Log("Temp scenes generated, no player will be built.");
491 | }
492 |
493 | if (sceneToLoad.Length > 0)
494 | TextSceneDeserializer.Load(sceneToLoad);
495 | }
496 | }
497 |
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneMonitor.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 |
6 | using UnityEngine;
7 | using UnityEditor;
8 | using System;
9 | using System.IO;
10 | using System.Timers;
11 | using System.Collections.Generic;
12 | using System.Text;
13 |
14 | ///
15 | /// Class that saves and loads a binary temp file for the TextScene system. It delays each
16 | /// operation by a set amount of frames because it seems to make the operation a lot
17 | /// more stable :S
18 | ///
19 | class TextSceneTempCreator
20 | {
21 | public enum Status
22 | {
23 | Working = 0,
24 | Complete,
25 | Failed
26 | }
27 |
28 | enum State
29 | {
30 | SaveTemp = 0,
31 | CreateNew,
32 | LoadTemp
33 | }
34 |
35 |
36 | private const float SAVE_AND_RELOAD_FRAMES = 20.0f;
37 |
38 | //HACK: To get around bug with save/load instantly after instantiating prefabs.
39 | private int saveAndReloadTimer = 0;
40 | private string saveAndReload = "";
41 | private TextSceneDeserializer.TempSceneSaved saveAndReloadCallback = null;
42 |
43 | State state;
44 |
45 | public TextSceneTempCreator(string scene, TextSceneDeserializer.TempSceneSaved callback)
46 | {
47 | state = State.SaveTemp;
48 |
49 | saveAndReloadTimer = Mathf.RoundToInt(SAVE_AND_RELOAD_FRAMES);
50 | saveAndReload = scene;
51 | saveAndReloadCallback = callback;
52 | }
53 |
54 | public Status Update()
55 | {
56 | if (saveAndReload.Length == 0)
57 | {
58 | Debug.LogError("Invalid saveandreload name! Cancelling load/save process...");
59 | return Status.Failed;
60 | }
61 |
62 | if (saveAndReloadTimer > 0)
63 | {
64 | EditorUtility.DisplayProgressBar("Creating temp...", "Creating binary temp file for TextScene: " + state.ToString(), 1.0f - saveAndReloadTimer / SAVE_AND_RELOAD_FRAMES);
65 |
66 | saveAndReloadTimer--;
67 | return Status.Working;
68 | }
69 | else
70 | {
71 | if (state == State.SaveTemp)
72 | {
73 | Debug.Log("SaveAndReload: " + saveAndReload);
74 |
75 | ///FIXME: Unity sometimes puts a lock on the scenes we try to save, this is a CRUEL way to
76 | ///get around it.
77 | ///
78 | ///Repro-steps: *Comment out the try/catch
79 | /// *Clean out tempscenes-folder.
80 | /// *Open up a scene (LEVEL1) from build settings, hit play
81 | /// *While playing, do something to make the game
82 | /// change to another level (LEVEL2).
83 | /// *Stop playing, you should now be back in the level where you
84 | /// hit play from.
85 | /// *Try to switch to the level you switched to in-game (LEVEL2).
86 | /// *You should, after the progress bar has completed, be prompted
87 | /// with an error saying Unity could not move file from Temp/Tempfile
88 | ///
89 | try
90 | {
91 | FileStream f = File.OpenWrite(saveAndReload);
92 | f.Close();
93 | }
94 | catch
95 | {
96 | Debug.LogWarning("HACK: Getting around 'access denied' on temp files!");
97 |
98 | //HACK: This seems to make Unity release the file so we can try to save it in a new go.
99 | if (!EditorApplication.OpenScene(saveAndReload))
100 | {
101 | //Uh oh.
102 | Debug.LogError("HACK failed! What to do next?");
103 | EditorUtility.ClearProgressBar();
104 | return Status.Failed;
105 | }
106 |
107 | TextSceneDeserializer.Load(EditorHelper.GetProjectFolder() + TextScene.TempToTextSceneFile(EditorApplication.currentScene), saveAndReloadCallback);
108 | return Status.Working;
109 | }
110 |
111 | if (!EditorApplication.SaveScene(saveAndReload))
112 | {
113 | Debug.LogError("Failed to save temp: " + saveAndReload);
114 |
115 |
116 | if (EditorUtility.DisplayDialog("ERROR", "Failed to save temp (" + saveAndReload + ") - try again?", "Yes", "No"))
117 | saveAndReloadTimer = Mathf.RoundToInt(SAVE_AND_RELOAD_FRAMES);
118 | else
119 | return Status.Failed;
120 |
121 |
122 | EditorUtility.ClearProgressBar();
123 | return Status.Working;
124 | }
125 |
126 | state = State.CreateNew;
127 | saveAndReloadTimer = Mathf.RoundToInt(SAVE_AND_RELOAD_FRAMES);
128 | return Status.Working;
129 | }
130 | else if (state == State.CreateNew)
131 | {
132 | EditorApplication.NewScene();
133 |
134 | state = State.LoadTemp;
135 | saveAndReloadTimer = Mathf.RoundToInt(SAVE_AND_RELOAD_FRAMES);
136 | return Status.Working;
137 | }
138 | else if (state == State.LoadTemp)
139 | {
140 | if (!EditorApplication.OpenScene(saveAndReload))
141 | {
142 | Debug.LogError("Failed to load temp: " + saveAndReload);
143 |
144 | if (EditorUtility.DisplayDialog("ERROR", "Failed to load temp (" + saveAndReload + ") - try again?", "Yes", "No"))
145 | saveAndReloadTimer = Mathf.RoundToInt(SAVE_AND_RELOAD_FRAMES);
146 | else
147 | return Status.Failed;
148 |
149 | EditorUtility.ClearProgressBar();
150 | return Status.Working;
151 | }
152 |
153 | string writtenFile = EditorHelper.GetProjectFolder() + EditorApplication.currentScene;
154 |
155 | DateTime writtenTime = File.GetLastWriteTime(writtenFile);
156 |
157 | Debug.Log("Wrote temp file at " + writtenTime);
158 |
159 | TextSceneMonitor.Instance.SetCurrentScene(EditorHelper.GetProjectFolder() + TextScene.TempToTextSceneFile(EditorApplication.currentScene));
160 |
161 | saveAndReload = "";
162 |
163 | EditorUtility.ClearProgressBar();
164 |
165 | return Status.Complete;
166 | }
167 | }
168 |
169 | Debug.LogError("Failing....");
170 | return Status.Failed;
171 | }
172 |
173 | public void InvokeCallback()
174 | {
175 | if (saveAndReloadCallback != null)
176 | saveAndReloadCallback();
177 | }
178 | }
179 |
180 | ///
181 | /// Class monitoring the state of TextScenes and user action. Tries to identify situations where
182 | /// the user does something he/she didn't want to, such as saving using built-in save functionality,
183 | /// using regular binary unity scenes together with TextScenes etc. Also checks for externally changed
184 | /// files, which is handy when using VCS.
185 | ///
186 | /// FIXME: This class could have been a lot cleaner if there were hooks for the most common user actions
187 | /// and editor events.
188 | ///
189 | [Serializable]
190 | public class TextSceneMonitor
191 | {
192 | TextSceneTempCreator process;
193 |
194 | private static TextSceneMonitor instance;
195 | //private static System.Timers.Timer timer;
196 |
197 | public static TextSceneMonitor Instance
198 | {
199 | get
200 | {
201 | if (instance == null)
202 | {
203 | instance = new TextSceneMonitor();
204 | /*
205 | timer = new System.Timers.Timer(1000);
206 |
207 | timer.Elapsed += MonitorUpdate;
208 | timer.AutoReset = true;
209 | timer.Enabled = true;
210 | */
211 | //FIXME: These seem to get lost during play sessions?
212 | EditorApplication.update += MonitorUpdate;
213 | EditorApplication.playmodeStateChanged += MonitorStateChange;
214 |
215 |
216 | }
217 |
218 | return instance;
219 | }
220 | }
221 |
222 | private static void MonitorStateChange()
223 | {
224 | if (EditorApplication.isPlayingOrWillChangePlaymode)
225 | {
226 | List invalidScenes;
227 |
228 | if (!TextScene.ValidateBuildSettings(out invalidScenes))
229 | {
230 | EditorApplication.isPlaying = false;
231 |
232 |
233 | StringBuilder sb = new StringBuilder();
234 |
235 | sb.Append("Errors were found while validating Build Settings: \n");
236 |
237 | foreach(string scene in invalidScenes)
238 | {
239 | sb.Append(" ");
240 | sb.Append(scene);
241 | sb.Append('\n');
242 | }
243 |
244 | sb.Append("Your levels may not switch correctly in play-mode! Do you want to fix up any outdated/missing temp file issues now?");
245 |
246 | if (EditorUtility.DisplayDialog("Build settings", sb.ToString(), "Yes", "No"))
247 | TextScene.BuildTempScenes(invalidScenes);
248 | }
249 | }
250 | }
251 |
252 | /*
253 | private static void MonitorStateChange()
254 | {
255 | if (EditorApplication.isPlayingOrWillChangePlaymode)
256 | {
257 | Debug.Log("Editor about to start playmode: " + EditorApplication.timeSinceStartup.ToString("F3"));
258 |
259 | //HACK: We're creating this so it can tell us whenever we leave playmode :S
260 | GameObject go = new GameObject("TextSceneMonitorRelauncher");
261 | go.hideFlags = HideFlags.NotEditable;
262 | go.AddComponent();
263 | }
264 | else
265 | {
266 | Debug.Log("Editor not in playmode: " + EditorApplication.timeSinceStartup.ToString("F3"));
267 |
268 | TextSceneMonitorRelauncher[] objects = Helper.GetObjectsOfType();
269 |
270 | foreach(TextSceneMonitorRelauncher o in objects)
271 | GameObject.DestroyImmediate(o.gameObject);
272 | }
273 | }
274 | */
275 | /*
276 | public static void MonitorCallbacks(object sender, System.Timers.ElapsedEventArgs e)
277 | {
278 | Debug.Log("Setting editor callbacks");
279 |
280 | //EditorApplication.update += MonitorUpdate;
281 | EditorApplication.playmodeStateChanged += MonitorStateChange;
282 | }
283 | */
284 |
285 | ///
286 | /// Wrapper for running an update on the monitor.
287 | ///
288 | public static void MonitorUpdate()
289 | {
290 | Instance.Update();
291 | }
292 |
293 | private double nextCheckForChangedFile = 0.0f;
294 |
295 | //Absolute path of currently monitored scene, and when it was last loaded.
296 | private string currentScene = "";
297 | private string currentTempBinaryScene = "";
298 | private DateTime currentSceneLoaded;
299 |
300 | //Current scene open. If this changes unexpectedly, the user will be notified.
301 | private string alarmingEditorScene;
302 |
303 |
304 |
305 | ///
306 | /// Creates a new TextSceneMonitor and reads in relevant values from PlayerPrefs (such as
307 | /// last monitored scene).
308 | ///
309 | public TextSceneMonitor()
310 | {
311 | process = null;
312 |
313 | currentScene = PlayerPrefs.GetString("TextSceneMonitorCurrentScene", "");
314 |
315 | alarmingEditorScene = PlayerPrefs.GetString("TextSceneAlarmingEditorScene", "");
316 |
317 | currentTempBinaryScene = EditorHelper.GetProjectFolder() + alarmingEditorScene;
318 |
319 | //Use file timestamp instead of DateTime.Now for consistency reasons.
320 | if (File.Exists(currentTempBinaryScene))
321 | {
322 | currentSceneLoaded = File.GetLastWriteTime(currentTempBinaryScene);
323 | }
324 | else
325 | {
326 | Debug.LogWarning("Unable to find temp file: " + currentTempBinaryScene);
327 | currentSceneLoaded = DateTime.Now;
328 | }
329 |
330 | Debug.Log("Creating new TextSceneMonitor instance: " + currentScene);
331 | }
332 |
333 | ///
334 | /// Sets the current TextScene we will monitor for changes. Also stores the current
335 | /// scene Unity has open (the binary version) so we can detect if the user suddenly
336 | /// changes to either a new scene or a different binary scene without going via
337 | /// the TextScene functionality.
338 | ///
339 | ///
340 | /// A holding the full absolute path to the TextScene file.
341 | ///
342 | public void SetCurrentScene(string filename)
343 | {
344 | alarmingEditorScene = EditorApplication.currentScene;
345 | currentTempBinaryScene = EditorHelper.GetProjectFolder() + alarmingEditorScene;
346 |
347 | currentScene = filename;
348 |
349 | //Use file timestamp instead of DateTime.Now for consistency reasons.
350 | if (File.Exists(currentTempBinaryScene))
351 | {
352 | currentSceneLoaded = File.GetLastWriteTime(currentTempBinaryScene);
353 | }
354 | else
355 | {
356 | Debug.LogWarning("Unable to find temp file: " + currentTempBinaryScene);
357 | currentSceneLoaded = DateTime.Now;
358 | }
359 |
360 | Debug.Log("TextSceneMonitor setting current scene: " + filename + " (" + currentSceneLoaded + ")");
361 |
362 | nextCheckForChangedFile = EditorApplication.timeSinceStartup + 1.0f;
363 |
364 | PlayerPrefs.SetString("TextSceneMonitorCurrentScene", currentScene);
365 | PlayerPrefs.SetString("TextSceneAlarmingEditorScene", alarmingEditorScene);
366 | }
367 |
368 | ///
369 | /// Returns the current scene being monitored.
370 | ///
371 | ///
372 | /// A holding the absolute path to the currently monitored scene.
373 | ///
374 | public string GetCurrentScene()
375 | {
376 | return currentScene;
377 | }
378 |
379 | public void DoSaveAndReload(string scene, TextSceneDeserializer.TempSceneSaved callback)
380 | {
381 | //Must wait a couple of frames before we do the save for prefabs to behave correctly.
382 | //TODO: Report bug.
383 | process = new TextSceneTempCreator(scene, callback);
384 | }
385 |
386 | ///
387 | /// Regularly checks if something worthy of notifying the user happens. This includes
388 | /// unexpected scene changes (double-clicking .unity files), creating new scenes and
389 | /// source TextScene files having been changed between now and the time it was loaded.
390 | ///
391 | private void Update()
392 | {
393 | //HACK: To get around bug (TOOD: Insert case #) where Save immediately
394 | // after instantiating prefabs results in weird behaviour.
395 | if (process != null)
396 | {
397 | TextSceneTempCreator.Status status = process.Update();
398 |
399 | switch (status)
400 | {
401 | case TextSceneTempCreator.Status.Failed:
402 | Debug.LogError("Creating temp files failed!");
403 | process = null;
404 | break;
405 | case TextSceneTempCreator.Status.Complete:
406 | Debug.Log("Creating temp files succeeded!");
407 |
408 | //FIXME: Either do this, or get a reference to the delegate and call
409 | // if after clearing tempCreator. The callback might
410 | // end up setting the tempCreator again, typically in a build-cycle,
411 | // for example.
412 | TextSceneTempCreator tempCreatorRef = process;
413 |
414 | process = null;
415 |
416 | tempCreatorRef.InvokeCallback();
417 |
418 | break;
419 | default:
420 | break;
421 | }
422 |
423 | return;
424 | }
425 |
426 | //Did the user create a new scene?
427 | if (currentScene.Length > 0
428 | && EditorApplication.currentScene.Length == 0)
429 | {
430 | if (CheckForChangedTemp())
431 | EditorApplication.NewScene();
432 |
433 | currentScene = "";
434 | alarmingEditorScene = "";
435 |
436 | TextSceneSerializer.SaveCurrent();
437 |
438 | //Warn the user if the save scene dialog was cancelled.
439 | if (currentScene.Length == 0)
440 | EditorUtility.DisplayDialog("New Scene", "You have started a new scene after using the TextScene system. Please use the TextScene menu to save it if you want to continue using the TextScene system", "OK");
441 | }
442 |
443 | //Try to detect when we go from a TextScene to a regular unit scene.
444 | if ((currentScene.Length > 0
445 | && EditorApplication.currentScene.Length > 0)
446 | || (currentScene.Length == 0
447 | && alarmingEditorScene.Length == 0))
448 | {
449 | if (alarmingEditorScene != EditorApplication.currentScene)
450 | {
451 | string current = EditorApplication.currentScene;
452 |
453 | if (CheckForChangedTemp())
454 | EditorApplication.OpenScene(current);
455 |
456 | if (EditorUtility.DisplayDialog("TextScene/built-in mixed usage", "It is not recommended to mix TextScene usage with built-in Unity scenes. This may cause the TextScene system to miss updates or simply behave totally weird! If you plan on using the TextScene system, you should save the current scene via the TextScene menu item and - if successfully saved (please inspect the log for errors and warnings) - remove the original from the Assets folder. Please note that not all components/Unity objects can be saved into the TextScene format, so don't delete the original until you are 100% sure you saved what you need!", "Save to TextScene now!", "I know what I'm doing"))
457 | {
458 | TextSceneSerializer.SaveCurrent();
459 | }
460 | else
461 | {
462 | alarmingEditorScene = EditorApplication.currentScene;
463 | currentScene = "";
464 | }
465 | }
466 | }
467 |
468 | //Regular checks to see if the scene file was edited since our last load.
469 | if (currentScene.Length > 0
470 | && EditorApplication.timeSinceStartup > nextCheckForChangedFile)
471 | {
472 | if (File.Exists(currentScene))
473 | {
474 | DateTime lastWriteTime = File.GetLastWriteTime(currentScene);
475 |
476 | if (lastWriteTime > currentSceneLoaded)
477 | {
478 | int result = EditorUtility.DisplayDialogComplex("Scene changed", "The TextScene you currently have open changed (" + lastWriteTime + "). Do you want to reload it?", "Yes", "Backup mine first", "No");
479 |
480 | if (result == 0)//Yes
481 | {
482 | TextSceneDeserializer.Load(currentScene);
483 | }
484 | else if (result == 1)//Backup first
485 | {
486 | string filename = EditorUtility.SaveFilePanel("Backup TextScene", currentScene.Substring(0, currentScene.LastIndexOf('/')), "backup", "txt");
487 |
488 | if (filename.Length != 0)
489 | {
490 | //This is overwritten during the save.
491 | string toLoad = currentScene;
492 |
493 | TextSceneSerializer.Save(filename);
494 | TextSceneDeserializer.Load(toLoad);
495 | }
496 | else
497 | {
498 | EditorUtility.DisplayDialog("Unsaved", "You chose to cancel the backup of your own scene file. It is recommended that you manually save a copy, and merge with the updated file from disk (" + currentScene + ")", "OK");
499 | }
500 | }
501 | else//No
502 | {
503 | //HACK: Shut this message up. We don't want to get asked this again until the
504 | // file changes again.
505 | currentSceneLoaded = DateTime.Now;
506 | }
507 | }
508 | }
509 | else
510 | {
511 | if (EditorUtility.DisplayDialog("TextScene file gone!", "It seems like the TextScene representation of your open file has been deleted. Do you want to re-save it (" + currentScene + ")?", "Yes", "No"))
512 | TextSceneSerializer.Save(currentScene);
513 | else
514 | currentScene = "";
515 | }
516 |
517 | //Also check for changed temp files (.unity in TempScenes). This happens if
518 | //the user uses the built-in save functionality.
519 | CheckForChangedTemp();
520 |
521 | nextCheckForChangedFile = EditorApplication.timeSinceStartup + 1.0f;
522 | }
523 | }
524 |
525 | public void SaveIfTempIsNewer()
526 | {
527 | if (File.Exists(currentTempBinaryScene))
528 | {
529 | if (File.GetLastWriteTime(currentTempBinaryScene) > currentSceneLoaded)
530 | {
531 | TextSceneSerializer.SaveCurrent();
532 | Debug.Log("Temp file updated: " + currentTempBinaryScene);
533 | }
534 | else
535 | Debug.Log("Temp file already up to date: " + currentTempBinaryScene);
536 | }
537 | else
538 | Debug.LogWarning("Temp file does not exist!");
539 | }
540 |
541 | private bool CheckForChangedTemp()
542 | {
543 | //Also check for temp binary file changes, so we can give the user some options.
544 | if (File.Exists(currentTempBinaryScene))
545 | {
546 | DateTime lastTempWrite = File.GetLastWriteTime(currentTempBinaryScene);
547 |
548 | if (lastTempWrite > currentSceneLoaded)
549 | {
550 | if (EditorUtility.DisplayDialog("Temp scene changed", "The temp scene changed (" + lastTempWrite + "), this probably means that either you or Unity saved using the standard menu items - you should re-save using the TextScene menu if you want these changes to be kept!", "Re-save now", "I'll do it later"))
551 | {
552 | if (EditorApplication.currentScene != alarmingEditorScene)
553 | EditorApplication.OpenScene(alarmingEditorScene);
554 |
555 |
556 | TextSceneSerializer.SaveCurrent();
557 | return true;
558 | }
559 | else
560 | SetCurrentScene(currentScene);//Reset the timers so we don't get this popping up more than once per save.
561 |
562 | }
563 | }
564 |
565 | return false;
566 | }
567 | }
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneSerializer.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// TODO: *Don't write component/field count at the start of each listing - makes editing more complicated
6 | /// than it needs to be. This must of course be reflected in TextSceneDeserializer.
7 | /// *Prefabs with changed instance parameters are not supported (it will be 'reverted' on scene load).
8 |
9 | using UnityEngine;
10 | using UnityEditor;
11 |
12 | using System.Xml.Serialization;
13 | using System.IO;
14 | using System.Reflection;
15 | using System.Collections.Generic;
16 | using System.Text;
17 |
18 | ///
19 | /// Class responsible for writing Unity scene hierarchies to a textual, humanly readable format.
20 | ///
21 | public class TextSceneSerializer
22 | {
23 | private static int warnings = 0;
24 |
25 | public static void SaveAs()
26 | {
27 | string currentScene = EditorApplication.currentScene;
28 |
29 | string startFolder = "";
30 |
31 |
32 |
33 | if (currentScene.Length == 0)
34 | {
35 | SaveCurrent();
36 | return;
37 | }
38 | else if (currentScene.StartsWith("Assets/"))
39 | {
40 | string needResaveAs = currentScene.Substring(currentScene.IndexOf('/'));
41 |
42 | needResaveAs = needResaveAs.Replace(".unity", ".txt");
43 |
44 | needResaveAs = Application.dataPath + needResaveAs;
45 |
46 | startFolder = needResaveAs.Substring(0, needResaveAs.LastIndexOf('/'));
47 | }
48 | else
49 | {
50 | //TODO: Verify that it starts with TempScenes?
51 |
52 | startFolder = EditorHelper.GetProjectFolder() + TextScene.TempToTextSceneFile(currentScene);
53 |
54 | startFolder = startFolder.Substring(0, startFolder.LastIndexOf('/'));
55 | }
56 |
57 | string fileName = EditorUtility.SaveFilePanel("Save TextScene as", startFolder, "textscene", "txt");
58 |
59 | if (fileName.Length > 0)
60 | Save(fileName);
61 | }
62 |
63 | public static void SaveCurrent()
64 | {
65 | string currentScene = EditorApplication.currentScene;
66 |
67 | string needResaveAs = "";
68 |
69 | //Don't save over scenes not in the TempScenes-folder.
70 | if (currentScene.StartsWith("Assets/"))
71 | {
72 | if (!EditorUtility.DisplayDialog("Need resave", "This scene is residing in your Assets folder. For the TextScenes we need to re-save the .unity file in a temporary folder - you should from now on not be working on this scene file, but rather use the new TextScene file that will be generated next to your currently open scene.", "OK", "Hell no."))
73 | return;
74 |
75 | needResaveAs = currentScene.Substring(currentScene.IndexOf('/'));
76 |
77 | needResaveAs = needResaveAs.Replace(".unity", ".txt");
78 |
79 | needResaveAs = Application.dataPath + needResaveAs;
80 |
81 |
82 |
83 | if (File.Exists(needResaveAs))
84 | {
85 | string overwriteFile = currentScene.Replace(".unity", ".txt");
86 |
87 | Object o = AssetDatabase.LoadAssetAtPath(overwriteFile, typeof(TextAsset));
88 |
89 | Selection.activeObject = o;
90 |
91 | if (!EditorUtility.DisplayDialog("Overwrite?", "A file already exists at the default save position (" + overwriteFile +") - do you want to overwrite, or choose a new name?", "Overwrite", "Choose new name"))
92 | needResaveAs = "";
93 | else
94 | Debug.Log("Converting and overwriting scene to text: " + needResaveAs);
95 |
96 | }
97 | else
98 | Debug.Log("Converting scene to text: " + needResaveAs);
99 |
100 |
101 | currentScene = "";
102 | }
103 |
104 |
105 | if (currentScene.Length == 0 || needResaveAs.Length > 0)
106 | {
107 | string startPath = Application.dataPath;
108 |
109 | string path = needResaveAs.Length > 0 ? needResaveAs : EditorUtility.SaveFilePanel("Save scene file", startPath, "newtextscene", "txt");
110 |
111 | if (path.Length == 0)
112 | return;
113 |
114 | currentScene = path.Replace(EditorHelper.GetProjectFolder(), "");
115 |
116 | Debug.LogWarning("Saving new scene to text: " + currentScene);
117 | }
118 | else
119 | {
120 | Debug.LogWarning("Re-saving temp scene to text: " + EditorApplication.currentScene);
121 | }
122 |
123 |
124 | string textScene = currentScene.Substring(currentScene.IndexOf('/'));
125 |
126 |
127 | string saveFile = textScene.Replace(".unity", ".txt");
128 |
129 | Save(Application.dataPath + saveFile);
130 | }
131 |
132 | private static int CompareTransform(Transform obj1, Transform obj2)
133 | {
134 | return string.Compare(obj1.name + obj1.position, obj2.name + obj2.position);
135 | }
136 |
137 | public static void Save(string filePath)
138 | {
139 | if (EditorApplication.isPlayingOrWillChangePlaymode)
140 | {
141 | EditorUtility.DisplayDialog("Game is running", "You cannot save in Play-mode", "OK");
142 | return;
143 | }
144 |
145 |
146 |
147 | Transform[] sceneObjects = Helper.GetObjectsOfType();
148 |
149 | List sortedTransforms = new List();
150 |
151 | //Sort these to get a somewhat predictable output
152 | foreach (Transform o in sceneObjects)
153 | {
154 | //Only serialize root objects, children are handled by their parents
155 | if (o.parent == null)
156 | sortedTransforms.Add(o);
157 | }
158 |
159 | warnings = 0;
160 |
161 | sortedTransforms.Sort(CompareTransform);
162 |
163 | StringBuilder sceneText = new StringBuilder();
164 |
165 | foreach(Transform o in sortedTransforms)
166 | Serialize(sceneText, o.gameObject, 0);
167 |
168 |
169 | //TODO: If the save didn't go without warnings, show a message/confirmation
170 | // dialog here.
171 | if (warnings > 0)
172 | {
173 | EditorUtility.DisplayDialog("ERROR: Scene not saved", "You had " + warnings + " errors or warnings during the save. Please fix them up and try again.", "OK");
174 | return;
175 | }
176 |
177 |
178 | StreamWriter fileStream = File.CreateText(filePath);
179 | fileStream.Write(sceneText.ToString());
180 | fileStream.Close();
181 |
182 | Debug.Log("Wrote scene to file: " + filePath);
183 |
184 | string assetPath = filePath.Replace(EditorHelper.GetProjectFolder(), "");
185 |
186 | Debug.Log("Reimporting: " + assetPath);
187 |
188 | //Import the asset.
189 | AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
190 |
191 | Selection.activeObject = AssetDatabase.LoadAssetAtPath(assetPath, typeof(TextAsset));
192 |
193 | TextSceneDeserializer.Load(filePath);
194 | }
195 |
196 |
197 |
198 | private static void Serialize(StringBuilder stream, GameObject go, int indent)
199 | {
200 | //FIXME: Uh-oh. Seems like I missed this the first time - I might have overcomplicated
201 | // matters slightly :S Need to investigate this further.
202 | /*
203 | SerializedObject serializeObject = new SerializedObject(go);
204 |
205 | SerializedProperty seralizedProperty = serializeObject.GetIterator();
206 |
207 | do
208 | {
209 | stream.WriteLine(seralizedProperty.name + " = " + seralizedProperty.ToString());
210 | }while(seralizedProperty.Next(true));
211 |
212 | Component[] cList = Helper.GetComponentsInChildren(go);
213 |
214 | foreach(Component c in cList)
215 | {
216 | stream.WriteLine("Component: " + c.GetType().ToString());
217 |
218 | serializeObject = new SerializedObject(go);
219 |
220 | seralizedProperty = serializeObject.GetIterator();
221 |
222 | do
223 | {
224 | stream.WriteLine(seralizedProperty.name + " = " + seralizedProperty.stringValue);
225 | }while(seralizedProperty.Next(true));
226 | }
227 |
228 | stream.WriteLine();
229 |
230 | return;
231 | */
232 |
233 | Object prefab = EditorUtility.GetPrefabParent(go);
234 |
235 | if (prefab != null)
236 | {
237 | PrefabType prefabType = EditorUtility.GetPrefabType(go);
238 |
239 | if (prefabType == PrefabType.DisconnectedModelPrefabInstance
240 | || prefabType == PrefabType.DisconnectedPrefabInstance)
241 | {
242 | }
243 | else
244 | {
245 | string path = AssetDatabase.GetAssetPath(prefab);
246 |
247 | string guid = AssetDatabase.AssetPathToGUID(path);
248 |
249 | WriteLine(stream, indent, "prefab " + go.name);
250 | WriteLine(stream, indent, "assetpath " + path + ", " + guid);
251 | WriteLine(stream, indent, Vector3ToString(go.transform.localPosition));
252 | WriteLine(stream, indent, QuatToString(go.transform.localRotation));
253 | WriteLine(stream, indent, Vector3ToString(go.transform.localScale));
254 |
255 | if (indent == 0)
256 | WriteLine(stream, indent, "");
257 |
258 | return;
259 |
260 | }
261 | }
262 |
263 | TextSceneObject tso = go.GetComponent();
264 |
265 | if (tso)
266 | {
267 | string path = AssetDatabase.GetAssetPath(tso.textScene);
268 |
269 | if (path.Length == 0)
270 | {
271 | EditorUtility.DisplayDialog("ERROR", "TextSceneObjects must point at assets!", "OK");
272 | warnings++;
273 | return;
274 | }
275 |
276 | string fullPath = Helper.GetFullName(go);
277 |
278 | GameObject[] matchingObjects = Helper.FindGameObjectsFromFullName(fullPath);
279 |
280 | if (matchingObjects.Length > 1)
281 | {
282 | EditorUtility.DisplayDialog("WARNING", "There are several objects that has the path '" + fullPath + "' in the scene. It is not a good thing to let TextSceneObjects have the same path as other objects, because links within the TextSceneObjects may horribly break when you least need it.", "OK, I'll fix it");
283 | warnings++;
284 | }
285 |
286 | string guid = AssetDatabase.AssetPathToGUID(path);
287 |
288 | WriteLine(stream, indent, "textscene " + go.name);
289 | WriteLine(stream, indent, "assetpath " + path + ", " + guid);
290 | WriteLine(stream, indent, Vector3ToString(go.transform.localPosition));
291 | WriteLine(stream, indent, QuatToString(go.transform.localRotation));
292 | WriteLine(stream, indent, Vector3ToString(go.transform.localScale));
293 |
294 | if (indent == 0)
295 | WriteLine(stream, indent, "");
296 |
297 | return;
298 | }
299 |
300 | //Debug.Log("Writing object: " + go.name);
301 |
302 |
303 |
304 | WriteLine(stream, indent, "gameobject " + go.name);
305 | WriteLine(stream, indent, "tag " + go.tag + " layer " + go.layer);
306 |
307 | WriteLine(stream, indent, Vector3ToString(go.transform.localPosition));
308 | WriteLine(stream, indent, QuatToString(go.transform.localRotation));
309 | WriteLine(stream, indent, Vector3ToString(go.transform.localScale));
310 |
311 | int childCount = go.transform.GetChildCount();
312 |
313 |
314 | WriteLine(stream, indent, "children " + childCount);
315 |
316 | //Write out all children , sorted.
317 | List children = new List();
318 |
319 | foreach (Transform t in go.transform)
320 | children.Add(t);
321 |
322 | children.Sort(CompareTransform);
323 |
324 | foreach(Transform t in children)
325 | Serialize(stream, t.gameObject, indent + 1);
326 |
327 |
328 | Component[] components = go.GetComponents();
329 |
330 | List componentsToWrite = new List();
331 |
332 | foreach(Component comp in components)
333 | {
334 | if (comp is Transform)
335 | continue;
336 |
337 | //HACK. Fix this gracefully.
338 | if (comp is ParticleEmitter)
339 | {
340 | EditorUtility.DisplayDialog("Warning", "The component " + comp.GetType().ToString() + " cannot be saved. You can get around this by making the object " + Helper.GetFullName(go) + " into a prefab and drop that into the scene instead", "OK");
341 |
342 | Debug.LogWarning("Skipping abstract component: " + comp.GetType().ToString(), go);
343 |
344 | warnings++;
345 | continue;
346 | }
347 |
348 | componentsToWrite.Add(comp);
349 | }
350 |
351 | WriteLine(stream, indent, "components " + componentsToWrite.Count);
352 |
353 |
354 | foreach(Component comp in componentsToWrite)
355 | {
356 | Serialize(stream, comp, indent + 1);
357 | }
358 |
359 |
360 | if (indent == 0)
361 | WriteLine(stream, indent, "");
362 | }
363 |
364 | private static string Vector3ToString(Vector3 source)
365 | {
366 | return source.x.ToString("F5") + " " + source.y.ToString("F5") + " " + source.z.ToString("F5");
367 | }
368 |
369 | private static string QuatToString(Quaternion source)
370 | {
371 | return source.x.ToString("F5") + " " + source.y.ToString("F5") + " " + source.z.ToString("F5") + " " + source.w.ToString("F5");
372 | }
373 |
374 | private static void WriteLine(StringBuilder stream, int indent, string line)
375 | {
376 | if (indent > 0)
377 | stream.Append(IndentString(indent));
378 |
379 | stream.Append(line);
380 | stream.Append('\n');
381 | }
382 |
383 | private static string IndentString(int indent)
384 | {
385 | StringBuilder sb = new StringBuilder();
386 |
387 | for (int i = 0; i < indent; i++)
388 | sb.Append(" ");
389 |
390 | return sb.ToString();
391 | }
392 |
393 | private static void Serialize(StringBuilder stream, Component comp, int indent)
394 | {
395 | System.Type type = comp.GetType();
396 |
397 | //Save off the fields for later, so we know beforehand how many we need to
398 | //read when deserializing.
399 | List fieldList = new List();
400 |
401 |
402 | MemberInfo[] mil = type.GetMembers(BindingFlags.Public | BindingFlags.Instance);
403 |
404 | foreach(MemberInfo mi in mil)
405 | {
406 | FieldInfo fi = type.GetField(mi.Name);
407 |
408 | object val = null;
409 | string memberType = "";
410 | string memberName = mi.Name;
411 |
412 |
413 | if (fi != null)
414 | {
415 | memberType = "field";
416 | val = fi.GetValue(comp);
417 | }
418 |
419 | PropertyInfo pi = type.GetProperty(mi.Name);
420 |
421 | if (pi != null)
422 | {
423 | memberType = "property";
424 |
425 | MethodInfo getMethod = pi.GetGetMethod();
426 |
427 | //HACKs.
428 | if (memberName == "inertiaTensorRotation" && comp is Rigidbody)
429 | val = null;
430 | else if (memberName == "mesh" && comp is MeshFilter)
431 | val = null;//Debug.Log("Skipping mesh property because it will leak in edit mode");
432 | else if (memberName == "material" && comp is Renderer)
433 | val = null;//Debug.Log("Skipping renderer material property because it will leak in edit mode");
434 | else if (memberName == "materials" && comp is Renderer)
435 | val = null;//Debug.Log("Skipping renderer materials property because it will leak in edit mode");
436 | else if (memberName == "sharedMaterial" && comp is Renderer)
437 | val = null;//sharedMaterials will include this one, it's redundant.
438 | else if (memberName == "material" && comp is Collider)
439 | val = null;//Debug.Log("Skipping physics material property because it will leak in edit mode");
440 | else if (memberName == "mesh" && comp is Collider)
441 | val = null;//Debug.Log("Skipping physics mesh property because... I have no good reason.");
442 | else if (memberName == "particles" && comp is ParticleEmitter)
443 | val = null;//Debug.Log("Skipping particles property because it will change all the time if selected");
444 | else if (memberName == "name" || memberName == "tag" || memberName == "layer")
445 | val = null;
446 | else if (memberName == "active")
447 | val = null;//Deprecated, AFAIK.
448 | else if (memberName == "hideFlags" && comp.hideFlags == 0)
449 | val = null;//Skip this if it has the default value.
450 | else if (memberName == "enabled"
451 | && getMethod != null
452 | && (System.Boolean)getMethod.Invoke(comp, null) == true)
453 | val = null;//Skip this if it has the default value.
454 | //Camera properties not editable from the editor.
455 | else if (memberName == "pixelRect" && comp is Camera)
456 | val = null;
457 | else if (memberName == "aspect" && comp is Camera)
458 | val = null;
459 | else if (memberName == "layerCullDistances" && comp is Camera)
460 | val = null;
461 | //The reason we don't call this above, is that it will leak resources if called on "mesh" and maybe "material".
462 | else if (getMethod != null && pi.GetSetMethod() != null)
463 | val = getMethod.Invoke(comp, null);
464 | }
465 |
466 |
467 | if (val != null)
468 | {
469 | WriteComponentValue(comp, memberType, memberName, val, fieldList);
470 | }
471 | }
472 |
473 |
474 | WriteLine(stream, indent, type.ToString() + " " + fieldList.Count);
475 |
476 | //To correctly indent lines not directly fields (such as array entries)
477 | string indentString = "\n" + IndentString(indent+1);
478 |
479 | foreach(string field in fieldList)
480 | {
481 | string indentedField = field.Replace("\n", indentString);
482 |
483 | WriteLine(stream, indent+1, indentedField);
484 | }
485 | }
486 |
487 | private static void PopupInSceneLinkWarning(Component comp, string memberName, string linkedObject)
488 | {
489 | Selection.activeObject = comp.gameObject;
490 |
491 | EditorUtility.DisplayDialog("Warning", "There seems to be an unresolvable link from '" + comp.GetType().ToString() + "' on '" + comp.gameObject.name + "' (" + memberName + " = " + linkedObject + "). This link will be lost! Once the save has completed, you can resolve the problem manually.", "OK");
492 |
493 | warnings++;
494 | }
495 |
496 | private static bool WriteComponentValue(Component comp, string memberType, string memberName, object val, List fieldList)
497 | {
498 | //Debug.Log("Writing component member: " + memberName);
499 |
500 | if (val is UnityEngine.Object)
501 | {
502 | Object uo = val as Object;
503 |
504 | string assetPath = val == null ? "" : AssetDatabase.GetAssetPath(uo);
505 | string assetGUID = "";
506 |
507 | string uoName = uo == null ? "" : uo.name;
508 |
509 | if (assetPath.Length > 0)
510 | assetGUID = AssetDatabase.AssetPathToGUID(assetPath);
511 |
512 | if (assetPath.Length > 0)
513 | {
514 | fieldList.Add(memberType + " " + memberName + " asset " + val.GetType() + " = " + assetPath + ", " + uoName + ", " + assetGUID);
515 | }
516 | else if (uoName.Length > 0)
517 | {
518 | string fullGOName = "";
519 |
520 | if (uo is Component)
521 | fullGOName = Helper.GetFullName((uo as Component).gameObject);
522 | else if (uo is GameObject)
523 | fullGOName = Helper.GetFullName((uo as GameObject));
524 |
525 | if (fullGOName.Length > 0)
526 | {
527 | //GameObject tmpGo = GameObject.Find(fullGOName);
528 |
529 | //if (tmpGo == null)
530 | // EditorUtility.DisplayDialog("WARNING", "Unable to find: '" + fullGOName + "'", "OK");
531 |
532 | GameObject[] gos = Helper.FindGameObjectsFromFullName(fullGOName);
533 |
534 | if (gos.Length > 1)
535 | {
536 | warnings++;
537 |
538 | if (!EditorUtility.DisplayDialog("WARNING", "There are several objects that will have the path '" + fullGOName + "' - please rename them so they become unique. The TextScene in-scene links rely on unique object paths", "Continue", "Stop writing this object"))
539 | return false;
540 | }
541 | fieldList.Add(memberType + " " + memberName + " scenelink " + val.GetType() + " = " + fullGOName);
542 | }
543 | else if (uo is Mesh)
544 | {
545 | fieldList.Add(memberType + " " + memberName + " builtinmesh " + val.GetType() + " = " + uoName);
546 | }
547 |
548 | else if (uo is Material)
549 | {
550 | fieldList.Add(memberType + " " + memberName + " builtinmaterial " + val.GetType() + " = " + uoName);
551 | }
552 | else
553 | {
554 | PopupInSceneLinkWarning(comp, memberName, uoName);
555 |
556 | Debug.LogWarning("In-scene links currently not supported for UnityEngine.Object: " + comp.gameObject.name + ":" + memberName + " (" + uoName + ")");
557 |
558 | return false;
559 | }
560 | }
561 | else
562 | {
563 | return false;
564 | }
565 | }
566 | else
567 | {
568 | System.Type valueType = val.GetType();
569 |
570 | if (valueType == typeof(System.Int32)
571 | || valueType == typeof(System.Single)
572 | || valueType == typeof(System.Boolean)
573 | || valueType == typeof(System.String)
574 | || valueType == typeof(UnityEngine.Vector2)
575 | || valueType == typeof(UnityEngine.Vector3)
576 | || valueType == typeof(UnityEngine.Vector4)
577 | || valueType == typeof(UnityEngine.Quaternion)
578 | || valueType.IsEnum)
579 | fieldList.Add(memberType + " " + memberName + " primitive " + valueType.ToString() + " = " + val.ToString());
580 | else if (valueType == typeof(UnityEngine.Color))
581 | {
582 | fieldList.Add(memberType + " " + memberName + " primitive " + valueType.ToString() + " = " + val.ToString().Replace("RGBA", ""));
583 | }
584 | else if (valueType == typeof(UnityEngine.Rect))
585 | {
586 | string rectStrippedString = val.ToString();
587 |
588 | rectStrippedString = rectStrippedString.Replace("left:", "");
589 | rectStrippedString = rectStrippedString.Replace("width:", "");
590 | rectStrippedString = rectStrippedString.Replace("top:", "");
591 | rectStrippedString = rectStrippedString.Replace("height:", "");
592 |
593 | fieldList.Add(memberType + " " + memberName + " primitive " + valueType.ToString() + " = " + rectStrippedString);
594 | }
595 | else if (valueType == typeof(Matrix4x4))
596 | {
597 | //'Gracefully' skip matrices.
598 | }
599 | else if (val.GetType().IsArray)
600 | {
601 | System.Array array = (val as System.Array);
602 |
603 | List arrayEntryList = new List();
604 |
605 | arrayEntryList.Add(memberType + " " + memberName + " array " + valueType.ToString() + " = " + array.Length);
606 |
607 | for (int i = 0; i < array.Length; i++)
608 | {
609 | if (array.GetValue(i) == null)
610 | arrayEntryList.Add("null");
611 | else
612 | {
613 | if (!WriteComponentValue(comp, " ", " ", array.GetValue(i), arrayEntryList))
614 | {
615 | arrayEntryList.Clear();
616 | break;
617 | }
618 | }
619 | }
620 |
621 |
622 | StringBuilder sb = new StringBuilder();
623 |
624 | for (int i = 0; i < arrayEntryList.Count; i++)
625 | {
626 | if (i == arrayEntryList.Count-1)
627 | sb.Append(arrayEntryList[i]);
628 | else
629 | sb.Append(arrayEntryList[i] + '\n');
630 | }
631 |
632 | if (sb.Length > 0)
633 | fieldList.Add(sb.ToString());
634 | }
635 | else
636 | {
637 | //Try to write "complex" stuff, typically structs/classes we haven't converted to seomthing
638 | //more handy/readable.
639 | try
640 | {
641 | List complexEntryList = new List();
642 |
643 | FieldInfo[] fields = val.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
644 |
645 | foreach(FieldInfo fi in fields)
646 | {
647 | WriteComponentValue(comp, " field", fi.Name, fi.GetValue(val), complexEntryList);
648 | }
649 |
650 |
651 | PropertyInfo[] properties = val.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
652 |
653 | foreach(PropertyInfo pi in properties)
654 | {
655 | MethodInfo getMethod = pi.GetGetMethod();
656 |
657 | MethodInfo setMethd = pi.GetSetMethod();
658 |
659 | if (getMethod != null && setMethd != null)
660 | {
661 | //Debug.LogError(memberName + " Comp: " + comp.gameObject.name + " - " + comp.GetType().ToString() + " Prop: " + pi.Name);
662 | object propertyValue = getMethod.Invoke(val, null);
663 |
664 | WriteComponentValue(comp, " property", pi.Name, propertyValue, complexEntryList);
665 | }
666 | }
667 |
668 |
669 | StringBuilder sb = new StringBuilder();
670 |
671 | sb.Append(memberType + " " + memberName + " complex " + valueType.ToString() + " = " + complexEntryList.Count + '\n');
672 |
673 | for (int i = 0; i < complexEntryList.Count; i++)
674 | {
675 | if (i == complexEntryList.Count-1)
676 | sb.Append(complexEntryList[i]);
677 | else
678 | sb.Append(complexEntryList[i] + '\n');
679 | }
680 |
681 | if (sb.Length > 0)
682 | fieldList.Add(sb.ToString());
683 |
684 | return true;
685 | }
686 | catch (System.Exception e)
687 | {
688 | warnings++;
689 | Debug.LogError("Failed to write: " + memberName + " on " + comp.GetType().ToString() + " on object " + comp.name + " Exception: " + e);
690 | return false;
691 | }
692 | }
693 | }
694 |
695 | return true;
696 | }
697 | }
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneHierarchy.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// BUGS: Somewhat fixed:
6 | /// *Prefab instances gets renamed to their original prefab name if you load a TextScene and hit
7 | /// "apply" on the prefab instance (See unity case 336621)
8 | ///
9 | /// TODO: *Add support for multiple selected items
10 |
11 |
12 | using UnityEngine;
13 | using UnityEditor;
14 | using System.Reflection;
15 | using System.Collections.Generic;
16 |
17 | ///
18 | /// Custom hierarchy view compatible with TextScene functionality (scene-in-scene links, various
19 | /// constraints and drag'n'drop instantiation of TextScene-links).
20 | ///
21 | public class TextSceneHierarchy : EditorWindow
22 | {
23 | enum EntryType
24 | {
25 | Normal = 0,
26 | PrefabChild,
27 | PrefabRoot,
28 | TextSceneChild,
29 | TextSceneRoot
30 | }
31 |
32 | static class EntryTypeStyle
33 | {
34 | private static Dictionary styles = new Dictionary();
35 |
36 | public static GUIStyle Get(EntryType type)
37 | {
38 | if (styles.ContainsKey(type))
39 | return styles[type];
40 |
41 | GUIStyle style = new GUIStyle("HI Label");
42 |
43 | if (type == EntryType.PrefabRoot)
44 | style.normal.textColor = new Color(0.2f, 0.2f, 0.9f);
45 | else if (type == EntryType.TextSceneRoot)
46 | style.normal.textColor = new Color(0.9f, 0.9f, 0.2f);
47 | else if (type == EntryType.PrefabChild)
48 | style.normal.textColor = new Color(0.2f, 0.2f, 0.6f);
49 | else if (type == EntryType.TextSceneChild)
50 | style.normal.textColor = new Color(0.6f, 0.6f, 0.2f);
51 | else
52 | style.normal.textColor = Color.black;
53 |
54 | styles.Add(type, style);
55 |
56 | return styles[type];
57 | }
58 | }
59 |
60 | static class Styles
61 | {
62 | public static GUIStyle foldout = new GUIStyle("IN Foldout");
63 | public static GUIStyle insertion = new GUIStyle("PR Insertion");
64 | }
65 |
66 | class Entry
67 | {
68 | private static Entry selected = null;
69 |
70 | private double expandedTimer = 0.0;
71 | public bool expanded = false;
72 | public GameObject go;
73 |
74 | private List entries = new List();
75 |
76 | private EntryType type = EntryType.Normal;
77 |
78 | public Entry()
79 | {
80 | }
81 |
82 | public Entry(GameObject go)
83 | {
84 | this.go = go;
85 | }
86 |
87 | public void AddChild(GameObject gameObject)
88 | {
89 | AddChild(gameObject, EntryType.Normal);
90 | }
91 |
92 | public bool hasChildren
93 | {
94 | get
95 | {
96 | return entries.Count > 0;
97 | }
98 | }
99 |
100 | private void Duplicate()
101 | {
102 | if (go == null)
103 | return;
104 |
105 | Undo.RegisterSceneUndo("Duplicate " + go.name);
106 |
107 | GameObject copy = null;
108 |
109 | GameObject prefab = EditorUtility.GetPrefabParent(go) as GameObject;
110 |
111 | if (prefab != null)
112 | {
113 | PrefabType type = EditorUtility.GetPrefabType(prefab);
114 |
115 | if (type == PrefabType.ModelPrefab || type == PrefabType.Prefab)
116 | {
117 | copy = EditorUtility.InstantiatePrefab(prefab) as GameObject;
118 | copy.transform.position = go.transform.position;
119 | copy.transform.rotation = go.transform.rotation;
120 | copy.transform.localScale = go.transform.localScale;
121 | }
122 | }
123 |
124 | if (copy == null)
125 | copy = GameObject.Instantiate(go, go.transform.position, go.transform.rotation) as GameObject;
126 |
127 | copy.transform.parent = go.transform.parent;
128 |
129 | copy.name = go.name + "_copy";
130 |
131 | Selection.activeGameObject = copy;
132 | }
133 |
134 | private void DisconnectPrefab()
135 | {
136 | if (go == null)
137 | return;
138 |
139 | GameObject copy = null;
140 |
141 | GameObject prefab = EditorUtility.GetPrefabParent(go) as GameObject;
142 |
143 | if (prefab != null)
144 | {
145 | PrefabType type = EditorUtility.GetPrefabType(prefab);
146 |
147 | if (type == PrefabType.ModelPrefab || type == PrefabType.Prefab)
148 | {
149 | Undo.RegisterSceneUndo("Disconnect prefab " + go.name);
150 |
151 | copy = GameObject.Instantiate(go, go.transform.position, go.transform.rotation) as GameObject;
152 | copy.transform.position = go.transform.position;
153 | copy.transform.rotation = go.transform.rotation;
154 | copy.transform.localScale = go.transform.localScale;
155 |
156 | copy.transform.parent = go.transform.parent;
157 |
158 | copy.name = go.name;
159 |
160 | DestroyImmediate(go);
161 |
162 | Selection.activeGameObject = copy;
163 | }
164 | }
165 | }
166 |
167 | private void DisconnectTextScene()
168 | {
169 | if (go == null)
170 | return;
171 |
172 | Undo.RegisterSceneUndo("Disconnect TextScene " + go.name);
173 |
174 | TextSceneObject textSceneComp = go.GetComponent();
175 |
176 | if (textSceneComp != null)
177 | DestroyImmediate(textSceneComp);
178 |
179 | TextSceneObject[] firstLayer = Helper.GetComponentsInChildren(go, 1);
180 |
181 | //Enable next layer of TextSceneObjects.
182 | foreach (TextSceneObject tso in firstLayer)
183 | {
184 | tso.hideFlags = 0;
185 | tso.transform.hideFlags = 0;
186 | }
187 |
188 |
189 | //Enable all disconnected gameobjects until the next TextSceneObject.
190 | foreach (Transform child in go.transform)
191 | {
192 | Component[] toEnable = Helper.GetComponentsInChildrenAboveType(child.gameObject);
193 |
194 | foreach (Component comp in toEnable)
195 | comp.hideFlags = 0;
196 | }
197 |
198 | TextSceneHierarchy tsh = EditorWindow.GetWindow(typeof(TextSceneHierarchy)) as TextSceneHierarchy;
199 | tsh.OnHierarchyChange();
200 | }
201 |
202 | private void Delete()
203 | {
204 | if (go != null)
205 | {
206 | Undo.RegisterSceneUndo("Delete " + go.name);
207 | DestroyImmediate(go);
208 | }
209 | }
210 |
211 | public void AddChild(GameObject gameObject, EntryType entryType)
212 | {
213 | Entry entry = entries.Find(delegate(Entry x) { return x.go == gameObject; });
214 |
215 | if (entry == null)
216 | {
217 | entry = new Entry(gameObject);
218 |
219 | entries.Add(entry);
220 | }
221 |
222 | entry.type = entryType;
223 |
224 | if (entryType == EntryType.Normal)
225 | {
226 | if (entry.go.GetComponent() != null)
227 | {
228 | entry.type = EntryType.TextSceneRoot;
229 | }
230 | else if (EditorUtility.GetPrefabParent(entry.go) != null)
231 | {
232 | entry.type = EntryType.PrefabRoot;
233 | }
234 | }
235 | else if (entryType == EntryType.PrefabRoot)
236 | entry.type = EntryType.PrefabChild;
237 | else if (entryType == EntryType.TextSceneRoot)
238 | entry.type = EntryType.TextSceneChild;
239 |
240 | foreach (Transform t in entry.go.transform)
241 | entry.AddChild(t.gameObject, entry.type);
242 | }
243 |
244 | public bool HandleDrag()
245 | {
246 | if (Event.current.type == EventType.DragUpdated
247 | || Event.current.type == EventType.DragPerform)
248 | {
249 | Entry e = DragAndDrop.GetGenericData("entry") as Entry;
250 |
251 | //Do not allow dragging stuff onto prefabs or textscene links.
252 | if (this.type != EntryType.Normal)
253 | {
254 | selected = null;
255 | DragAndDrop.visualMode = DragAndDropVisualMode.None;
256 | return false;
257 | }
258 | //Do not allow dragging prefab or textscene children around.
259 | else if (e != null
260 | && (e.type == EntryType.PrefabChild
261 | || e.type == EntryType.TextSceneChild))
262 | {
263 | selected = null;
264 | DragAndDrop.visualMode = DragAndDropVisualMode.None;
265 | return false;
266 | }
267 | else
268 | {
269 | selected = this;
270 |
271 | if (expanded == false
272 | && EditorApplication.timeSinceStartup > this.expandedTimer)
273 | this.expanded = true;
274 |
275 | DragAndDrop.visualMode = DragAndDropVisualMode.Link;
276 |
277 | if (Event.current.type == EventType.DragPerform)
278 | {
279 | DragAndDrop.AcceptDrag();
280 |
281 | if (e != null && e.go != null)
282 | {
283 | GameObject dragged = e.go;
284 |
285 | if (dragged != null)
286 | {
287 | if (dragged != go)
288 | {
289 | Undo.RegisterSceneUndo("Move " + dragged.name);
290 |
291 | //If the target is null (the root), we might want to orphan the object.
292 | if (go == null)
293 | {
294 | if (dragged.transform.parent != null)
295 | {
296 | if (EditorUtility.DisplayDialog("Orphan", "Do you want to make " + dragged.name + " an orphan?", "Yes", "No"))
297 | {
298 | dragged.transform.parent = null;
299 | Selection.activeGameObject = dragged;
300 | TextSceneHierarchy.Refresh();
301 | }
302 | }
303 | }
304 | else
305 | {
306 | //Change parent to whatever we're hovering above at the time of drag end.
307 | if (EditorUtility.DisplayDialog("Change parent", "Do you want to make " + go.name + " the parent of " + dragged.name + "?", "Yes", "No"))
308 | {
309 | dragged.transform.parent = go.transform;
310 | Selection.activeGameObject = dragged;
311 | TextSceneHierarchy.Refresh();
312 | }
313 | }
314 | }
315 | }
316 | }
317 | else if (DragAndDrop.objectReferences.Length > 0)
318 | {
319 | UnityEngine.Object dragged = DragAndDrop.objectReferences[0];
320 |
321 | MonoScript comp = dragged as MonoScript;
322 | GameObject draggedGO = dragged as GameObject;
323 | TextAsset draggedTextScene = dragged as TextAsset;
324 |
325 | if (comp != null)
326 | {
327 | if (this.go != null)
328 | {
329 | this.go.AddComponent(comp.GetClass());
330 | Selection.activeGameObject = this.go;
331 | }
332 | }
333 | else if (draggedTextScene != null)
334 | {
335 | Undo.RegisterSceneUndo("Add scene " + draggedTextScene.name);
336 |
337 | TextSceneDeserializer.LoadAdditive(draggedTextScene, this.go);
338 | }
339 | else if (draggedGO != null)
340 | {
341 | PrefabType pt = EditorUtility.GetPrefabType(dragged);
342 |
343 | if (pt == PrefabType.ModelPrefab || pt == PrefabType.Prefab)
344 | {
345 | Undo.RegisterSceneUndo("Create " + draggedGO.name);
346 |
347 | GameObject instance = EditorUtility.InstantiatePrefab(dragged) as GameObject;
348 |
349 | if (this.go != null)
350 | instance.transform.parent = this.go.transform;
351 |
352 | instance.transform.localPosition = Vector3.zero;
353 | instance.transform.localRotation = Quaternion.identity;
354 | instance.name = instance.name + "_instance";
355 |
356 | Selection.activeGameObject = instance;
357 | }
358 | }
359 | }
360 | }
361 |
362 |
363 | }
364 |
365 | Event.current.Use();
366 | return true;
367 | }
368 |
369 | return false;
370 | }
371 |
372 | public void OnGUI(int level, ref float offset)
373 | {
374 | float startOffset = offset;
375 |
376 | GUIStyle style = EntryTypeStyle.Get(type);
377 |
378 | if (go != null)
379 | {
380 | Rect lineRect = new Rect(0, offset, 1000, style.lineHeight);
381 | Rect labelRect = new Rect(level * 20.0f, offset, 1000, style.lineHeight);
382 | Rect expandRect = new Rect(level * 20.0f, offset, 20, style.lineHeight);
383 |
384 | offset += style.lineHeight;
385 |
386 |
387 |
388 | if (selected == this)
389 | style.Draw(lineRect, false, true, true, false);
390 |
391 |
392 | if (hasChildren)
393 | expanded = GUI.Toggle(expandRect, expanded, GUIContent.none, Styles.foldout);
394 |
395 | GUI.Label(labelRect, go.name, style);
396 |
397 | //FIXME: This is to get the scrollbar to work :S
398 | GUILayout.BeginHorizontal();
399 | GUILayout.Space(level * 20.0f);
400 | GUILayout.Label("", style);
401 | GUILayout.EndHorizontal();
402 |
403 | }
404 |
405 | if (go == null || expanded)
406 | {
407 | foreach (Entry e in entries)
408 | e.OnGUI(level + 1, ref offset);
409 | }
410 |
411 | Rect selectRect = new Rect(0, startOffset + style.lineHeight * 0.1f, 200, offset-startOffset - style.lineHeight * 0.1f);
412 |
413 | //Base entry
414 | if (go == null)
415 | selectRect.height = float.MaxValue;
416 |
417 | if (selectRect.Contains(Event.current.mousePosition))
418 | {
419 | if (go != null)
420 | {
421 | if (Event.current.type == EventType.MouseDown)
422 | {
423 | if (Event.current.button == 1)
424 | {
425 | EditorGUIUtility.PingObject(go);
426 |
427 | GenericMenu menu = new GenericMenu();
428 |
429 | if (type == EntryType.Normal
430 | || type == EntryType.PrefabRoot
431 | || type == EntryType.TextSceneRoot)
432 | {
433 | menu.AddItem(EditorHelper.TextContent("Duplicate"), false, new GenericMenu.MenuFunction(this.Duplicate));
434 | menu.AddItem(EditorHelper.TextContent("Delete"), false, new GenericMenu.MenuFunction(this.Delete));
435 |
436 | if (type == EntryType.PrefabRoot)
437 | {
438 | menu.AddSeparator("");
439 | menu.AddItem(EditorHelper.TextContent("Disconnect"), false, new GenericMenu.MenuFunction(this.DisconnectPrefab));
440 | }
441 | else if (type == EntryType.TextSceneRoot)
442 | {
443 | menu.AddSeparator("");
444 | menu.AddItem(EditorHelper.TextContent("Disconnect"), false, new GenericMenu.MenuFunction(this.DisconnectTextScene));
445 | }
446 | else if (type == EntryType.Normal)
447 | {
448 | menu.AddSeparator("");
449 | menu.AddItem(EditorHelper.TextContent("Create Empty"), false,
450 | new GenericMenu.MenuFunction(delegate
451 | {
452 | GameObject empty = new GameObject();
453 | empty.transform.parent = go.transform;
454 | empty.transform.localPosition = Vector3.zero;
455 | empty.transform.localRotation = Quaternion.identity;
456 | }));
457 | }
458 | }
459 | else
460 | menu.AddItem(EditorHelper.TextContent("No options"), false, null);
461 |
462 | menu.ShowAsContext();
463 | }
464 | else if (Event.current.clickCount == 2)
465 | expanded = !expanded;
466 |
467 | Event.current.Use();
468 | }
469 | else if (Event.current.type == EventType.MouseUp)
470 | {
471 | if (Event.current.clickCount == 1)
472 | {
473 | Selection.activeGameObject = go;
474 |
475 | TextSceneObject tso = go.GetComponent();
476 |
477 | if (tso != null)
478 | EditorGUIUtility.PingObject(tso.textScene);
479 |
480 | Object prefabParent = EditorUtility.GetPrefabParent(go);
481 |
482 | if (prefabParent != null)
483 | EditorGUIUtility.PingObject(prefabParent);
484 | }
485 |
486 | Event.current.Use();
487 | }
488 | else if (Event.current.type == EventType.MouseDrag)
489 | {
490 | DragAndDrop.PrepareStartDrag();
491 |
492 | UnityEngine.Object[] draggedGO = new Object[1];
493 |
494 | draggedGO[0] = go;
495 |
496 |
497 | DragAndDrop.objectReferences = draggedGO;
498 | DragAndDrop.SetGenericData("entry", this);
499 | DragAndDrop.StartDrag("Dragging " + go.name);
500 |
501 | Event.current.Use();
502 | }
503 | }
504 |
505 | HandleDrag();
506 | }
507 | else
508 | {
509 | //Reset expand timer if the cursor moves out of the entry rect.
510 | expandedTimer = EditorApplication.timeSinceStartup + 0.3;
511 | }
512 |
513 | if (selected == this)
514 | {
515 | if (go != null)
516 | {
517 | if (Event.current.type == EventType.KeyDown)
518 | {
519 | if (Event.current.keyCode == KeyCode.Delete)
520 | {
521 | this.Delete();
522 | }
523 | //FIXME: Use control/command, not shift.
524 | else if (Event.current.shift && Event.current.keyCode == KeyCode.D)
525 | {
526 | this.Duplicate();
527 | }
528 | }
529 | }
530 | }
531 | }
532 |
533 | public bool FindAndTagSelected(GameObject gameObject)
534 | {
535 | if (go == gameObject)
536 | {
537 | selected = this;
538 | return true;
539 | }
540 |
541 | foreach (Entry e in entries)
542 | {
543 | if (e.FindAndTagSelected(gameObject))
544 | {
545 | expanded = true;
546 | return true;
547 | }
548 | }
549 |
550 | return false;
551 | }
552 |
553 | internal void Sort()
554 | {
555 | entries.Sort(delegate(Entry x, Entry y) { return string.Compare(x.go.name, y.go.name); });
556 | }
557 |
558 | internal void FillExpanded(List expandedObjects)
559 | {
560 | if (expanded)
561 | expandedObjects.Add(go);
562 |
563 | foreach (Entry e in entries)
564 | e.FillExpanded(expandedObjects);
565 | }
566 |
567 | internal void Expand(GameObject gameObject)
568 | {
569 | if (go == gameObject)
570 | {
571 | expanded = true;
572 | return;
573 | }
574 |
575 | foreach (Entry e in entries)
576 | e.Expand(gameObject);
577 | }
578 | }
579 |
580 | Entry root = new Entry();
581 |
582 | private Vector2 scroll = new Vector2();
583 |
584 | private double nextCheckForDefaultHierarchy = 0.0f;
585 |
586 | private static bool CheckForDefaultHierarchy()
587 | {
588 | Assembly editorAssembly = Assembly.GetAssembly(typeof(EditorWindow));
589 |
590 | System.Type hierarchyWindowType = editorAssembly.GetType("UnityEditor.HierarchyWindow");
591 |
592 | Object[] window = Object.FindObjectsOfTypeAll(hierarchyWindowType);
593 |
594 | if (window.Length > 0)
595 | {
596 | if (EditorUtility.DisplayDialog("Default Hierarchy", "The default hierarchy window seems to be up. It is recommended that this one is not used, as it lets you do a few things regarding TextSceneObjects that the system will not correctly pick up (such as moving TextScene subobjects out of their parent). The default hierarchy do have a lot of handy functionality, though, so you can feel free to ignore this message if you know what the limitations of the TextScene system is. You can turn off future warnings by disabling it in the TextScene menu item.", "Close default hierarchy", "Ignore, I know the limitations!"))
597 | {
598 | foreach (EditorWindow w in window)
599 | {
600 | if (w != null)
601 | w.Close();
602 | }
603 | }
604 | else
605 | return false;
606 | }
607 |
608 | return true;
609 | }
610 |
611 | public static void CreateHierarchy()
612 | {
613 | CheckForDefaultHierarchy();
614 |
615 | TextSceneHierarchy tsh = EditorWindow.GetWindow(typeof(TextSceneHierarchy)) as TextSceneHierarchy;
616 |
617 | tsh.Show();
618 | tsh.OnHierarchyChange();
619 | }
620 |
621 | protected void OnEnable()
622 | {
623 | nextCheckForDefaultHierarchy = EditorApplication.timeSinceStartup + 2.0f;
624 | OnHierarchyChange();
625 | }
626 |
627 | protected void Update()
628 | {
629 | //Redundant update. FIXME: Necessary?
630 | TextSceneMonitor.MonitorUpdate();
631 |
632 | if (!EditorApplication.isPlaying && EditorApplication.timeSinceStartup > nextCheckForDefaultHierarchy)
633 | {
634 | if (PlayerPrefs.GetInt("ShowDefaultHierarchyWarning", 1) > 0)
635 | {
636 | if (CheckForDefaultHierarchy())
637 | nextCheckForDefaultHierarchy = EditorApplication.timeSinceStartup + 2.0f;
638 | else
639 | nextCheckForDefaultHierarchy = double.MaxValue;
640 | }
641 |
642 |
643 | }
644 | }
645 |
646 | public static void Refresh()
647 | {
648 | TextSceneHierarchy tsh = EditorWindow.GetWindow(typeof(TextSceneHierarchy)) as TextSceneHierarchy;
649 | tsh.OnHierarchyChange();
650 | }
651 |
652 | protected void OnHierarchyChange()
653 | {
654 | Transform[] transforms = Helper.GetObjectsOfType();
655 |
656 | List expanded = new List();
657 |
658 | root.FillExpanded(expanded);
659 |
660 | root = new Entry();
661 |
662 | foreach (Transform t in transforms)
663 | {
664 | if (t.parent == null)
665 | root.AddChild(t.gameObject);
666 | }
667 |
668 | foreach (GameObject go in expanded)
669 | root.Expand(go);
670 |
671 | root.FindAndTagSelected(Selection.activeGameObject);
672 |
673 | root.Sort();
674 |
675 | Repaint();
676 | }
677 |
678 | protected void OnSelectionChange()
679 | {
680 | root.FindAndTagSelected(Selection.activeGameObject);
681 |
682 | Repaint();
683 | }
684 |
685 | protected void OnGUI()
686 | {
687 | float offset = 0.0f;
688 |
689 | scroll = GUILayout.BeginScrollView(scroll);
690 | GUILayout.BeginVertical();
691 | root.OnGUI(-1, ref offset);
692 | GUILayout.EndVertical();
693 | GUILayout.EndScrollView();
694 | }
695 | }
--------------------------------------------------------------------------------
/Assets/Scripts/Editor/TextScene/TextSceneDeserializer.cs:
--------------------------------------------------------------------------------
1 | ///
2 | /// Copyright (c) 2010 TerraVision AS
3 | /// See LICENSE file for licensing details
4 | ///
5 | /// TODO: *Ensure that nested, complex objects don't blow up the entire scenefile.
6 | /// *Clean up and comment - subroutine each "type" (primitive, complex, scenelink)
7 | /// *Make the Load functions return a bool instead of a string, since the loading is
8 | /// now somewhat async - the callback passed to Load should provide the caller with
9 | /// the binary name (the scene isn't fully loaded when Load returns, the TextSceneMonitor
10 | /// handles the rest).
11 |
12 | using UnityEngine;
13 | using UnityEditor;
14 |
15 | using System.Xml.Serialization;
16 | using System.IO;
17 | using System.Reflection;
18 | using System.Collections.Generic;
19 | using System.Globalization;
20 |
21 | ///
22 | /// Class reading and populating a scene based on text-based scene files.
23 | ///
24 | public class TextSceneDeserializer
25 | {
26 | public delegate void TempSceneSaved();
27 |
28 | private enum Pass
29 | {
30 | CreateObjects = 0,
31 | ValueAssignment
32 | }
33 |
34 | private Pass currentPass;
35 |
36 | private Queue gameObjects;
37 | private Queue gameComponents;
38 |
39 | private Dictionary builtinMesh;
40 | private Material builtinMaterial;
41 |
42 | private GameObject container;
43 | private string recursionGuard = "";
44 |
45 |
46 | ///
47 | /// Loads a TextScene into a clean scene. Will ask user for path.
48 | ///
49 | public static void Load()
50 | {
51 | string path = EditorUtility.OpenFilePanel("Load scene file", Application.dataPath, "txt");
52 |
53 | TextSceneDeserializer.LoadSafe(path);
54 | }
55 |
56 | ///
57 | /// Loads the currently selected TextScene. Will fail and notify user if the selected file is
58 | /// not a text file.
59 | ///
60 | public static void LoadContext()
61 | {
62 | if (Selection.activeObject == null)
63 | {
64 | EditorUtility.DisplayDialog ("Nothing selected", "You need to select a text scene asset to load one", "OK");
65 | return;
66 | }
67 |
68 | TextAsset asset = Selection.activeObject as TextAsset;
69 |
70 | string assetPath = "";
71 |
72 | if (asset != null)
73 | assetPath = AssetDatabase.GetAssetPath(asset);
74 |
75 | if (!assetPath.EndsWith(".txt"))
76 | EditorUtility.DisplayDialog ("Not a text file", "Text scenes can only be loaded from TextAssets (*.txt)", "OK");
77 | else
78 | {
79 | string fullPath = Application.dataPath
80 | + assetPath.Substring(assetPath.IndexOf('/'));
81 |
82 | TextSceneDeserializer.LoadSafe(fullPath);
83 | }
84 | }
85 |
86 | ///
87 | /// Additively loads a TextScene. All objects from the TextScene will be children of
88 | /// the passed parent, if any. The TextScene will be added with a link to its original
89 | /// source asset, so it can easily be treated as a prefab, or scene-in-scene link.
90 | ///
91 | public static void LoadAdditive(TextAsset asset, GameObject parent)
92 | {
93 | string assetPath = "";
94 |
95 | if (asset != null)
96 | assetPath = AssetDatabase.GetAssetPath(asset);
97 |
98 | if (!assetPath.EndsWith(".txt"))
99 | EditorUtility.DisplayDialog ("Not a text file", "Text scenes can only be loaded from TextAssets (*.txt)", "OK");
100 | else
101 | {
102 | string fullPath = Application.dataPath
103 | + assetPath.Substring(assetPath.IndexOf('/'));
104 |
105 | if (fullPath.ToLower() == TextSceneMonitor.Instance.GetCurrentScene().ToLower())
106 | {
107 | EditorUtility.DisplayDialog("ERROR", "Adding this as a TextScene object (" + assetPath + ") would cause an infinite circular dependency", "OK");
108 | return;
109 | }
110 |
111 | //string name = assetPath.Substring(assetPath.LastIndexOf('/'));
112 |
113 | string spawnPath = '/' + asset.name;
114 |
115 | if (parent != null)
116 | {
117 | string uniqueParentPath = Helper.GetFullName(parent);
118 |
119 | //Make sure there will be no confusion regarding the parent path.
120 | GameObject[] gos = Helper.FindGameObjectsFromFullName(uniqueParentPath);
121 |
122 | if (gos.Length > 1)
123 | {
124 | EditorUtility.DisplayDialog("ERROR", "There are multiple objects that have the path " + uniqueParentPath + ". Please rename your parent object, or create a new, uniquely named one", "OK");
125 | return;
126 | }
127 |
128 | spawnPath = uniqueParentPath + spawnPath;
129 | }
130 |
131 | //"Random" name until we get the links set up.
132 | //FIXME: Make sure this is unbreakable :S
133 | GameObject go = new GameObject("TextScene-" + EditorApplication.timeSinceStartup);
134 |
135 | go.AddComponent().textScene = asset;
136 |
137 | (new TextSceneDeserializer(go, TextSceneMonitor.Instance.GetCurrentScene())).LoadScene(fullPath);
138 |
139 | if (parent != null)
140 | {
141 | go.transform.parent = parent.transform;
142 | go.transform.localPosition = Vector3.zero;
143 | go.transform.localRotation = Quaternion.identity;
144 | }
145 |
146 | //If we collide with another path, create a new with a suffix
147 | int suffix = 1;
148 |
149 | string suffixedSpawnPath = spawnPath;
150 | string suffixedName = asset.name;
151 |
152 | while (Helper.FindGameObjectsFromFullName(suffixedSpawnPath).Length > 0)
153 | {
154 | suffixedName = asset.name + suffix;
155 |
156 | suffixedSpawnPath = spawnPath + suffix;
157 |
158 | suffix++;
159 | }
160 |
161 | go.name = suffixedName;
162 |
163 | Selection.activeObject = go;
164 | }
165 | }
166 |
167 | ///
168 | /// Loads a new scene from path. Path is absolute.
169 | ///
170 | public static string Load(string path)
171 | {
172 | return Load(path, null);
173 | }
174 |
175 | public static string Load(string path, TempSceneSaved callback)
176 | {
177 | return (new TextSceneDeserializer()).LoadNewScene(path, callback);
178 | }
179 |
180 | public static string LoadSafe(string path)
181 | {
182 | //Check if the user wants to save his current work
183 | if (EditorApplication.SaveCurrentSceneIfUserWantsTo())
184 | {
185 | TextSceneMonitor.Instance.SaveIfTempIsNewer();
186 | }
187 | else
188 | {
189 | Debug.Log("Cancelled TextScene load");
190 | return "";
191 | }
192 |
193 | return Load(path);
194 | }
195 |
196 | private TextSceneDeserializer()
197 | {
198 |
199 | }
200 |
201 | private TextSceneDeserializer(GameObject container)
202 | {
203 | this.container = container;
204 | }
205 |
206 | private TextSceneDeserializer(GameObject container, string recursionGuard)
207 | {
208 | this.container = container;
209 | this.recursionGuard = recursionGuard;
210 | }
211 |
212 | public string LoadNewScene(string path)
213 | {
214 | return LoadNewScene(path, null);
215 | }
216 |
217 | //TODO: Return bool instead of string. Pass the binary temp scene path with the callback.
218 | public string LoadNewScene(string path, TempSceneSaved callback)
219 | {
220 | if (EditorApplication.isPlayingOrWillChangePlaymode)
221 | {
222 | EditorUtility.DisplayDialog("Game is running", "You cannot load in Play-mode", "OK");
223 | return "";
224 | }
225 |
226 | if (path == null || path.Length == 0)
227 | return "";
228 |
229 | string startPath = Application.dataPath;
230 |
231 | Debug.Log("Opening scene file: " + path + " current: " + EditorApplication.currentScene);
232 |
233 | //Make sure the file exists
234 | if (!File.Exists(path))
235 | {
236 | Debug.LogError("File does not exist: " + path);
237 | return "";
238 | }
239 |
240 | EditorApplication.NewScene();
241 |
242 | Transform[] sceneObjects = Helper.GetObjectsOfType();
243 |
244 | foreach (Transform o in sceneObjects)
245 | {
246 | if (o != null && o.parent == null)
247 | {
248 | Debug.Log("Destroying object: " + o.name);
249 | GameObject.DestroyImmediate(o.gameObject);
250 | }
251 | }
252 |
253 | recursionGuard = path;
254 |
255 | LoadScene(path);
256 |
257 |
258 | string tmpScenePath = startPath.Substring(0, startPath.LastIndexOf('/'));
259 |
260 | string assetPath = path.Replace(startPath, "");
261 |
262 | string[] subFolders = assetPath.Split('/');
263 |
264 | Debug.Log("tmpScenePath: " + tmpScenePath + " assetPath: " + assetPath);
265 |
266 | //First, make sure we have a temp scenes folder
267 | tmpScenePath += "/TempScenes";
268 |
269 | if (!Directory.Exists(tmpScenePath))
270 | Directory.CreateDirectory(tmpScenePath);
271 |
272 | //Laat entry is the filename itself.
273 | for (int i = 0; i < subFolders.Length-1; i++)
274 | {
275 | tmpScenePath += subFolders[i] + "/";
276 |
277 | if (!Directory.Exists(tmpScenePath))
278 | Directory.CreateDirectory(tmpScenePath);
279 | }
280 |
281 | string fileName = subFolders[subFolders.Length-1];
282 |
283 | fileName = fileName.Replace(".txt", ".unity");
284 |
285 | string scenePath = tmpScenePath + fileName;
286 |
287 |
288 | Debug.Log("Saving binary unity scene at path: " + scenePath + " (" + path + ")");
289 |
290 | //EditorApplication.SaveScene(scenePath);
291 | //EditorApplication.OpenScene(scenePath);
292 |
293 | //TextSceneMonitor.Instance.SetCurrentScene(path);
294 |
295 |
296 | //FIXME: Bugz0rs: Need to delay the save & reload or else prefabs will behave weirdly,
297 | // such as getting name reset if doing changes on the prefab and position reset
298 | // to prefab pos if hitting revert.
299 | TextSceneMonitor.Instance.DoSaveAndReload(scenePath, callback);
300 |
301 | return scenePath;
302 | }
303 |
304 | ///
305 | /// Loads objects from TextScene file into the currently open scene.
306 | ///
307 | public bool LoadScene(string path)
308 | {
309 | try
310 | {
311 | StreamReader fileStream = File.OpenText(path);
312 |
313 |
314 | gameObjects = new Queue();
315 | gameComponents = new Queue();
316 |
317 | SetupBuiltinMeshes();
318 |
319 | //We're doing this in two passes - first pass simply creates all objects and
320 | //components, the second pass resolves links and assigns them (the second pass
321 | //is necessary for in-scene links, so we're just handling everything in there,
322 | //even prefab and other asset links).
323 | currentPass = Pass.CreateObjects;
324 | Deserialize(fileStream, container);
325 |
326 | fileStream.Close();
327 |
328 | GameObject[] gameObjectArray = gameObjects.ToArray();
329 | Component[] gameComponentArray = gameComponents.ToArray();
330 |
331 | fileStream = File.OpenText(path);
332 |
333 | currentPass = Pass.ValueAssignment;
334 | Deserialize(fileStream, container);
335 |
336 | fileStream.Close();
337 |
338 | //Don't let the user edit scene-in-scenes (aka prefabs), since
339 | //we currently have no way of applying changes.
340 | if (container != null)
341 | {
342 | foreach (GameObject go in gameObjectArray)
343 | go.hideFlags = HideFlags.NotEditable;
344 |
345 | foreach (Component comp in gameComponentArray)
346 | comp.hideFlags = HideFlags.NotEditable;
347 | }
348 | }
349 | catch (System.Exception e)
350 | {
351 | Debug.LogError("Exception raised when loading level: " + path + " " + e.ToString());
352 | return false;
353 | }
354 |
355 | return true;
356 | }
357 |
358 | ///
359 | /// Instantiates and destroys the built-in primitives so we can get a reference to their meshes.
360 | /// FIXME: It would be a lot better if we could get these mesh references in a less
361 | /// hack-ish way!
362 | ///
363 | private void SetupBuiltinMeshes()
364 | {
365 | builtinMesh = new Dictionary();
366 |
367 | GameObject tmp = GameObject.CreatePrimitive(PrimitiveType.Cube);
368 | builtinMesh.Add("Cube", tmp.GetComponent().sharedMesh);
369 |
370 | GameObject.DestroyImmediate(tmp);
371 |
372 | tmp = GameObject.CreatePrimitive(PrimitiveType.Sphere);
373 | builtinMesh.Add("Sphere", tmp.GetComponent().sharedMesh);
374 |
375 | GameObject.DestroyImmediate(tmp);
376 |
377 | tmp = GameObject.CreatePrimitive(PrimitiveType.Plane);
378 | builtinMesh.Add("Plane", tmp.GetComponent().sharedMesh);
379 |
380 | GameObject.DestroyImmediate(tmp);
381 |
382 | tmp = GameObject.CreatePrimitive(PrimitiveType.Capsule);
383 | builtinMesh.Add("Capsule", tmp.GetComponent().sharedMesh);
384 |
385 | GameObject.DestroyImmediate(tmp);
386 |
387 | tmp = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
388 | builtinMesh.Add("Cylinder", tmp.GetComponent().sharedMesh);
389 |
390 | builtinMaterial = tmp.GetComponent().sharedMaterial;
391 |
392 | GameObject.DestroyImmediate(tmp);
393 | }
394 |
395 | ///
396 | /// Main function for deserializing the scene from file.
397 | ///
398 | private void Deserialize(StreamReader stream, GameObject parent)
399 | {
400 | while (!stream.EndOfStream)
401 | {
402 | string line = stream.ReadLine().Trim();
403 |
404 | ProcessLine(stream, line, parent);
405 | }
406 | }
407 |
408 | private void ProcessLine(StreamReader stream, string line, GameObject parent)
409 | {
410 | if (line.StartsWith("gameobject"))
411 | {
412 | DeserializeGameObject(stream, line.Substring(line.IndexOf(' ')).Trim(), parent);
413 | }
414 | else if (line.StartsWith("prefab"))
415 | {
416 | DeserializePrefab(stream, line.Substring(line.IndexOf(' ')).Trim(), parent);
417 | }
418 | else if (line.StartsWith("textscene"))
419 | {
420 | DeserializeTextScene(stream, line.Substring(line.IndexOf(' ')).Trim(), parent);
421 | }
422 | }
423 |
424 | private void DeserializeTransform(StreamReader stream, Transform transform)
425 | {
426 | string position = stream.ReadLine().Trim();
427 | string rotation = stream.ReadLine().Trim();
428 | string localScale = stream.ReadLine().Trim();
429 |
430 | string[] floats = position.Split();
431 |
432 | transform.localPosition = new Vector3(
433 | float.Parse(floats[0], CultureInfo.InvariantCulture),
434 | float.Parse(floats[1], CultureInfo.InvariantCulture),
435 | float.Parse(floats[2], CultureInfo.InvariantCulture)
436 | );
437 |
438 | floats = rotation.Split();
439 |
440 | transform.localRotation = new Quaternion(
441 | float.Parse(floats[0], CultureInfo.InvariantCulture),
442 | float.Parse(floats[1], CultureInfo.InvariantCulture),
443 | float.Parse(floats[2], CultureInfo.InvariantCulture),
444 | float.Parse(floats[3], CultureInfo.InvariantCulture)
445 | );
446 |
447 | floats = localScale.Split();
448 |
449 | transform.localScale = new Vector3(
450 | float.Parse(floats[0], CultureInfo.InvariantCulture),
451 | float.Parse(floats[1], CultureInfo.InvariantCulture),
452 | float.Parse(floats[2], CultureInfo.InvariantCulture)
453 | );
454 | }
455 |
456 | private void DeserializeTextScene(StreamReader stream, string name, GameObject parent)
457 | {
458 | string line = stream.ReadLine().Trim();
459 |
460 | char [] separators = new char [] {' ', ','};
461 |
462 | string[] parts = line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
463 |
464 | string assetPath = null;
465 |
466 | //Expected format:
467 | // assetpath Asset/Path/asset, GUID
468 | if (parts.Length > 1)
469 | {
470 | assetPath = parts[1].Trim();
471 |
472 | //Prefer GUID over path if present.
473 | if (parts.Length > 2)
474 | {
475 | string guid = parts[2].Trim();
476 |
477 | assetPath = AssetDatabase.GUIDToAssetPath(guid);
478 | }
479 | }
480 |
481 | if (assetPath == null)
482 | {
483 | Debug.LogError("Failed to get path for textscene object '" + name + "': " + line);
484 | return;
485 | }
486 |
487 | string fullPath = EditorHelper.GetProjectFolder() + assetPath;
488 |
489 | GameObject go = null;
490 |
491 | if (currentPass == Pass.CreateObjects)
492 | {
493 | //Random initial name until the local links have been set up.
494 | //FIXME: I can imagine this random stuff can blow up at any point when we least need it.
495 | //go = new GameObject(Random.Range(10000, 100000).ToString());
496 | go = new GameObject(name);
497 | go.AddComponent().textScene = AssetDatabase.LoadAssetAtPath(assetPath, typeof(TextAsset)) as TextAsset;
498 |
499 | //Debug.Log("Full: " + fullPath + " recguard: " + recursionGuard);
500 |
501 | string fullName = "/" + name;
502 |
503 | if (parent != null)
504 | {
505 | fullName = Helper.GetFullName(parent) + fullName;
506 | }
507 |
508 | if (fullPath.ToLower() == recursionGuard.ToLower())
509 | {
510 | EditorUtility.DisplayDialog("Recursion guard", "Loading this TextScene (" + assetPath + ") into the current TextScene will throw you into an infinite recursion. Please remove the TextScene object (" + fullName + ") or change the TextScene it points to so it no longer results in a loop", "OK");
511 | }
512 | else
513 | {
514 | bool result = (new TextSceneDeserializer(go, recursionGuard)).LoadScene(fullPath);
515 |
516 | if (!result)
517 | Debug.LogError("Failed to load TextSceneObject: " + line);
518 | }
519 |
520 | gameObjects.Enqueue(go);
521 | }
522 | else
523 | {
524 | go = gameObjects.Dequeue();
525 | }
526 |
527 | go.name = name;
528 |
529 | if (parent != null)
530 | go.transform.parent = parent.transform;
531 |
532 | DeserializeTransform(stream, go.transform);
533 | }
534 |
535 | private void DeserializePrefab(StreamReader stream, string name, GameObject parent)
536 | {
537 | GameObject prefab = null;
538 |
539 | string line = stream.ReadLine().Trim();
540 |
541 | char [] separators = new char [] {' ', ','};
542 |
543 | string[] parts = line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
544 |
545 | //Expected format:
546 | // assetpath Asset/Path/asset, GUID
547 | if (parts.Length > 1)
548 | {
549 | string assetPath = parts[1].Trim();
550 |
551 | //Prefer GUID over path if present.
552 | if (parts.Length > 2)
553 | {
554 | string guid = parts[2].Trim();
555 |
556 | assetPath = AssetDatabase.GUIDToAssetPath(guid);
557 | }
558 |
559 |
560 | prefab = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
561 | }
562 |
563 | if (prefab == null)
564 | {
565 | Debug.LogError("Failed to load asset for object '" + name + "': " + line);
566 | return;
567 | }
568 |
569 |
570 | GameObject go = null;
571 |
572 |
573 | if (currentPass == Pass.CreateObjects)
574 | {
575 | go = EditorUtility.InstantiatePrefab(prefab) as GameObject;
576 | gameObjects.Enqueue(go);
577 | }
578 | else
579 | go = gameObjects.Dequeue();
580 |
581 | go.name = name;
582 |
583 | if (parent != null)
584 | go.transform.parent = parent.transform;
585 |
586 | DeserializeTransform(stream, go.transform);
587 |
588 |
589 |
590 | /*
591 | //Had to try to see if this was related to save/load prefab bugs (336621)
592 |
593 | //EditorUtility.ResetGameObjectToPrefabState(go);
594 |
595 |
596 |
597 | SerializedObject serObj = new SerializedObject(go);
598 |
599 | SerializedProperty nameProp = serObj.FindProperty("m_Name");
600 | //SerializedProperty localPosX = serObj.FindProperty("m_LocalPosition.x");
601 | //SerializedProperty localPosY = serObj.FindProperty("m_LocalPosition.y");
602 | //SerializedProperty localPosZ = serObj.FindProperty("m_LocalPosition.z");
603 |
604 | nameProp.stringValue = name;
605 | //localPosX.floatValue = go.transform.localPosition.x;
606 | //localPosY.floatValue = go.transform.localPosition.y;
607 | //localPosZ.floatValue = go.transform.localPosition.z;
608 | nameProp.serializedObject.ApplyModifiedProperties();
609 |
610 | serObj.ApplyModifiedProperties();*/
611 | }
612 |
613 | private void DeserializeGameObject(StreamReader stream, string name, GameObject parent)
614 | {
615 | //Debug.Log("Loading GameObject: " + name);
616 |
617 | GameObject go = null;
618 |
619 |
620 | if (currentPass == Pass.CreateObjects)
621 | {
622 | go = new GameObject();
623 | gameObjects.Enqueue(go);
624 | }
625 | else
626 | go = gameObjects.Dequeue();
627 |
628 | go.name = name;
629 |
630 | if (parent != null)
631 | {
632 | //Debug.Log("Parent: " + parent.name);
633 | go.transform.parent = parent.transform;
634 | }
635 | else
636 | {
637 | //Debug.Log("Parent: null");
638 | }
639 |
640 | string tagLayerLine = stream.ReadLine();
641 |
642 | string[] tagLayerLineElements = tagLayerLine.Trim().Split();
643 |
644 | go.tag = tagLayerLineElements[1];
645 | go.layer = int.Parse(tagLayerLineElements[3], CultureInfo.InvariantCulture);
646 |
647 | DeserializeTransform(stream, go.transform);
648 |
649 | string children = stream.ReadLine().Trim();
650 |
651 | if (children.Contains("children"))
652 | {
653 | int childrenCount = int.Parse(children.Split()[1], CultureInfo.InvariantCulture);
654 |
655 | for (int i = 0; i < childrenCount; i++)
656 | {
657 | string child = stream.ReadLine().Trim();
658 |
659 | ProcessLine(stream, child, go);
660 | }
661 | }
662 |
663 | string components = stream.ReadLine().Trim();
664 |
665 | if (components.Contains("components"))
666 | {
667 | int componentCount = int.Parse(components.Split()[1], CultureInfo.InvariantCulture);
668 |
669 | //TODO: Subroutine each component read.
670 | for (int c = 0; c < componentCount; c++)
671 | {
672 | string line = stream.ReadLine().Trim();
673 |
674 | //Debug.Log("Reading component: " + line);
675 |
676 | string[] componentSplit = line.Trim().Split();
677 |
678 | string componentName = componentSplit[1];
679 |
680 | int fieldCount = int.Parse(componentName, CultureInfo.InvariantCulture);
681 |
682 | Component comp = null;
683 |
684 | if (currentPass == Pass.CreateObjects)
685 | {
686 | comp = go.AddComponent(componentSplit[0]);
687 | gameComponents.Enqueue(comp);
688 | }
689 | else
690 | comp = gameComponents.Dequeue();
691 |
692 | for (int i = 0; i < fieldCount; i++)
693 | {
694 | ProcessValueLine(stream, componentName, comp);
695 | }
696 | }
697 | }
698 | }
699 |
700 | private bool ProcessValueLine(StreamReader stream, string name, object obj)
701 | {
702 | //TODO: Rename to 'member'
703 | string field = stream.ReadLine().Trim();
704 |
705 | //Debug.Log("Reading member: " + field);
706 |
707 | string[] fieldElements = field.Split();
708 |
709 | string fieldType = fieldElements[0];
710 | string fieldName = fieldElements[1];
711 | string fieldEditorType = fieldElements[2];
712 | string fieldObjectType = fieldElements[3];
713 | string fieldValue = field.Substring(field.LastIndexOf('=')+1).Trim();
714 |
715 | //Debug.Log("Reading component member: " + field);
716 |
717 | object parameter = null;
718 |
719 | if (ReadComponentValue(stream, fieldEditorType, fieldObjectType, fieldValue, ref parameter) == false)
720 | return false;
721 |
722 |
723 | if (obj == null)
724 | {
725 | Debug.LogWarning("Skipping member for null object: " + name);
726 | return false;
727 | }
728 |
729 |
730 | if (currentPass == Pass.ValueAssignment)
731 | AssignValue(obj, fieldType, fieldName, parameter);
732 |
733 | return true;
734 | }
735 |
736 | private void AssignValue(object comp, string fieldType, string fieldName, object parameter)
737 | {
738 | try
739 | {
740 | if (fieldType == "field")
741 | {
742 | FieldInfo fi = comp.GetType().GetField(fieldName);
743 |
744 | if (fi != null)
745 | {
746 | fi.SetValue(comp, parameter);
747 | }
748 | else
749 | Debug.LogWarning("Failed to find field (" + fieldName + ") on object: " + comp.GetType().ToString());
750 | }
751 | else if (fieldType == "property")
752 | {
753 | PropertyInfo pi = comp.GetType().GetProperty(fieldName);
754 |
755 | if (pi != null)
756 | {
757 | MethodInfo mi = pi.GetSetMethod();
758 |
759 | if (mi != null)
760 | {
761 | object[] paramList = {parameter};
762 |
763 | //Debug.Log("Set property: " + fieldName);
764 |
765 | mi.Invoke(comp, paramList);
766 | }
767 | else
768 | {
769 | Debug.LogWarning("No setter for property: " + fieldName);
770 | }
771 | }
772 | else
773 | Debug.LogWarning("Failed to find property (" + fieldName + ") on object: " + comp.GetType().ToString());
774 |
775 | }
776 | else
777 | Debug.LogWarning("Assignment of '" + fieldName + "' failed - invalid field type: " + fieldType);
778 | }
779 | catch (System.Exception e)
780 | {
781 | Debug.LogWarning("Assignment of '" + fieldName + "' failed. Did it change type since the TextScene was saved? " + e.ToString());
782 | }
783 | }
784 |
785 | private static float ConvertFloat(string s)
786 | {
787 | try
788 | {
789 | return float.Parse(s, CultureInfo.InvariantCulture);
790 | }
791 | catch
792 | {
793 | if (s.ToLower() == "infinity")
794 | return float.PositiveInfinity;
795 | }
796 |
797 | return float.NaN;
798 | }
799 |
800 | private bool ReadComponentValue(StreamReader stream, string fieldEditorType, string fieldObjectType, string fieldValue, ref object parameter)
801 | {
802 | if (fieldEditorType == "asset")
803 | {
804 | System.Type type = Assembly.GetAssembly(typeof(UnityEngine.Object)).GetType(fieldObjectType);
805 |
806 | if (type == null)
807 | {
808 | //FIXME: Hacky way to get assembly?
809 | type = Assembly.GetAssembly(typeof(TextSceneObject)).GetType(fieldObjectType);
810 |
811 | if (type == null)
812 | {
813 | Debug.LogError("Failed to get asset type: " + fieldObjectType);
814 | return false;
815 | }
816 | }
817 |
818 |
819 | if (fieldValue.Length > 0)
820 | {
821 | string[] assetParts = fieldValue.Split(',');
822 |
823 | //Expected format:
824 | // Asset/Path/Comes/First, AssetName, GUID
825 | if (assetParts.Length >= 1)
826 | {
827 | string assetPath = assetParts[0].Trim();
828 |
829 | //We'll use the GUID instead of the filename to get the asset.
830 | if (assetParts.Length >= 3)
831 | {
832 | string guid = assetParts[2].Trim();
833 |
834 | assetPath = AssetDatabase.GUIDToAssetPath(guid);
835 | }
836 |
837 | //If there is an assetname defined, we'll search all assets at path instead
838 | //of using the root object.
839 | if (assetParts.Length >= 2)
840 | {
841 | Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
842 |
843 | string assetName = assetParts[1].Trim();
844 |
845 | foreach(Object asset in assets)
846 | {
847 | if (asset.GetType() == type && asset.name == assetName)
848 | {
849 | parameter = asset;
850 | break;
851 | }
852 | }
853 | }
854 | else
855 | {
856 | //Just use whatever the assetpath resolves to if no assetname is defined.
857 | parameter = AssetDatabase.LoadAssetAtPath(assetPath, type);
858 | }
859 | }
860 | }
861 | }
862 | else if (fieldEditorType == "scenelink")
863 | {
864 | System.Type type = Assembly.GetAssembly(typeof(UnityEngine.Object)).GetType(fieldObjectType);
865 |
866 | if (type == null)
867 | {
868 | //FIXME: Hacky way to get assembly?
869 | type = Assembly.GetAssembly(typeof(TextSceneObject)).GetType(fieldObjectType);
870 |
871 | if (type == null)
872 | {
873 | Debug.LogError("Failed to get object type: " + fieldObjectType);
874 | return false;
875 | }
876 | }
877 |
878 |
879 | if (currentPass == Pass.ValueAssignment && fieldValue.Length > 0)
880 | {
881 | string prefix = container != null ? Helper.GetFullName(container) : "";
882 |
883 | string fullName = prefix + fieldValue;
884 |
885 | GameObject[] matches = Helper.FindGameObjectsFromFullName(fullName);
886 | GameObject go = matches.Length > 0 ? matches[0] : null;
887 |
888 | //FIXME: Why on Earth doesn't this work always?
889 | // Try it having a reference to /Geometry/box/box_instance2 and
890 | // having a second object in the scene at the path
891 | // /Geometry/box/box_instance
892 | // Reported unity case #336886 on this.
893 | //GameObject go = GameObject.Find(fullName);
894 |
895 | if (go == null)
896 | {
897 | EditorUtility.DisplayDialog("Error", "Unable to find object in scene: '" + fullName + "'", "OK");
898 | return false;
899 | }
900 | else
901 | {
902 | if (type.Equals(typeof(GameObject)))
903 | parameter = go;
904 | else
905 | parameter = go.GetComponent(type);
906 | }
907 | }
908 | }
909 | else if (fieldEditorType == "builtinmesh")
910 | {
911 | if (currentPass == Pass.ValueAssignment)
912 | {
913 | if (builtinMesh.ContainsKey(fieldValue))
914 | parameter = builtinMesh[fieldValue];
915 | else
916 | Debug.LogWarning("Unknown builtin mesh: " + fieldValue);
917 | }
918 | }
919 | else if (fieldEditorType == "builtinmaterial")
920 | {
921 | if (currentPass == Pass.ValueAssignment)
922 | {
923 | parameter = builtinMaterial;
924 | }
925 | }
926 | else if (fieldEditorType == "primitive")
927 | {
928 | System.Type primitiveType = System.Type.GetType(fieldObjectType);
929 |
930 | if (primitiveType == null)
931 | {
932 | primitiveType = Assembly.GetAssembly(typeof(UnityEngine.Object)).GetType(fieldObjectType);
933 | }
934 | if (primitiveType == null)
935 | {
936 | primitiveType = Assembly.GetAssembly(typeof(TextSceneObject)).GetType(fieldObjectType);
937 | }
938 | if (primitiveType == null)
939 | {
940 | Debug.LogError("No primitive type found for '" + fieldObjectType + "'");
941 | return false;
942 | }
943 |
944 |
945 | //Debug.Log("Handling type: " + fieldObjectType);
946 |
947 | char [] separators = new char [] {' ', ',', '(', ')' };
948 |
949 | if (typeof(System.Single) == primitiveType)
950 | {
951 | parameter = ConvertFloat(fieldValue);
952 | }
953 | else if (typeof(System.Int32) == primitiveType)
954 | parameter = int.Parse(fieldValue, CultureInfo.InvariantCulture);
955 | else if (typeof(System.Boolean) == primitiveType)
956 | parameter = bool.Parse(fieldValue);
957 | else if (typeof(System.String) == primitiveType)
958 | parameter = fieldValue;
959 | else if (typeof(UnityEngine.Vector4) == primitiveType)
960 | {
961 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
962 |
963 | parameter = new Vector4(float.Parse(v[0], CultureInfo.InvariantCulture),
964 | float.Parse(v[1], CultureInfo.InvariantCulture),
965 | float.Parse(v[2], CultureInfo.InvariantCulture),
966 | float.Parse(v[3], CultureInfo.InvariantCulture));
967 | }
968 | else if (typeof(UnityEngine.Quaternion) == primitiveType)
969 | {
970 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
971 |
972 | parameter = new Quaternion(float.Parse(v[0], CultureInfo.InvariantCulture),
973 | float.Parse(v[1], CultureInfo.InvariantCulture),
974 | float.Parse(v[2], CultureInfo.InvariantCulture),
975 | float.Parse(v[3], CultureInfo.InvariantCulture));
976 | }
977 | else if (typeof(UnityEngine.Vector3) == primitiveType)
978 | {
979 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
980 |
981 | parameter = new Vector3(float.Parse(v[0], CultureInfo.InvariantCulture),
982 | float.Parse(v[1], CultureInfo.InvariantCulture),
983 | float.Parse(v[2], CultureInfo.InvariantCulture));
984 | }
985 | else if (typeof(UnityEngine.Vector2) == primitiveType)
986 | {
987 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
988 |
989 | parameter = new Vector2(float.Parse(v[0], CultureInfo.InvariantCulture),
990 | float.Parse(v[1], CultureInfo.InvariantCulture));
991 | }
992 | else if (typeof(UnityEngine.Color) == primitiveType)
993 | {
994 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
995 |
996 | parameter = new Color(float.Parse(v[0], CultureInfo.InvariantCulture),
997 | float.Parse(v[1], CultureInfo.InvariantCulture),
998 | float.Parse(v[2], CultureInfo.InvariantCulture),
999 | float.Parse(v[3], CultureInfo.InvariantCulture));
1000 | }
1001 | else if (typeof(UnityEngine.Rect) == primitiveType)
1002 | {
1003 | string[] v = fieldValue.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
1004 |
1005 | parameter = new Rect(float.Parse(v[0], CultureInfo.InvariantCulture),
1006 | float.Parse(v[1], CultureInfo.InvariantCulture),
1007 | float.Parse(v[2], CultureInfo.InvariantCulture),
1008 | float.Parse(v[3], CultureInfo.InvariantCulture));
1009 | }
1010 | else if (primitiveType.IsEnum)
1011 | {
1012 | string[] names = System.Enum.GetNames(primitiveType);
1013 | System.Array values = System.Enum.GetValues(primitiveType);
1014 |
1015 | for (int ei = 0; ei < names.Length; ei++)
1016 | {
1017 | if (names[ei] == fieldValue)
1018 | {
1019 | //Debug.Log(primitiveType.ToString() + ": '" + names[ei] + "' set value: '" + fieldValue + "'");
1020 | parameter = values.GetValue(ei);
1021 | break;
1022 | }
1023 | }
1024 | }
1025 | else
1026 | Debug.LogWarning("Unhandled field type: " + fieldObjectType);
1027 | }
1028 | else if (fieldEditorType == "array")
1029 | {
1030 | System.Type arrayType = System.Type.GetType(fieldObjectType);
1031 |
1032 | if (arrayType == null)
1033 | arrayType = Assembly.GetAssembly(typeof(UnityEngine.Object)).GetType(fieldObjectType);
1034 |
1035 | //FIXME: Hacky way to get assembly?
1036 | if (arrayType == null)
1037 | arrayType = Assembly.GetAssembly(typeof(TextSceneObject)).GetType(fieldObjectType);
1038 |
1039 | if (arrayType == null)
1040 | {
1041 | Debug.LogWarning("Found array of unresolvable type: " + fieldObjectType);
1042 | return false;
1043 | }
1044 |
1045 | System.Type arrayElementType = arrayType.GetElementType();
1046 |
1047 | parameter = System.Array.CreateInstance(arrayElementType, int.Parse(fieldValue, CultureInfo.InvariantCulture));
1048 |
1049 |
1050 | System.Array arrayEntries = parameter as System.Array;
1051 |
1052 |
1053 | //Debug.Log("Found array: " + arrayElementType.ToString() + " length: " + arrayEntries.Length);
1054 |
1055 | for (int e = 0; e < arrayEntries.Length; e++)
1056 | {
1057 | string arrayEntryLine = stream.ReadLine().Trim();
1058 |
1059 | //Debug.Log("Reading array entry: " + arrayEntryLine);
1060 |
1061 | string[] elements = arrayEntryLine.Split();
1062 |
1063 | string editorType = elements[0];
1064 | string objectType = elements[1];
1065 | string val = arrayEntryLine.Substring(arrayEntryLine.LastIndexOf('=')+1).Trim();
1066 |
1067 | //Debug.Log("Array entry: " + editorType + " " + objectType + " = " + val);
1068 |
1069 | object arrayEntry = null;
1070 |
1071 | ReadComponentValue(stream, editorType, objectType, val, ref arrayEntry);
1072 |
1073 | arrayEntries.SetValue(arrayEntry, e);
1074 | }
1075 | }
1076 | else if (fieldEditorType == "complex")
1077 | {
1078 | System.Type complexType = System.Type.GetType(fieldObjectType);
1079 |
1080 | if (complexType == null)
1081 | complexType = Assembly.GetAssembly(typeof(UnityEngine.Object)).GetType(fieldObjectType);
1082 |
1083 | //FIXME: Hacky way to get assembly?
1084 | if (complexType == null)
1085 | complexType = Assembly.GetAssembly(typeof(TextSceneObject)).GetType(fieldObjectType);
1086 |
1087 | if (complexType == null)
1088 | {
1089 | Debug.LogWarning("Failed to find type: " + fieldObjectType);
1090 | return false;
1091 | }
1092 |
1093 | parameter = System.Activator.CreateInstance(complexType);
1094 |
1095 | int members = int.Parse(fieldValue, CultureInfo.InvariantCulture);
1096 |
1097 | //Debug.Log("Reading complex type: " + complexType.ToString() + " members: " + members);
1098 |
1099 | for(int i = 0; i < members; i++)
1100 | ProcessValueLine(stream, fieldObjectType, parameter);
1101 |
1102 | return true;
1103 | }
1104 | else
1105 | return false;
1106 |
1107 | return true;
1108 | }
1109 | }
--------------------------------------------------------------------------------