- Home /
Post-Process All Unity Scene GameObjects
I want to loop through all of my project's scenes in a specific folder to figure out if they have certain GameObjects a player can collect. Super Metroid does this in a similar fashion, where it marks the location of items you can collect on a map. Then if you collect an item, its marked as collected.
I need to store the item location data inside a file or GameObject I can reference while the game is running. This should be doable from the Unity editor through a menu command.
Answer by jimfleming · Dec 11, 2014 at 06:09 PM
I think this is what you want. If you need to be able to iterate through the scenes to gather the game objects while its running you'll have to manage the play state manually using EditorApplication.isPlaying
.
using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public static class GatherGameObjects {
public static List<string> GetScenes(string basePath = "") {
List<string> scenes = new List<string>();
foreach (UnityEditor.EditorBuildSettingsScene scene in UnityEditor.EditorBuildSettings.scenes) {
if (scene.enabled && scene.path.StartsWith(basePath)) {
scenes.Add(scene.path);
}
}
return scenes;
}
public static List<GameObject> GatherGameObjectsByTag(List<string> scenes, string tag) {
// The GO references probably go out of scope when the scene closes
// (You'll want to store something else like their absolute path in the hierarchy)
var gameObjects = new List<GameObject>();
// Save the current scene before attempting to open others
var safe = EditorApplication.SaveCurrentSceneIfUserWantsTo();
if (safe) {
foreach (var scene in scenes) {
EditorApplication.OpenScene(scene);
// If you want to use different criteria try Object.FindObjectsOfType<GameObject>()
// I don't think this includes inactive but maybe that doesn't matter
gameObjects.AddRange(GameObject.FindGameObjectsWithTag(tag));
}
}
return gameObjects;
}
[MenuItem("GameObject/Gather Collectible GameObjects")]
public static void GatherCollectibleGameObjects() {
var scenes = GetScenes("Assets/");
var gameObjects = GatherGameObjectsByTag(scenes, "Collectible");
// TODO: Write to file
Debug.Log(gameObjects.Count);
}
}
Here is the solution I came up with based upon this code. It includes some extra logic to make things Web Player safe and includes JSON file writing logic.
using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
public class Record$$anonymous$$apLocations : $$anonymous$$onoBehaviour {
// Add a menu item named "Do Something" to $$anonymous$$y$$anonymous$$enu in the menu bar.
[$$anonymous$$enuItem ("PostProcess/Record $$anonymous$$ap Locations")]
static void DoSomething () {
# if UNITY_WEBPLAYER
Debug.LogError("File writing is blocked in Web Player platform, please change to a different platform and try again");
# elif UNITY_STANDALONE
bool safe = EditorApplication.SaveCurrentSceneIfUserWantsTo();
Dictionary<string, int> collectibleCount = new Dictionary<string, int>();
Dictionary<string, bool> respawn = new Dictionary<string, bool>();
// If the user doesn't make a menu choice to save exit early
if (safe) {
List<string> scenes = GetScenes("Assets/Scenes");
foreach (string s in scenes) {
EditorApplication.OpenScene(s);
string safeSceneName = s.Split('/').Last().Replace(".unity", "");
collectibleCount[safeSceneName] = CountGameObjectsByTag("Collectible");
respawn[safeSceneName] = CountGameObjectsByTag("Respawn") > 0;
}
// Serializes data to JSON, uses JSON.net from the Unity asset store, but feel free to use your
// own solution
string jsonCollectible = JsonConvert.SerializeObject(collectibleCount, Formatting.Indented);
string jsonRespawn = JsonConvert.SerializeObject(respawn, Formatting.Indented);
// Dump both strings into proper files via System.IO
WriteFile("$$anonymous$$apCollectibles.json", jsonCollectible);
WriteFile("$$anonymous$$apRespawn.json", jsonRespawn);
UnityEditor.AssetDatabase.Refresh();
}
#endif
}
public static void WriteFile (string fileName, string text) {
// Force suppress a Web Player platform error caused by removing System.IO file writing
# if UNITY_STANDALONE
File.WriteAllText(Application.dataPath + "/Resources/Data/" + fileName, text);
# endif
}
public static List<string> GetScenes(string basePath = "") {
List<string> scenes = new List<string>();
foreach (UnityEditor.EditorBuildSettingsScene scene in UnityEditor.EditorBuildSettings.scenes) {
if (scene.enabled && scene.path.StartsWith(basePath)) {
scenes.Add(scene.path);
}
}
return scenes;
}
// @I$$anonymous$$PORTANT This will wipe the user's current scene data if it isn't saved, be careful
public static int CountGameObjectsByTag (string tag) {
return GameObject.FindGameObjectsWithTag(tag).Length;
}
}