工作時候突然想到一個想法,讓用戶自己修改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