Last active
April 2, 2021 18:57
-
-
Save mmechtley/b603f8c1221bcfad5e64771aaa7d52b6 to your computer and use it in GitHub Desktop.
A reusable Unity editor window to show a list of gameobjects, with buttons to ping them in the scene / prefab stage / project / etc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using UnityEditor; | |
using UnityEngine; | |
namespace Code.Utilities.Editor | |
{ | |
public class PingableGameObjectList : EditorWindow | |
{ | |
private const string TypeNameEditorKey = "PingableGameObjectList_ActionTypeName"; | |
private const string MethodNameEditorKey = "PingableGameObjectList_ActionMethodName"; | |
/// <summary> | |
/// Show pingable list of objects with the given title. If refreshAction is supplied, we'll show a button to run | |
/// that action (which would then usually re-invoke this method). If it's a static method with 0 params, also | |
/// support re-binding the refresh action after script reload etc. | |
/// </summary> | |
public static void ShowWithObjects( string title, IEnumerable<GameObject> objects, Action refreshAction=null, | |
bool refreshOnSceneLoad=false ) | |
{ | |
var w = GetWindow<PingableGameObjectList>(); | |
if( w == null ) | |
{ | |
w = CreateWindow<PingableGameObjectList>(); | |
} | |
var methodInfo = refreshAction?.GetMethodInfo(); | |
if( methodInfo != null | |
&& methodInfo.IsStatic | |
&& methodInfo.GetParameters().Length == 0 ) | |
{ | |
EditorPrefs.SetString( TypeNameEditorKey, methodInfo.DeclaringType?.FullName ); | |
EditorPrefs.SetString( MethodNameEditorKey, methodInfo.Name ); | |
} | |
w.titleContent = new GUIContent( title ); | |
w.ShowUtility(); | |
w.minSize = new Vector2(320, 450); | |
w.objects = new List<GameObject>( objects ); | |
w.refreshAction = refreshAction; | |
w.refreshOnSceneLoad = refreshOnSceneLoad; | |
} | |
private List<GameObject> objects; | |
private Action refreshAction; | |
private bool refreshOnSceneLoad; | |
private Vector2 scroll; | |
/// <summary> | |
/// This attempts to rebind the refresh action if this window is open and we lose the method reference after a | |
/// script refresh etc. | |
/// </summary> | |
private void rebindIfNeeded() | |
{ | |
var methodName = EditorPrefs.GetString( MethodNameEditorKey ); | |
var typeName = EditorPrefs.GetString( TypeNameEditorKey ); | |
if( refreshAction == null && !string.IsNullOrEmpty( typeName ) && !string.IsNullOrEmpty( methodName )) | |
{ | |
var assemblies = AppDomain.CurrentDomain.GetAssemblies(); | |
foreach( var assembly in assemblies ) | |
{ | |
var type = assembly.GetType( typeName ); | |
var method = type?.GetMethod( methodName ); | |
var neededParams = method?.GetParameters(); | |
if( type != null && method != null && neededParams.Length == 0 && method.IsStatic ) | |
{ | |
// the cached action is anonymous now instead of the original reference but that doesn't matter because | |
// we'll reload from the strings again anyway | |
refreshAction = () => method.Invoke( null, null ); | |
break; | |
} | |
} | |
} | |
} | |
private void OnGUI() | |
{ | |
rebindIfNeeded(); | |
if( refreshAction != null ) | |
{ | |
if( GUILayout.Button( "Refresh" ) ) | |
{ | |
refreshAction.Invoke(); | |
} | |
EditorGUILayout.Separator(); | |
} | |
using( var scope = new GUILayout.ScrollViewScope( scroll ) ) | |
{ | |
scroll = scope.scrollPosition; | |
foreach( var gameObj in objects ) | |
{ | |
// deleted or something | |
if( gameObj == null ) | |
continue; | |
if( GUILayout.Button( gameObj.name ) ) | |
{ | |
Selection.SetActiveObjectWithContext( gameObj, null ); | |
} | |
} | |
} | |
} | |
private void OnDidOpenScene() | |
{ | |
if( refreshOnSceneLoad ) | |
refreshAction?.Invoke(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment