The current version limits itself to instantiating prefabs that does not differ from the blueprint, although it should be a rather simple fix to do that, I just don't need it myself :)
You need to use JSONFX to complete this tutorial
In this example I have a number of worlds, each with a number of levels.
Each of these levels contains a number of walls (All instances of a wall prefab with the tag “Wall”)
Additionally the level has a gameobject called info with the following monobehaviour attached: Basically the only this here is we have the ability to mark each level with a specific world, not at all necessary for everybody, but I need it :)
using UnityEngine; using System.Collections; public class LevelInfo : MonoBehaviour { public GameEnum.WorldEnum World; } public class GameEnum { public enum WorldEnum { World1, World2, World3 } public enum PrefabEnum { PrefabWalls } }
The following script must be placed in a Assets/Editor folder, and adds a menuitem under Tools/CustomHelpers. It runs through the currently open scene, locates all prefabs with tag “Wall” and serializes the scene to a textasset which is placed in a corresponding folder.
using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.IO; public class SerializeScene : ScriptableWizard { public string Folder = "Levels"; string assetPath; [UnityEditor.MenuItem("Tools/CustomHelpers/Serialize Scene")] static void SerializeOpenScene() { SerializeScene ss = (SerializeScene)ScriptableWizard.DisplayWizard("Serialize Scene", typeof(SerializeScene)); } void OnWizardCreate() { //Find Levelinfo and extract info on which world the level belongs to LevelInfo li = FindObjectOfType(typeof(LevelInfo)) as LevelInfo; GameEnum.WorldEnum world = li.World; // Get the path we'll use to write our assets: assetPath = Application.dataPath + "/Resources/" + Folder + "/" + world.ToString() + "/"; Debug.Log("Save at " + assetPath); // Create the folder that will hold our assets: Directory.CreateDirectory(assetPath); FindAssets(); // Make sure the new assets are (re-)imported: AssetDatabase.Refresh(); } private void FindAssets() { List<GameObject> objList = new List<GameObject>(); LevelData newLevel = new LevelData(); //Trim string to leave out folder information newLevel.Name = trimStringToSceneName(EditorApplication.currentScene); //Walls GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall"); Debug.Log("We found " + walls.Length + " walls"); addObjects(walls, GameEnum.PrefabEnum.PrefabWalls, ref newLevel); string newObject = JsonFx.Json.JsonWriter.Serialize(newLevel); FileStream fs = new FileStream(assetPath + newLevel.Name + ".txt", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter sw = new StreamWriter(fs); sw.Write(newObject); sw.Close(); fs.Close(); } private void addObjects(GameObject[] objList, GameEnum.PrefabEnum prefabEnum, ref LevelData level) { for (int i = 0; i < objList.Length; ++i) { //Only look for instances of prefabs if (PrefabType.PrefabInstance == EditorUtility.GetPrefabType(objList[i])) { GameObject root = EditorUtility.FindPrefabRoot(objList[i]); //Only add to list if its the root to make sure the same object is not added several times if (root == objList[i]) { Debug.Log("It's the Root"); level.AddNewPrefab(objList[i], prefabEnum); } else { Debug.Log("It's not the root, so we dont add it to list"); } } else { Debug.Log("NOT A PREFAB"); } } } private string trimStringToSceneName(string path) { string sceneName = string.Empty; //Removing all but levelname + ".unity" string tmpString = path.Remove(0, path.Length - 13); //removing ".unity" sceneName = tmpString.Substring(0, tmpString.Length - 6); return sceneName; } }
You will need these classes as well:
using System.Collections.Generic; using UnityEngine; public class LevelData { public string Name; public List<SpawnType> ObjectsToSpawn = new List<SpawnType>(); public void AddNewPrefab(GameObject newObj, GameEnum.PrefabEnum prefabEnum) { Vector3 pos = newObj.transform.position; ObjectsToSpawn.Add(new SpawnType(prefabEnum, pos)); } } public class SpawnType { public GameEnum.PrefabEnum PrefabType; public float PosX; public float PosY; public float PosZ; public SpawnType() { } public SpawnType(GameEnum.PrefabEnum prefabEnum, Vector3 _transformVector) { PrefabType = prefabEnum; PosX = _transformVector.x; PosY = _transformVector.y; PosZ = _transformVector.z; } public Vector3 GetVector3() { return new Vector3(PosX, PosY, PosZ); } }
Ok, so now we're able to serialize a scene, saving the type and location of prefabs, and tag it with the corresponding world. Now we need a scene which is able to deserialize the textassets and instantiate the prefabs. Create a new scene called LevelLoader, create an empty gameobject, name it “Loader” and give it the following LevelLoader script.
using UnityEngine; using System.Collections; public class LevelLoader : MonoBehaviour { public TextAsset LevelToLoad; private LevelData levelData; // Use this for initialization void Start() { levelData = JsonFx.Json.JsonReader.Deserialize<LevelData>(LevelToLoad.text); Debug.Log("leveldata loaded from textasset and deserialized"); initiateLevel(); } private void initiateLevel() { GameObject wallParent = new GameObject(); Transform walltransform = wallParent.transform; wallParent.name = "Walls"; foreach (SpawnType go in levelData.ObjectsToSpawn) { //You need to have you Prefabs placed in a "Prefabs" folder string resourcePos = "Prefabs/" + go.PrefabType.ToString(); Object objLoaded = Resources.Load(resourcePos); Vector3 pos = go.GetVector3(); GameObject newObj = Instantiate(objLoaded, pos, Quaternion.identity) as GameObject; newObj.transform.parent = walltransform; } } }The above code creates an empty gameobject called walls to use as a parent for all the instantiated prefab, feel free to make this more dynamic. I'll do it myself at a later stage but for the time being it suits my needs. This should allow you to instantiate any number of levels by serializing them and then just include a single scene in your final build that can deserialize it and spawn the relevant gameobjects. Screenshot of the Leveloader scene with the instantiated prefabs:
Hope you found this helpful, although it's mainly a proof of concept. I'll make it more dynamic when I get around to it.
Here's an example: Unity Package