浏览代码
Scene Selector window (#285)
Scene Selector window (#285)
* changed input phase check to match best practices * menu input - handles keyboard + gamepad inputs - handles mouse inputs - created component to place on selectable UI elements to register them as a pointer enter/exit receiver - updated inputreader to contain menu inputs as well - updated input actions to contain new menu inputs * updates * debug * docs * removed ununsed var * doc * found fix for todo #2 * added to component menu * was able to fix edge case #1 * doc * updates * undid prefab change * merged in main, added menu prefab to asset database * removed action asset reference in event system on prefab * readme * menu prefab into scene * updated readme * removed call to base.onenter in CC button to simplify selection logic * Use more reliable SceneAsset instead of error-prone scene name This also makes a custom editor unnecessary * Set proper SceneAssets and serialize * .../devlogs-3-input
当前提交
f5865370
共有 27 个文件被更改,包括 673 次插入 和 272 次删除
-
4UOP1_Project/Assets/Scripts/SceneManagement/ScriptableObjects/GameSceneSOEditor.cs
-
2UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.ColorSelectorWindow.cs.meta
-
2UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Data.cs.meta
-
2UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.PreferencesWindow.cs.meta
-
2UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Helper.cs.meta
-
18UOP1_Project/Assets/ScriptableObjects/SceneData/Initialization.asset
-
8UOP1_Project/Assets/ScriptableObjects/SceneData/Initialization.asset.meta
-
8UOP1_Project/Assets/Scripts/Editor/SceneSelector.meta
-
86UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.ColorSelectorWindow.cs
-
59UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Data.cs
-
125UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Helper.cs
-
170UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.PreferencesWindow.cs
-
180UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.cs
-
11UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.cs.meta
-
8UOP1_Project/Assets/Scenes/Examples/MultiSceneLoaderExample.meta
-
8UOP1_Project/Assets/ScriptableObjects/SceneData/Resources.meta
-
217UOP1_Project/Assets/Scripts/Editor/SceneAccessTool.cs
-
27UOP1_Project/Assets/Scripts/SceneManagement/ScriptableObjects/SceneAccessHolderSO.cs
-
8UOP1_Project/Assets/Scripts/Attributes.meta
-
0/UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.ColorSelectorWindow.cs.meta
-
0/UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Data.cs.meta
-
0/UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.PreferencesWindow.cs.meta
-
0/UOP1_Project/Assets/Scripts/Editor/SceneSelector/SceneSelector.Helper.cs.meta
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!114 &11400000 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_CorrespondingSourceObject: {fileID: 0} |
|||
m_PrefabInstance: {fileID: 0} |
|||
m_PrefabAsset: {fileID: 0} |
|||
m_GameObject: {fileID: 0} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: 7eabf0d1b44567046bb98dff8a1cd7ac, type: 3} |
|||
m_Name: Initialization |
|||
m_EditorClassIdentifier: |
|||
sceneName: Initialization |
|||
shortDescription: |
|||
music: {fileID: 0} |
|||
musicVolume: 0 |
|
|||
fileFormatVersion: 2 |
|||
guid: abfc6b5ef9fe68048bae8ffe84afa7a0 |
|||
NativeFormatImporter: |
|||
externalObjects: {} |
|||
mainObjectFileID: 0 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: ea3c4d16eb54b4a40ac681e562989fe9 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using SceneSelectorInternal; |
|||
|
|||
public partial class SceneSelector : EditorWindow |
|||
{ |
|||
private class ColorSelectorWindow : EditorWindow |
|||
{ |
|||
private static readonly float kCellSize = PreferencesWindow.kColorMarkerFieldSize * 2.0f; |
|||
private static readonly Color kCellBackColor = new Color(0.0f, 0.0f, 0.0f, 0.1f); |
|||
private static readonly Vector2 kCellOffset = new Vector2(1.0f, 1.0f); |
|||
private static readonly Vector2Int kCount = new Vector2Int(5, 5); |
|||
|
|||
private PreferencesWindow _owner; |
|||
private Color[,] _colors; |
|||
private Item _item; |
|||
|
|||
public static ColorSelectorWindow Open(Rect rect, PreferencesWindow owner, Item item) |
|||
{ |
|||
var window = CreateInstance<ColorSelectorWindow>(); |
|||
window.Init(rect, owner, item); |
|||
return window; |
|||
} |
|||
|
|||
private void Init(Rect rect, PreferencesWindow owner, Item item) |
|||
{ |
|||
var size = (Vector2)kCount * kCellSize; |
|||
ShowAsDropDown(rect, size); |
|||
_owner = owner; |
|||
_item = item; |
|||
} |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
wantsMouseMove = true; |
|||
InitColors(); |
|||
} |
|||
|
|||
private void OnGUI() |
|||
{ |
|||
Helper.RepaintOnMouseMove(this); |
|||
DrawMarkers(); |
|||
} |
|||
|
|||
private void DrawMarkers() |
|||
{ |
|||
var size = new Vector2(kCellSize, kCellSize); |
|||
for (int x = 0; x < kCount.x; ++x) |
|||
{ |
|||
for (int y = 0; y < kCount.y; ++y) |
|||
{ |
|||
var color = _colors[x, y]; |
|||
var position = size * new Vector2(x, y); |
|||
var rect = new Rect(position, size); |
|||
{ |
|||
var cellBackRect = rect; |
|||
cellBackRect.position += kCellOffset; |
|||
cellBackRect.size -= kCellOffset * 2.0f; |
|||
EditorGUI.DrawRect(cellBackRect, kCellBackColor); |
|||
} |
|||
if (Helper.DrawColorMarker(rect, color, true, true)) |
|||
{ |
|||
_item.color = color; |
|||
_owner.RepaintAll(); |
|||
Close(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void InitColors() |
|||
{ |
|||
var count = kCount.x * kCount.y; |
|||
_colors = new Color[kCount.x, kCount.y]; |
|||
for (int x = 0; x < kCount.x; ++x) |
|||
{ |
|||
var h = x * kCount.y; |
|||
for (int y = 0; y < kCount.y; ++y) |
|||
{ |
|||
float hue = (float)(h + y) / count; |
|||
_colors[x, y] = Color.HSVToRGB(hue, 1.0f, 1.0f); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
|
|||
public partial class SceneSelector : EditorWindow |
|||
{ |
|||
[Serializable] |
|||
private class Item |
|||
{ |
|||
public string guid; |
|||
public int order = int.MaxValue; |
|||
public bool isVisible = true; |
|||
public Color color = Color.clear; |
|||
|
|||
[NonSerialized] |
|||
public GameSceneSO gameScene; |
|||
} |
|||
|
|||
private class Styles |
|||
{ |
|||
public GUIStyle item; |
|||
} |
|||
|
|||
private class Textures |
|||
{ |
|||
public Texture2D visibilityOn; |
|||
public Texture2D visibilityOff; |
|||
} |
|||
|
|||
[Serializable] |
|||
private class Storage : ISerializationCallbackReceiver |
|||
{ |
|||
[SerializeField] |
|||
public List<Item> items = new List<Item>(); |
|||
|
|||
[NonSerialized] |
|||
public Dictionary<string, Item> itemsMap = new Dictionary<string, Item>(); |
|||
|
|||
void ISerializationCallbackReceiver.OnBeforeSerialize() |
|||
{ |
|||
for (int i = 0, count = items.Count; i < count; ++i) |
|||
{ |
|||
var item = items[i]; |
|||
item.order = i; |
|||
} |
|||
} |
|||
|
|||
void ISerializationCallbackReceiver.OnAfterDeserialize() |
|||
{ |
|||
items.OrderBy(x => x.order); |
|||
foreach (var item in items) |
|||
{ |
|||
itemsMap.Add(item.guid, item); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEditor.SceneManagement; |
|||
using UnityEngine; |
|||
|
|||
namespace SceneSelectorInternal |
|||
{ |
|||
internal static class KeyValuePairExtension |
|||
{ |
|||
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value) |
|||
{ |
|||
key = tuple.Key; |
|||
value = tuple.Value; |
|||
} |
|||
} |
|||
|
|||
internal static class Helper |
|||
{ |
|||
public const float kColorMarkerNormalSize = 4.0f; |
|||
public const float kColorMarkerHoveredSize = 6.0f; |
|||
public static readonly Color kColorMarkerDarkTint = Color.gray; |
|||
public static readonly Color kColorMarkerLightTint = new Color(1.0f, 1.0f, 1.0f, 0.32f); |
|||
|
|||
public struct ReplaceColor : IDisposable |
|||
{ |
|||
public static ReplaceColor With(Color color) => new ReplaceColor(color); |
|||
|
|||
private Color _oldColor; |
|||
|
|||
private ReplaceColor(Color color) |
|||
{ |
|||
_oldColor = GUI.color; |
|||
GUI.color = color; |
|||
} |
|||
|
|||
void IDisposable.Dispose() => GUI.color = _oldColor; |
|||
} |
|||
|
|||
private static readonly Dictionary<Type, Color> kDefaultMarkerColors = new Dictionary<Type, Color>() |
|||
{ |
|||
{ typeof(PersistentManagersSO), Color.magenta }, |
|||
{ typeof(LocationSO), Color.green }, |
|||
{ typeof(MenuSO), Color.cyan }, |
|||
}; |
|||
|
|||
public static void RepaintOnMouseMove(EditorWindow window) |
|||
{ |
|||
if (Event.current.type == EventType.MouseMove) |
|||
window.Repaint(); |
|||
} |
|||
|
|||
public static bool DrawColorMarker(Rect rect, Color color, bool isClickable = false, bool isHoverable = false) |
|||
{ |
|||
bool isClicked = false; |
|||
if (isClickable) |
|||
isClicked = GUI.Button(rect, GUIContent.none, GUIStyle.none); |
|||
|
|||
var currentEvent = Event.current; |
|||
var isHovered = isHoverable && rect.Contains(currentEvent.mousePosition); |
|||
var targetSize = isHovered ? kColorMarkerHoveredSize : kColorMarkerNormalSize; |
|||
|
|||
var size = rect.size; |
|||
rect.size = new Vector2(targetSize, targetSize); |
|||
rect.position += (size - rect.size) * 0.5f; |
|||
|
|||
Rect shadowRect = rect; |
|||
shadowRect.position -= Vector2.one; |
|||
shadowRect.size += Vector2.one; |
|||
Rect lightRect = rect; |
|||
lightRect.size += Vector2.one; |
|||
|
|||
GUIUtility.RotateAroundPivot(45.0f, rect.center); |
|||
EditorGUI.DrawRect(shadowRect, color * kColorMarkerDarkTint); |
|||
EditorGUI.DrawRect(lightRect, kColorMarkerLightTint); |
|||
EditorGUI.DrawRect(rect, color); |
|||
GUIUtility.RotateAroundPivot(-45.0f, rect.center); |
|||
|
|||
return isClicked; |
|||
} |
|||
|
|||
public static void OpenSceneSafe(string path) |
|||
{ |
|||
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) |
|||
{ |
|||
EditorSceneManager.OpenScene(path); |
|||
} |
|||
} |
|||
|
|||
public static Color GetDefaultColor(GameSceneSO gameScene) |
|||
{ |
|||
var type = gameScene.GetType(); |
|||
if (kDefaultMarkerColors.TryGetValue(type, out var color)) |
|||
return color; |
|||
return Color.red; |
|||
} |
|||
|
|||
public static void RunOnNextUpdate(Action action) |
|||
{ |
|||
void Run() |
|||
{ |
|||
action?.Invoke(); |
|||
EditorApplication.update -= Run; |
|||
} |
|||
EditorApplication.update += Run; |
|||
} |
|||
|
|||
public static int FindAssetsByType<T>(List<T> assets) where T : UnityEngine.Object |
|||
{ |
|||
int foundAssetsCount = 0; |
|||
var guids = AssetDatabase.FindAssets($"t:{typeof(T)}"); |
|||
for (int i = 0, count = guids.Length; i < count; ++i) |
|||
{ |
|||
var path = AssetDatabase.GUIDToAssetPath(guids[i]); |
|||
T asset = AssetDatabase.LoadAssetAtPath<T>(path); |
|||
if (asset != null) |
|||
{ |
|||
assets.Add(asset); |
|||
foundAssetsCount += 1; |
|||
} |
|||
} |
|||
return foundAssetsCount; |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEditorInternal; |
|||
using UnityEngine; |
|||
using SceneSelectorInternal; |
|||
|
|||
public partial class SceneSelector : EditorWindow |
|||
{ |
|||
private class PreferencesWindow : EditorWindow |
|||
{ |
|||
private class Styles |
|||
{ |
|||
public GUIStyle itemBorder; |
|||
public GUIStyle buttonVisibilityOn; |
|||
public GUIStyle buttonVisibilityOff; |
|||
} |
|||
|
|||
private const string kWindowCaption = "Scene Selector Preferences"; |
|||
private const float kHeaderHeight = 0.0f; |
|||
private const float kItemHeight = 24.0f; |
|||
private const float kVisibilityButtonSize = 16.0f; |
|||
|
|||
public static float kColorMarkerFieldSize = Mathf.Ceil(Helper.kColorMarkerNormalSize * 1.41f + 8.0f); |
|||
private static readonly Color kItemBorderColor = new Color(1.0f, 1.0f, 1.0f, 0.16f); |
|||
|
|||
private SceneSelector _owner; |
|||
private ColorSelectorWindow _colorSelectorWindow; |
|||
private ReorderableList _itemsReorderableList; |
|||
private Styles _styles; |
|||
private Vector2 _windowScrollPosition; |
|||
|
|||
private List<Item> items => _owner._storage.items; |
|||
|
|||
public static PreferencesWindow Open(SceneSelector owner) |
|||
{ |
|||
var window = GetWindow<PreferencesWindow>(true, kWindowCaption, true); |
|||
window.Init(owner); |
|||
return window; |
|||
} |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
wantsMouseMove = true; |
|||
} |
|||
|
|||
private void OnDisable() |
|||
{ |
|||
_owner.SaveStorage(); |
|||
if (_colorSelectorWindow != null) |
|||
_colorSelectorWindow.Close(); |
|||
} |
|||
|
|||
private void OnGUI() |
|||
{ |
|||
EnsureStyles(); |
|||
Helper.RepaintOnMouseMove(this); |
|||
DrawWindow(); |
|||
} |
|||
|
|||
public void RepaintAll() |
|||
{ |
|||
RepaintOwner(); |
|||
Repaint(); |
|||
} |
|||
|
|||
private void Init(SceneSelector owner) |
|||
{ |
|||
_owner = owner; |
|||
CreateReorderableList(); |
|||
} |
|||
|
|||
private void CreateReorderableList() |
|||
{ |
|||
_itemsReorderableList = new ReorderableList(items, typeof(Item), true, true, false, false); |
|||
_itemsReorderableList.drawElementCallback = DrawItem; |
|||
_itemsReorderableList.drawElementBackgroundCallback = DrawItemBackground; |
|||
_itemsReorderableList.onReorderCallback = OnReorder; |
|||
_itemsReorderableList.headerHeight = kHeaderHeight; |
|||
_itemsReorderableList.elementHeight = kItemHeight; |
|||
} |
|||
|
|||
private void DrawWindow() |
|||
{ |
|||
using (var scrollScope = new EditorGUILayout.ScrollViewScope(_windowScrollPosition)) |
|||
{ |
|||
GUILayout.Space(4.0f); |
|||
_itemsReorderableList.DoLayoutList(); |
|||
_windowScrollPosition = scrollScope.scrollPosition; |
|||
} |
|||
} |
|||
|
|||
private void DrawItem(Rect rect, int index, bool isActive, bool isFocused) |
|||
{ |
|||
var item = items[index]; |
|||
var gameScene = item.gameScene; |
|||
if (gameScene != null) |
|||
{ |
|||
var colorMarkerRect = rect; |
|||
colorMarkerRect.width = colorMarkerRect.height; |
|||
|
|||
if (Helper.DrawColorMarker(colorMarkerRect, item.color, true, true)) |
|||
{ |
|||
var colorSelectorRect = GUIUtility.GUIToScreenRect(colorMarkerRect); |
|||
_colorSelectorWindow = ColorSelectorWindow.Open(colorSelectorRect, this, item); |
|||
} |
|||
|
|||
var itemLabelRect = rect; |
|||
itemLabelRect.x += colorMarkerRect.width; |
|||
itemLabelRect.width -= kVisibilityButtonSize + colorMarkerRect.width; |
|||
|
|||
GUI.Label(itemLabelRect, gameScene.name); |
|||
|
|||
var visibilityButtonRect = new Rect(rect); |
|||
visibilityButtonRect.width = kVisibilityButtonSize; |
|||
visibilityButtonRect.height = kVisibilityButtonSize; |
|||
visibilityButtonRect.x = itemLabelRect.x + itemLabelRect.width; |
|||
visibilityButtonRect.y += (rect.height - visibilityButtonRect.height) * 0.5f; |
|||
|
|||
var visibilityStyle = item.isVisible |
|||
? _styles.buttonVisibilityOn |
|||
: _styles.buttonVisibilityOff; |
|||
|
|||
if (GUI.Button(visibilityButtonRect, GUIContent.none, visibilityStyle)) |
|||
{ |
|||
item.isVisible = !item.isVisible; |
|||
RepaintOwner(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void DrawItemBackground(Rect rect, int index, bool isActive, bool isFocused) |
|||
{ |
|||
ReorderableList.defaultBehaviours.DrawElementBackground(rect, index, isActive, isFocused, true); |
|||
using (Helper.ReplaceColor.With(kItemBorderColor)) |
|||
{ |
|||
GUI.Box(rect, GUIContent.none, _styles.itemBorder); |
|||
} |
|||
} |
|||
|
|||
private void OnReorder(ReorderableList _) |
|||
{ |
|||
RepaintOwner(); |
|||
} |
|||
|
|||
private void RepaintOwner() |
|||
{ |
|||
_owner.Repaint(); |
|||
} |
|||
|
|||
private void EnsureStyles() |
|||
{ |
|||
if (_styles == null) |
|||
{ |
|||
_styles = new Styles(); |
|||
|
|||
_styles.itemBorder = new GUIStyle(GUI.skin.GetStyle("HelpBox")); |
|||
|
|||
_styles.buttonVisibilityOn = new GUIStyle(GUI.skin.label); |
|||
_styles.buttonVisibilityOn.padding = new RectOffset(0, 0, 0, 0); |
|||
_styles.buttonVisibilityOn.normal.background = EditorGUIUtility.FindTexture("d_scenevis_visible"); |
|||
_styles.buttonVisibilityOn.hover.background = EditorGUIUtility.FindTexture("d_scenevis_visible_hover"); |
|||
|
|||
_styles.buttonVisibilityOff = new GUIStyle(GUI.skin.label); |
|||
_styles.buttonVisibilityOff.padding = new RectOffset(0, 0, 0, 0); |
|||
_styles.buttonVisibilityOff.normal.background = EditorGUIUtility.FindTexture("d_scenevis_hidden"); |
|||
_styles.buttonVisibilityOff.hover.background = EditorGUIUtility.FindTexture("d_scenevis_hidden_hover"); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using SceneSelectorInternal; |
|||
|
|||
public partial class SceneSelector : EditorWindow, IHasCustomMenu |
|||
{ |
|||
private const string kPreferencesKey = "uop1.SceneSelector.Preferences"; |
|||
private const int kItemContentLeftPadding = 32; |
|||
private static readonly GUIContent kOpenPreferencesItemContent = new GUIContent("Open Preferences"); |
|||
|
|||
private Styles _styles; |
|||
private Storage _storage; |
|||
private PreferencesWindow _preferencesWindow; |
|||
private Vector2 _windowScrollPosition; |
|||
private bool _hasEmptyItems; |
|||
|
|||
private List<Item> items => _storage.items; |
|||
private Dictionary<string, Item> itemsMap => _storage.itemsMap; |
|||
|
|||
[MenuItem("Window/Scene Selector", false, 100)] |
|||
private static void Open() |
|||
{ |
|||
GetWindow<SceneSelector>(); |
|||
} |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
wantsMouseMove = true; |
|||
LoadStorage(); |
|||
PopulateItems(); |
|||
GameSceneSO.onEnabled += OnGameSceneSOCreated; |
|||
} |
|||
|
|||
private void OnDisable() |
|||
{ |
|||
if (_preferencesWindow != null) |
|||
_preferencesWindow.Close(); |
|||
SaveStorage(); |
|||
GameSceneSO.onEnabled -= OnGameSceneSOCreated; |
|||
} |
|||
|
|||
private void OnGUI() |
|||
{ |
|||
EnsureStyles(); |
|||
Helper.RepaintOnMouseMove(this); |
|||
RemoveEmptyItemsIfRequired(); |
|||
DrawWindow(); |
|||
} |
|||
|
|||
private void DrawWindow() |
|||
{ |
|||
using (var scrollScope = new EditorGUILayout.ScrollViewScope(_windowScrollPosition)) |
|||
{ |
|||
GUILayout.Space(4.0f); |
|||
DrawItems(); |
|||
_windowScrollPosition = scrollScope.scrollPosition; |
|||
} |
|||
} |
|||
|
|||
private void DrawItems() |
|||
{ |
|||
foreach (var item in items) |
|||
{ |
|||
DrawItem(item); |
|||
} |
|||
} |
|||
|
|||
private void DrawItem(Item item) |
|||
{ |
|||
if (item.isVisible) |
|||
{ |
|||
var gameScene = item.gameScene; |
|||
if (gameScene != null) |
|||
{ |
|||
if (GUILayout.Button(gameScene.name, _styles.item)) |
|||
{ |
|||
Helper.OpenSceneSafe(gameScene.scenePath); |
|||
} |
|||
|
|||
var colorMarkerRect = GUILayoutUtility.GetLastRect(); |
|||
colorMarkerRect.width = colorMarkerRect.height; |
|||
colorMarkerRect.x += (_styles.item.padding.left - colorMarkerRect.width) * 0.5f; |
|||
Helper.DrawColorMarker(colorMarkerRect, item.color); |
|||
} |
|||
else |
|||
{ |
|||
// In case GameSceneSO was removed (see RemoveEmptyItemsIfRequired)
|
|||
_hasEmptyItems = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void LoadStorage() |
|||
{ |
|||
_storage = new Storage(); |
|||
if (EditorPrefs.HasKey(kPreferencesKey)) |
|||
{ |
|||
var preferencesJSON = EditorPrefs.GetString(kPreferencesKey); |
|||
EditorJsonUtility.FromJsonOverwrite(preferencesJSON, _storage); |
|||
} |
|||
} |
|||
|
|||
private void SaveStorage() |
|||
{ |
|||
var preferencesJSON = EditorJsonUtility.ToJson(_storage); |
|||
EditorPrefs.SetString(kPreferencesKey, preferencesJSON); |
|||
} |
|||
|
|||
private void PopulateItems() |
|||
{ |
|||
var gameScenes = new List<GameSceneSO>(); |
|||
Helper.FindAssetsByType(gameScenes); |
|||
|
|||
foreach (var gameScene in gameScenes) |
|||
{ |
|||
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(gameScene, out var guid, out long _)) |
|||
{ |
|||
if (itemsMap.TryGetValue(guid, out var item)) |
|||
{ |
|||
item.gameScene = gameScene; |
|||
} |
|||
else |
|||
{ |
|||
item = new Item() |
|||
{ |
|||
gameScene = gameScene, |
|||
guid = guid, |
|||
color = Helper.GetDefaultColor(gameScene) |
|||
}; |
|||
items.Add(item); |
|||
itemsMap.Add(guid, item); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void RemoveEmptyItemsIfRequired() |
|||
{ |
|||
if (_hasEmptyItems) |
|||
{ |
|||
for (int i = items.Count - 1; i >= 0; --i) |
|||
{ |
|||
var sceneItem = items[i]; |
|||
if (sceneItem == null || sceneItem.gameScene == null) |
|||
{ |
|||
items.RemoveAt(i); |
|||
itemsMap.Remove(sceneItem.guid); |
|||
} |
|||
} |
|||
} |
|||
_hasEmptyItems = false; |
|||
} |
|||
|
|||
private void OnGameSceneSOCreated(GameSceneSO _) |
|||
{ |
|||
Helper.RunOnNextUpdate(PopulateItems); |
|||
} |
|||
|
|||
private void EnsureStyles() |
|||
{ |
|||
if (_styles == null) |
|||
{ |
|||
_styles = new Styles(); |
|||
|
|||
_styles.item = "MenuItem"; |
|||
_styles.item.padding.left = kItemContentLeftPadding; |
|||
} |
|||
} |
|||
|
|||
private void OpenPreferences() |
|||
{ |
|||
_preferencesWindow = PreferencesWindow.Open(this); |
|||
} |
|||
|
|||
void IHasCustomMenu.AddItemsToMenu(GenericMenu menu) |
|||
{ |
|||
menu.AddItem(kOpenPreferencesItemContent, false, OpenPreferences); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 78e26d6372ed406448b1b3bb3ea08b46 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 3b1c2544e599a634ea09627dd05ef60e |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: dd754fdc4b0731f4f9633351d8dcd5f3 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using System.IO; |
|||
using UnityEditorInternal; |
|||
using UnityEditor.SceneManagement; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
public class SceneAccessTool : EditorWindow, IHasCustomMenu |
|||
{ |
|||
private SceneAccessHolderSO _inspected; |
|||
private SerializedObject _serializedObject; |
|||
private Editor _sceneAccessHolderSOEditor; |
|||
public enum Layout { List, Grid } |
|||
private Layout _layout; |
|||
private bool _showOptions = false; |
|||
private bool _editMode = false; |
|||
private ReorderableList list; |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
|
|||
_inspected = Resources.Load<SceneAccessHolderSO>("SceneAccessHolder"); |
|||
SetUpList(); |
|||
} |
|||
private void SetUpList() |
|||
{ |
|||
_serializedObject = new SerializedObject(_inspected); |
|||
|
|||
if (_editMode) |
|||
{ |
|||
list = new ReorderableList(_serializedObject, |
|||
_serializedObject.FindProperty("sceneList"), |
|||
true, false, false, false); |
|||
list.drawElementCallback = |
|||
(Rect rect, int index, bool isActive, bool isFocused) => |
|||
{ |
|||
var element = list.serializedProperty.GetArrayElementAtIndex(index); |
|||
rect.y += 2; |
|||
EditorGUI.PropertyField( |
|||
new Rect(rect.x, rect.y, rect.width - 30, EditorGUIUtility.singleLineHeight), |
|||
element.FindPropertyRelative("name"), GUIContent.none); |
|||
|
|||
EditorGUI.PropertyField( |
|||
new Rect(rect.x + rect.width - 25, rect.y, 25, EditorGUIUtility.singleLineHeight), |
|||
element.FindPropertyRelative("visible"), GUIContent.none); |
|||
}; |
|||
} |
|||
} |
|||
[MenuItem("Tools/SceneAccessTool")] |
|||
public static void ShowWindow() |
|||
{ |
|||
GetWindow<SceneAccessTool>("SceneAccessTool"); |
|||
} |
|||
private void OnGUI() |
|||
{ |
|||
if (_inspected != null) |
|||
{ |
|||
if (_showOptions) |
|||
ShowOptions(); |
|||
ShowSceneList(); |
|||
} |
|||
} |
|||
|
|||
public void ShowOptions() |
|||
{ |
|||
GUILayout.BeginHorizontal(); |
|||
if (GUILayout.Button("Populate Scene List")) |
|||
{ |
|||
PopulateSceneList(); |
|||
} |
|||
if (GUILayout.Button("Clear List")) |
|||
{ |
|||
ClearSceneList(); |
|||
} |
|||
if (GUILayout.Button("Edit Mode")) |
|||
{ |
|||
ToggleEditMode(); |
|||
} |
|||
GUILayout.EndHorizontal(); |
|||
} |
|||
public void ShowSceneList() |
|||
{ |
|||
if (_editMode) |
|||
{ |
|||
_serializedObject.Update(); |
|||
list.DoLayoutList(); |
|||
_serializedObject.ApplyModifiedProperties(); |
|||
} |
|||
else |
|||
{ |
|||
if (_layout == Layout.List) |
|||
{ |
|||
for (int i = 0; i < _inspected.sceneList.Count; i++) |
|||
{ |
|||
var sceneItem = _inspected.sceneList[i]; |
|||
if (!sceneItem.visible) |
|||
{ |
|||
continue; |
|||
} |
|||
if (GUILayout.Button(sceneItem.name)) |
|||
{ |
|||
EditorSceneManager.OpenScene(sceneItem.path); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
int gridSize = 48; |
|||
int widthCount = gridSize; |
|||
GUILayout.BeginHorizontal(); |
|||
for (int i = 0; i < _inspected.sceneList.Count; i++) |
|||
{ |
|||
var sceneItem = _inspected.sceneList[i]; |
|||
if (!sceneItem.visible) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
GUIStyle customButton = new GUIStyle("button"); |
|||
customButton.fontSize = 10; |
|||
customButton.alignment = TextAnchor.UpperLeft; |
|||
customButton.wordWrap = true; |
|||
GUIContent guiContent = new GUIContent(sceneItem.name); |
|||
if (GUILayout.Button( |
|||
guiContent, |
|||
customButton, |
|||
GUILayout.Width(gridSize), |
|||
GUILayout.Height(gridSize))) |
|||
{ |
|||
EditorSceneManager.OpenScene(sceneItem.path); |
|||
} |
|||
|
|||
widthCount += gridSize + 4; |
|||
|
|||
if (widthCount > position.width) |
|||
{ |
|||
GUILayout.EndHorizontal(); |
|||
GUILayout.BeginHorizontal(); |
|||
widthCount = gridSize; |
|||
} |
|||
} |
|||
|
|||
GUILayout.EndHorizontal(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Find all scenes in the project and put them in the list
|
|||
/// </summary>
|
|||
private void PopulateSceneList() |
|||
{ |
|||
EditorBuildSettingsScene[] currentScenes = EditorBuildSettings.scenes; |
|||
EditorBuildSettingsScene[] filteredScenes = currentScenes.Where(ebss => File.Exists(ebss.path)).ToArray(); |
|||
List<SceneAccessHolderSO.SceneInfo> allScene = new List<SceneAccessHolderSO.SceneInfo>(); |
|||
for (int i = 0; i < filteredScenes.Length; i++) |
|||
{ |
|||
allScene.Add( |
|||
new SceneAccessHolderSO.SceneInfo |
|||
{ |
|||
name = Path.GetFileNameWithoutExtension(filteredScenes[i].path), |
|||
path = Path.GetFullPath(filteredScenes[i].path), |
|||
visible = true |
|||
}); |
|||
} |
|||
//add the new scenes
|
|||
foreach (SceneAccessHolderSO.SceneInfo sceneInfo in allScene) |
|||
{ |
|||
if (!_inspected.sceneList.Contains(sceneInfo)) |
|||
{ |
|||
_inspected.sceneList.Add(sceneInfo); |
|||
} |
|||
} |
|||
//remove the deleted scenes
|
|||
foreach (SceneAccessHolderSO.SceneInfo sceneInfo in _inspected.sceneList.ToList()) |
|||
{ |
|||
if (!allScene.Contains(sceneInfo)) |
|||
{ |
|||
_inspected.sceneList.Remove(sceneInfo); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void ClearSceneList() |
|||
{ |
|||
_inspected.sceneList.Clear(); |
|||
} |
|||
|
|||
public void AddItemsToMenu(GenericMenu menu) |
|||
{ |
|||
menu.AddItem(new GUIContent("ToggleOptions"), false, ToggleOptions); |
|||
menu.AddItem(new GUIContent("ToggleLayout"), false, ToggleLayout); |
|||
} |
|||
|
|||
private void ToggleOptions() |
|||
{ |
|||
_showOptions = !_showOptions; |
|||
} |
|||
private void ToggleLayout() |
|||
{ |
|||
if (_layout == Layout.List) |
|||
{ |
|||
_layout = Layout.Grid; |
|||
} |
|||
else |
|||
{ |
|||
_layout = Layout.List; |
|||
} |
|||
} |
|||
private void ToggleEditMode() |
|||
{ |
|||
_editMode = !_editMode; |
|||
SetUpList(); |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
/// <summary>
|
|||
/// It holds a list of scenes that's shown in the scene quick access tool
|
|||
/// </summary>
|
|||
[CreateAssetMenu(fileName = "SceneAccessHolder", menuName = "Scene Data/SceneAccessHolder")] |
|||
public class SceneAccessHolderSO : ScriptableObject |
|||
{ |
|||
[Serializable] |
|||
public struct SceneInfo : IEquatable<SceneInfo> |
|||
{ |
|||
[ReadOnly] public string name; |
|||
[ReadOnly] public string path; |
|||
public bool visible; |
|||
|
|||
public bool Equals(SceneInfo other) |
|||
{ |
|||
// Would still want to check for null etc. first.
|
|||
return this.path == other.path; |
|||
} |
|||
} |
|||
|
|||
[Header("List of Scenes")] |
|||
public List<SceneInfo> sceneList; |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b20537032508d384c9b7e9decf64c3ae |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue