Ciro Continisio
4 年前
当前提交
2a0d8691
共有 23 个文件被更改,包括 1087 次插入 和 4 次删除
-
12UOP1_Project/Assets/Scripts/Editor/ReplaceTool.cs
-
8UOP1_Project/Assets/ScriptableObjects/SceneData/Resources.meta
-
113UOP1_Project/Assets/Scripts/Editor/GameObjectPreview.cs
-
3UOP1_Project/Assets/Scripts/Editor/GameObjectPreview.cs.meta
-
267UOP1_Project/Assets/Scripts/Editor/PrefabSelectionTreeView.cs
-
11UOP1_Project/Assets/Scripts/Editor/PrefabSelectionTreeView.cs.meta
-
69UOP1_Project/Assets/Scripts/Editor/ReplaceContextMenu.cs
-
3UOP1_Project/Assets/Scripts/Editor/ReplaceContextMenu.cs.meta
-
190UOP1_Project/Assets/Scripts/Editor/ReplacePrefabSearchPopup.cs
-
3UOP1_Project/Assets/Scripts/Editor/ReplacePrefabSearchPopup.cs.meta
-
38UOP1_Project/Assets/Scripts/Editor/ScriptableObjectHelper.cs
-
11UOP1_Project/Assets/Scripts/Editor/ScriptableObjectHelper.cs.meta
-
217UOP1_Project/Assets/Scripts/EditorTools/SceneAccessTool.cs
-
11UOP1_Project/Assets/Scripts/EditorTools/SceneAccessTool.cs.meta
-
8UOP1_Project/Assets/Scripts/Input/Editor.meta
-
51UOP1_Project/Assets/Scripts/SceneManagement/ScriptableObjects/SceneAccessHolderSO.cs
-
11UOP1_Project/Assets/Scripts/SceneManagement/ScriptableObjects/SceneAccessHolderSO.cs.meta
-
30UOP1_Project/Assets/ScriptableObjects/SceneData/Resources/SceneAccessHolder.asset
-
8UOP1_Project/Assets/ScriptableObjects/SceneData/Resources/SceneAccessHolder.asset.meta
-
16UOP1_Project/Assets/Scripts/Input/Editor/InputReaderEditor.cs
-
11UOP1_Project/Assets/Scripts/Input/Editor/InputReaderEditor.cs.meta
|
|||
fileFormatVersion: 2 |
|||
guid: dd754fdc4b0731f4f9633351d8dcd5f3 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace UOP1.EditorTools |
|||
{ |
|||
internal class GameObjectPreview |
|||
{ |
|||
private static Type gameObjectInspectorType; |
|||
private static MethodInfo getPreviewDataMethod; |
|||
private static FieldInfo renderUtilityField; |
|||
|
|||
private Rect renderRect; |
|||
private Color light0Color; |
|||
private Color light1Color; |
|||
private PreviewRenderUtility renderUtility; |
|||
|
|||
private Editor cachedEditor; |
|||
|
|||
public RenderTexture outputTexture; |
|||
|
|||
[InitializeOnLoadMethod] |
|||
private static void OnInitialize() |
|||
{ |
|||
gameObjectInspectorType = typeof(Editor).Assembly.GetType("UnityEditor.GameObjectInspector"); |
|||
var previewDataType = gameObjectInspectorType.GetNestedType("PreviewData", BindingFlags.NonPublic); |
|||
|
|||
getPreviewDataMethod = gameObjectInspectorType.GetMethod("GetPreviewData", BindingFlags.NonPublic | BindingFlags.Instance); |
|||
renderUtilityField = previewDataType.GetField("renderUtility", BindingFlags.Public | BindingFlags.Instance); |
|||
} |
|||
|
|||
public void CreatePreviewForTarget(GameObject target) |
|||
{ |
|||
if (!cachedEditor || cachedEditor.target != target) |
|||
{ |
|||
renderUtility = null; |
|||
// There is a bug that breaks previews and Prefab mode after creating too many editors.
|
|||
// Simply using CreateCachedEditor is fixing that problem.
|
|||
Editor.CreateCachedEditor(target, gameObjectInspectorType, ref cachedEditor); |
|||
} |
|||
} |
|||
|
|||
public void RenderInteractivePreview(Rect rect) |
|||
{ |
|||
if (!cachedEditor) |
|||
return; |
|||
|
|||
if (renderUtility == null || renderUtility.lights[0] == null) |
|||
{ |
|||
var previewData = getPreviewDataMethod.Invoke(cachedEditor, null); |
|||
renderUtility = renderUtilityField.GetValue(previewData) as PreviewRenderUtility; |
|||
|
|||
light0Color = renderUtility.lights[0].color; |
|||
light1Color = renderUtility.lights[1].color; |
|||
} |
|||
|
|||
renderUtility.lights[0].color = light0Color * 1.6f; |
|||
renderUtility.lights[1].color = light1Color * 6f; |
|||
var backColor = renderUtility.camera.backgroundColor; |
|||
renderUtility.camera.backgroundColor = new Color(backColor.r, backColor.g, backColor.b, 0); |
|||
renderUtility.camera.clearFlags = CameraClearFlags.Depth; |
|||
|
|||
var color = GUI.color; |
|||
|
|||
// Hide default preview texture, since it has no alpha blending
|
|||
GUI.color = new Color(1, 1, 1, 0); |
|||
|
|||
cachedEditor.OnPreviewGUI(rect, null); |
|||
|
|||
GUI.color = color; |
|||
|
|||
outputTexture = renderUtility.camera.targetTexture; |
|||
} |
|||
|
|||
public void DrawPreviewTexture(Rect rect) |
|||
{ |
|||
GUI.DrawTexture(rect, outputTexture, ScaleMode.ScaleToFit, true, 0); |
|||
} |
|||
|
|||
public static bool HasRenderableParts(GameObject go) |
|||
{ |
|||
var renderers = go.GetComponentsInChildren<Renderer>(); |
|||
foreach (var renderer in renderers) |
|||
{ |
|||
switch (renderer) |
|||
{ |
|||
case MeshRenderer _: |
|||
var filter = renderer.gameObject.GetComponent<MeshFilter>(); |
|||
if (filter && filter.sharedMesh) |
|||
return true; |
|||
break; |
|||
case SkinnedMeshRenderer skinnedMesh: |
|||
if (skinnedMesh.sharedMesh) |
|||
return true; |
|||
break; |
|||
case SpriteRenderer sprite: |
|||
if (sprite.sprite) |
|||
return true; |
|||
break; |
|||
case BillboardRenderer billboard: |
|||
if (billboard.billboard && billboard.sharedMaterial) |
|||
return true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bb1d562e678248bbb3b897536cf62c50 |
|||
timeCreated: 1606495882 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEditor.IMGUI.Controls; |
|||
using UnityEngine; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace UOP1.EditorTools.Replacer |
|||
{ |
|||
internal class PrefabSelectionTreeView : TreeView |
|||
{ |
|||
private static Texture2D prefabOnIcon = EditorGUIUtility.IconContent("Prefab On Icon").image as Texture2D; |
|||
private static Texture2D prefabVariantOnIcon = EditorGUIUtility.IconContent("PrefabVariant On Icon").image as Texture2D; |
|||
private static Texture2D folderIcon = EditorGUIUtility.IconContent("Folder Icon").image as Texture2D; |
|||
private static Texture2D folderOnIcon = EditorGUIUtility.IconContent("Folder On Icon").image as Texture2D; |
|||
|
|||
private static GUIStyle whiteLabel; |
|||
private static GUIStyle foldout; |
|||
|
|||
public int RowsCount => rows.Count; |
|||
private Event evt => Event.current; |
|||
|
|||
public Action<GameObject> onSelectEntry; |
|||
|
|||
private List<TreeViewItem> rows = new List<TreeViewItem>(); |
|||
private HashSet<string> paths = new HashSet<string>(); |
|||
|
|||
private Dictionary<int, RenderTexture> previewCache = new Dictionary<int, RenderTexture>(); |
|||
private HashSet<int> renderableItems = new HashSet<int>(); |
|||
|
|||
private GameObjectPreview itemPreview = new GameObjectPreview(); |
|||
private GUIContent itemContent = new GUIContent(); |
|||
|
|||
private int selectedId; |
|||
|
|||
public PrefabSelectionTreeView(TreeViewState state) : base(state) |
|||
{ |
|||
foldoutOverride = FoldoutOverride; |
|||
Reload(); |
|||
} |
|||
|
|||
private bool FoldoutOverride(Rect position, bool expandedState, GUIStyle style) |
|||
{ |
|||
position.width = Screen.width; |
|||
position.height = 20; |
|||
position.y -= 2; |
|||
|
|||
expandedState = GUI.Toggle(position, expandedState, GUIContent.none, style); |
|||
|
|||
return expandedState; |
|||
} |
|||
|
|||
public void Cleanup() |
|||
{ |
|||
foreach (var texture in previewCache.Values) |
|||
Object.DestroyImmediate(texture); |
|||
} |
|||
|
|||
public bool IsRenderable(int id) |
|||
{ |
|||
return renderableItems.Contains(id); |
|||
} |
|||
|
|||
private void CachePreview(int itemId) |
|||
{ |
|||
var copy = new RenderTexture(itemPreview.outputTexture); |
|||
var previous = RenderTexture.active; |
|||
Graphics.Blit(itemPreview.outputTexture, copy); |
|||
RenderTexture.active = previous; |
|||
previewCache.Add(itemId, copy); |
|||
} |
|||
|
|||
protected override bool CanMultiSelect(TreeViewItem item) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
private bool IsPrefabAsset(int id, out GameObject prefab) |
|||
{ |
|||
var obj = EditorUtility.InstanceIDToObject(id); |
|||
|
|||
if (obj is GameObject go) |
|||
{ |
|||
prefab = go; |
|||
return true; |
|||
} |
|||
|
|||
prefab = null; |
|||
return false; |
|||
} |
|||
|
|||
protected override void DoubleClickedItem(int id) |
|||
{ |
|||
if (IsPrefabAsset(id, out var prefab)) |
|||
onSelectEntry(prefab); |
|||
else |
|||
SetExpanded(id, !IsExpanded(id)); |
|||
} |
|||
|
|||
protected override void KeyEvent() |
|||
{ |
|||
var key = evt.keyCode; |
|||
if (key == KeyCode.KeypadEnter || key == KeyCode.Return) |
|||
DoubleClickedItem(selectedId); |
|||
} |
|||
|
|||
protected override void SelectionChanged(IList<int> selectedIds) |
|||
{ |
|||
if (selectedIds.Count > 0) |
|||
selectedId = selectedIds[0]; |
|||
} |
|||
|
|||
protected override TreeViewItem BuildRoot() |
|||
{ |
|||
var root = new TreeViewItem(0, -1); |
|||
rows.Clear(); |
|||
paths.Clear(); |
|||
|
|||
foreach (var guid in AssetDatabase.FindAssets("t:Prefab")) |
|||
{ |
|||
var path = AssetDatabase.GUIDToAssetPath(guid); |
|||
var splits = path.Split('/'); |
|||
var depth = splits.Length - 2; |
|||
|
|||
if (splits[0] != "Assets") |
|||
break; |
|||
|
|||
var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path); |
|||
|
|||
AddFoldersItems(splits); |
|||
AddPrefabItem(asset, depth); |
|||
} |
|||
|
|||
SetupParentsAndChildrenFromDepths(root, rows); |
|||
|
|||
return root; |
|||
} |
|||
|
|||
protected override float GetCustomRowHeight(int row, TreeViewItem item) |
|||
{ |
|||
// Hide folders during search
|
|||
if (!IsPrefabAsset(item.id, out _) && hasSearch) |
|||
return 0; |
|||
|
|||
return 20; |
|||
} |
|||
|
|||
public override void OnGUI(Rect rect) |
|||
{ |
|||
if (whiteLabel == null) |
|||
whiteLabel = new GUIStyle(EditorStyles.label) { normal = { textColor = EditorStyles.whiteLabel.normal.textColor } }; |
|||
|
|||
base.OnGUI(rect); |
|||
} |
|||
|
|||
protected override void RowGUI(RowGUIArgs args) |
|||
{ |
|||
var rect = args.rowRect; |
|||
var item = args.item; |
|||
|
|||
var isRenderable = IsRenderable(item.id); |
|||
var isSelected = IsSelected(item.id); |
|||
var isFocused = HasFocus() && isSelected; |
|||
var isPrefab = IsPrefabAsset(item.id, out var prefab); |
|||
var isFolder = !isPrefab; |
|||
|
|||
if (isFolder && hasSearch) |
|||
return; |
|||
|
|||
if (isFolder) |
|||
{ |
|||
if (rect.Contains(evt.mousePosition) && evt.type == EventType.MouseUp) |
|||
{ |
|||
SetSelection(new List<int> { item.id }); |
|||
SetFocus(); |
|||
} |
|||
} |
|||
|
|||
var labelStyle = isFocused ? whiteLabel : EditorStyles.label; |
|||
var contentIndent = GetContentIndent(item); |
|||
|
|||
customFoldoutYOffset = 2; |
|||
itemContent.text = item.displayName; |
|||
|
|||
rect.x += contentIndent; |
|||
rect.width -= contentIndent; |
|||
|
|||
var iconRect = new Rect(rect) { width = 20 }; |
|||
|
|||
if (isPrefab) |
|||
{ |
|||
var type = PrefabUtility.GetPrefabAssetType(prefab); |
|||
var onIcon = type == PrefabAssetType.Regular ? prefabOnIcon : prefabVariantOnIcon; |
|||
|
|||
var labelRect = new Rect(rect); |
|||
|
|||
if (isRenderable) |
|||
{ |
|||
var previewRect = new Rect(rect) { width = 32, height = 32 }; |
|||
|
|||
if (!previewCache.TryGetValue(item.id, out var previewTexture)) |
|||
{ |
|||
itemPreview.CreatePreviewForTarget(prefab); |
|||
itemPreview.RenderInteractivePreview(previewRect); |
|||
|
|||
if (itemPreview.outputTexture) |
|||
CachePreview(item.id); |
|||
} |
|||
|
|||
if (!previewTexture) |
|||
Repaint(); |
|||
else |
|||
GUI.DrawTexture(iconRect, previewTexture, ScaleMode.ScaleAndCrop); |
|||
|
|||
labelRect.x += iconRect.width; |
|||
labelRect.width -= iconRect.width + 24; |
|||
|
|||
GUI.Label(labelRect, args.label, labelStyle); |
|||
|
|||
if (isSelected) |
|||
{ |
|||
var prefabIconRect = new Rect(iconRect) { x = rect.xMax - 24 }; |
|||
GUI.Label(prefabIconRect, isFocused ? onIcon : item.icon); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
itemContent.image = isSelected ? onIcon : item.icon; |
|||
GUI.Label(rect, itemContent, labelStyle); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
itemContent.image = isFocused ? folderOnIcon : folderIcon; |
|||
GUI.Label(rect, itemContent, labelStyle); |
|||
} |
|||
} |
|||
|
|||
private void AddFoldersItems(string[] splits) |
|||
{ |
|||
for (int i = 1; i < splits.Length - 1; i++) |
|||
{ |
|||
var split = splits[i]; |
|||
|
|||
if (!paths.Contains(split)) |
|||
{ |
|||
rows.Add(new TreeViewItem(split.GetHashCode(), i - 1, " " + split) { icon = folderIcon }); |
|||
paths.Add(split); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void AddPrefabItem(GameObject asset, int depth) |
|||
{ |
|||
var id = asset.GetInstanceID(); |
|||
var content = new GUIContent(EditorGUIUtility.ObjectContent(asset, asset.GetType())); |
|||
|
|||
if (GameObjectPreview.HasRenderableParts(asset)) |
|||
renderableItems.Add(id); |
|||
|
|||
rows.Add(new TreeViewItem(id, depth, content.text) |
|||
{ |
|||
icon = content.image as Texture2D |
|||
}); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 241edd7da6766964687c5bf319a6163f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
using UnityEngine.UIElements; |
|||
|
|||
namespace UOP1.EditorTools.Replacer |
|||
{ |
|||
internal class ReplaceContextMenu |
|||
{ |
|||
private static Type hierarchyType; |
|||
|
|||
private static EditorWindow focusedWindow; |
|||
private static IMGUIContainer hierarchyGUI; |
|||
|
|||
private static Vector2 mousePosition; |
|||
private static bool hasExecuted; |
|||
|
|||
[InitializeOnLoadMethod] |
|||
private static void OnInitialize() |
|||
{ |
|||
hierarchyType = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); |
|||
|
|||
EditorApplication.update += TrackFocusedHierarchy; |
|||
} |
|||
|
|||
private static void TrackFocusedHierarchy() |
|||
{ |
|||
if (focusedWindow != EditorWindow.focusedWindow) |
|||
{ |
|||
focusedWindow = EditorWindow.focusedWindow; |
|||
|
|||
if (focusedWindow?.GetType() == hierarchyType) |
|||
{ |
|||
if (hierarchyGUI != null) |
|||
hierarchyGUI.onGUIHandler -= OnFocusedHierarchyGUI; |
|||
|
|||
hierarchyGUI = focusedWindow.rootVisualElement.parent.Query<IMGUIContainer>(); |
|||
hierarchyGUI.onGUIHandler += OnFocusedHierarchyGUI; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void OnFocusedHierarchyGUI() |
|||
{ |
|||
// As Event.current is null during context-menu callback, we need to track mouse position on hierarchy GUI
|
|||
mousePosition = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); |
|||
} |
|||
|
|||
[MenuItem("GameObject/Replace", true, priority = 0)] |
|||
private static bool ReplaceSelectionValidate() |
|||
{ |
|||
return Selection.gameObjects.Length > 0; |
|||
} |
|||
|
|||
[MenuItem("GameObject/Replace", priority = 0)] |
|||
private static void ReplaceSelection() |
|||
{ |
|||
if (hasExecuted) |
|||
return; |
|||
|
|||
var rect = new Rect(mousePosition, new Vector2(240, 360)); |
|||
|
|||
ReplacePrefabSearchPopup.Show(rect); |
|||
|
|||
EditorApplication.delayCall += () => hasExecuted = false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a36e948ccc254a1cbffab62792cda09d |
|||
timeCreated: 1606469766 |
|
|||
using System; |
|||
using System.IO; |
|||
using UnityEditor; |
|||
using UnityEditor.Callbacks; |
|||
using UnityEditor.IMGUI.Controls; |
|||
using UnityEngine; |
|||
using static UnityEditor.EditorGUIUtility; |
|||
using static UnityEditor.EditorJsonUtility; |
|||
using static UnityEngine.Application; |
|||
|
|||
namespace UOP1.EditorTools.Replacer |
|||
{ |
|||
internal class ReplacePrefabSearchPopup : EditorWindow |
|||
{ |
|||
private const float previewHeight = 128; |
|||
|
|||
private class ViewState : ScriptableObject |
|||
{ |
|||
public TreeViewState treeViewState = new TreeViewState(); |
|||
} |
|||
|
|||
private static ReplacePrefabSearchPopup window; |
|||
private static Styles styles; |
|||
|
|||
private static Event evt => Event.current; |
|||
private static string assetPath => Path.Combine(dataPath.Remove(dataPath.Length - 7, 7), "Library", "ReplacePrefabTreeState.asset"); |
|||
|
|||
private bool hasSelection => tree.state.selectedIDs.Count > 0; |
|||
private int selectedId => tree.state.selectedIDs[0]; |
|||
private GameObject instance => EditorUtility.InstanceIDToObject(selectedId) as GameObject; |
|||
|
|||
private SearchField searchField; |
|||
private PrefabSelectionTreeView tree; |
|||
private ViewState viewState; |
|||
|
|||
private Vector2 startPos; |
|||
private Vector2 startSize; |
|||
private Vector2 lastSize; |
|||
|
|||
private GameObjectPreview selectionPreview = new GameObjectPreview(); |
|||
|
|||
public static void Show(Rect rect) |
|||
{ |
|||
var windows = Resources.FindObjectsOfTypeAll<ReplacePrefabSearchPopup>(); |
|||
window = windows.Length != 0 ? windows[0] : CreateInstance<ReplacePrefabSearchPopup>(); |
|||
|
|||
window.Init(); |
|||
|
|||
window.startPos = rect.position; |
|||
window.startSize = rect.size; |
|||
|
|||
window.position = new Rect(rect.position, rect.size); |
|||
// Need to predict start window size to avoid trash frame
|
|||
window.SetInitialSize(); |
|||
|
|||
// This type of window supports resizing, but is also persistent, so we need to close it manually
|
|||
window.ShowPopup(); |
|||
|
|||
//onSelectEntry += _ => window.Close();
|
|||
} |
|||
|
|||
private void Init() |
|||
{ |
|||
viewState = CreateInstance<ViewState>(); |
|||
|
|||
if (File.Exists(assetPath)) |
|||
FromJsonOverwrite(File.ReadAllText(assetPath), viewState); |
|||
|
|||
tree = new PrefabSelectionTreeView(viewState.treeViewState); |
|||
tree.onSelectEntry += OnSelectEntry; |
|||
|
|||
AssetPreview.SetPreviewTextureCacheSize(tree.RowsCount); |
|||
|
|||
searchField = new SearchField(); |
|||
searchField.downOrUpArrowKeyPressed += tree.SetFocusAndEnsureSelectedItem; |
|||
searchField.SetFocus(); |
|||
} |
|||
|
|||
private void OnSelectEntry(GameObject prefab) |
|||
{ |
|||
ReplaceTool.ReplaceSelectedObjects(Selection.gameObjects, prefab); |
|||
} |
|||
|
|||
private void OnEnable() |
|||
{ |
|||
Init(); |
|||
} |
|||
|
|||
private void OnDisable() |
|||
{ |
|||
tree.Cleanup(); |
|||
} |
|||
|
|||
public new void Close() |
|||
{ |
|||
SaveState(); |
|||
base.Close(); |
|||
} |
|||
|
|||
private void SaveState() |
|||
{ |
|||
File.WriteAllText(assetPath, ToJson(viewState)); |
|||
} |
|||
|
|||
private void OnGUI() |
|||
{ |
|||
if (evt.type == EventType.KeyDown && evt.keyCode == KeyCode.Escape) |
|||
{ |
|||
if (tree.hasSearch) |
|||
tree.searchString = ""; |
|||
else |
|||
Close(); |
|||
} |
|||
|
|||
if (focusedWindow != this) |
|||
Close(); |
|||
|
|||
if (styles == null) |
|||
styles = new Styles(); |
|||
|
|||
DoToolbar(); |
|||
DoTreeView(); |
|||
DoSelectionPreview(); |
|||
} |
|||
|
|||
void DoToolbar() |
|||
{ |
|||
tree.searchString = searchField.OnToolbarGUI(tree.searchString); |
|||
|
|||
GUILayout.Label("Replace With...", styles.headerLabel); |
|||
} |
|||
|
|||
void DoTreeView() |
|||
{ |
|||
var rect = GUILayoutUtility.GetRect(0, 10000, 0, 10000); |
|||
rect.x += 2; |
|||
rect.width -= 4; |
|||
|
|||
rect.y += 2; |
|||
rect.height -= 4; |
|||
|
|||
tree.OnGUI(rect); |
|||
} |
|||
|
|||
void DoSelectionPreview() |
|||
{ |
|||
if (hasSelection && tree.IsRenderable(selectedId)) |
|||
{ |
|||
SetSize(startSize.x, startSize.y + previewHeight); |
|||
var previewRect = GUILayoutUtility.GetRect(position.width, previewHeight); |
|||
|
|||
selectionPreview.CreatePreviewForTarget(instance); |
|||
selectionPreview.RenderInteractivePreview(previewRect); |
|||
|
|||
selectionPreview.DrawPreviewTexture(previewRect); |
|||
} |
|||
else |
|||
{ |
|||
SetSize(startSize.x, startSize.y); |
|||
} |
|||
} |
|||
|
|||
private void SetInitialSize() |
|||
{ |
|||
if (hasSelection && tree.IsRenderable(selectedId)) |
|||
SetSize(startSize.x, startSize.y + previewHeight); |
|||
else |
|||
SetSize(startSize.x, startSize.y); |
|||
} |
|||
|
|||
private void SetSize(float width, float height) |
|||
{ |
|||
var newSize = new Vector2(width, height); |
|||
if (newSize != lastSize) |
|||
{ |
|||
lastSize = newSize; |
|||
position = new Rect(position.x, position.y, width, height); |
|||
} |
|||
} |
|||
|
|||
private class Styles |
|||
{ |
|||
public GUIStyle headerLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel) |
|||
{ |
|||
fontSize = 11, |
|||
fontStyle = FontStyle.Bold |
|||
}; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 566745c65b224919bb9886aba499be67 |
|||
timeCreated: 1606459889 |
|
|||
using System; |
|||
using UnityEngine; |
|||
|
|||
public static class ScriptableObjectHelper |
|||
{ |
|||
public static void GenerateButtonsForEvents<T>(UnityEngine.Object target) |
|||
where T : ScriptableObject |
|||
{ |
|||
var targetIr = target as T; |
|||
if (targetIr != null) |
|||
{ |
|||
var typeIr = targetIr.GetType(); |
|||
var events = typeIr.GetEvents(); |
|||
|
|||
foreach (var ev in events) |
|||
{ |
|||
if (GUILayout.Button(ev.Name)) |
|||
{ |
|||
//Delegates doesn't support direct access to RaiseMethod, must use backing field
|
|||
// https://stackoverflow.com/questions/14885325/eventinfo-getraisemethod-always-null
|
|||
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/44b0d573-5c53-47b0-8e85-6056cbae95b0/raising-an-event-via-reflection
|
|||
|
|||
var eventDelagate = typeIr.GetField(ev.Name, System.Reflection.BindingFlags.Instance | |
|||
System.Reflection.BindingFlags.NonPublic) |
|||
?.GetValue(targetIr) as MulticastDelegate; |
|||
try |
|||
{ |
|||
eventDelagate?.DynamicInvoke(); |
|||
} |
|||
catch |
|||
{ |
|||
Debug.LogWarning($"Event '{ev.Name}' requires some arguments which weren't provided. Delegate cannot be invoked directly from UI."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0a7eba3a614f3d74fb12d27b80cd0597 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
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(); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c8ba2b93ee72bce49aa87ecc1b52ea6f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 8aed24cb0c323134e96fb92cecbeafc4 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
|
|||
public class ReadOnlyAttribute : PropertyAttribute |
|||
{ |
|||
|
|||
} |
|||
|
|||
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))] |
|||
public class ReadOnlyDrawer : PropertyDrawer |
|||
{ |
|||
public override float GetPropertyHeight(SerializedProperty property, |
|||
GUIContent label) |
|||
{ |
|||
return EditorGUI.GetPropertyHeight(property, label, true); |
|||
} |
|||
|
|||
public override void OnGUI(Rect position, |
|||
SerializedProperty property, |
|||
GUIContent label) |
|||
{ |
|||
GUI.enabled = false; |
|||
EditorGUI.PropertyField(position, property, label, true); |
|||
GUI.enabled = true; |
|||
} |
|||
} |
|||
/// <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: 041bdbf27ff47ea4586c6b730fc5114c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
%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: 041bdbf27ff47ea4586c6b730fc5114c, type: 3} |
|||
m_Name: SceneAccessHolder |
|||
m_EditorClassIdentifier: |
|||
sceneList: |
|||
- name: MainMenu |
|||
path: D:\indie\_OnGoing\open-project-Unity\UOP1_Project\Assets\Scenes\Menus\MainMenu.unity |
|||
visible: 1 |
|||
- name: Forest |
|||
path: D:\indie\_OnGoing\open-project-Unity\UOP1_Project\Assets\Scenes\Locations\Forest.unity |
|||
visible: 0 |
|||
- name: Beach |
|||
path: D:\indie\_OnGoing\open-project-Unity\UOP1_Project\Assets\Scenes\Locations\Beach.unity |
|||
visible: 1 |
|||
- name: TestingGround |
|||
path: D:\indie\_OnGoing\open-project-Unity\UOP1_Project\Assets\Scenes\TestingGround.unity |
|||
visible: 1 |
|||
- name: Initialization |
|||
path: D:\indie\_OnGoing\open-project-Unity\UOP1_Project\Assets\Scenes\Initialization.unity |
|||
visible: 1 |
|
|||
fileFormatVersion: 2 |
|||
guid: 40fa063512d1a5b46a597c15c6160a5e |
|||
NativeFormatImporter: |
|||
externalObjects: {} |
|||
mainObjectFileID: 0 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
|
|||
[CustomEditor(typeof(InputReader))] |
|||
public class InputReaderEditor : Editor |
|||
{ |
|||
public override void OnInspectorGUI() |
|||
{ |
|||
DrawDefaultInspector(); |
|||
|
|||
if (!Application.isPlaying) |
|||
return; |
|||
|
|||
ScriptableObjectHelper.GenerateButtonsForEvents<InputReader>(target); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0e46e2d2cecfc354a8ac70cae65b2343 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue