Unity 编辑器-UGUI拓展Button,一个和原Button一样按钮⭐

news2024/11/15 21:28:12

拓展Button

    • 需求
    • 实现
      • 1.创建继承自Button的类
      • 2.处理Inspector 显示问题
      • 3.处理在prafab中和hierarchy中创建按钮
      • 4.处理一些细节
    • 完成

需求

想拓展一下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;
    }
}

完成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

针对防火墙IPSec业务不通或业务丢包问题,防火墙如何做流量统计、远程抓包、报文示踪

问题描述 针对防火墙IPSec业务不通或业务丢包问题&#xff0c;防火墙如何做流量统计、远程抓包、报文示踪 解决方案 1&#xff09;配置流统和远程抓包用的ACL&#xff1b; system [sysname] acl 3555 [sysname-acl-adv-3555] rule permit icmp source 10.82.100.215 0 destin…

Vue实现表格数据的增删改查

整体效果图&#xff1a; 一、创建表格数据 效果图&#xff1a; el-table组件&#xff1a;表格组件 <el-table:data"dataList"borderv-loading"dataListLoading"selection-change"selectionChangeHandle"style"width: 100%;">&l…

KISS(Keep It Sample,Stupid)[完整代码]

根据KISS原文它的伪随机数产生器的Period about 2^123&#xff0c;周期很长&#xff0c;并且来自于多个产生器的共同作用之下而得&#xff0c; 原文的这这句话很有意思&#xff0c;&#xff08;车轱辘压我脸上了&#xff09;一个人不好的时候&#xff0c;试一试两个人&#xff…

线段树的原理

1.如果知道两个子范围上的最值&#xff0c;通过比较就可以知道整个范围上的最值 2.如果知道两个子范围上分别出现的次数最多的数&#xff0c;但无法知道整个范围上出现最多的数 范围修改logn的前提&#xff1a;如果维护的是区域和&#xff0c;要把区域上的每个数字加上a&#…

【蓝桥杯集训100题】scratch绘制扇子 蓝桥杯scratch比赛专项预测编程题 集训模拟练习题第28题

scratch绘制扇子 蓝桥杯集训100题第28题模拟练习解析 此题曾经作为第十届省赛的真题考过 一、题目要求 以坐标&#xff08;0,0&#xff09;点为中心绘制一把扇子&#xff1b;扇面和扇把都是三分之一圆&#xff0c;扇面的半径 为 100 左右&#xff0c;扇把的半径为 20 左右。…

HTML实现2048小游戏

游戏概述 实现一个基本的2048游戏涉及综合运用HTML、CSS和JavaScript这三种关键技术。 HTML用于构建游戏的结构框架&#xff0c;包括游戏容器、网格布局以及可能的用户交互元素&#xff08;如按钮或得分显示&#xff09;。 CSS则负责美化游戏界面&#xff0c;通过样式表定义网格…

培训班和科班出生有什么区别

IT业的萌新来啦 每到毕业季 总有大量萌新走进职场 IT圈子的程序员 有的是科班出生 比如 计算机科学与技术、软件工程、大数据技术 有的是相关专业出生的 比如 信息安全、网络工程、物联网工程等等 除此之外 还有各种其他专业转行过来的 文理不限、专业不限 科班出生…

mydump 文件拆分 mysqldumpsplitter

mydump 文件拆分 如何将mysqldump的输出拆分为较小的文件&#xff1f; 要将mysqldump的输出拆分为较小的文件&#xff0c;可以使用–max-allowed-packet和–single-transaction选项。 使用–max-allowed-packet选项设置每个查询允许的最大数据包大小。这将确保mysqldump在执行…

精通 Stable Diffusion 调优秘籍

一、Stable Diffusion 调优概览 Stable Diffusion 作为一款强大的 AI 绘画工具&#xff0c;其调优具有至关重要的意义。通过合理的调优&#xff0c;可以显著提升图像生成的质量、速度和准确性&#xff0c;满足用户更加多样化和精细化的需求。 调优主要涵盖了多个方面。首先是模…

鸿蒙(API 12 Beta3版)【使用Image完成图片解码】图片开发指导依赖JS对象

图片解码指将所支持格式的存档图片解码成统一的[PixelMap]&#xff0c;以便在应用或系统中进行图片显示或[图片处理]。当前支持的存档图片格式包括JPEG、PNG、GIF、WebP、BMP、SVG、ICO、DNG。 开发步骤 添加依赖 在进行应用开发之前&#xff0c;开发者需要打开native工程的…

YOLOv5改进 | 融合改进 | C3 融合Efficient Multi-Scale Conv提升检测效果

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a; 《YOLOv5入门 改…

博立的个人代表作品集锦

1. 比赛同创 国一_2023年全国大学生电子设计竞赛 【信号分离装置】 国二_2023年全国大学生集成电路创新创业大赛半决赛作品&#xff08;robei赛道&#xff09; 【基于robei EDA的可重构无线控制小车设计】 省一_2022年重庆市大学生电子设计竞赛 【混沌信号发生装置】 国…

Oracle RAC 集群启动顺序

大家好&#xff0c;这里是 Lucifer三思而后行&#xff0c;专注于提升数据库运维效率。 目录 前言Oracle 11GR2 RAC 集群启动顺序官方文档11GR212CR218C19C21C23ai 往期精彩文章推荐 前言 前几天使用脚本在 RockyLinux 9.4 安装 Oracle 11GR2 RAC&#xff0c;安装完之后发现集群…

Adobe Premiere Pro (PR2024)win/mac 视频编辑软件安装下载(附安装包)

一、软件概述 1.1 Premiere Pro 简介 Adobe Premiere Pro (简称PR) 是一款专业的视频编辑软件&#xff0c;广泛应用于电影、电视、广告、网络视频等多种视频制作领域。它提供了强大的编辑工具、丰富的特效和灵活的工作流程&#xff0c;帮助用户高效地完成从素材整理到最终输出…

[Linux#41][线程] 线程的特性 | 分离线程 | 并发的问题

1.线程的特性 进程和线程的关系如下图: 关于进程线程的问题 • 如何看待之前学习的单进程&#xff1f;具有一个线程执行流的进程 线程 ID 及进程地址空间布局 pthread_ create 函数会产生一个线程 ID&#xff0c;存放在第一个参数指向的地址中。 该线程 ID 和前面说的线程 ID …

持久化SSE对象

SpringBoot整合SSE&#xff0c;实现后端主动推送DEMO 前些日子写了整合SSE得demo。但是SSE对象是存储在ConcurrentHashMap<String, SseEmitter>中。在正式环境明显就不行了&#xff0c;服务重启一下的话都没有了。 那么要持久化&#xff0c;第一选择放redis 1、写了一个…

When Do We Not Need Larger Vision Models?

总结 传统观点挑战&#xff1a;传统上&#xff0c;扩大视觉模型的大小一直被认为是提升视觉表示能力和下游任务性能的关键途径。然而&#xff0c;本文重新审视了这一观点&#xff0c;提出了通过在不同图像尺度上运行较小的预训练视觉模型&#xff08;如ViT-B或ViT-L&#xff0…

Linux入门——11 线程

线程的概念&#xff0c;线程的控制&#xff0c;线程的同步和互斥&#xff0c;队列结构&#xff0c;线程池&#xff0c;锁 1.预备知识 1.1可重入函数 1.1.1链表的头插 main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的时候,因为硬件中断…

续——网络通信编程

一、网络通信 1、编程 &#xff08;1&#xff09;基于UDP c/s通信模型 -------server——服务端——被动角色------- socket 全双工的&#xff08;可读可写&#xff09;。同上篇。 bind int bind(int sockfd , struct sockaddr *my_addr&#xff08;所绑定的地址信息&…

Linux的进程详解(进程创建函数fork和vfork的区别,资源回收函数wait,进程的状态(孤儿进程,僵尸进程),加载进程函数popen)

目录 什么是进程 Linux下操作进程的相关命令 进程的状态&#xff08;生老病死&#xff09; 创建进程系统api介绍&#xff1a; fork() 父进程和子进程的区别 vfork() 进程的状态补充&#xff1a; 孤儿进程 僵尸进程 回收进程资源api介绍&#xff1a; wait() waitpid…