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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM