编辑器扩展添加RayButton预制体

VR/3D 项目中 ,输入设备 不一样 不是鼠标因此 鼠标交互都不能用了,取而代之的可能是一支笔,一个手柄, 头盔,
这个时候,需要改写 UI 交互方式, 有很多种 交互方式, 可以借助Unity中的 碰撞检测,用物理碰撞检测 也是 OK的 用个Cube 缩放 很小, 这种方式本人看来比较Low。 可能就碰撞到 一个物体就 设置它的 宽度 Transform 里Scale属性, 有个物体就是 当 有多个 碰撞体的 时候 不是 会有 穿透现象吗?
因此 呢 解决了一时而已, 还是用RayCast射线交互 比较实在。也不 会有很多问题。

上次写了一个视线交互按钮后每次 需要用到按钮的时候 都需要去 添加RayButton组件, 很不方便, 因此就萌生 写编辑器扩展的念头, 其实 也挺简单的。

在这里插入图片描述
看了源码之后发现 在一个类里MenuOptions 编辑器的菜单显示层,具体 生产 由 DefaultControls 负责;
DefaultControls CreateButton 方法 是把 所有小的组件 组合 加工成 Button ;

我这里是利用预制体,因为预制体已经保存了 很多信息了,不需要我们再 一个 一个小小的组件去添加。
创建的时候要 找到一个Canvas作为它的父物体;下面是我的做法

      public class RayButtonEditor : Editor
    {
        static GameObject rayButton = Resources.Load<GameObject>(FolderName.PrefabFolder + "UI/RayBtn");

        public static Vector2 CanvasSize = new Vector2(1920, 1080);
        [MenuItem("GameObject/UI/RayButton", false, 2029)]
        static public void AddButton(MenuCommand menuCommand)
        {



            GameObject parent = menuCommand.context as GameObject;
            if (rayButton == null)
            {
                Debug.LogError("The Path is Wrong");
            }

            rayButton.name = "RayButton";

            if (parent == null)
            {
                Canvas ca = GameObject.FindObjectOfType<Canvas>();
                
                if (ca == null)
                {
                    //Create Canvas
                    parent = CreatecCanvas(CanvasSize);
                }
                else
                {
                    parent = ca.gameObject;
                }
            }else if (parent.GetComponentInParent<Canvas>() == null&&parent.GetComponentInChildren<Canvas>()==null)
            {
                Canvas ca = GameObject.FindObjectOfType<Canvas>();
                if (ca == null)
                {
                    //Create Canvas
                    parent = CreatecCanvas(CanvasSize);

                }
                else
                {
                    parent = ca.gameObject;
                }
            }
            else
            {
                if (parent.GetComponentInParent<Canvas>() != null)
                {
                    parent = parent.GetComponentInParent<Canvas>().transform.gameObject;
                }

                if (parent.GetComponentInChildren<Canvas>() != null)
                {
                    parent = parent.GetComponentInChildren<Canvas>().transform.gameObject;
                }
                
            }


            GameObject go = Instantiate(rayButton, parent.transform);
            Selection.activeGameObject = go;
            go.name = rayButton.name;
            go.transform.localPosition = Vector3.zero;
            Undo.RegisterCreatedObjectUndo(go, "Create go");

        }

       static  GameObject  CreatecCanvas(Vector2 size)
        {

           GameObject  root = new GameObject("Canvas");
            root.layer = LayerMask.NameToLayer("UI");
            Canvas canvas = root.AddComponent<Canvas>();

            canvas.renderMode = RenderMode.WorldSpace;


            root.AddComponent<CanvasScaler>();
            root.AddComponent<GraphicRaycaster>();
            root.GetComponent<RectTransform>().sizeDelta =size;
            root.transform.position = Vector3.zero;

            Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);

           return root;

        }


    }

可以利用 Untiy的预制体, 这里最好是写个资源工厂管理, 我这里是直接通过Resources加载, 很粗暴;
官方源码呢 是用DefaultControls 里的CreateButton方法 去 new出来 再去组合的。
复用性比较低;

    Undo.RegisterCreatedObjectUndo(go, "Create go");
        GameObject parent = menuCommand.context as GameObject; 这个是获取
    就是把 创建出来的物体 注册 到撤销 Undo里。

考虑到用Resource 加载会添加一些资源 负担, 因此 有了下面的版本

      private const float kWidth = 160f;
        private const float kThickHeight = 30f;
        
       // static GameObject rayButton = Resources.Load<GameObject>("Prefabs/"+ "UI/RayBtn");
    
        public static Vector2 CanvasSize = new Vector2(1920, 1080);


        [MenuItem("GameObject/UI/RayButton", false, 2029)]
        static public void AddButton(MenuCommand menuCommand)
        {

            GameObject parent = menuCommand.context as GameObject;


            GameObject buttonRoot = CreateUIElementRoot("Button",  new Vector2(kWidth, kThickHeight));

            GameObject childText = new GameObject("Text");
            childText.AddComponent<RectTransform>();
            SetParentAndAlign(childText, buttonRoot);
            Image image = buttonRoot.AddComponent<Image>();

            image.sprite = AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");

            image.type = Image.Type.Sliced;
            image.color = new Color(1f, 1f, 1f, 1f);

            Button bt = buttonRoot.AddComponent<RayButton>();
            SetDefaultColorTransitionValues(bt);

            Text text = childText.AddComponent<Text>();
            text.text = "RayButton";
            text.alignment = TextAnchor.MiddleCenter;
            SetDefaultTextValues(text);

            RectTransform textRectTransform = childText.GetComponent<RectTransform>();
            textRectTransform.anchorMin = Vector2.zero;
            textRectTransform.anchorMax = Vector2.one;
            textRectTransform.sizeDelta = Vector2.zero;


            //if (rayButton == null)
            //{
            //   // Debug.LogError("The Path is Wrong");
             
            //}

            //rayButton.name = "RayButton";

            if (parent == null)
            {
                Canvas ca = GameObject.FindObjectOfType<Canvas>();
                
                if (ca == null)
                {
                    //Create Canvas
                    parent = CreatecCanvas(CanvasSize);
                }
                else
                {
                    parent = ca.gameObject;
                }
            }else if (parent.GetComponentInParent<Canvas>() == null&&parent.GetComponentInChildren<Canvas>()==null)
            {
                Canvas ca = GameObject.FindObjectOfType<Canvas>();
                if (ca == null)
                {
                    //Create Canvas
                    parent = CreatecCanvas(CanvasSize);

                }
                else
                {
                    parent = ca.gameObject;
                }
            }
            else
            {
                if (parent.GetComponentInParent<Canvas>() != null)
                {
                    parent = parent.GetComponentInParent<Canvas>().transform.gameObject;
                }

                if (parent.GetComponentInChildren<Canvas>() != null)
                {
                    parent = parent.GetComponentInChildren<Canvas>().transform.gameObject;
                }
                
            }


            GameObject go = buttonRoot;  //Instantiate(rayButton, parent.transform);
            go.transform.SetParent(parent.transform);
            Selection.activeGameObject = go;
            go.name = buttonRoot.name;
            go.transform.localPosition = Vector3.zero;
            Undo.RegisterCreatedObjectUndo(go, "Create go");

        }

       static  GameObject CreatecCanvas(Vector2 size)
        {

           GameObject  root = new GameObject("Canvas");
            root.layer = LayerMask.NameToLayer("UI");
            Canvas canvas = root.AddComponent<Canvas>();

#if XRMODE
            canvas.renderMode = RenderMode.WorldSpace;

#else
            canvas.renderMode = RenderMode.ScreenSpaceOverlay;

            UnityEngine.EventSystems.EventSystem tempSystem =
                GameObject.FindObjectOfType<UnityEngine.EventSystems.EventSystem>();
            if (tempSystem==null)
            {
                GameObject go = new GameObject("EventSystem");
                tempSystem = go.AddComponent<UnityEngine.EventSystems.EventSystem>();
                Undo.AddComponent<StandaloneInputModule>(go);
            }
#endif
            root.AddComponent<CanvasScaler>();
            root.AddComponent<GraphicRaycaster>();
            root.GetComponent<RectTransform>().sizeDelta =size;
            root.transform.position = Vector3.zero;

            Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);

           return root;

        }

        #region Tool's method

        private static void SetDefaultTextValues(Text lbl)
        {
            // Set text values we want across UI elements in default controls.
            // Don't set values which are the same as the default values for the Text component,
            // since there's no point in that, and it's good to keep them as consistent as possible.

            lbl.color =Color.black;

            // Reset() is not called when playing. We still want the default font to be assigned
            lbl.font= Resources.GetBuiltinResource<Font>("Arial.ttf");
        }
        private static void SetDefaultColorTransitionValues(Selectable slider)
        {
            ColorBlock colors = slider.colors;
            colors.highlightedColor = new Color(0.882f, 0.882f, 0.882f);
            colors.pressedColor = new Color(0.698f, 0.698f, 0.698f);
            colors.disabledColor = new Color(0.521f, 0.521f, 0.521f);
        }
        private static GameObject CreateUIElementRoot(string name, Vector2 size)
        {
            GameObject child = new GameObject(name);
            RectTransform rectTransform = child.AddComponent<RectTransform>();
            rectTransform.sizeDelta = size;
            return child;
        }
        private static void SetParentAndAlign(GameObject child, GameObject parent)
        {
            if (parent == null)
                return;

            child.transform.SetParent(parent.transform, false);
            SetLayerRecursively(child, parent.layer);
        }
        private static void SetLayerRecursively(GameObject go, int layer)
        {
            go.layer = layer;
            Transform t = go.transform;
            for (int i = 0; i < t.childCount; i++)
                SetLayerRecursively(t.GetChild(i).gameObject, layer);
        }
        #endregion
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值