需求
想拓展一下UGUI的Button,找了几个帖子,只是能实现功能,但是用起来总有些不尽人意的地方,想办法处理一下
实现
1.创建继承自Button的类
using UnityEngine;
using UnityEngine.UI;
public class CustomButton : Button
{
[SerializeField] public int audioKey = -1;
protected override void Start()
{
base.Start();
onClick.AddListener(PlaySound);
}
private void PlaySound()
{
if (audioKey != -1)
{
// AudioManager.Instance.PlaySound(audioKey);
}
}
}
这里只临时做一丢丢功能示例,我们预期的是上面代码中的属性在Inspector中显示出来,然而并没有
2.处理Inspector 显示问题
using UnityEditor;
[CustomEditor(typeof(CustomButton))]
public class CustomButtonEditor : UnityEditor.UI.ButtonEditor
{
public override void OnInspectorGUI()
{
var btn = (CustomButton)target;
btn.audioKey = EditorGUILayout.IntField("Audio Key", btn.audioKey);
base.OnInspectorGUI();
}
}
如图,属性已经能正确显示
目前只能通过AddComponent的方式创建,我们预期的是和unity 自带的Button一样的创建方式
注意:editor脚本继承自对应组件的Editor,如上面Button的,继承自 UnityEditor.UI.ButtonEditor
,否则Inspector显示可能出现问题
3.处理在prafab中和hierarchy中创建按钮
- 整理一下Button的使用体验
- 1. 右键创建
- 2. 自动挂载Image,用于接收raycast
- 3. 自动挂载text显示文案
- 4. 创建节点时,需要区分是否是编辑prefab模式,是否有选中的父节点,确定生成节点的位置
- 5. 创建节点时,需要区分是否在canvas下等情况,是否需要生成canvas
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public class CustomUI : Editor
{
[MenuItem("GameObject/UI/CustomButton")]
public static void CreateCustomButton()
{
//创建按钮
var btn = ObjectFactory.CreateGameObject("CustomButton");
ObjectFactory.AddComponent<RectTransform>(btn);
var img = ObjectFactory.AddComponent<Image>(btn);
img.raycastTarget = true;
img.type = Image.Type.Sliced;
img.sprite = AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");
ObjectFactory.AddComponent<CustomButton>(btn);
if (!UnityEditor.SceneManagement.EditorSceneManager.IsPreviewSceneObject(btn))
{
//非prefab编辑模式下,需要找父节点canvas
if (Selection.activeObject != null && Selection.activeObject is GameObject)
{
var go = (GameObject)Selection.activeObject;
if (go.GetComponentInParent<Canvas>(true))
{
btn.transform.SetParent(go.transform);
}
else
{
var canvas = CreateCanvas();
canvas.transform.SetParent(go.transform);
btn.transform.SetParent(canvas.transform);
}
}
else
{
var canvas = FindFirstObjectByType<Canvas>();
if(canvas == null)
{
canvas = CreateCanvas();
}
btn.transform.SetParent(canvas.transform);
}
}
else
{
//prefab编辑模式下,直接添加到选中的物体下
if (Selection.activeObject != null && Selection.activeObject is GameObject)
{
var go = (GameObject)Selection.activeObject;
btn.transform.SetParent(go.transform);
}
}
btn.GetComponent<RectTransform>().sizeDelta = new Vector2(160, 50);
btn.transform.localPosition = Vector3.zero;
//选中按钮节点
Selection.activeObject = btn;
//展开到当前节点
Expend(btn.transform.parent,true);
//文本
var txt = ObjectFactory.CreateGameObject("Text");
ObjectFactory.AddComponent<RectTransform>(txt);
ObjectFactory.AddComponent<TextMeshProUGUI>(txt);
txt.transform.SetParent(btn.transform);
txt.transform.localPosition = Vector3.zero;
txt.GetComponent<RectTransform>().sizeDelta = new Vector2(160, 50);
//tmp
var tmp = txt.GetComponent<TextMeshProUGUI>();
tmp.alignment = TextAlignmentOptions.Center;
tmp.raycastTarget = false;
tmp.text = "button";
}
/// <summary>
/// 创建Canvas
/// </summary>
/// <returns>canvas</returns>
private static Canvas CreateCanvas()
{
var canvas = ObjectFactory.CreateGameObject("Canvas");
var c = ObjectFactory.AddComponent<Canvas>(canvas);
c.renderMode = RenderMode.ScreenSpaceOverlay;
ObjectFactory.AddComponent<CanvasScaler>(canvas);
ObjectFactory.AddComponent<GraphicRaycaster>(canvas);
canvas.layer = LayerMask.NameToLayer("UI");
return c;
}
private static void Expend(Transform transform,bool expand)
{
SceneHierarchyUtility.SetExpanded(transform.gameObject,expand);
if(transform.parent != null)
{
Expend(transform.parent,expand);
}
}
}
添加按钮菜单如图所示
4.处理一些细节
创建后如图所示,节点正确创建了,但我们的预期是下面这样,自动选中+展开
自动选中比较简单
Selection.activeObject = btn;
展开找了 网友的方法,很好用
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Editor functionalities from internal SceneHierarchyWindow and SceneHierarchy classes.
/// For that we are using reflection.
/// </summary>
public static class SceneHierarchyUtility
{
/// <summary>
/// Check if the target GameObject is expanded (aka unfolded) in the Hierarchy view.
/// </summary>
public static bool IsExpanded(GameObject go)
{
return GetExpandedGameObjects().Contains(go);
}
/// <summary>
/// Get a list of all GameObjects which are expanded (aka unfolded) in the Hierarchy view.
/// </summary>
public static List<GameObject> GetExpandedGameObjects()
{
object sceneHierarchy = GetSceneHierarchy();
MethodInfo methodInfo = sceneHierarchy
.GetType()
.GetMethod("GetExpandedGameObjects");
object result = methodInfo.Invoke(sceneHierarchy, new object[0]);
return (List<GameObject>)result;
}
/// <summary>
/// Set the target GameObject as expanded (aka unfolded) in the Hierarchy view.
/// </summary>
public static void SetExpanded(GameObject go, bool expand)
{
object sceneHierarchy = GetSceneHierarchy();
MethodInfo methodInfo = sceneHierarchy
.GetType()
.GetMethod("ExpandTreeViewItem", BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo.Invoke(sceneHierarchy, new object[] { go.GetInstanceID(), expand });
}
/// <summary>
/// Set the target GameObject and all children as expanded (aka unfolded) in the Hierarchy view.
/// </summary>
public static void SetExpandedRecursive(GameObject go, bool expand)
{
object sceneHierarchy = GetSceneHierarchy();
MethodInfo methodInfo = sceneHierarchy
.GetType()
.GetMethod("SetExpandedRecursive", BindingFlags.Public | BindingFlags.Instance);
methodInfo.Invoke(sceneHierarchy, new object[] { go.GetInstanceID(), expand });
}
private static object GetSceneHierarchy()
{
EditorWindow window = GetHierarchyWindow();
object sceneHierarchy = typeof(EditorWindow).Assembly
.GetType("UnityEditor.SceneHierarchyWindow")
.GetProperty("sceneHierarchy")
.GetValue(window);
return sceneHierarchy;
}
private static EditorWindow GetHierarchyWindow()
{
// For it to open, so that it the current focused window.
EditorApplication.ExecuteMenuItem("Window/General/Hierarchy");
return EditorWindow.focusedWindow;
}
}