您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
320 行
12 KiB
320 行
12 KiB
using Unity.Animations.SpringBones.GameObjectExtensions;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEditor.Localization.Editor;
|
|
|
|
namespace Unity.Animations.SpringBones
|
|
{
|
|
public class FindGameObjectsWindow : EditorWindow
|
|
{
|
|
private static class Styles
|
|
{
|
|
public static readonly string editorWindowTitle = Localization.Tr("Name Selector Tool");
|
|
|
|
public static readonly string textName = Localization.Tr("Name");
|
|
public static readonly string textIgnore = Localization.Tr("Ignore");
|
|
public static readonly string textComponent = Localization.Tr("Component");
|
|
public static readonly string textSelect = Localization.Tr("Select");
|
|
public static readonly string textSelectingMultipleObjectFormat = Localization.Tr("Selecting {0} objects");
|
|
}
|
|
|
|
[MenuItem("Window/Animation/SpringBone/Name Selector Tool")]
|
|
public static void ShowWindow()
|
|
{
|
|
var window = GetWindow<FindGameObjectsWindow>(Styles.editorWindowTitle);
|
|
window.OnShow();
|
|
}
|
|
|
|
public static IEnumerable<GameObject> FindObjectsByComponent
|
|
(
|
|
string componentPattern,
|
|
IEnumerable<string> ignorePatterns = null
|
|
)
|
|
{
|
|
var gameObjects = GameObjectUtil.GetAllGameObjects();
|
|
var matchingObjects = FindObjectsWithComponentByPattern(gameObjects, componentPattern);
|
|
if (ignorePatterns != null)
|
|
{
|
|
foreach (var ignorePattern in ignorePatterns)
|
|
{
|
|
matchingObjects = RemoveObjectsByPattern(matchingObjects, ignorePattern);
|
|
}
|
|
}
|
|
return matchingObjects;
|
|
}
|
|
|
|
public static IEnumerable<GameObject> FindObjectsByPattern
|
|
(
|
|
IEnumerable<string> namePatterns,
|
|
IEnumerable<string> ignorePatterns,
|
|
string componentPattern
|
|
)
|
|
{
|
|
if (namePatterns == null || !namePatterns.Any())
|
|
{
|
|
return !System.String.IsNullOrEmpty(componentPattern)
|
|
? FindObjectsByComponent(componentPattern, ignorePatterns)
|
|
: new List<GameObject>();
|
|
}
|
|
|
|
// The first pattern is required
|
|
// If there any patterns after the first, then at least one must match
|
|
var requiredPattern = "*" + namePatterns.First() + "*";
|
|
var additionalPatterns = namePatterns.Skip(1)
|
|
.Select(pattern => "*" + pattern + "*")
|
|
.ToArray();
|
|
|
|
System.Func<string, bool> isNameAMatch = (name) =>
|
|
{
|
|
return StringUtil.GlobMatch(name, requiredPattern)
|
|
&& (additionalPatterns.Length == 0
|
|
|| additionalPatterns.Any(pattern => StringUtil.GlobMatch(name, pattern)));
|
|
};
|
|
|
|
var matchingObjects = GameObjectUtil.GetAllGameObjects()
|
|
.Where(gameObject => isNameAMatch(gameObject.name));
|
|
|
|
if (ignorePatterns != null)
|
|
{
|
|
foreach (var ignorePattern in ignorePatterns)
|
|
{
|
|
matchingObjects = RemoveObjectsByPattern(matchingObjects, ignorePattern);
|
|
}
|
|
}
|
|
|
|
if (!System.String.IsNullOrEmpty(componentPattern))
|
|
{
|
|
matchingObjects = FindObjectsWithComponentByPattern(matchingObjects, componentPattern);
|
|
}
|
|
|
|
return matchingObjects;
|
|
}
|
|
|
|
// private
|
|
|
|
private const int SearchFrameCount = 2;
|
|
private const string SearchNameField = "SearchNameField";
|
|
|
|
private string objectPattern = "";
|
|
private string ignorePattern = "";
|
|
private string componentPattern = "";
|
|
private int searchCountdown;
|
|
private bool isInitialShow;
|
|
|
|
private static bool HasExactComponentMatch(GameObject gameObject, string componentTypeToFind)
|
|
{
|
|
var componentTypes = gameObject.GetComponents<Component>()
|
|
.Where(component => component != null)
|
|
.Select(component => component.GetType().ToString().ToLowerInvariant());
|
|
var dottedComponentTypeToFind = "." + componentTypeToFind;
|
|
return componentTypes.Any(type =>
|
|
type == componentTypeToFind
|
|
|| type.EndsWith(dottedComponentTypeToFind));
|
|
}
|
|
|
|
private static IEnumerable<GameObject> FindObjectsWithComponentByPattern
|
|
(
|
|
IEnumerable<GameObject> sourceObjects,
|
|
string componentPattern
|
|
)
|
|
{
|
|
// Remove all whitespace
|
|
string[] whitespaceList = { " ", "\t", " ", "\r", "\n" };
|
|
foreach (var whitespaceString in whitespaceList)
|
|
{
|
|
componentPattern = componentPattern.Replace(whitespaceString, "");
|
|
}
|
|
|
|
// For components, if there are any objects whose components match exactly (whole word),
|
|
// then only return those
|
|
componentPattern = componentPattern.ToLowerInvariant();
|
|
var exactMatches = sourceObjects.Where(gameObject =>
|
|
HasExactComponentMatch(gameObject, componentPattern));
|
|
if (exactMatches.Any())
|
|
{
|
|
return exactMatches;
|
|
}
|
|
|
|
// Otherwise do a find-anywhere search
|
|
componentPattern = "*" + componentPattern + "*";
|
|
return sourceObjects
|
|
.Where(gameObject => gameObject.GetComponents<Component>().Any(
|
|
component => component != null && StringUtil.GlobMatch(component.GetType().ToString(), componentPattern)));
|
|
}
|
|
|
|
private static IEnumerable<GameObject> RemoveObjectsByPattern
|
|
(
|
|
IEnumerable<GameObject> sourceObjects,
|
|
string removePattern
|
|
)
|
|
{
|
|
removePattern = "*" + removePattern + "*";
|
|
return sourceObjects
|
|
.Where(gameObject => !StringUtil.GlobMatch(gameObject.name, removePattern));
|
|
}
|
|
|
|
private void ReacquireData()
|
|
{
|
|
if (objectPattern == null) { objectPattern = ""; }
|
|
if (ignorePattern == null) { ignorePattern = ""; }
|
|
if (componentPattern == null) { componentPattern = ""; }
|
|
}
|
|
|
|
private void FindAndSelectObjects()
|
|
{
|
|
const string WhitespaceString = " \t\r\n ";
|
|
var whitespaceList = WhitespaceString.ToCharArray();
|
|
|
|
var objectPatterns = objectPattern.Split(whitespaceList, System.StringSplitOptions.RemoveEmptyEntries);
|
|
var ignorePatterns = ignorePattern.Split(whitespaceList, System.StringSplitOptions.RemoveEmptyEntries);
|
|
Selection.objects = FindObjectsByPattern(objectPatterns, ignorePatterns, componentPattern).ToArray();
|
|
searchCountdown = 0;
|
|
}
|
|
|
|
private void RestartSearchCountdown()
|
|
{
|
|
searchCountdown = SearchFrameCount;
|
|
}
|
|
|
|
private static void SetRectYPositions(IList<Rect> rects, float yPosition)
|
|
{
|
|
for (var rectIndex = 0; rectIndex < rects.Count; ++rectIndex)
|
|
{
|
|
var rect = rects[rectIndex];
|
|
rect.y = yPosition;
|
|
rects[rectIndex] = rect;
|
|
}
|
|
}
|
|
|
|
private static void DoTextFieldWithClearButton
|
|
(
|
|
ref TextFieldPositions fieldPositions,
|
|
string label,
|
|
ref string textItem,
|
|
System.Action onTextChanged,
|
|
string textFieldControlName = null
|
|
)
|
|
{
|
|
GUI.Label(fieldPositions.GetLabelRect(), label);
|
|
|
|
if (textFieldControlName != null)
|
|
{
|
|
GUI.SetNextControlName(textFieldControlName);
|
|
}
|
|
|
|
var newText = GUI.TextField(fieldPositions.GetFieldRect(), textItem);
|
|
if (newText != textItem)
|
|
{
|
|
textItem = newText;
|
|
onTextChanged();
|
|
}
|
|
|
|
if (GUI.Button(fieldPositions.GetButtonRect(), "×") && textItem.Length > 0)
|
|
{
|
|
textItem = "";
|
|
onTextChanged();
|
|
}
|
|
}
|
|
|
|
private void OnSelectionChange()
|
|
{
|
|
Repaint();
|
|
}
|
|
|
|
private void OnInspectorUpdate()
|
|
{
|
|
if (searchCountdown > 0)
|
|
{
|
|
searchCountdown--;
|
|
if (searchCountdown == 0)
|
|
{
|
|
FindAndSelectObjects();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Rect[] GetRowRects(float x, float y, IList<float> widths, float height, float spacing)
|
|
{
|
|
var rectCount = widths.Count;
|
|
var rects = new Rect[rectCount];
|
|
for (int rectIndex = 0; rectIndex < rectCount; rectIndex++)
|
|
{
|
|
rects[rectIndex] = new Rect(x, y, widths[rectIndex], height);
|
|
x += widths[rectIndex] + spacing;
|
|
}
|
|
return rects;
|
|
}
|
|
|
|
private struct TextFieldPositions
|
|
{
|
|
public float x;
|
|
public float y;
|
|
public float labelWidth;
|
|
public float fieldWidth;
|
|
public float buttonWidth;
|
|
public float height;
|
|
public float spacing;
|
|
|
|
public Rect GetLabelRect() { return new Rect(x, y, labelWidth, height); }
|
|
public Rect GetFieldRect() { return new Rect(x + labelWidth + spacing, y, fieldWidth, height); }
|
|
public Rect GetButtonRect() { return new Rect(x + labelWidth + fieldWidth + spacing * 2f, y, buttonWidth, height); }
|
|
}
|
|
|
|
private static TextFieldPositions GetTextFieldPositions(float x, float y, float rowWidth, float rowHeight, float spacing)
|
|
{
|
|
const int LabelMinWidth = 40;
|
|
const int LabelMaxWidth = 120;
|
|
const float ButtonWidth = 20f;
|
|
var labelWidth = Mathf.Clamp(rowWidth * 0.25f, LabelMinWidth, LabelMaxWidth);
|
|
return new TextFieldPositions
|
|
{
|
|
x = x,
|
|
y = y,
|
|
labelWidth = labelWidth,
|
|
fieldWidth = rowWidth - labelWidth - ButtonWidth - spacing * 2f,
|
|
buttonWidth = ButtonWidth,
|
|
height = rowHeight,
|
|
spacing = spacing
|
|
};
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
ReacquireData();
|
|
|
|
const float RowHeight = 30f;
|
|
const float Spacing = 8f;
|
|
const float RowOffset = RowHeight + Spacing;
|
|
|
|
var rowWidth = position.width - Spacing * 2f;
|
|
var fieldPositions = GetTextFieldPositions(Spacing, Spacing, rowWidth, RowHeight, Spacing);
|
|
DoTextFieldWithClearButton(ref fieldPositions, Styles.textName, ref objectPattern, RestartSearchCountdown, SearchNameField);
|
|
fieldPositions.y += RowOffset;
|
|
DoTextFieldWithClearButton(ref fieldPositions, Styles.textIgnore, ref ignorePattern, RestartSearchCountdown);
|
|
fieldPositions.y += RowOffset;
|
|
DoTextFieldWithClearButton(ref fieldPositions, Styles.textComponent, ref componentPattern, RestartSearchCountdown);
|
|
fieldPositions.y += RowOffset;
|
|
|
|
var rowRect = new Rect(fieldPositions.x, fieldPositions.y, rowWidth, RowHeight);
|
|
if (GUI.Button(rowRect, Styles.textSelect)) { FindAndSelectObjects(); }
|
|
|
|
rowRect.y += RowOffset;
|
|
var message = string.Format(Styles.textSelectingMultipleObjectFormat, Selection.objects.Length);
|
|
GUI.Label(rowRect, message);
|
|
|
|
if (isInitialShow)
|
|
{
|
|
EditorGUI.FocusTextInControl(SearchNameField);
|
|
isInitialShow = false;
|
|
}
|
|
}
|
|
|
|
private void OnShow()
|
|
{
|
|
searchCountdown = 0;
|
|
isInitialShow = true;
|
|
}
|
|
}
|
|
}
|