美术制作好界面预设之后,程序需要绑定ui元素进行逻辑控制,可以定义public变量,然后拉ui元素赋值给public变量,或者用Find函数获取ui元素,但是这样总感觉有点不舒服,于是想着自己做一个工具,来完成这样的绑定。(工具代码在文章最后)
使用
首先做一个界面预设,如下图所示
给预设挂PrefabSlot组件,打开PrefabSlot编辑器,绑定ui元素
最后,最后给预设再挂一个运行脚本PanelBhv.cs
// PanelBhv.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PanelBhv : MonoBehaviour
{
void Start()
{
var slot = gameObject.GetComponent<PrefabSlot>();
SetUi(slot);
}
private void SetUi(PrefabSlot slot)
{
var text = slot.SetText("contentText", "Hello world");
slot.SetButton("btn", (btn) =>
{
text.text = "On Btn Click";
});
}
}
运行效果如下
工程下载: https://download.csdn.net/download/linxinfa/11856420
工具代码
PrefabSlot.cs
// PrefabSlot.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
public class PrefabSlot : MonoBehaviour {
[Serializable]
public class Item
{
public string name;
public UnityEngine.Object obj;
}
public Item[] items = new Item[0];
public UnityEngine.Object GetObj(string name)
{
if (string.IsNullOrEmpty(name)) return null;
for (int i = 0, cnt = items.Length; i < cnt; i++)
{
Item item = items[i];
if (item.name.Equals(name))
{
return item.obj;
}
}
return null;
}
public T GetObj<T>(string name) where T : UnityEngine.Object
{
try
{
return (T)GetObj(name);
}
catch (Exception e)
{
Debug.LogError("PrefabSlot GetObj name = " + name);
return default(T);
}
}
#region 设置ui接口
public Text SetText(string name, string textStr)
{
var text = GetObj<Text>(name);
if(null != text)
{
text.text = textStr;
}
else
{
Debug.LogError("PrefabSlot SetText Error, obj is null: " + name);
}
return text;
}
public Button SetButton(string name, Action<GameObject> onClick)
{
var btn = GetObj<Button>(name);
if(null != btn)
{
btn.onClick.AddListener(() =>
{
onClick(btn.gameObject);
});
}
else
{
Debug.LogError("PrefabSlot SetButton Error, obj is null: " + name);
}
return btn;
}
///TODO: 其他接口可自行拓展
#endregion 设置ui接口
}
PrefabSlotEditor.cs
// PrefabSlotEditor.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
public class PrefabSlotEditor : EditorWindow
{
private GameObject m_prefabslotObj;
private PrefabSlot m_slot;
private List<PrefabSlot.Item> m_itemList;
private List<PrefabSlot.Item> m_searchMatchItemList = new List<PrefabSlot.Item>();
private Vector2 m_scrollViewPos;
private List<Component> m_comList = new List<Component>();
private string m_itemName;
private string m_itemNameSearch;
private string m_selectedItemName;
private string m_lockBtnName;
private Object m_itemObj;
private bool m_lock;
private string m_componentStr;
enum ItemOption
{
AddItem,
RemoveItem,
ClearItems,
SearchItems
}
private GUIStyle m_labelSytleYellow;
private GUIStyle m_labelStyleNormal;
[MenuItem("Tools/Aux/PrefabSlot工具")]
public static void SlotWindow()
{
var window = GetWindow<PrefabSlotEditor>();
window.titleContent = new GUIContent("PrefabSlotEditor", AssetPreview.GetMiniTypeThumbnail(typeof(UnityEngine.EventSystems.EventSystem)), "decent");
window.Init();
}
[MenuItem("GameObject/PrefabSlot Window", priority = 0)]
public static void PrefabSlotWindow()
{
if (Selection.activeGameObject.GetComponent<PrefabSlot>())
SlotWindow();
else
Debug.LogError("no PrefabSlot on this GameObject");
}
void Awake()
{
m_labelStyleNormal = new GUIStyle(EditorStyles.miniButton);
m_labelStyleNormal.fontSize = 12;
m_labelStyleNormal.normal.textColor = Color.white;
m_labelSytleYellow = new GUIStyle(EditorStyles.miniButton);
m_labelSytleYellow.fontSize = 12;
m_labelSytleYellow.normal.textColor = Color.yellow;
}
void OnEnable()
{
EditorApplication.update += Repaint;
}
void OnDisable()
{
EditorApplication.update -= Repaint;
}
void Init()
{
m_itemList = new List<PrefabSlot.Item>();
m_comList = new List<Component>();
m_lockBtnName = "锁定item组件列表";
m_componentStr = string.Empty;
m_lock = false;
if (Selection.activeGameObject.GetComponent<PrefabSlot>())
{
m_prefabslotObj = Selection.activeGameObject;
OnRefreshBtnClicked();
}
}
void OnGUI()
{
BeginBox(new Rect(0, 0, 3 * Screen.width / 7f, Screen.height));
DrawSearchBtn();
DrawSearchItemList();
EndBox();
BeginBox(new Rect(3 * Screen.width / 7f + 2, 0, 2 * Screen.width / 7f, Screen.height));
DrawLockBtn();
GUILayout.Space(2);
DrawComponentList();
EndBox();
BeginBox(new Rect(5 * Screen.width / 7f + 4, 0, 2 * Screen.width / 7f - 4, Screen.height));
DrawPrefabSlotField();
GUILayout.Space(2);
DrawItemField();
EndBox();
}
private void DrawSearchBtn()
{
GUILayout.BeginHorizontal();
string before = m_itemNameSearch;
string after = EditorGUILayout.TextField("", before, "SearchTextField");
if (before != after) m_itemNameSearch = after;
if (GUILayout.Button("", "SearchCancelButton"))
{
m_itemNameSearch = "";
GUIUtility.keyboardControl = 0;
}
ComponentOperation(m_slot, ItemOption.SearchItems, after);
GUILayout.EndHorizontal();
}
private void DrawPrefabSlotField()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("slot");
var oldObj = m_prefabslotObj;
m_prefabslotObj = EditorGUILayout.ObjectField(m_prefabslotObj, typeof(GameObject), true) as GameObject;
//if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Refresh"), EditorStyles.toolbarButton, GUILayout.Width(30)))
//{
// Repaint();
//}
//if (GUILayout.Button(new GUIContent("Clear All", "清除无用的item"), EditorStyles.toolbarButton))
//{
// if (m_prefabslot != null)
// {
// ComponentOperation(m_prefabslot, itemOption.clearItems);
// GameLogger.LogBlue("清理完成");
// }
//}
EditorGUILayout.EndHorizontal();
if (!m_prefabslotObj)
{
EditorGUILayout.HelpBox("Select a PrefabSlot Object", MessageType.Warning);
}
else if (oldObj != m_prefabslotObj)
{
m_slot = m_prefabslotObj.GetComponent<PrefabSlot>();
}
}
private void BeginBox(Rect rect)
{
rect.height -= 20;
GUILayout.BeginArea(rect);
GUILayout.Box("", GUILayout.Width(rect.width), GUILayout.Height(rect.height));
GUILayout.EndArea();
GUILayout.BeginArea(rect);
}
private void EndBox()
{
GUILayout.EndArea();
}
private void DrawSearchItemList()
{
if (null == m_prefabslotObj || null == m_slot)
m_searchMatchItemList.Clear();
m_scrollViewPos = EditorGUILayout.BeginScrollView(m_scrollViewPos);
foreach (var item in m_searchMatchItemList)
{
GUILayout.BeginHorizontal();
item.name = EditorGUILayout.TextField(item.name);
item.obj = EditorGUILayout.ObjectField(item.obj, typeof(GameObject), true);
if (GUILayout.Button("-", GUILayout.Width(20)))
{
m_itemList.Remove(item);
m_slot.items = m_itemList.ToArray();
GUILayout.EndHorizontal();
break;
}
GUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
private void DrawItemField()
{
EditorGUILayout.BeginVertical();
GUILayout.Label(string.IsNullOrEmpty(m_componentStr) ? "null" : m_componentStr);
m_itemName = EditorGUILayout.TextField(m_itemName);
if (GUILayout.Button(new GUIContent("Add Item", "添加item"), GUILayout.Height(80)))
{
ComponentOperation(m_slot, ItemOption.AddItem);
}
if (GUILayout.Button(new GUIContent("Delete Item", "删除指定的item")))
{
if (m_prefabslotObj != null)
{
if (string.IsNullOrEmpty(m_itemName))
Debug.LogWarning("请输入要删除的项目名称");
else
ComponentOperation(m_slot, ItemOption.RemoveItem);
}
}
if (GUILayout.Button(new GUIContent("Refresh", "刷新")))
{
OnRefreshBtnClicked();
}
ItemTip();
}
private void OnRefreshBtnClicked()
{
if (null != m_prefabslotObj)
m_slot = m_prefabslotObj.GetComponent<PrefabSlot>();
if (null == m_slot)
{
m_itemList.Clear();
m_comList.Clear();
}
}
private void DrawLockBtn()
{
if (GUILayout.Button(new GUIContent(m_lockBtnName, m_lockBtnName), EditorStyles.toolbarButton))
{
m_lock = !m_lock;
if (m_lock == false)
m_lockBtnName = "锁定item组件列表";
else
m_lockBtnName = "解锁item组件列表";
}
}
private void DrawComponentList()
{
var go = Selection.activeObject as GameObject; //获取选中对象
if (go && m_lock == false)
{
Component[] components = go.GetComponents<Component>();
m_comList.Clear();
m_comList.AddRange(components);
m_selectedItemName = go.name;
}
if (go == null)
{
m_comList.Clear();
m_selectedItemName = "无选中对象";
}
if (go && GUILayout.Button("GameObject", "GameObject" == m_componentStr ? m_labelSytleYellow : m_labelStyleNormal))
{
m_itemObj = go;
m_componentStr = "GameObject";
}
foreach (var com in m_comList)
{
GUILayout.Space(2);
var comType = com.GetType().ToString();
comType = comType.Replace("UnityEngine.UI.", "");
comType = comType.Replace("UnityEngine.", "");
if (GUILayout.Button(comType, comType == m_componentStr ? m_labelSytleYellow : m_labelStyleNormal))
{
m_itemObj = com;
m_componentStr = comType;
}
}
EditorGUILayout.EndVertical();
}
#region private method
private void ComponentOperation(PrefabSlot slot, ItemOption option, string name = " ")
{
if (null == slot) return;
PrefabSlot.Item item = new PrefabSlot.Item();
switch (option)
{
case ItemOption.AddItem:
AddItem(item, slot);
break;
case ItemOption.RemoveItem:
RemoveItem(item, slot);
break;
case ItemOption.ClearItems:
ClearItem(item, slot);
break;
case ItemOption.SearchItems:
SearchItem(item, slot, name);
break;
}
slot.items = m_itemList.ToArray();
}
private void AddItem(PrefabSlot.Item item, PrefabSlot Ps)
{
item.name = m_itemName;
item.obj = m_itemObj;
m_itemList = Ps.items.ToList();
List<string> nameList = new List<string>();
foreach (var iL in m_itemList)
{
nameList.Add(iL.name);
}
if (!string.IsNullOrEmpty(m_itemName) && m_itemObj != null)
{
if (nameList.Contains(m_itemName))
{
Debug.LogError("重复元素");
m_itemList.Add(item);
}
else
m_itemList.Add(item);
}
}
private void RemoveItem(PrefabSlot.Item item, PrefabSlot Ps)
{
item.name = m_itemName;
m_itemList = Ps.items.ToList();
for (int i = 0; i < m_itemList.Count; i++)
{
if (m_itemList[i].name.ToLower() == item.name.ToLower())
{
m_itemList.Remove(m_itemList[i]);
break;
}
}
}
private void ClearItem(PrefabSlot.Item item, PrefabSlot Ps)
{
item.name = m_itemName;
item.obj = m_itemObj;
m_itemList = Ps.items.ToList();
for (int i = 0; i < m_itemList.Count; i++)
{
if (m_itemList[i].obj == null || string.IsNullOrEmpty(m_itemList[i].name))
{
m_itemList.Remove(m_itemList[i]);
}
}
}
private void SearchItem(PrefabSlot.Item item, PrefabSlot slot, string name)
{
m_itemList = slot.items.ToList();
m_searchMatchItemList.Clear();
foreach (var o in m_itemList)
{
if (string.IsNullOrEmpty(name))
{
m_searchMatchItemList.Add(o);
}
else
{
if (o.name.ToLower().Contains(name.ToLower()))
{
m_searchMatchItemList.Add(o);
}
else if (null != o.obj)
{
var objName = o.obj.name;
if (objName.ToLower().Contains(name.ToLower()))
{
m_searchMatchItemList.Add(o);
}
}
}
}
}
private void ItemTip()
{
if (string.IsNullOrEmpty(m_itemName) || m_itemObj == null)
{
string msg = string.Empty;
if (m_itemObj == null)
{
msg = "请选择项目组件";
}
else if (string.IsNullOrEmpty(m_itemName))
{
msg = "请输入要添加的项的名字";
}
EditorGUILayout.HelpBox(msg, MessageType.Warning);
EditorGUILayout.Space();
}
}
#endregion
}
PrefabSlotInspector.cs
// PrefabSlotInspector.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(PrefabSlot), true)]
public class PrefabSlotInspector : Editor
{
void Awake()
{
btnStyle = new GUIStyle(EditorStyles.miniButton);
btnStyle.fontSize = 12;
btnStyle.normal.textColor = Color.green;
}
public override void OnInspectorGUI()
{
if (GUILayout.Button("打开PrefabSlot编辑器", btnStyle))
{
PrefabSlotEditor.SlotWindow();
}
base.OnInspectorGUI();
}
private GUIStyle btnStyle;
}