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

 
posted @ 2020-05-13 14:33  有只小耗子  阅读(1773)  评论(2编辑  收藏  举报