Unity界面预设绑定ui元素(PrefabSlotEditor)

美术制作好界面预设之后,程序需要绑定ui元素进行逻辑控制,可以定义public变量,然后拉ui元素赋值给public变量,或者用Find函数获取ui元素,但是这样总感觉有点不舒服,于是想着自己做一个工具,来完成这样的绑定。(工具代码在文章最后)

使用

首先做一个界面预设,如下图所示
Unity界面预设绑定ui元素(PrefabSlotEditor)_第1张图片
给预设挂PrefabSlot组件,打开PrefabSlot编辑器,绑定ui元素
Unity界面预设绑定ui元素(PrefabSlotEditor)_第2张图片
最后,最后给预设再挂一个运行脚本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";
        });
    }
}

运行效果如下
Unity界面预设绑定ui元素(PrefabSlotEditor)_第3张图片

工程下载: 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;
}

你可能感兴趣的:(Unity界面预设绑定ui元素(PrefabSlotEditor))