Add Play Scene Tool
This commit is contained in:
1 parent
fd869c9cf8
commit
db10eef525
7 files changed
+473
No files matched your search
@@ -0,0 +1,45 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Chameleon
|
||||
{
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Tools/Chameleon/Default Parent (Editor)")]
|
||||
public class DefaultParentForEditor : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
static DefaultParentForEditor selection;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void Init() => selection = null;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
return;
|
||||
|
||||
var activeGameObject = Selection.activeGameObject;
|
||||
if (activeGameObject != gameObject)
|
||||
return;
|
||||
|
||||
if (!activeGameObject.TryGetComponent(out DefaultParentForEditor defaultParent))
|
||||
return;
|
||||
|
||||
if (selection && selection != defaultParent)
|
||||
{
|
||||
selection = null;
|
||||
EditorUtility.ClearDefaultParentObject();
|
||||
}
|
||||
|
||||
if (!selection)
|
||||
{
|
||||
selection = this;
|
||||
EditorUtility.SetDefaultParentObject(gameObject);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00d9c432a5ead1540b39d22553c41f76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: a360cdd2cf64e48469885fdcb0737dd1, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dde47c6dc3068974989b83b6a1aad5bb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,354 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using F = System.Reflection.BindingFlags;
|
||||
|
||||
namespace Chameleon.Editor
|
||||
{
|
||||
[ExecuteAlways]
|
||||
public class PlaySceneWindow : EditorWindow
|
||||
{
|
||||
[MenuItem("Tools/Chameleon/Play Scene/Open Window")]
|
||||
public static void Open()
|
||||
=> _ = GetWindow<PlaySceneWindow>("Play Scene");
|
||||
|
||||
[SerializeField] bool enabled = true;
|
||||
|
||||
[SerializeField] string lastActiveScene;
|
||||
[SerializeField] List<string> lastScenePaths = new();
|
||||
|
||||
[SerializeField] string scenePath;
|
||||
[SerializeField] int sceneIndex;
|
||||
|
||||
static List<(string path, SceneCollection collection)> sceneCollections;
|
||||
static string[] scenePaths;
|
||||
static string[] sceneNames;
|
||||
|
||||
static string activeScenePath
|
||||
=> SceneManager.GetActiveScene().path;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
OnDisable();
|
||||
|
||||
RefreshSceneCollections();
|
||||
|
||||
scenePaths = EditorBuildSettings.scenes.Select(x => x.path).ToArray();
|
||||
sceneNames = scenePaths.Select(Path.GetFileNameWithoutExtension).ToArray();
|
||||
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
void RefreshSceneCollections()
|
||||
{
|
||||
sceneCollections = AssetDatabase.FindAssets($"t:{nameof(SceneCollection)}")
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(AssetDatabase.LoadAssetAtPath<SceneCollection>)
|
||||
.Where(x => x)
|
||||
.Select(x => (
|
||||
string.Join(";", x.sceneAssets.Select(AssetDatabase.GetAssetPath)),
|
||||
x
|
||||
))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (state != PlayModeStateChange.ExitingEditMode)
|
||||
return;
|
||||
|
||||
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
|
||||
EditorApplication.ExitPlaymode();
|
||||
|
||||
lastActiveScene = SceneManager.GetActiveScene().path;
|
||||
lastScenePaths = Enumerable
|
||||
.Range(0, SceneManager.sceneCount)
|
||||
.Select(i => SceneManager.GetSceneAt(i).path)
|
||||
.ToList();
|
||||
|
||||
scenePath = scenePaths[sceneIndex];
|
||||
EditorSceneManager.OpenScene(scenePath);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (EditorApplication.isPlaying)
|
||||
return;
|
||||
|
||||
if (lastScenePaths.Count == 0)
|
||||
return;
|
||||
|
||||
if (scenePath is not null)
|
||||
return;
|
||||
|
||||
var scene = lastScenePaths.First();
|
||||
if (scene == string.Empty)
|
||||
return;
|
||||
|
||||
EditorSceneManager.OpenScene(scene);
|
||||
foreach (var scenePath in lastScenePaths.Skip(1))
|
||||
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
||||
|
||||
if (!string.IsNullOrEmpty(lastActiveScene))
|
||||
{
|
||||
var activeScene = SceneManager.GetSceneByPath(lastActiveScene);
|
||||
if (activeScene.IsValid())
|
||||
SceneManager.SetActiveScene(activeScene);
|
||||
lastActiveScene = string.Empty;
|
||||
}
|
||||
|
||||
lastScenePaths.Clear();
|
||||
}
|
||||
|
||||
static GUIStyle blockStyle1;
|
||||
static GUIStyle blockStyle2;
|
||||
static GUIStyle buttonStyle;
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
blockStyle1 ??= new() { padding = new(6, 6, 6, 3) };
|
||||
blockStyle2 ??= new() { padding = new(6, 6, 3, 6) };
|
||||
buttonStyle ??= new(GUI.skin.button) { padding = new(3, 3, 1, 1) };
|
||||
|
||||
System.Action action = null;
|
||||
|
||||
var isPlaying = EditorApplication.isPlaying;
|
||||
|
||||
GUI.FocusControl(null);
|
||||
|
||||
GUILayout.BeginHorizontal(blockStyle1);
|
||||
{
|
||||
GUI.enabled = !isPlaying;
|
||||
|
||||
var path = string.Join(";", Enumerable
|
||||
.Range(0, SceneManager.sceneCount)
|
||||
.Select(i => SceneManager.GetSceneAt(i).path)
|
||||
);
|
||||
|
||||
var options = sceneCollections.Select(x => x.collection.name);
|
||||
|
||||
var index = sceneCollections.FindIndex(x => x.path == path);
|
||||
if (index == -1)
|
||||
{
|
||||
index = 0;
|
||||
options = options.Prepend("(New Scene Collection)");
|
||||
var newIndex = EditorGUILayout.Popup(index, options.ToArray());
|
||||
if (newIndex != index)
|
||||
sceneCollections[newIndex - 1].collection.Open();
|
||||
if (GUILayout.Button("Create", buttonStyle, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
var sceneCollection = CreateInstance<SceneCollection>();
|
||||
sceneCollection.sceneAssets.AddRange(
|
||||
Enumerable
|
||||
.Range(0, SceneManager.sceneCount)
|
||||
.Select(i => SceneManager.GetSceneAt(i).path)
|
||||
.Select(AssetDatabase.LoadAssetAtPath<SceneAsset>)
|
||||
);
|
||||
var assetPath = AssetDatabase.GenerateUniqueAssetPath($"Assets/Untitled Scene Collection.asset");
|
||||
AssetDatabase.CreateAsset(sceneCollection, assetPath);
|
||||
AssetDatabase.Refresh();
|
||||
RefreshSceneCollections();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newIndex = EditorGUILayout.Popup(index, options.ToArray());
|
||||
if (newIndex != index)
|
||||
sceneCollections[newIndex].collection.Open();
|
||||
}
|
||||
|
||||
if (GUILayout.Button(EditorGUIUtility.IconContent("d_Refresh"), buttonStyle, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
action = OnEnable;
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
if (isPlaying)
|
||||
{
|
||||
GUI.color = new(.5f, 1, .5f);
|
||||
GUI.enabled = false;
|
||||
if (scenePath is not null && scenePath == activeScenePath)
|
||||
scenePath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enabled)
|
||||
GUI.color = new(.75f, 1, .75f);
|
||||
|
||||
if (scenePath == activeScenePath)
|
||||
EditorApplication.EnterPlaymode();
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(blockStyle2);
|
||||
{
|
||||
enabled = EditorGUILayout.Toggle(enabled, GUILayout.Width(16));
|
||||
GUI.enabled = enabled && !isPlaying;
|
||||
sceneIndex = EditorGUILayout.Popup(sceneIndex, sceneNames);
|
||||
|
||||
GUI.enabled = !isPlaying;
|
||||
var disabled = !EditorSettings.enterPlayModeOptionsEnabled;
|
||||
GUI.color = disabled ? new(1, .75f, .5f) : new(.825f, .825f, .825f);
|
||||
disabled = EditorGUILayout.Toggle(disabled, GUILayout.Width(14));
|
||||
EditorGUILayout.LabelField("Reload Domain", GUILayout.Width(86));
|
||||
EditorSettings.enterPlayModeOptionsEnabled = !disabled;
|
||||
EditorSettings.enterPlayModeOptions
|
||||
= EnterPlayModeOptions.DisableDomainReload
|
||||
| EnterPlayModeOptions.DisableSceneReload;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
action?.Invoke();
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Chameleon/Play Scene/Focus Selection")]
|
||||
static void FocusSelection()
|
||||
{
|
||||
Collapse();
|
||||
|
||||
var gameObject = Selection.activeGameObject;
|
||||
if (gameObject)
|
||||
{
|
||||
EditorGUIUtility.PingObject(gameObject);
|
||||
SceneManager.SetActiveScene(Selection.activeGameObject.scene);
|
||||
}
|
||||
|
||||
var rootGameObjects = SceneManager
|
||||
.GetActiveScene()
|
||||
.GetRootGameObjects();
|
||||
|
||||
var parent = rootGameObjects.FirstOrDefault(
|
||||
x => x.TryGetComponent(out DefaultParentForEditor _)
|
||||
);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
EditorUtility.SetDefaultParentObject(parent);
|
||||
if (!gameObject)
|
||||
gameObject = parent;
|
||||
}
|
||||
|
||||
if (!gameObject)
|
||||
{
|
||||
gameObject = rootGameObjects.FirstOrDefault();
|
||||
if (!gameObject)
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent)
|
||||
{
|
||||
var transform = gameObject.transform;
|
||||
if (transform.childCount > 0)
|
||||
gameObject = transform.GetChild(0).gameObject;
|
||||
}
|
||||
|
||||
EditorGUIUtility.PingObject(gameObject);
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Chameleon/Play Scene/Focus Scene")]
|
||||
static void FocusScene()
|
||||
{
|
||||
Collapse();
|
||||
|
||||
var gameObject = Selection.activeGameObject;
|
||||
if (gameObject)
|
||||
SceneManager.SetActiveScene(Selection.activeGameObject.scene);
|
||||
|
||||
var rootGameObjects = SceneManager
|
||||
.GetActiveScene()
|
||||
.GetRootGameObjects();
|
||||
|
||||
gameObject = rootGameObjects.FirstOrDefault(
|
||||
x => x.TryGetComponent(out DefaultParentForEditor _)
|
||||
);
|
||||
|
||||
if (gameObject)
|
||||
{
|
||||
EditorUtility.SetDefaultParentObject(gameObject);
|
||||
var transform = gameObject.transform;
|
||||
if (transform.childCount > 0)
|
||||
gameObject = transform.GetChild(0).gameObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject = rootGameObjects.FirstOrDefault();
|
||||
if (!gameObject)
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUIUtility.PingObject(gameObject);
|
||||
Selection.activeObject = null;
|
||||
}
|
||||
|
||||
static void Collapse()
|
||||
{
|
||||
var activeObject = Selection.activeObject;
|
||||
Collapse(repaint: false);
|
||||
Collapse(repaint: true);
|
||||
Selection.activeObject = activeObject;
|
||||
}
|
||||
|
||||
static void Collapse(bool repaint)
|
||||
{
|
||||
Selection.activeObject = null;
|
||||
|
||||
var scopeFlags = F.Public | F.NonPublic;
|
||||
var instanceFlags = scopeFlags | F.Instance;
|
||||
|
||||
var window = typeof(EditorWindow).Assembly
|
||||
.GetType("UnityEditor.SceneHierarchyWindow")
|
||||
.GetField("s_LastInteractedHierarchy", scopeFlags | F.Static)
|
||||
.GetValue(null) as EditorWindow;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
var treeOwner = window
|
||||
.GetType()
|
||||
.GetField("m_SceneHierarchy", instanceFlags)
|
||||
.GetValue(window);
|
||||
|
||||
var tree = treeOwner
|
||||
.GetType()
|
||||
.GetField("m_TreeView", instanceFlags)
|
||||
.GetValue(treeOwner);
|
||||
|
||||
if (tree is null)
|
||||
return;
|
||||
|
||||
var source = tree
|
||||
.GetType()
|
||||
.GetProperty("data", instanceFlags)
|
||||
.GetValue(tree, null);
|
||||
|
||||
source
|
||||
.GetType()
|
||||
.GetMethod("SetExpandedIDs", instanceFlags)
|
||||
.Invoke(source, new[] { new int[0] });
|
||||
|
||||
if (repaint)
|
||||
window.Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e362fe89183ef0c458ec2a3a64894ba0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Chameleon.Editor
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Chameleon/Scene Collection (Editor)")]
|
||||
public class SceneCollection : UnityEngine.ScriptableObject
|
||||
{
|
||||
public List<SceneAsset> sceneAssets = new();
|
||||
|
||||
[UnityEditor.Callbacks.OnOpenAsset]
|
||||
public static bool OnOpenAsset(int instanceID, int _)
|
||||
{
|
||||
var @object = EditorUtility.EntityIdToObject(instanceID);
|
||||
if (@object is SceneCollection sceneCollection)
|
||||
sceneCollection.Open();
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
if (sceneAssets.Count == 0)
|
||||
return;
|
||||
|
||||
OpenScene(sceneAssets.First(), OpenSceneMode.Single);
|
||||
|
||||
foreach (var sceneAsset in sceneAssets.Skip(1))
|
||||
OpenScene(sceneAsset, OpenSceneMode.Additive);
|
||||
}
|
||||
|
||||
static void OpenScene(SceneAsset sceneAsset, OpenSceneMode mode)
|
||||
{
|
||||
var scenePath = AssetDatabase.GetAssetPath(sceneAsset);
|
||||
EditorSceneManager.OpenScene(scenePath, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d51f5e57ffbddc4dab2f2d0f5d8e693
|
||||
Reference in new issue
Block a user