工作时候突然想到一个想法,让用户自己修改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