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

     
  • 相关阅读:
    java OA系统 自定义表单 流程审批 电子印章 手写文字识别 电子签名 即时通讯
    flowable 获取当前任务流程图片的输入流
    最新 接口api插件 Swagger3 更新配置详解
    springboot 集成 activiti 流程引擎
    java 在线考试系统源码 springboot 在线教育 视频直播功能 支持手机端
    阿里 Nacos 注册中心 配置启动说明
    springboot 集成外部tomcat war包部署方式
    java 监听 redis 过期事件
    springcloudalibaba 组件版本关系
    java WebSocket 即时通讯配置使用说明
  • 原文地址:https://www.cnblogs.com/yzxhz/p/12881999.html
Copyright © 2011-2022 走看看