NGUI Version 3.9.0數組
//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using UnityEditor; using System.Collections.Generic; /// <summary> /// Inspector class used to edit UIWidgets. /// </summary> [CanEditMultipleObjects] [CustomEditor(typeof(UIWidget), true)] public class UIWidgetInspector : UIRectEditor { static public new UIWidgetInspector instance; // public enum Action // YUTODO這個什麼做用? { None, Move, // 移動 Scale,// 縮放 Rotate, } Action mAction = Action.None; Action mActionUnderMouse = Action.None; bool mAllowSelection = true; protected UIWidget mWidget; // 保存一個widget static protected bool mUseShader = false;// 默認不使用shader // GUIStyle 是什麼鬼? // 各類顏色的點 static GUIStyle mBlueDot = null; // 藍點? static GUIStyle mYellowDot = null; static GUIStyle mRedDot = null; static GUIStyle mOrangeDot = null; static GUIStyle mGreenDot = null; static GUIStyle mGreyDot = null; static MouseCursor mCursor = MouseCursor.Arrow; // 還能控制鼠標光標?666 // 樞軸點 static public UIWidget.Pivot[] pivotPoints = { UIWidget.Pivot.BottomLeft, UIWidget.Pivot.TopLeft, UIWidget.Pivot.TopRight, UIWidget.Pivot.BottomRight, UIWidget.Pivot.Left, UIWidget.Pivot.Top, UIWidget.Pivot.Right, UIWidget.Pivot.Bottom, }; // static int s_Hash = "WidgetHash".GetHashCode(); // 座標 Vector3 mLocalPos = Vector3.zero; Vector3 mWorldPos = Vector3.zero; // 起始寬高 int mStartWidth = 0; int mStartHeight = 0; // 起始拖動和鼠標? Vector3 mStartDrag = Vector3.zero; Vector2 mStartMouse = Vector2.zero; // 起始方向和旋轉角度 Vector3 mStartRot = Vector3.zero; Vector3 mStartDir = Vector3.right; // 起始的上下左右? Vector2 mStartLeft = Vector2.zero; Vector2 mStartRight = Vector2.zero; Vector2 mStartBottom = Vector2.zero; Vector2 mStartTop = Vector2.zero; // 拖動樞軸點 默認爲中心 UIWidget.Pivot mDragPivot = UIWidget.Pivot.Center; /// <summary> /// Raycast into the screen. /// 判斷當前鼠標是否射中傳入的平面 /// </summary> /// <param name="corners">傳入待檢測平面的三個點</param> /// <param name="hit">射到的點的座標</param> /// <returns>射到返回true</returns> static public bool Raycast (Vector3[] corners, out Vector3 hit) { Plane plane = new Plane(corners[0], corners[1], corners[2]); // 三點肯定一個平面 Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); // 轉換2D GUI位置到世界空間的ray float dist = 0f; bool isHit = plane.Raycast(ray, out dist); // 檢查射線有沒有射中平面 hit = isHit ? ray.GetPoint(dist) : Vector3.zero; // GetPoint 返回離射線距離爲 dist 的那個點的位置。 return isHit; } /// <summary> /// Color used by the handles based on the current color scheme. /// 根據當前顏色主題返回顏色 /// </summary> static public Color handlesColor { get { if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange) { return new Color(1f, 0.5f, 0f); } else if (NGUISettings.colorMode == NGUISettings.ColorMode.Green) { return Color.green; } return Color.white; } } /// <summary> /// Draw a control dot at the specified world position. /// 在世界座標繪製控制點 /// </summary> static public void DrawKnob (Vector3 point, bool selected, bool canResize, int id) { // TODO 這是一種什麼賦值方法? if (mGreyDot == null) mGreyDot = "sv_label_0"; // 一種內置的編輯器風格 if (mBlueDot == null) mBlueDot = "sv_label_1"; if (mGreenDot == null) mGreenDot = "sv_label_3"; if (mYellowDot == null) mYellowDot = "sv_label_4"; if (mOrangeDot == null) mOrangeDot = "sv_label_5"; if (mRedDot == null) mRedDot = "sv_label_6"; // Vector2 screenPoint = HandleUtility.WorldToGUIPoint(point); // 矩陣 #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 Rect rect = new Rect(screenPoint.x - 7f, screenPoint.y - 7f, 14f, 14f); #else Rect rect = new Rect(screenPoint.x - 5f, screenPoint.y - 9f, 14f, 14f); #endif // 根據傳入的狀態判斷顯示的顏色 if (selected) { if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange) { mRedDot.Draw(rect, GUIContent.none, id); } else { mOrangeDot.Draw(rect, GUIContent.none, id); } } else if (canResize) { if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange) { mOrangeDot.Draw(rect, GUIContent.none, id); } else if (NGUISettings.colorMode == NGUISettings.ColorMode.Green) { mGreenDot.Draw(rect, GUIContent.none, id); } else { mBlueDot.Draw(rect, GUIContent.none, id); } } else mGreyDot.Draw(rect, GUIContent.none, id); // } /// <summary> /// Screen-space distance from the mouse position to the specified world position. /// 屏幕空間下 鼠標到某點的距離 /// </summary> static public float GetScreenDistance (Vector3 worldPos, Vector2 mousePos) { Vector2 screenPos = HandleUtility.WorldToGUIPoint(worldPos); return Vector2.Distance(mousePos, screenPos); } /// <summary> /// Closest screen-space distance from the mouse position to one of the specified world points. /// 屏幕空間下 在鼠標到各點的距離中取最小距離 /// </summary> static public float GetScreenDistance (Vector3[] worldPoints, Vector2 mousePos, out int index) { float min = float.MaxValue; index = 0; for (int i = 0; i < worldPoints.Length; ++i) { float distance = GetScreenDistance(worldPoints[i], mousePos); if (distance < min) { index = i; min = distance; } } return min; } /// <summary> /// Set the mouse cursor rectangle, refreshing the screen when it gets changed. /// 鼠標光標類型發生改變時從新設置它的矩形區域 /// </summary> static public void SetCursorRect (Rect rect, MouseCursor cursor) { EditorGUIUtility.AddCursorRect(rect, cursor); if (Event.current.type == EventType.MouseMove) { if (mCursor != cursor) { mCursor = cursor; Event.current.Use(); } } } /// <summary> /// /// </summary> protected override void OnDisable () { base.OnDisable(); NGUIEditorTools.HideMoveTool(false); instance = null; } /// <summary> /// Convert the specified 4 corners into 8 pivot points (adding left, top, right, bottom -- in that order). /// 把四個角落的座標數組擴展爲八個點的座標數組 /// </summary> static public Vector3[] GetHandles (Vector3[] corners) { Vector3[] v = new Vector3[8]; v[0] = corners[0]; v[1] = corners[1]; v[2] = corners[2]; v[3] = corners[3]; v[4] = (corners[0] + corners[1]) * 0.5f; v[5] = (corners[1] + corners[2]) * 0.5f; v[6] = (corners[2] + corners[3]) * 0.5f; v[7] = (corners[0] + corners[3]) * 0.5f; return v; } /// <summary> /// Determine what kind of pivot point is under the mouse and update the cursor accordingly. /// 經過鼠標的位置判斷應當進行什麼樣的操做。 /// 默認返回 center 錨點,可以縮放時返回 目標錨點 /// </summary> static public UIWidget.Pivot GetPivotUnderMouse (Vector3[] worldPos, Event e, bool[] resizable, bool movable, ref Action action) { // Time to figure out what kind of action is underneath the mouse UIWidget.Pivot pivotUnderMouse = UIWidget.Pivot.Center;// 初始化爲 錨點中心 // if (action == Action.None) { int index = 0; float dist = GetScreenDistance(worldPos, e.mousePosition, out index); // 獲取最小的距離,並記錄最小距離對應點的下標 bool alt = (e.modifiers & EventModifiers.Alt) != 0; // 判斷是否是按了組合鍵 // if (resizable[index] && dist < 10f) // 若是這個位置能動,而且距離小於10f { pivotUnderMouse = pivotPoints[index]; //鼠標點到的樞軸點是index標示出的index action = Action.Scale;// 進行縮放動做 } else if (!alt && NGUIEditorTools.SceneViewDistanceToRectangle(worldPos, e.mousePosition) == 0f) // 沒有按組合鍵而且鼠標點在矩形框裏面 { action = movable ? Action.Move : Action.Rotate; // 能移動僅進行移動,不能移動就進行旋轉 } else if (dist < 30f) { action = Action.Rotate; // 其餘狀況就進行旋轉 } } // Change the mouse cursor to a more appropriate one // Vector2[] screenPos = new Vector2[8]; for (int i = 0; i < 8; ++i) screenPos[i] = HandleUtility.WorldToGUIPoint(worldPos[i]); Bounds b = new Bounds(screenPos[0], Vector3.zero); // 給定中心點和大小。,後面那個大小表明(width,height,depth) for (int i = 1; i < 8; ++i) b.Encapsulate(screenPos[i]);// 包含八個點的最小矩形框,在這裏八個點都剛好在矩形框的邊上。 Vector2 min = b.min;// min = center - extents; extents 是 size 的一半。例如中心點爲(0,0,0),extents(2,3,5),那麼min(-2,-3,-5) Vector2 max = b.max; // 在原來的基礎上擴展 30f。depth 沒用到因此這裏沒處理 min.x -= 30f; max.x += 30f; min.y -= 30f; max.y += 30f; Rect rect = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); // 獲取矩形框 // 根據動做的狀態 顯示鼠標光標的形狀 if (action == Action.Rotate) { SetCursorRect(rect, MouseCursor.RotateArrow); } else if (action == Action.Move) { SetCursorRect(rect, MouseCursor.MoveArrow); } else if (action == Action.Scale) { SetCursorRect(rect, MouseCursor.ScaleArrow); } else SetCursorRect(rect, MouseCursor.Arrow); return pivotUnderMouse; } /// <summary> /// Draw the specified anchor point. /// 繪製黃色的錨點以及鏈接線、距離標籤 /// </summary> static public void DrawAnchorHandle (UIRect.AnchorPoint anchor, Transform myTrans, Vector3[] myCorners, int side, int id) { if (!anchor.target) return; // 沒有定錨點就不用畫了 int i0, i1; //矩形四個點在數組中的序列與相對位置的關係以下 //12 //03 //根據是哪邊的錨點來選出決定那條邊的兩個頂點 if (side == 0) { // Left i0 = 0; i1 = 1; } else if (side == 1) { // Top i0 = 1; i1 = 2; } else if (side == 2) { // Right i0 = 3; i1 = 2; } else { // Bottom i0 = 0; i1 = 3; } // Vector3 myPos = (myCorners[i0] + myCorners[i1]) * 0.5f; // 錨點相關位置是對應邊的中點 Vector3[] sides = null; // 目標對象矩形的四個節點 if (anchor.rect != null) { sides = anchor.rect.worldCorners; } else { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 Camera cam = anchor.target.camera; // 獲取錨點目標的 camera #else Camera cam = anchor.target.GetComponent<Camera>(); #endif if (cam != null) sides = cam.GetWorldCorners(); // 獲取相機的邊界 } Vector3 theirPos; // 黃點位置,必然在目標矩形上面 if (sides != null) { Vector3 v0, v1; if (side == 0 || side == 2) { // Left or right v0 = Vector3.Lerp(sides[0], sides[3], anchor.relative); // 在兩個向量V0、V1間作線性插值,根據第三個參數t決定返回某個插值。例如 t=0 返回V0,t=0.5 返回V0、V1的中間插值。 v1 = Vector3.Lerp(sides[1], sides[2], anchor.relative); // } else { // Top or bottom v0 = Vector3.Lerp(sides[0], sides[1], anchor.relative); v1 = Vector3.Lerp(sides[3], sides[2], anchor.relative); } // 黃點是 myPos 在 v0 和 v1 鏈接線上的投影點,若是超過v0v1線段,則黃點設置爲v0或v1。 theirPos = HandleUtility.ProjectPointLine(myPos, v0, v1); } else { theirPos = anchor.target.position; } // 畫鏈接線 NGUIHandles.DrawShadowedLine(myCorners, myPos, theirPos, Color.yellow); // 位置偏離的時候在必定條件下繪製一個標記負值距離的標籤 if (Event.current.GetTypeForControl(id) == EventType.Repaint) // 控制ID id 當前事件類型若是是重繪 { Vector2 screenPoint = HandleUtility.WorldToGUIPoint(theirPos); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 Rect rect = new Rect(screenPoint.x - 7f, screenPoint.y - 7f, 14f, 14f); #else Rect rect = new Rect(screenPoint.x - 5f, screenPoint.y - 9f, 14f, 14f); #endif if (mYellowDot == null) mYellowDot = "sv_label_4"; Vector3 v0 = HandleUtility.WorldToGUIPoint(myPos); Vector3 v1 = HandleUtility.WorldToGUIPoint(theirPos); Handles.BeginGUI(); // 開始一個 2D 繪製塊 mYellowDot.Draw(rect, GUIContent.none, id); // 繪製黃點 Vector3 diff = v1 - v0; bool isHorizontal = Mathf.Abs(diff.x) > Mathf.Abs(diff.y);// 判斷是否爲水平線 float mag = diff.magnitude; // 獲取線的長度 if ((isHorizontal && mag > 60f) || (!isHorizontal && mag > 30f)) // 長度超過 60 的水平線或者長度大於 30 的豎直線 { Vector3 pos = (myPos + theirPos) * 0.5f; // string text = anchor.absolute.ToString();// GUI.color = Color.yellow; if (side == 0) { if (theirPos.x < myPos.x) //若是目標點的位置比出發點的位置還要靠左,那麼顯示一個標記距離的label NGUIHandles.DrawCenteredLabel(pos, text); } else if (side == 1) { if (theirPos.y > myPos.y) NGUIHandles.DrawCenteredLabel(pos, text); } else if (side == 2) { if (theirPos.x > myPos.x) NGUIHandles.DrawCenteredLabel(pos, text); } else if (side == 3) { if (theirPos.y < myPos.y) NGUIHandles.DrawCenteredLabel(pos, text); } GUI.color = Color.white; } Handles.EndGUI(); } } /// <summary> /// Draw the on-screen selection, knobs, and handle all interaction logic. /// /// </summary> public void OnSceneGUI () { if (Selection.objects.Length > 1) return; // 選中了多個對象,返回 NGUIEditorTools.HideMoveTool(true); // 隱藏移動工具 if (!UIWidget.showHandles) return; // 沒有須要顯示的 handles,返回 mWidget = target as UIWidget; // 把選中對象做爲一個 UIWidget 組件處理 Transform t = mWidget.cachedTransform; // 獲取對象的 transform Event e = Event.current; // int id = GUIUtility.GetControlID(s_Hash, FocusType.Passive); // 獲取一個控制ID,這個控制不能獲取鍵盤焦點 EventType type = e.GetTypeForControl(id); // 獲取控制ID的事件類型 Action actionUnderMouse = mAction; // 移動、縮放或是旋轉 Vector3[] handles = GetHandles(mWidget.worldCorners); // 獲取組件的八個樞軸點點 // 繪製矩形框 NGUIHandles.DrawShadowedLine(handles, handles[0], handles[1], handlesColor); NGUIHandles.DrawShadowedLine(handles, handles[1], handles[2], handlesColor); NGUIHandles.DrawShadowedLine(handles, handles[2], handles[3], handlesColor); NGUIHandles.DrawShadowedLine(handles, handles[0], handles[3], handlesColor); // If the widget is anchored, draw the anchors // 組件有錨點的話就繪製錨點 if (mWidget.isAnchored) { DrawAnchorHandle(mWidget.leftAnchor, mWidget.cachedTransform, handles, 0, id); DrawAnchorHandle(mWidget.topAnchor, mWidget.cachedTransform, handles, 1, id); DrawAnchorHandle(mWidget.rightAnchor, mWidget.cachedTransform, handles, 2, id); DrawAnchorHandle(mWidget.bottomAnchor, mWidget.cachedTransform, handles, 3, id); } // 須要時顯示細節 if (type == EventType.Repaint) { bool showDetails = (mAction == UIWidgetInspector.Action.Scale) || NGUISettings.drawGuides; // 進行縮放時顯示細節 if (mAction == UIWidgetInspector.Action.None && e.modifiers == EventModifiers.Control) showDetails = true; if (NGUITools.GetActive(mWidget) && mWidget.parent == null) showDetails = true; if (showDetails) NGUIHandles.DrawSize(handles, mWidget.width, mWidget.height); } // Presence of the legacy stretch component prevents resizing // TODO 顯示原來的軌跡組件放置重繪 ?? bool canResize = (mWidget.GetComponent<UIStretch>() == null); // 沒有這個組件說明是能夠自由縮放的 bool[] resizable = new bool[8]; // resizable[4] = canResize; // left resizable[5] = canResize; // top resizable[6] = canResize; // right resizable[7] = canResize; // bottom UILabel lbl = mWidget as UILabel; // 若是這個 widget 組件其實是個一個 Label 組件 if (lbl != null) { if (lbl.overflowMethod == UILabel.Overflow.ResizeFreely) // 若是 label 設置爲能夠隨意縮放,那麼其錨點都定住 { resizable[4] = false; // left resizable[5] = false; // top resizable[6] = false; // right resizable[7] = false; // bottom } else if (lbl.overflowMethod == UILabel.Overflow.ResizeHeight) // 若是是高度能夠自由縮放,那麼上下兩個錨點也定住 { resizable[5] = false; // top resizable[7] = false; // bottom } } if (mWidget.keepAspectRatio == UIWidget.AspectRatioSource.BasedOnHeight) // 基於高度變換,那麼左右錨點就不須要設置了 { resizable[4] = false; resizable[6] = false; } else if (mWidget.keepAspectRatio == UIWidget.AspectRatioSource.BasedOnWidth) { resizable[5] = false; resizable[7] = false; } // 角落的錨點狀況根據上下左右的可用狀況設置 resizable[0] = resizable[7] && resizable[4]; // bottom-left resizable[1] = resizable[5] && resizable[4]; // top-left resizable[2] = resizable[5] && resizable[6]; // top-right resizable[3] = resizable[7] && resizable[6]; // bottom-right // 獲取錨點,沒有的話返回 center UIWidget.Pivot pivotUnderMouse = GetPivotUnderMouse(handles, e, resizable, true, ref actionUnderMouse); switch (type) { case EventType.Repaint: // 繪製控制點 { Vector3 v0 = HandleUtility.WorldToGUIPoint(handles[0]); Vector3 v2 = HandleUtility.WorldToGUIPoint(handles[2]); if ((v2 - v0).magnitude > 60f) // 斜邊 長度大於 60 才繪製控制點 { Vector3 v1 = HandleUtility.WorldToGUIPoint(handles[1]); Vector3 v3 = HandleUtility.WorldToGUIPoint(handles[3]); Handles.BeginGUI(); { for (int i = 0; i < 4; ++i) DrawKnob(handles[i], mWidget.pivot == pivotPoints[i], resizable[i], id); if ((v1 - v0).magnitude > 80f) { if (mWidget.leftAnchor.target == null || mWidget.leftAnchor.absolute != 0) DrawKnob(handles[4], mWidget.pivot == pivotPoints[4], resizable[4], id); if (mWidget.rightAnchor.target == null || mWidget.rightAnchor.absolute != 0) DrawKnob(handles[6], mWidget.pivot == pivotPoints[6], resizable[6], id); } if ((v3 - v0).magnitude > 80f) { if (mWidget.topAnchor.target == null || mWidget.topAnchor.absolute != 0) DrawKnob(handles[5], mWidget.pivot == pivotPoints[5], resizable[5], id); if (mWidget.bottomAnchor.target == null || mWidget.bottomAnchor.absolute != 0) DrawKnob(handles[7], mWidget.pivot == pivotPoints[7], resizable[7], id); } } Handles.EndGUI(); } } break; case EventType.MouseDown: { if (actionUnderMouse != Action.None) { mStartMouse = e.mousePosition; // 記錄鼠標起始位置 mAllowSelection = true; // 標記拖動的開始,拖動過程當中沒法再次選中 if (e.button == 1) // { if (e.modifiers == 0) // 沒有用組合鍵 { GUIUtility.hotControl = GUIUtility.keyboardControl = id; e.Use(); // 使用該事件,用於表示這個事件已經處理過了 } } else if (e.button == 0 && actionUnderMouse != Action.None && Raycast(handles, out mStartDrag)) // 鼠標左鍵&&可以操做&&點到了這個平面 // TODO 不知道爲何須要這麼多判斷 { mWorldPos = t.position; mLocalPos = t.localPosition; mStartRot = t.localRotation.eulerAngles; mStartDir = mStartDrag - t.position; // 用向量來表示角度,鼠標與中點連線的夾角 mStartWidth = mWidget.width; mStartHeight = mWidget.height; // TODO 上下左右, mStartLeft.x = mWidget.leftAnchor.relative; mStartLeft.y = mWidget.leftAnchor.absolute; mStartRight.x = mWidget.rightAnchor.relative; mStartRight.y = mWidget.rightAnchor.absolute; mStartBottom.x = mWidget.bottomAnchor.relative; mStartBottom.y = mWidget.bottomAnchor.absolute; mStartTop.x = mWidget.topAnchor.relative; mStartTop.y = mWidget.topAnchor.absolute; // mDragPivot = pivotUnderMouse; mActionUnderMouse = actionUnderMouse; // GUIUtility.hotControl = GUIUtility.keyboardControl = id; e.Use(); } } } break; case EventType.MouseDrag: { // Prevent selection once the drag operation begins // 若是已經進行了拖動操做,那麼沒法再進行選中了 bool dragStarted = (e.mousePosition - mStartMouse).magnitude > 3f; if (dragStarted) mAllowSelection = false; if (GUIUtility.hotControl == id) // 若是熱鍵控制 與 id 一致 { e.Use(); // TODO 這個爲何設置在前面? if (mAction != Action.None || mActionUnderMouse != Action.None) // { Vector3 pos; if (Raycast(handles, out pos)) { // 拖動開始時進行數據記錄 if (mAction == Action.None && mActionUnderMouse != Action.None) // 前面的 mAction 用於標記是不是拖動剛開始 { // Wait until the mouse moves by more than a few pixels if (dragStarted) { if (mActionUnderMouse == Action.Move) // { NGUISnap.Recalculate(mWidget); // 從新計算 } else if (mActionUnderMouse == Action.Rotate) // { mStartRot = t.localRotation.eulerAngles; // 返回歐垃角 mStartDir = mStartDrag - t.position; // 旋轉的角度 } else if (mActionUnderMouse == Action.Scale) { mStartWidth = mWidget.width; mStartHeight = mWidget.height; mDragPivot = pivotUnderMouse; } mAction = actionUnderMouse; } } // if (mAction != Action.None) { // 設置撤銷點 NGUIEditorTools.RegisterUndo("Change Rect", t); NGUIEditorTools.RegisterUndo("Change Rect", mWidget); // Reset the widget before adjusting anything // 在進行任何調整前先進行重置 t.position = mWorldPos; mWidget.width = mStartWidth; mWidget.height = mStartHeight; // widget 的上下左右錨點都重置爲初始值 mWidget.leftAnchor.Set(mStartLeft.x, mStartLeft.y); mWidget.rightAnchor.Set(mStartRight.x, mStartRight.y); mWidget.bottomAnchor.Set(mStartBottom.x, mStartBottom.y); mWidget.topAnchor.Set(mStartTop.x, mStartTop.y); // 移動操做 if (mAction == Action.Move) { // Move the widget t.position = mWorldPos + (pos - mStartDrag); // t.position = t.positon + (pos - mStartDrag);//mWorldPos 和 mStartDrag 應該是同一個值,不知道爲何要分開來 Vector3 after = t.localPosition; // bool snapped = false; // Transform parent = t.parent; if (parent != null) { UIGrid grid = parent.GetComponent<UIGrid>(); // // 若是說這個widget的父節點具備 Grid 組件,而且按照 CellSnap 的佈局方式進行佈局 // 那麼根據grid的cell大小進行對齊操做。 if (grid != null && grid.arrangement == UIGrid.Arrangement.CellSnap) { snapped = true; if (grid.cellWidth > 0) after.x = Mathf.Round(after.x / grid.cellWidth) * grid.cellWidth; if (grid.cellHeight > 0) after.y = Mathf.Round(after.y / grid.cellHeight) * grid.cellHeight; } } // 若是還沒對齊過,進行對齊 if (!snapped) { // Snap the widget after = NGUISnap.Snap(after, mWidget.localCorners, e.modifiers != EventModifiers.Control); } // Calculate the final delta // 本地座標的變化量 Vector3 localDelta = (after - mLocalPos); // Restore the position t.position = mWorldPos; // 恢復座標位置 // Adjust the widget by the delta // 根據 delta 來移動 Widget 。 // TODO 這跟直接移動 postion 有什麼區別? NGUIMath.MoveRect(mWidget, localDelta.x, localDelta.y); } else if (mAction == Action.Rotate) { // 這部分代碼不是太懂呢。。 Vector3 dir = pos - t.position; float angle = Vector3.Angle(mStartDir, dir); if (angle > 0f) { float dot = Vector3.Dot(Vector3.Cross(mStartDir, dir), t.forward); if (dot < 0f) angle = -angle; angle = mStartRot.z + angle; // 爲何要加 z 值 angle = (NGUISnap.allow && e.modifiers != EventModifiers.Control) ? Mathf.Round(angle / 15f) * 15f : Mathf.Round(angle); // 計算歐拉角 t.localRotation = Quaternion.Euler(mStartRot.x, mStartRot.y, angle); } } else if (mAction == Action.Scale) { // Move the widget // t.position = mWorldPos + (pos - mStartDrag); // Calculate the final delta Vector3 localDelta = (t.localPosition - mLocalPos); // Restore the position t.position = mWorldPos; // Adjust the widget's position and scale based on the delta, restricted by the pivot // 以樞軸點爲中心進行縮放 NGUIMath.ResizeWidget(mWidget, mDragPivot, localDelta.x, localDelta.y, 2, 2); ReEvaluateAnchorType(); } } } } } } break; // 釋放鼠標 case EventType.MouseUp: { if (e.button == 2) break; // 中鍵不處理 if (GUIUtility.hotControl == id) { // 收尾處理 GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; // if (e.button < 2) { bool handled = false; // if (e.button == 1) { // Right-click: Open a context menu listing all widgets underneath NGUIEditorTools.ShowSpriteSelectionMenu(e.mousePosition); handled = true; } else if (mAction == Action.None) { // 選擇對象 if (mAllowSelection) { // Left-click: Select the topmost widget NGUIEditorTools.SelectWidget(e.mousePosition); handled = true; } } else { // Finished dragging something Vector3 pos = t.localPosition; pos.x = Mathf.Round(pos.x); pos.y = Mathf.Round(pos.y); pos.z = Mathf.Round(pos.z); t.localPosition = pos; //對相對位置進行圓整 handled = true; } if (handled) e.Use(); // 若是處理過了,那麼該事件結束 } // Clear the actions mActionUnderMouse = Action.None; mAction = Action.None; } else if (mAllowSelection) // 沒控制可是容許選中,那麼選擇第一個 widget { List<UIWidget> widgets = NGUIEditorTools.SceneViewRaycast(e.mousePosition); if (widgets.Count > 0) Selection.activeGameObject = widgets[0].gameObject; } mAllowSelection = true; } break; // 上下左右鍵對矩形框進行移動 case EventType.KeyDown: { if (e.keyCode == KeyCode.UpArrow) { NGUIEditorTools.RegisterUndo("Nudge Rect", t); NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget); NGUIMath.MoveRect(mWidget, 0f, 1f); e.Use(); } else if (e.keyCode == KeyCode.DownArrow) { NGUIEditorTools.RegisterUndo("Nudge Rect", t); NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget); NGUIMath.MoveRect(mWidget, 0f, -1f); e.Use(); } else if (e.keyCode == KeyCode.LeftArrow) { NGUIEditorTools.RegisterUndo("Nudge Rect", t); NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget); NGUIMath.MoveRect(mWidget, -1f, 0f); e.Use(); } else if (e.keyCode == KeyCode.RightArrow) { NGUIEditorTools.RegisterUndo("Nudge Rect", t); NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget); NGUIMath.MoveRect(mWidget, 1f, 0f); e.Use(); } else if (e.keyCode == KeyCode.Escape) { if (GUIUtility.hotControl == id) { if (mAction != Action.None) Undo.PerformUndo(); GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; mActionUnderMouse = Action.None; mAction = Action.None; e.Use(); } else Selection.activeGameObject = null; } } break; } } /// <summary> /// Cache the reference. /// 記錄引用 /// </summary> protected override void OnEnable () { base.OnEnable(); instance = this; mWidget = target as UIWidget; // 在對 mWidget 進行操做時爲何不須要判空? } /// <summary> /// All widgets have depth, color and make pixel-perfect options /// 全部組件都有 depth,color和 pixel-perfect 選項 /// </summary> protected override void DrawCustomProperties () { if (NGUISettings.unifiedTransform) // 這個不知道是哪裏設置的。。若是是統一的transform,某些部分就不須要顯示了。 { DrawColor(serializedObject, mWidget); } else DrawInspectorProperties(serializedObject, mWidget, true); // 在檢視器中顯示Widget組件的操做面板 } /// <summary> /// Draw common widget properties that can be shown as a part of the Transform Inspector. /// 在檢視器中顯示通用的 widget 屬性,不顯示顏色部分 /// </summary> public void DrawWidgetTransform () { DrawInspectorProperties(serializedObject, mWidget, false); } /// <summary> /// Draw the widget's color. /// 在檢視器中顯示,設置 widget 的顏色和透明度 /// </summary> static public void DrawColor (SerializedObject so, UIWidget w) { if ((w.GetType() != typeof(UIWidget))) { NGUIEditorTools.DrawProperty("Color Tint", so, "mColor", GUILayout.MinWidth(20f)); } else if (so.isEditingMultipleObjects) { NGUIEditorTools.DrawProperty("Alpha", so, "mColor.a", GUILayout.Width(120f)); } else { GUI.changed = false; float alpha = EditorGUILayout.Slider("Alpha", w.alpha, 0f, 1f); if (GUI.changed) { NGUIEditorTools.RegisterUndo("Alpha change", w); w.alpha = alpha; } } } /// <summary> /// Draw common widget properties. /// 在檢視器中顯示通用 widget 屬性 /// </summary> static public void DrawInspectorProperties (SerializedObject so, UIWidget w, bool drawColor) { if (drawColor) { DrawColor(so, w); GUILayout.Space(3f); } PrefabType type = PrefabUtility.GetPrefabType(w.gameObject); // TODO 爲何要辨別出 prefab 的類型?有些狀況不須要顯示? if (NGUIEditorTools.DrawHeader("Widget")) // 包裹起來 { NGUIEditorTools.BeginContents(); if (NGUISettings.minimalisticLook) NGUIEditorTools.SetLabelWidth(70f); DrawPivot(so, w); //樞軸點 DrawDepth(so, w, type == PrefabType.Prefab);// 深度 DrawDimensions(so, w, type == PrefabType.Prefab);// 大小 if (NGUISettings.minimalisticLook) NGUIEditorTools.SetLabelWidth(70f); SerializedProperty ratio = so.FindProperty("aspectRatio"); SerializedProperty aspect = so.FindProperty("keepAspectRatio"); GUILayout.BeginHorizontal(); { if (!aspect.hasMultipleDifferentValues && aspect.intValue == 0) // 單個或多個對象選中時 aspect 都是第一種狀況 { EditorGUI.BeginDisabledGroup(true); // 設置爲禁用 NGUIEditorTools.DrawProperty("Aspect", ratio, false, GUILayout.Width(130f)); EditorGUI.EndDisabledGroup(); } else NGUIEditorTools.DrawProperty("Aspect", ratio, false, GUILayout.Width(130f)); NGUIEditorTools.DrawProperty("", aspect, false, GUILayout.MinWidth(20f)); } GUILayout.EndHorizontal(); // 編輯多個對象或者對象存在 碰撞盒的時候顯示 if (so.isEditingMultipleObjects || w.hasBoxCollider) { GUILayout.BeginHorizontal(); { NGUIEditorTools.DrawProperty("Collider", so, "autoResizeBoxCollider", GUILayout.Width(100f)); GUILayout.Label("auto-adjust to match"); } GUILayout.EndHorizontal(); } NGUIEditorTools.EndContents(); } } /// <summary> /// Draw widget's dimensions. /// </summary> static void DrawDimensions (SerializedObject so, UIWidget w, bool isPrefab) { GUILayout.BeginHorizontal(); { bool freezeSize = so.isEditingMultipleObjects; UILabel lbl = w as UILabel; if (!freezeSize && lbl) freezeSize = (lbl.overflowMethod == UILabel.Overflow.ResizeFreely); if (freezeSize) { EditorGUI.BeginDisabledGroup(true); NGUIEditorTools.DrawProperty("Size", so, "mWidth", GUILayout.MinWidth(100f)); EditorGUI.EndDisabledGroup(); } else { GUI.changed = false; int val = EditorGUILayout.IntField("Size", w.width, GUILayout.MinWidth(100f)); if (GUI.changed) { NGUIEditorTools.RegisterUndo("Dimensions Change", w); w.width = val; } } if (!freezeSize && lbl) { UILabel.Overflow ov = lbl.overflowMethod; freezeSize = (ov == UILabel.Overflow.ResizeFreely || ov == UILabel.Overflow.ResizeHeight); } NGUIEditorTools.SetLabelWidth(12f); if (freezeSize) { EditorGUI.BeginDisabledGroup(true); NGUIEditorTools.DrawProperty("x", so, "mHeight", GUILayout.MinWidth(30f)); EditorGUI.EndDisabledGroup(); } else { GUI.changed = false; int val = EditorGUILayout.IntField("x", w.height, GUILayout.MinWidth(30f)); if (GUI.changed) { NGUIEditorTools.RegisterUndo("Dimensions Change", w); w.height = val; } } NGUIEditorTools.SetLabelWidth(80f); if (isPrefab) { GUILayout.Space(70f); } else { EditorGUI.BeginDisabledGroup(so.isEditingMultipleObjects); if (GUILayout.Button("Snap", GUILayout.Width(60f))) { foreach (GameObject go in Selection.gameObjects) { UIWidget pw = go.GetComponent<UIWidget>(); if (pw != null) { NGUIEditorTools.RegisterUndo("Snap Dimensions", pw); NGUIEditorTools.RegisterUndo("Snap Dimensions", pw.transform); pw.MakePixelPerfect(); } } } EditorGUI.EndDisabledGroup(); } } GUILayout.EndHorizontal(); } /// <summary> /// Draw widget's depth. /// </summary> static void DrawDepth (SerializedObject so, UIWidget w, bool isPrefab) { if (isPrefab) return; GUILayout.Space(2f); GUILayout.BeginHorizontal(); { EditorGUILayout.PrefixLabel("Depth"); if (GUILayout.Button("Back", GUILayout.MinWidth(46f))) { foreach (GameObject go in Selection.gameObjects) { UIWidget pw = go.GetComponent<UIWidget>(); if (pw != null) pw.depth = w.depth - 1; } } NGUIEditorTools.DrawProperty("", so, "mDepth", GUILayout.MinWidth(20f)); if (GUILayout.Button("Forward", GUILayout.MinWidth(60f))) { foreach (GameObject go in Selection.gameObjects) { UIWidget pw = go.GetComponent<UIWidget>(); if (pw != null) pw.depth = w.depth + 1; } } } GUILayout.EndHorizontal(); int matchingDepths = 1; UIPanel p = w.panel; if (p != null) { for (int i = 0, imax = p.widgets.Count; i < imax; ++i) { UIWidget pw = p.widgets[i]; if (pw != w && pw.depth == w.depth) ++matchingDepths; } } if (matchingDepths > 1) { EditorGUILayout.HelpBox(matchingDepths + " widgets are sharing the depth value of " + w.depth, MessageType.Info); } } /// <summary> /// Draw the widget's pivot. /// </summary> static void DrawPivot (SerializedObject so, UIWidget w) { SerializedProperty pv = so.FindProperty("mPivot"); if (pv.hasMultipleDifferentValues) { // TODO: Doing this doesn't keep the widget's position where it was. Another approach is needed. NGUIEditorTools.DrawProperty("Pivot", so, "mPivot"); } else { // Pivot point -- the new, more visual style GUILayout.BeginHorizontal(); GUILayout.Label("Pivot", GUILayout.Width(NGUISettings.minimalisticLook ? 66f : 76f)); Toggle(w, "\u25C4", "ButtonLeft", UIWidget.Pivot.Left, true); Toggle(w, "\u25AC", "ButtonMid", UIWidget.Pivot.Center, true); Toggle(w, "\u25BA", "ButtonRight", UIWidget.Pivot.Right, true); Toggle(w, "\u25B2", "ButtonLeft", UIWidget.Pivot.Top, false); Toggle(w, "\u258C", "ButtonMid", UIWidget.Pivot.Center, false); Toggle(w, "\u25BC", "ButtonRight", UIWidget.Pivot.Bottom, false); GUILayout.EndHorizontal(); pv.enumValueIndex = (int)w.pivot; } } /// <summary> /// Draw a toggle button for the pivot point. /// </summary> static void Toggle (UIWidget w, string text, string style, UIWidget.Pivot pivot, bool isHorizontal) { bool isActive = false; switch (pivot) { case UIWidget.Pivot.Left: isActive = IsLeft(w.pivot); break; case UIWidget.Pivot.Right: isActive = IsRight(w.pivot); break; case UIWidget.Pivot.Top: isActive = IsTop(w.pivot); break; case UIWidget.Pivot.Bottom: isActive = IsBottom(w.pivot); break; case UIWidget.Pivot.Center: isActive = isHorizontal ? pivot == GetHorizontal(w.pivot) : pivot == GetVertical(w.pivot); break; } if (GUILayout.Toggle(isActive, text, style) != isActive) SetPivot(w, pivot, isHorizontal); } static bool IsLeft (UIWidget.Pivot pivot) { return pivot == UIWidget.Pivot.Left || pivot == UIWidget.Pivot.TopLeft || pivot == UIWidget.Pivot.BottomLeft; } static bool IsRight (UIWidget.Pivot pivot) { return pivot == UIWidget.Pivot.Right || pivot == UIWidget.Pivot.TopRight || pivot == UIWidget.Pivot.BottomRight; } static bool IsTop (UIWidget.Pivot pivot) { return pivot == UIWidget.Pivot.Top || pivot == UIWidget.Pivot.TopLeft || pivot == UIWidget.Pivot.TopRight; } static bool IsBottom (UIWidget.Pivot pivot) { return pivot == UIWidget.Pivot.Bottom || pivot == UIWidget.Pivot.BottomLeft || pivot == UIWidget.Pivot.BottomRight; } static UIWidget.Pivot GetHorizontal (UIWidget.Pivot pivot) { if (IsLeft(pivot)) return UIWidget.Pivot.Left; if (IsRight(pivot)) return UIWidget.Pivot.Right; return UIWidget.Pivot.Center; } static UIWidget.Pivot GetVertical (UIWidget.Pivot pivot) { if (IsTop(pivot)) return UIWidget.Pivot.Top; if (IsBottom(pivot)) return UIWidget.Pivot.Bottom; return UIWidget.Pivot.Center; } static UIWidget.Pivot Combine (UIWidget.Pivot horizontal, UIWidget.Pivot vertical) { if (horizontal == UIWidget.Pivot.Left) { if (vertical == UIWidget.Pivot.Top) return UIWidget.Pivot.TopLeft; if (vertical == UIWidget.Pivot.Bottom) return UIWidget.Pivot.BottomLeft; return UIWidget.Pivot.Left; } if (horizontal == UIWidget.Pivot.Right) { if (vertical == UIWidget.Pivot.Top) return UIWidget.Pivot.TopRight; if (vertical == UIWidget.Pivot.Bottom) return UIWidget.Pivot.BottomRight; return UIWidget.Pivot.Right; } return vertical; } static void SetPivot (UIWidget w, UIWidget.Pivot pivot, bool isHorizontal) { UIWidget.Pivot horizontal = GetHorizontal(w.pivot); UIWidget.Pivot vertical = GetVertical(w.pivot); pivot = isHorizontal ? Combine(pivot, vertical) : Combine(horizontal, pivot); if (w.pivot != pivot) { NGUIEditorTools.RegisterUndo("Pivot change", w); w.pivot = pivot; } } protected override void OnDrawFinalProperties () { if (mAnchorType == AnchorType.Advanced || !mWidget.isAnchored) return; SerializedProperty sp = serializedObject.FindProperty("leftAnchor.target"); if (!IsRect(sp)) { GUILayout.Space(3f); GUILayout.BeginHorizontal(); GUILayout.Space(6f); NGUIEditorTools.DrawProperty("", serializedObject, "hideIfOffScreen", GUILayout.Width(18f)); GUILayout.Label("Hide if off-screen", GUILayout.MinWidth(20f)); GUILayout.EndHorizontal(); } } }