zoukankan      html  css  js  c++  java
  • Unity在运行时动态编辑UI

    工作时候突然想到一个想法,让用户自己修改ui尺寸,做个类似Unity的编辑模式。

    先上张动图:

    思路:在每个物体的四周生成四个条状Image,四个角同样生成四个Image,

    然后使用核心函数Transform.SetInsetAndSizeFromParentEdge(Edge edge, float inset, float size)处理物体拉动边界修改尺寸

    参数含义:

    edge
    Left = 0,

    Right = 1,

    Top = 2,

    Bottom = 3

    依据父物体移动的边界

    inset 插入值 如:edge为Top,那么inset代表当前物体位置,相对于父物体上边界的距离
    size 尺寸 如:edge为Top,那么size代表当前物体垂直方向的尺寸

      具体详细内容看这篇文章:https://www.jianshu.com/p/4592bf809c8b

    直接上代码:

     这是整个外边框的父类

    using System;
    using UnityEngine;
    
    public class OutLine: IOutLine
    {
        /// <summary>
        /// 自身的框transform
        /// </summary>
        public RectTransform selfRect;
    
        /// <summary>
        /// 生成的物体名字
        /// </summary>
        public string insLineName;
        /// <summary>
        /// 是否初始化
        /// </summary>
        public bool isInit = false;
        /// <summary>
        /// 是否正在拖拽
        /// </summary>
        public bool isDrag = false;
    
        /// <summary>
        /// 外框预设
        /// </summary>
        GameObject outLinePrefab;
        /// <summary>
        /// 鼠标图片icon
        /// </summary>
        Texture2D enterIcon;
        GameObject lineObj;
        protected RectTransform lineObjRect;
    
        protected Vector2 startDragMousePos;
        /// <summary>
        /// 开始拖拽的时候物体sizeDelta的X值
        /// </summary>
        protected float startDragObjSizeDeltaX;
        protected float startDragObjSizeDeltaY;
        /// <summary>
        /// 开始拖拽时候物体距离父物体边界距离
        /// </summary>
        protected float startDragObjPosX;
        protected float startDragObjPosY;
    
        /// <summary>
        /// 鼠标移动后计算出来的物体size
        /// </summary>
        protected float newObjDisX;
        protected float newObjDisY;
    
        /// <summary>
        /// 记录物体世界坐标临时值
        /// </summary>
        Vector2 worldPos;
    
        public virtual void Init(GameObject go)
        {
            selfRect = go.GetComponent<RectTransform>();
    
            outLinePrefab = Resources.Load<GameObject>("Prefabs/OutLine");
            enterIcon = Resources.Load<Texture2D>("Texture/MouseEnterIcon");
            lineObj = GameObject.Instantiate(outLinePrefab, selfRect);
            lineObj.name = insLineName;
            lineObjRect = lineObj.GetComponent<RectTransform>();
    
            EventTriggerListener.Get(lineObj).OnMouseDrag = DragLine;
            EventTriggerListener.Get(lineObj).OnMouseBeginDrag = BeginDragLine;
            EventTriggerListener.Get(lineObj).OnMouseEndDrag = EndDragLine;
            EventTriggerListener.Get(lineObj).OnMouseEnter = EnterLine;
            EventTriggerListener.Get(lineObj).OnMouseExit = ExitLine;
            isInit = true;
    
        }
        /// <summary>
        /// updata中刷新调用(后续可添加颜色、材质球等属性)
        /// </summary>
        /// <param name="points">物体的四个边界顶点</param>
        /// <param name="lineWidth">线条的宽度</param>
        public virtual void RefreshRect(Vector2[] points,float lineWidth)
        {
    
        }
        /// <summary>
        /// 鼠标进入事件 更改鼠标icon
        /// </summary>
        void EnterLine()
        {
            if (!isDrag)
            {
                Cursor.SetCursor(enterIcon, Vector2.zero, CursorMode.Auto);
            }
        }
        /// <summary>
        /// 鼠标退出事件,恢复鼠标icon
        /// </summary>
        void ExitLine()
        {
            if (!isDrag)
            {
                Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
            }
        }
        /// <summary>
        /// 开始拖拽事件
        /// </summary>
        void BeginDragLine()
        {
    
            isDrag = true;
            startDragMousePos = Input.mousePosition;
    
            worldPos = selfRect.position;//先记录先物体的世界坐标,防止在更改锚点的时候无法恢复原位
    
            startDragObjSizeDeltaX = selfRect.sizeDelta.x;
            startDragObjSizeDeltaY = selfRect.sizeDelta.y;
    
            SetAnchoredPos(); //更改锚点设置
            selfRect.ForceUpdateRectTransforms();//强制刷新下
            selfRect.position = worldPos;
            GetStartDragObjPos();
        }
        /// <summary>
        /// 更改锚点设置
        /// </summary>
        protected virtual void SetAnchoredPos()
        {
    
        }
        /// <summary>
        /// 获取距离父物体边界值
        /// </summary>
        protected virtual void GetStartDragObjPos()
        {
    
        }
        /// <summary>
        /// 拖拽事件
        /// </summary>
        protected virtual void DragLine()
        {
    
        }
        /// <summary>
        /// 拖拽结束
        /// </summary>
        void EndDragLine()
        {
            isDrag = false;
        }
    
    
    }
    
    public interface IOutLine
    {
        void Init(GameObject go);
        void RefreshRect(Vector2[] points, float lineWidth);
    }

    上面父类中EventTriggerListener就是很常用的鼠标事件监听:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class EventTriggerListener : EventTrigger
    {
        public Action OnMouseDown;
        public Action OnMouseUp;
        public Action OnMouseClick;
        public Action OnMouseDrag;
        public Action OnMouseBeginDrag;
        public Action OnMouseEndDrag;
        public Action OnMouseEnter;
        public Action OnMouseExit;
    
        public static EventTriggerListener Get(GameObject go)
        {
            EventTriggerListener eventTriggerListener = go.GetComponent<EventTriggerListener>();
            if (eventTriggerListener == null)
            {
                eventTriggerListener = go.AddComponent<EventTriggerListener>();
            }
            return eventTriggerListener;
        }
    
        public override void OnPointerDown(PointerEventData eventData)
        {
            OnMouseDown?.Invoke();
        }
        public override void OnPointerClick(PointerEventData eventData)
        {
            OnMouseClick?.Invoke();
        }
        public override void OnPointerUp(PointerEventData eventData)
        {
            OnMouseUp?.Invoke();
        }
        public override void OnDrag(PointerEventData eventData)
        {
            OnMouseDrag?.Invoke();
        }
        public override void OnPointerEnter(PointerEventData eventData)
        {
            OnMouseEnter?.Invoke();
        }
        public override void OnPointerExit(PointerEventData eventData)
        {
            OnMouseExit?.Invoke();
        }
        public override void OnBeginDrag(PointerEventData eventData)
        {
            OnMouseBeginDrag?.Invoke();
        }
        public override void OnEndDrag(PointerEventData eventData)
        {
            OnMouseEndDrag?.Invoke();
        }
    }

    然后子类继承此父类,重写方法:

    using UnityEngine;
    
    
    #region 四周的线
    public class OutLineUp: OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(0.5f, 0);
            selfRect.anchorMin = new Vector2(0.5f, 0);
    
        }
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(Mathf.Abs(points[1].x - points[0].x), lineWidth);
            lineObjRect.anchoredPosition = new Vector2(0, selfRect.sizeDelta.y / 2f + lineWidth / 2f);
        }
        protected override void GetStartDragObjPos()
        {
            startDragObjPosY = selfRect.anchoredPosition.y - selfRect.sizeDelta.y / 2f;
        }
        protected override void DragLine()
        {
            newObjDisY = startDragObjSizeDeltaY + (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, startDragObjPosY, newObjDisY);
        }
    }
    
    public class OutLineRight : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(0f, 0.5f);
            selfRect.anchorMin = new Vector2(0f, 0.5f);
        }
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth, Mathf.Abs(points[1].y - points[2].y));
            lineObjRect.anchoredPosition = new Vector2(selfRect.sizeDelta.x / 2f + lineWidth / 2f, 0);
        }
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = selfRect.anchoredPosition.x - selfRect.sizeDelta.x / 2f;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX + (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, startDragObjPosX, newObjDisX);
        }
    }
    
    public class OutLineDown : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(0.5f, 1);
            selfRect.anchorMin = new Vector2(0.5f, 1);
        }
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(Mathf.Abs(points[3].x - points[2].x), lineWidth);
            lineObjRect.anchoredPosition = new Vector2(0, -selfRect.sizeDelta.y / 2f - lineWidth / 2f);
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosY = -selfRect.anchoredPosition.y - selfRect.sizeDelta.y / 2f;
        }
    
        protected override void DragLine()
        {
            newObjDisY = startDragObjSizeDeltaY - (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, startDragObjPosY, newObjDisY);
        }
    
    }
    
    public class OutLineLeft : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(1, 0.5f);
            selfRect.anchorMin = new Vector2(1, 0.5f);
        }
    
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth, Mathf.Abs(points[1].y - points[3].y));
            lineObjRect.anchoredPosition = new Vector2(-(selfRect.sizeDelta.x / 2f + lineWidth / 2f), 0);
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = -selfRect.anchoredPosition.x - selfRect.sizeDelta.x/2f;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX - (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, startDragObjPosX, newObjDisX);
        }
    }
    #endregion
    
    
    #region 四个顶点
    public class OutPointLeftUp : OutLine
    {
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(1, 0);
            selfRect.anchorMin = new Vector2(1, 0);
        }
    
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth*2, lineWidth*2);
            lineObjRect.position = points[0];
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = -selfRect.anchoredPosition.x - selfRect.sizeDelta.x/2f ;
    
            startDragObjPosY = selfRect.anchoredPosition.y- selfRect.sizeDelta.y / 2f;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX - (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, startDragObjPosX, newObjDisX);
    
            newObjDisY = startDragObjSizeDeltaY + (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, startDragObjPosY, newObjDisY);
        }
    }
    
    
    public class OutPointRightUp : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(0, 0);
            selfRect.anchorMin = new Vector2(0, 0);
        }
    
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth * 2, lineWidth * 2);
            lineObjRect.position = points[1];
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = selfRect.anchoredPosition.x - selfRect.sizeDelta.x / 2f;
    
            startDragObjPosY = selfRect.anchoredPosition.y - selfRect.sizeDelta.y / 2f;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX + (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, startDragObjPosX, newObjDisX);
    
            newObjDisY = startDragObjSizeDeltaY + (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, startDragObjPosY, newObjDisY);
        }
    }
    
    
    public class OutPointRightDown : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
    
            selfRect.anchorMax = new Vector2(0, 1);
            selfRect.anchorMin = new Vector2(0, 1);
        }
    
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth * 2, lineWidth * 2);
            lineObjRect.position = points[2];
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = selfRect.anchoredPosition.x - selfRect.sizeDelta.x / 2f;
    
            startDragObjPosY = -selfRect.anchoredPosition.y - selfRect.sizeDelta.y/2f ;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX + (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, startDragObjPosX, newObjDisX);
    
            newObjDisY = startDragObjSizeDeltaY - (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, startDragObjPosY, newObjDisY);
        }
    }
    
    
    public class OutPointLeftDown : OutLine
    {
    
        protected override void SetAnchoredPos()
        {
            selfRect.anchorMax = new Vector2(1, 1);
            selfRect.anchorMin = new Vector2(1, 1);
        }
    
        public override void RefreshRect(Vector2[] points, float lineWidth)
        {
            lineObjRect.sizeDelta = new Vector2(lineWidth * 2, lineWidth * 2);
            lineObjRect.position = points[3];
        }
    
        protected override void GetStartDragObjPos()
        {
            startDragObjPosX = -selfRect.anchoredPosition.x - selfRect.sizeDelta.x / 2f;
    
            startDragObjPosY = -selfRect.anchoredPosition.y - selfRect.sizeDelta.y / 2f;
        }
        protected override void DragLine()
        {
            newObjDisX = startDragObjSizeDeltaX - (Input.mousePosition.x - startDragMousePos.x);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, startDragObjPosX, newObjDisX);
    
            newObjDisY = startDragObjSizeDeltaY - (Input.mousePosition.y - startDragMousePos.y);
            selfRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, startDragObjPosY, newObjDisY);
        }
    }
    
    #endregion

    此类中包含4个顶点,4条线段位置,尺寸等信息的计算,图方便写到了一个文件下,嘿嘿嘿

     

    之后是一个工厂类:

    public class OutLineManager : SingleMono<OutLineManager>
    {
        public OutLine GetOutLine(string key)
        {
            OutLine outLine;
            switch (key)
            {
                case "Up":
                    outLine = new OutLineUp();
                    break;
                case "Right":
                    outLine = new OutLineRight();
                    break;
                case "Down":
                    outLine = new OutLineDown();
                    break;
                case "Left":
                    outLine = new OutLineLeft();
                    break;
                case "LeftUp":
                    outLine = new OutPointLeftUp();
                    break;
                case "RightUp":
                    outLine = new OutPointRightUp();
                    break;
                case "RightDown":
                    outLine = new OutPointRightDown();
                    break;
                case "LeftDown":
                    outLine = new OutPointLeftDown();
                    break;
                default:
                    outLine = null;
                    break;
            }
            if(outLine!=null)
                outLine.insLineName = key;
            return outLine;
        }
    }

    上面用了一个单例:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SingleMono<T>:MonoBehaviour where T:MonoBehaviour
    {
        private static T instance;
    
        public static T Instance
        {
            get {
                if (instance == null)
                {
                    instance = GameObject.FindObjectOfType<T>();
                }
                if (instance == null)
                {
                    GameObject go= new GameObject("Single_" + typeof(T).ToString());
                    instance=go.AddComponent<T>();
                }
                return instance;
            }
        }
    
    }

    最后客户端调用:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    
    public class InsButton : MonoBehaviour
    {
        Button self;
        OutLine outLineUp;
        OutLine outLineRight;
        OutLine outLineDown;
        OutLine outLineLeft ;
    
        OutLine outPointLeftUp;
        OutLine outPointRightUp;
        OutLine outPointLeftDown;
        OutLine outPointRightDown;
        private void Awake()
        {
            self = GetComponent<Button>();
    
            Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
    
            InsOutLine();
        }
    
        /// <summary>
        /// 初始化外边框
        /// </summary>
        void InsOutLine()
        {
            outLineUp = OutLineManager.Instance.GetOutLine("Up");
            outLineRight = OutLineManager.Instance.GetOutLine("Right");
            outLineDown = OutLineManager.Instance.GetOutLine("Down");
            outLineLeft = OutLineManager.Instance.GetOutLine("Left");
    
            outPointLeftUp = OutLineManager.Instance.GetOutLine("LeftUp");
            outPointRightUp = OutLineManager.Instance.GetOutLine("RightUp");
            outPointLeftDown = OutLineManager.Instance.GetOutLine("LeftDown");
            outPointRightDown = OutLineManager.Instance.GetOutLine("RightDown");
    
            outLineUp.Init(gameObject);
            outLineRight.Init(gameObject);
            outLineDown.Init(gameObject);
            outLineLeft.Init(gameObject);
    
            outPointLeftUp.Init(gameObject);
            outPointRightUp.Init(gameObject);
            outPointLeftDown.Init(gameObject);
            outPointRightDown.Init(gameObject);
        }
    
        private void Update()
        {
            Refresh();
    
            outLineUp.RefreshRect(points, 5);
            outLineRight.RefreshRect(points, 5);
            outLineDown.RefreshRect(points, 5);
            outLineLeft.RefreshRect(points, 5);
    
            outPointLeftUp.RefreshRect(points, 5);
            outPointRightUp.RefreshRect(points, 5);
            outPointLeftDown.RefreshRect(points, 5);
            outPointRightDown.RefreshRect(points, 5);
    
        }
        /// <summary>
        /// 物体的四个边界顶点
        /// </summary>
        Vector2[] points;
        /// <summary>
        /// 刷新获取四周的点(当前使用物体的rectTransform,后续可改为bounds)
        /// </summary>
        void Refresh()
        {
            Rect rect = self.image.rectTransform.rect;
            rect.position = (Vector2)self.image.rectTransform.position - rect.size / 2f;
            points = new Vector2[5];
            GetCornerPoint(rect, out points[0], out points[1], out points[2], out points[3]);
        }
        /// <summary>
        /// 在编辑器中画出线
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <param name="p3"></param>
        /// <param name="p4"></param>
        private void GetCornerPoint(Rect rect, out Vector2 p1, out Vector2 p2, out Vector2 p3, out Vector2 p4)
        {
            p1 = new Vector2(rect.center.x - rect.size.x / 2f, rect.center.y + rect.size.y / 2f);
            p2 = new Vector2(rect.center.x + rect.size.x / 2f, rect.center.y + rect.size.y / 2f);
            p3 = new Vector2(rect.center.x + rect.size.x / 2f, rect.center.y - rect.size.y / 2f);
            p4 = new Vector2(rect.center.x - rect.size.x / 2f, rect.center.y - rect.size.y / 2f);
    
            Debug.DrawLine(p1, p2, Color.blue);
            Debug.DrawLine(p2, p3, Color.blue);
            Debug.DrawLine(p3, p4, Color.blue);
            Debug.DrawLine(p4, p1, Color.blue);
        }
    }

    工程链接:https://github.com/wtb521thl/CreatByUser/tree/Develop

     
  • 相关阅读:
    docker registry
    2019最新EI源刊目录
    在Asp.net Core中使用中间件来管理websocket
    自定义WPF窗体形状
    Font Awesome矢量版,十六进制版,WPF字体使用
    什么是fortran语言之fortran语言入门
    Java各国首都列表
    世界各国货币,C#数字货币计算
    Ocelot中文文档入门
    F#语言入门之什么是F#语言
  • 原文地址:https://www.cnblogs.com/yzxhz/p/12881999.html
Copyright © 2011-2022 走看看