Unity扩展Editor菜单:提供一个统一的接口,来让策划调节prefab里相关脚本的数值
2017-06-29 16:24
585 查看
扩展Editor的目的:提供一个统一的接口,来让策划调节prefab里相关脚本的数值,避免策划直接调整数值时不小心修改了prefab里其他组件的设置
思路:
1、创建1个EditorWindow
2、Project里选择相应的目录或者文件,处理得到目录Directory
3、获取Directory下所有的prefab文件,得到一个文件名数组
4、给出文件名的下拉选择框,根据选择得到index,进而可以得到文件在Assets里的Path
5、根据Path加载Asset,获取其所有的MonoBehaviour组件,给出下拉选择框
6、显示所选择的MonoBehaviour的所有public的属性
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public class ExposeProperties
{
public static void Expose(System.Object obj)
{
Expose(GetProperties(obj));
}
public static void Expose(ScriptProperty[] properties)
{
GUILayoutOption[] emptyOptions = new GUILayoutOption[0];
EditorGUILayout.BeginVertical(emptyOptions);
foreach (ScriptProperty property in properties)
{
EditorGUILayout.BeginHorizontal();
switch (property.Type)
{
case SerializedPropertyType.Integer:
property.SetValue(EditorGUILayout.IntField(property.Name, (int)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Float:
property.SetValue(EditorGUILayout.FloatField(property.Name, (float)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Boolean:
property.SetValue(EditorGUILayout.Toggle(property.Name, (bool)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.String:
property.SetValue(EditorGUILayout.TextField(property.Name, (String)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Vector2:
property.SetValue(EditorGUILayout.Vector2Field(property.Name, (Vector2)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Vector3:
property.SetValue(EditorGUILayout.Vector3Field(property.Name, (Vector3)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Enum:
property.SetValue(EditorGUILayout.EnumPopup(property.Name, (Enum)property.GetValue(), emptyOptions));
break;
default:
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndHorizontal();
}
public static ScriptProperty[] GetProperties(System.Object obj)
{
List<ScriptProperty> properties = new List<ScriptProperty>();
FieldInfo[] infos = obj.GetType().GetFields();
foreach (FieldInfo info in infos)
{
SerializedPropertyType type = SerializedPropertyType.Integer;
if (ScriptProperty.GetPropertyType(info, out type))
{
ScriptProperty property = new ScriptProperty(obj, info, type);
properties.Add(property);
}
}
return properties.ToArray();
}
}
public class ScriptProperty
{
System.Object m_Instance;
FieldInfo m_Info;
SerializedPropertyType m_Type;
public SerializedPropertyType Type
{
get { return m_Type; }
}
public string Name
{
get { return ObjectNames.NicifyVariableName(m_Info.Name); }
}
public ScriptProperty(System.Object instance, FieldInfo info, SerializedPropertyType propertyType)
{
m_Instance = instance;
m_Info = info;
m_Type = propertyType;
}
public System.Object GetValue()
{
return m_Info.GetValue(m_Instance);
}
public void SetValue(System.Object obj)
{
m_Info.SetValue(m_Instance, obj);
}
public static bool GetPropertyType(FieldInfo info, out SerializedPropertyType propertyType)
{
propertyType = SerializedPropertyType.Generic;
Type type = info.FieldType;
if (type == typeof(int))
{
propertyType = SerializedPropertyType.Integer;
return true;
}
if (type == typeof(float))
{
propertyType = SerializedPropertyType.Float;
return true;
}
if (type == typeof(bool))
{
propertyType = SerializedPropertyType.Boolean;
return true;
}
if (type == typeof(string))
{
propertyType = SerializedPropertyType.String;
return true;
}
if (type == typeof(Vector2))
{
propertyType = SerializedPropertyType.Vector2;
return true;
}
if (type == typeof(Vector3))
{
propertyType = SerializedPropertyType.Vector3;
return true;
}
if (type.IsEnum)
{
propertyType = SerializedPropertyType.Enum;
return true;
}
return false;
}
}
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public class PrefabEditor : EditorWindow
{
[MenuItem("Prefab/Editor")]
static void OpenEditor()
{
EditorWindow.GetWindow(typeof(PrefabEditor));
}
private static string extension = ".prefab";
private static string assetTopFolder = "Assets/";
private string destinationPath;
private string[] childrenNames;
private int childIndex;
private int componentIndex;
void OnGUI()
{
DrawSeparator();
string oldPath = destinationPath; // keep old path for comparision purpose
// process selection in project
string selectionPath = GetSelectionPath();
string selectionName = Path.GetFileNameWithoutExtension(selectionPath);
destinationPath = GetDirectory(selectionPath);
if (destinationPath == null)
{
EditorGUILayout.LabelField("You must select a folder first");
return;
}
bool isDirectory = false; // original selection is a directory or file
if (destinationPath == selectionPath)
isDirectory = true;
destinationPath = destinationPath.Substring(assetTopFolder.Length);
EditorGUILayout.LabelField("Directory: ", destinationPath);
if (destinationPath != oldPath)
{
childrenNames = GetFiles(destinationPath, "*" + extension, true);
childIndex = GetSelectionIndex(selectionName, childrenNames);
componentIndex = 0;
}
else if (!isDirectory)
{
childIndex = GetSelectionIndex(selectionName, childrenNames);
}
if (childrenNames.Length > 0)
{
int lastIndex = childIndex; // keep old index to check if we switch to another one, which reset componentIndex
childIndex = EditorGUILayout.Popup("Choose Asset: ", childIndex, childrenNames);
if (childIndex != lastIndex)
{
componentIndex = 0;
}
string assetPath = Path.Combine(assetTopFolder + destinationPath, childrenNames[childIndex] + extension);
GameObject asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Object)) as GameObject;
// asset must not be null, since the assetPath is always a reasonable path, so i don't test it for null
Selection.activeObject = asset; // make asset as active object
ExposeAsset(asset);
}
else
{
EditorGUILayout.LabelField("No prefab found!");
}
}
void OnSelectionChange()
{
Repaint();
}
string GetSelectionPath()
{
string path = null;
Object[] selections = Selection.GetFiltered(typeof(Object), SelectionMode.Assets);
if (selections != null && selections.Length > 0)
path = AssetDatabase.GetAssetPath(selections[0]);
return path;
}
string GetDirectory(string path)
{
string directory = path;
if (directory != null && Path.GetExtension(directory) != "")
{
directory = Path.GetDirectoryName(directory);
}
return directory;
}
int GetSelectionIndex(string selectionName, string[] names)
{
int index = 0;
foreach (string name in names)
{
if (selectionName == name)
{
return index;
}
index++;
}
return 0; // if not found, return 0
}
string[] GetFiles(string relativePath, string pattern, bool topOnly = true)
{
string[] names = null;
SearchOption option = topOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
string path = Path.Combine(Application.dataPath, relativePath);
try
{
string[] files = Directory.GetFiles(path, pattern, option);
names = new string[files.Length];
for (int i = 0; i < files.Length; ++i)
names[i]= Path.GetFileNameWithoutExtension(files[i]);
}
catch (DirectoryNotFoundException e)
{
Debug.LogError(e);
}
return names;
}
void ExposeAsset(GameObject asset)
{
MonoBehaviour[] scripts = asset.GetComponents<MonoBehaviour>();
if (scripts != null && scripts.Length > 0)
{
List<string> names = new List<string>();
foreach (MonoBehaviour scr in scripts)
{
string scriptName = ObjectNames.NicifyVariableName(scr.GetType().ToString());
names.Add(scriptName);
}
componentIndex = EditorGUILayout.Popup("Choose Component: ", componentIndex, names.ToArray());
MonoBehaviour script = scripts[componentIndex];
DrawSeparator();
ExposeProperties.Expose(script);
GUILayout.Space(20f);
if (GUILayout.Button("Save"))
{
EditorUtility.SetDirty(script); // very important: save script component to disk
}
}
}
void DrawSeparator()
{
GUILayout.Space(12f);
if (Event.current.type == EventType.Repaint)
{
Texture2D tex = EditorGUIUtility.whiteTexture;
Rect rect = GUILayoutUtility.GetLastRect();
GUI.color = new Color(0f, 0f, 0f, 0.25f);
GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 4f), tex);
GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 1f), tex);
GUI.DrawTexture(new Rect(0f, rect.yMin + 9f, Screen.width, 1f), tex);
GUI.color = Color.white;
}
}
}
注意:两个脚本必须同时放在Editor文件夹下
思路:
1、创建1个EditorWindow
2、Project里选择相应的目录或者文件,处理得到目录Directory
3、获取Directory下所有的prefab文件,得到一个文件名数组
4、给出文件名的下拉选择框,根据选择得到index,进而可以得到文件在Assets里的Path
5、根据Path加载Asset,获取其所有的MonoBehaviour组件,给出下拉选择框
6、显示所选择的MonoBehaviour的所有public的属性
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public class ExposeProperties
{
public static void Expose(System.Object obj)
{
Expose(GetProperties(obj));
}
public static void Expose(ScriptProperty[] properties)
{
GUILayoutOption[] emptyOptions = new GUILayoutOption[0];
EditorGUILayout.BeginVertical(emptyOptions);
foreach (ScriptProperty property in properties)
{
EditorGUILayout.BeginHorizontal();
switch (property.Type)
{
case SerializedPropertyType.Integer:
property.SetValue(EditorGUILayout.IntField(property.Name, (int)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Float:
property.SetValue(EditorGUILayout.FloatField(property.Name, (float)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Boolean:
property.SetValue(EditorGUILayout.Toggle(property.Name, (bool)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.String:
property.SetValue(EditorGUILayout.TextField(property.Name, (String)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Vector2:
property.SetValue(EditorGUILayout.Vector2Field(property.Name, (Vector2)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Vector3:
property.SetValue(EditorGUILayout.Vector3Field(property.Name, (Vector3)property.GetValue(), emptyOptions));
break;
case SerializedPropertyType.Enum:
property.SetValue(EditorGUILayout.EnumPopup(property.Name, (Enum)property.GetValue(), emptyOptions));
break;
default:
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndHorizontal();
}
public static ScriptProperty[] GetProperties(System.Object obj)
{
List<ScriptProperty> properties = new List<ScriptProperty>();
FieldInfo[] infos = obj.GetType().GetFields();
foreach (FieldInfo info in infos)
{
SerializedPropertyType type = SerializedPropertyType.Integer;
if (ScriptProperty.GetPropertyType(info, out type))
{
ScriptProperty property = new ScriptProperty(obj, info, type);
properties.Add(property);
}
}
return properties.ToArray();
}
}
public class ScriptProperty
{
System.Object m_Instance;
FieldInfo m_Info;
SerializedPropertyType m_Type;
public SerializedPropertyType Type
{
get { return m_Type; }
}
public string Name
{
get { return ObjectNames.NicifyVariableName(m_Info.Name); }
}
public ScriptProperty(System.Object instance, FieldInfo info, SerializedPropertyType propertyType)
{
m_Instance = instance;
m_Info = info;
m_Type = propertyType;
}
public System.Object GetValue()
{
return m_Info.GetValue(m_Instance);
}
public void SetValue(System.Object obj)
{
m_Info.SetValue(m_Instance, obj);
}
public static bool GetPropertyType(FieldInfo info, out SerializedPropertyType propertyType)
{
propertyType = SerializedPropertyType.Generic;
Type type = info.FieldType;
if (type == typeof(int))
{
propertyType = SerializedPropertyType.Integer;
return true;
}
if (type == typeof(float))
{
propertyType = SerializedPropertyType.Float;
return true;
}
if (type == typeof(bool))
{
propertyType = SerializedPropertyType.Boolean;
return true;
}
if (type == typeof(string))
{
propertyType = SerializedPropertyType.String;
return true;
}
if (type == typeof(Vector2))
{
propertyType = SerializedPropertyType.Vector2;
return true;
}
if (type == typeof(Vector3))
{
propertyType = SerializedPropertyType.Vector3;
return true;
}
if (type.IsEnum)
{
propertyType = SerializedPropertyType.Enum;
return true;
}
return false;
}
}
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public class PrefabEditor : EditorWindow
{
[MenuItem("Prefab/Editor")]
static void OpenEditor()
{
EditorWindow.GetWindow(typeof(PrefabEditor));
}
private static string extension = ".prefab";
private static string assetTopFolder = "Assets/";
private string destinationPath;
private string[] childrenNames;
private int childIndex;
private int componentIndex;
void OnGUI()
{
DrawSeparator();
string oldPath = destinationPath; // keep old path for comparision purpose
// process selection in project
string selectionPath = GetSelectionPath();
string selectionName = Path.GetFileNameWithoutExtension(selectionPath);
destinationPath = GetDirectory(selectionPath);
if (destinationPath == null)
{
EditorGUILayout.LabelField("You must select a folder first");
return;
}
bool isDirectory = false; // original selection is a directory or file
if (destinationPath == selectionPath)
isDirectory = true;
destinationPath = destinationPath.Substring(assetTopFolder.Length);
EditorGUILayout.LabelField("Directory: ", destinationPath);
if (destinationPath != oldPath)
{
childrenNames = GetFiles(destinationPath, "*" + extension, true);
childIndex = GetSelectionIndex(selectionName, childrenNames);
componentIndex = 0;
}
else if (!isDirectory)
{
childIndex = GetSelectionIndex(selectionName, childrenNames);
}
if (childrenNames.Length > 0)
{
int lastIndex = childIndex; // keep old index to check if we switch to another one, which reset componentIndex
childIndex = EditorGUILayout.Popup("Choose Asset: ", childIndex, childrenNames);
if (childIndex != lastIndex)
{
componentIndex = 0;
}
string assetPath = Path.Combine(assetTopFolder + destinationPath, childrenNames[childIndex] + extension);
GameObject asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Object)) as GameObject;
// asset must not be null, since the assetPath is always a reasonable path, so i don't test it for null
Selection.activeObject = asset; // make asset as active object
ExposeAsset(asset);
}
else
{
EditorGUILayout.LabelField("No prefab found!");
}
}
void OnSelectionChange()
{
Repaint();
}
string GetSelectionPath()
{
string path = null;
Object[] selections = Selection.GetFiltered(typeof(Object), SelectionMode.Assets);
if (selections != null && selections.Length > 0)
path = AssetDatabase.GetAssetPath(selections[0]);
return path;
}
string GetDirectory(string path)
{
string directory = path;
if (directory != null && Path.GetExtension(directory) != "")
{
directory = Path.GetDirectoryName(directory);
}
return directory;
}
int GetSelectionIndex(string selectionName, string[] names)
{
int index = 0;
foreach (string name in names)
{
if (selectionName == name)
{
return index;
}
index++;
}
return 0; // if not found, return 0
}
string[] GetFiles(string relativePath, string pattern, bool topOnly = true)
{
string[] names = null;
SearchOption option = topOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
string path = Path.Combine(Application.dataPath, relativePath);
try
{
string[] files = Directory.GetFiles(path, pattern, option);
names = new string[files.Length];
for (int i = 0; i < files.Length; ++i)
names[i]= Path.GetFileNameWithoutExtension(files[i]);
}
catch (DirectoryNotFoundException e)
{
Debug.LogError(e);
}
return names;
}
void ExposeAsset(GameObject asset)
{
MonoBehaviour[] scripts = asset.GetComponents<MonoBehaviour>();
if (scripts != null && scripts.Length > 0)
{
List<string> names = new List<string>();
foreach (MonoBehaviour scr in scripts)
{
string scriptName = ObjectNames.NicifyVariableName(scr.GetType().ToString());
names.Add(scriptName);
}
componentIndex = EditorGUILayout.Popup("Choose Component: ", componentIndex, names.ToArray());
MonoBehaviour script = scripts[componentIndex];
DrawSeparator();
ExposeProperties.Expose(script);
GUILayout.Space(20f);
if (GUILayout.Button("Save"))
{
EditorUtility.SetDirty(script); // very important: save script component to disk
}
}
}
void DrawSeparator()
{
GUILayout.Space(12f);
if (Event.current.type == EventType.Repaint)
{
Texture2D tex = EditorGUIUtility.whiteTexture;
Rect rect = GUILayoutUtility.GetLastRect();
GUI.color = new Color(0f, 0f, 0f, 0.25f);
GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 4f), tex);
GUI.DrawTexture(new Rect(0f, rect.yMin + 6f, Screen.width, 1f), tex);
GUI.DrawTexture(new Rect(0f, rect.yMin + 9f, Screen.width, 1f), tex);
GUI.color = Color.white;
}
}
}
注意:两个脚本必须同时放在Editor文件夹下
相关文章推荐
- 【Unity】Unity Editor菜单按钮扩展
- Unity Editor 编辑器扩展 七 创建脚本模版
- 数值极限类 大笑一般来说,数值类型的极值是一个与平台相关的特性。c++标准程序库通过template numeric_limits提供这些极值,取代传统C语言所采用的预处numeric_limits
- REST接口提供服务的一个小脚本
- Unity拖拽物体到另外一个物体中的检测相关脚本(萝卜和坑)
- unity脚本中运行时实例化一个prefab
- Unity属性——AddComponentMenu 字面理解:添加 组件选项菜单 分析:可能是添加一个脚本或者组件到一个物体上 验证: 新建一个脚本:AttributeTest 提示:添
- 一个为扩展过的自定义DataGridiew控件提供多行表头重绘所需的TreeNode和Columns代码,以满足列自动增加的功能。大家看看帮忙优化下
- 【转帖】扩展微软DDK中的NDIS IM驱动的功能:添加一个DeviceIoControl接口
- 最近看见了一个提供地图接口的很好的网站
- 提供一个通用的Javascript验证页面输入的脚本给大家,并希望大家提意见呀
- 短信平台(提供数据接口,可以与任何软件扩展)
- 一个GridView客户端扩展脚本库
- 扩展微软DDK中的NDIS IM驱动的功能:添加一个DeviceIoControl接口
- facade -- 对于有很多接口的提供一个简单的接口
- 提供一个简单的滑动菜单的数据结构
- 用C语言编辑的魔方阵脚本谁能提供一个
- facade 提供一个接口,通过这个接口,可以使一个子系统更容易使用。
- 提供一个生成CVS用户的脚本
- 扩展GridView控件以包含一个与排序相关的箭头标记