//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//----------------------------------------------
#define FUNCELL_MODIFIED
using UnityEngine;
using System.Collections.Generic;
///
/// Base class for all UI components that should be derived from when creating new widget types.
///
[ExecuteInEditMode]
[AddComponentMenu("NGUI/UI/NGUI Widget")]
public class UIWidget : UIRect
{
public enum Pivot
{
TopLeft,
Top,
TopRight,
Left,
Center,
Right,
BottomLeft,
Bottom,
BottomRight,
}
// Cached and saved values
[HideInInspector][SerializeField] protected Color mColor = Color.white;
#if FUNCELL_MODIFIED
//保存上一帧的alhpa值,用于判断改变后计算alpha
private float _oldAlpha = -1f;
[System.NonSerialized] public bool IsOnlyWidget = false;
#endif
[HideInInspector][SerializeField] public bool useGradient = false;
[HideInInspector][SerializeField] public Color ltColor = Color.white;
[HideInInspector][SerializeField] public Color lbColor = Color.white;
[HideInInspector][SerializeField] public Color rtColor = Color.white;
[HideInInspector][SerializeField] public Color rbColor = Color.white;
[HideInInspector][SerializeField] protected Pivot mPivot = Pivot.Center;
[HideInInspector][SerializeField] protected int mWidth = 100;
[HideInInspector][SerializeField] protected int mHeight = 100;
[HideInInspector][SerializeField] protected int mDepth = 0;
[HideInInspector][SerializeField] protected Vector2 lbOffset = Vector2.zero;
[HideInInspector][SerializeField] protected Vector2 ltOffset = Vector2.zero;
[HideInInspector][SerializeField] protected Vector2 rbOffset = Vector2.zero;
[HideInInspector][SerializeField] protected Vector2 rtOffset = Vector2.zero;
public delegate void OnDimensionsChanged ();
public delegate void OnPostFillCallback (UIWidget widget, int bufferOffset, BetterList verts, BetterList uvs, BetterList cols);
///
/// Notification triggered when the widget's dimensions or position changes.
///
public OnDimensionsChanged onChange;
///
/// Notification triggered after the widget's buffer has been filled.
///
public OnPostFillCallback onPostFill;
///
/// Callback triggered when the widget is about to be renderered (OnWillRenderObject).
/// NOTE: This property is only exposed for the sake of speed to avoid property execution.
/// In most cases you will want to use UIWidget.onRender instead.
///
public UIDrawCall.OnRenderCallback mOnRender;
///
/// Set the callback that will be triggered when the widget is being rendered (OnWillRenderObject).
/// This is where you would set material properties and shader values.
///
public UIDrawCall.OnRenderCallback onRender
{
get
{
return mOnRender;
}
set
{
#if UNITY_FLASH
if (!(mOnRender == value))
#else
if (mOnRender != value)
#endif
{
#if !UNITY_FLASH
if (drawCall != null && drawCall.onRender != null && mOnRender != null)
drawCall.onRender -= mOnRender;
#endif
mOnRender = value;
if (drawCall != null) drawCall.onRender += value;
}
}
}
///
/// If set to 'true', the box collider's dimensions will be adjusted to always match the widget whenever it resizes.
///
public bool autoResizeBoxCollider = false;
///
/// Hide the widget if it happens to be off-screen.
///
public bool hideIfOffScreen = false;
public enum AspectRatioSource
{
Free,
BasedOnWidth,
BasedOnHeight,
}
///
/// Whether the rectangle will attempt to maintain a specific aspect ratio.
///
public AspectRatioSource keepAspectRatio = AspectRatioSource.Free;
///
/// If you want the anchored rectangle to keep a specific aspect ratio, set this value.
///
public float aspectRatio = 1f;
public delegate bool HitCheck (Vector3 worldPos);
///
/// Custom hit check function. If set, all hit checks (including events) will call this function,
/// passing the world position. Return 'true' if it's within the bounds of your choice, 'false' otherwise.
///
public HitCheck hitCheck;
///
/// Panel that's managing this widget.
///
[System.NonSerialized] public UIPanel panel;
//当前Panel的ID
private int _panelInstID = 0;
///
/// Widget's generated geometry.
///
[System.NonSerialized] public UIGeometry geometry = new UIGeometry();
///
/// If set to 'false', the widget's OnFill function will not be called, letting you define custom geometry at will.
///
[System.NonSerialized] public bool fillGeometry = true;
[System.NonSerialized] protected bool mPlayMode = true;
[System.NonSerialized] protected Vector4 mDrawRegion = new Vector4(0f, 0f, 1f, 1f);
[System.NonSerialized] Matrix4x4 mLocalToPanel;
[System.NonSerialized] bool mIsVisibleByAlpha = true;
[System.NonSerialized] bool mIsVisibleByPanel = true;
[System.NonSerialized] bool mIsInFront = true;
[System.NonSerialized] float mLastAlpha = 0f;
[System.NonSerialized] bool mMoved = false;
///
/// Internal usage -- draw call that's drawing the widget.
///
[System.NonSerialized] public UIDrawCall drawCall;
[System.NonSerialized] protected Vector3[] mCorners = new Vector3[4];
#if FUNCELL_MODIFIED
//是否为灰色
[HideInInspector]
[SerializeField]
private bool _isGray = false;
public bool IsGray
{
get
{
return _isGray;
}
set
{
if (_isGray != value)
{
_isGray = value;
OnReDrawWidget();
}
}
}
//是否为流光
[HideInInspector]
[SerializeField]
private bool _isFlowLight = false;
public bool IsFlowLight
{
get
{
return _isFlowLight;
}
set
{
if (_isFlowLight != value)
{
_isFlowLight = value;
OnReDrawWidget();
}
}
}
//流光的颜色--这里使用整形,容易比较(很坑的是Color32不能比较,所以选择这个值)
//这里使用int来表示float,有效数字为10000.
[HideInInspector]
[SerializeField]
private Vector3Int _flowColor = new Vector3Int(3000, 3000, 3000);
public Vector3Int FlowColor
{
get
{
return _flowColor;
}
set
{
if (_flowColor != value)
{
_flowColor = value;
OnReDrawWidget();
}
}
}
//流光的参数 v1:宽度[0,10000] v2:速度, 光在UI上流过的速度 v3:滚动的位置[0,10000]
//这里使用int来表示float,有效数字为10000.
[HideInInspector]
[SerializeField]
private Vector3Int _flowParam = new Vector3Int(5000, 0, 0);
public Vector3Int FlowParam
{
get
{
return _flowParam;
}
set
{
if (_flowParam != value)
{
_flowParam = value;
OnReDrawWidget();
}
}
}
public int FlowWidth
{
get
{
return _flowParam.x;
}
set
{
if (_flowParam.x != value)
{
_flowParam.x = value;
OnReDrawWidget();
}
}
}
public int FlowSpeed
{
get
{
return _flowParam.y;
}
set
{
if (_flowParam.y != value)
{
_flowParam.y = value;
OnReDrawWidget();
}
}
}
public int FlowValue
{
get
{
return _flowParam.z;
}
set
{
if (_flowParam.z != value)
{
_flowParam.z = value;
OnReDrawWidget();
}
}
}
[HideInInspector]
[SerializeField]
private bool _isAutoFlow = false;
public bool IsAutoFlow
{
get
{
return _isAutoFlow;
}
set
{
if (_isAutoFlow != value)
{
_isAutoFlow = value;
if (_isAutoFlow)
{
_flowParam.y = 15000;
}
else
{
_flowParam.y = 0;
}
OnReDrawWidget();
}
}
}
//是否为叠加
[HideInInspector]
[SerializeField]
private bool _isOverlay = false;
public bool IsOverlay
{
get
{
return _isOverlay;
}
set
{
if (_isOverlay != value)
{
_isOverlay = value;
OnReDrawWidget();
}
}
}
//重新刷新Widget
private void OnReDrawWidget()
{
if (panel != null) panel.RemoveWidget(this);
if (panel != null)
{
panel.AddWidget(this);
if (!Application.isPlaying)
{
panel.SortWidgets();
panel.RebuildAllDrawCalls();
}
}
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
#endif
///
/// Draw region alters how the widget looks without modifying the widget's rectangle.
/// A region is made up of 4 relative values (0-1 range). The order is Left (X), Bottom (Y), Right (Z) and Top (W).
/// To have a widget's left edge be 30% from the left side, set X to 0.3. To have the widget's right edge be 30%
/// from the right hand side, set Z to 0.7.
///
public Vector4 drawRegion
{
get
{
return mDrawRegion;
}
set
{
if (mDrawRegion != value)
{
mDrawRegion = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
///
/// Pivot offset in relative coordinates. Bottom-left is (0, 0). Top-right is (1, 1).
///
public Vector2 pivotOffset { get { return NGUIMath.GetPivotOffset(pivot); } }
///
/// Widget's width in pixels.
///
public int width
{
get
{
return mWidth;
}
set
{
int min = minWidth;
if (value < min) value = min;
if (mWidth != value && keepAspectRatio != AspectRatioSource.BasedOnHeight)
{
if (isAnchoredHorizontally)
{
if (leftAnchor.target != null && rightAnchor.target != null)
{
if (mPivot == Pivot.BottomLeft || mPivot == Pivot.Left || mPivot == Pivot.TopLeft)
{
NGUIMath.AdjustWidget(this, 0f, 0f, value - mWidth, 0f);
}
else if (mPivot == Pivot.BottomRight || mPivot == Pivot.Right || mPivot == Pivot.TopRight)
{
NGUIMath.AdjustWidget(this, mWidth - value, 0f, 0f, 0f);
}
else
{
int diff = value - mWidth;
diff = diff - (diff & 1);
if (diff != 0) NGUIMath.AdjustWidget(this, -diff * 0.5f, 0f, diff * 0.5f, 0f);
}
}
else if (leftAnchor.target != null)
{
NGUIMath.AdjustWidget(this, 0f, 0f, value - mWidth, 0f);
}
else NGUIMath.AdjustWidget(this, mWidth - value, 0f, 0f, 0f);
}
else SetDimensions(value, mHeight);
}
}
}
///
/// Widget's height in pixels.
///
public int height
{
get
{
return mHeight;
}
set
{
int min = minHeight;
if (value < min) value = min;
if (mHeight != value && keepAspectRatio != AspectRatioSource.BasedOnWidth)
{
if (isAnchoredVertically)
{
if (bottomAnchor.target != null && topAnchor.target != null)
{
if (mPivot == Pivot.BottomLeft || mPivot == Pivot.Bottom || mPivot == Pivot.BottomRight)
{
NGUIMath.AdjustWidget(this, 0f, 0f, 0f, value - mHeight);
}
else if (mPivot == Pivot.TopLeft || mPivot == Pivot.Top || mPivot == Pivot.TopRight)
{
NGUIMath.AdjustWidget(this, 0f, mHeight - value, 0f, 0f);
}
else
{
int diff = value - mHeight;
diff = diff - (diff & 1);
if (diff != 0) NGUIMath.AdjustWidget(this, 0f, -diff * 0.5f, 0f, diff * 0.5f);
}
}
else if (bottomAnchor.target != null)
{
NGUIMath.AdjustWidget(this, 0f, 0f, 0f, value - mHeight);
}
else NGUIMath.AdjustWidget(this, 0f, mHeight - value, 0f, 0f);
}
else SetDimensions(mWidth, value);
}
}
}
///
/// Color used by the widget.
///
public Color color
{
get
{
return mColor;
}
set
{
if (mColor != value)
{
bool alphaChange = (mColor.a != value.a);
mColor = value;
Invalidate(alphaChange);
}
}
}
///
/// Widget's alpha -- a convenience method.
///
public override float alpha
{
get
{
return mColor.a;
}
set
{
float val = Mathf.Clamp01(value);
if (mColor.a != val)
{
mColor.a = val;
Invalidate(true, true);
}
}
}
///
/// Whether the widget is currently visible.
///
public bool isVisible { get { return mIsVisibleByPanel && mIsVisibleByAlpha && mIsInFront && finalAlpha > 0.001f && NGUITools.GetActive(this); } }
///
/// Whether the widget has vertices to draw.
///
public bool hasVertices { get { return geometry != null && geometry.hasVertices; } }
///
/// Change the pivot point and do not attempt to keep the widget in the same place by adjusting its transform.
///
public Pivot rawPivot
{
get
{
return mPivot;
}
set
{
if (mPivot != value)
{
mPivot = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
///
/// Set or get the value that specifies where the widget's pivot point should be.
///
public Pivot pivot
{
get
{
return mPivot;
}
set
{
if (mPivot != value)
{
Vector3 before = worldCorners[0];
mPivot = value;
mChanged = true;
Vector3 after = worldCorners[0];
Transform t = cachedTransform;
Vector3 pos = t.position;
float z = t.localPosition.z;
pos.x += (before.x - after.x);
pos.y += (before.y - after.y);
cachedTransform.position = pos;
pos = cachedTransform.localPosition;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
pos.z = z;
cachedTransform.localPosition = pos;
}
}
}
///
/// Depth controls the rendering order -- lowest to highest.
///
public int depth
{
get
{
// Experiment with a transform-based depth, uGUI style
//if (mDepth == int.MinValue)
//{
// int val = cachedTransform.GetSiblingIndex();
// UIWidget pt = parent as UIWidget;
// if (pt != null) val += pt.depth;
// return val;
//}
return mDepth;
}
set
{
if (mDepth != value)
{
if (panel != null) panel.RemoveWidget(this);
mDepth = value;
if (panel != null)
{
panel.AddWidget(this);
if (!Application.isPlaying)
{
panel.SortWidgets();
panel.RebuildAllDrawCalls();
}
}
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
}
///
/// Raycast depth order on widgets takes the depth of their panel into consideration.
/// This functionality is used to determine the "final" depth of the widget for drawing and raycasts.
///
public int raycastDepth
{
get
{
if (panel == null) CreatePanel();
return (panel != null) ? mDepth + panel.depth * 10000 : mDepth;
}
}
///
/// Local space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
///
public override Vector3[] localCorners
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
mCorners[0] = new Vector3(x0, y0);
mCorners[1] = new Vector3(x0, y1);
mCorners[2] = new Vector3(x1, y1);
mCorners[3] = new Vector3(x1, y0);
return mCorners;
}
}
///
/// Local width and height of the widget in pixels.
///
public virtual Vector2 localSize
{
get
{
Vector3[] cr = localCorners;
return cr[2] - cr[0];
}
}
///
/// Widget's center in local coordinates. Don't forget to transform by the widget's transform.
///
public Vector3 localCenter
{
get
{
Vector3[] cr = localCorners;
return Vector3.Lerp(cr[0], cr[2], 0.5f);
}
}
///
/// World-space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
///
public override Vector3[] worldCorners
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
Transform wt = cachedTransform;
mCorners[0] = wt.TransformPoint(x0, y0, 0f);
mCorners[1] = wt.TransformPoint(x0, y1, 0f);
mCorners[2] = wt.TransformPoint(x1, y1, 0f);
mCorners[3] = wt.TransformPoint(x1, y0, 0f);
return mCorners;
}
}
///
/// World-space center of the widget.
///
public Vector3 worldCenter { get { return cachedTransform.TransformPoint(localCenter); } }
///
/// Local space region where the actual drawing will take place.
/// X = left, Y = bottom, Z = right, W = top.
///
public virtual Vector4 drawingDimensions
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
return new Vector4(
mDrawRegion.x == 0f ? x0 : Mathf.Lerp(x0, x1, mDrawRegion.x),
mDrawRegion.y == 0f ? y0 : Mathf.Lerp(y0, y1, mDrawRegion.y),
mDrawRegion.z == 1f ? x1 : Mathf.Lerp(x0, x1, mDrawRegion.z),
mDrawRegion.w == 1f ? y1 : Mathf.Lerp(y0, y1, mDrawRegion.w));
}
}
///
/// Material used by the widget.
///
public virtual Material material
{
get
{
return null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no material setter");
}
}
///
/// Texture used by the widget.
///
public virtual Texture mainTexture
{
get
{
Material mat = material;
return (mat != null) ? mat.mainTexture : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no mainTexture setter");
}
}
///
/// Shader is used to create a dynamic material if the widget has no material to work with.
///
public virtual Shader shader
{
get
{
Material mat = material;
return (mat != null) ? mat.shader : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no shader setter");
}
}
///
/// Do not use this, it's obsolete.
///
[System.Obsolete("There is no relative scale anymore. Widgets now have width and height instead")]
public Vector2 relativeSize { get { return Vector2.one; } }
///
/// Convenience function that returns 'true' if the widget has a box collider.
///
public bool hasBoxCollider
{
get
{
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
BoxCollider box = collider as BoxCollider;
#else
BoxCollider box = GetComponent() as BoxCollider;
#endif
if (box != null) return true;
return GetComponent() != null;
}
}
///
/// Adjust the widget's dimensions without going through the anchor validation logic.
///
public void SetDimensions (int w, int h)
{
if (mWidth != w || mHeight != h)
{
mWidth = w;
mHeight = h;
if (keepAspectRatio == AspectRatioSource.BasedOnWidth)
mHeight = Mathf.RoundToInt(mWidth / aspectRatio);
else if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
mWidth = Mathf.RoundToInt(mHeight * aspectRatio);
else if (keepAspectRatio == AspectRatioSource.Free)
aspectRatio = mWidth / (float)mHeight;
mMoved = true;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
///
/// Get the sides of the rectangle relative to the specified transform.
/// The order is left, top, right, bottom.
///
public override Vector3[] GetSides (Transform relativeTo)
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
float cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f;
Transform trans = cachedTransform;
mCorners[0] = trans.TransformPoint(x0, cy, 0f);
mCorners[1] = trans.TransformPoint(cx, y1, 0f);
mCorners[2] = trans.TransformPoint(x1, cy, 0f);
mCorners[3] = trans.TransformPoint(cx, y0, 0f);
if (relativeTo != null)
{
for (int i = 0; i < 4; ++i)
mCorners[i] = relativeTo.InverseTransformPoint(mCorners[i]);
}
return mCorners;
}
[System.NonSerialized] int mAlphaFrameID = -1;
#if FUNCELL_MODIFIED
public override void SelfCalculateFinalAlpha(float parentAlpha)
{
finalAlpha = parentAlpha * mColor.a;
for(int i = 0; i < mChildren.size; ++i)
{
mChildren[i].SelfCalculateFinalAlpha(finalAlpha);
}
}
#endif
///
/// Widget's final alpha, after taking the panel's alpha into account.
///
///
public override float CalculateFinalAlpha (int frameID)
{
#if UNITY_EDITOR
if (mAlphaFrameID != frameID || !Application.isPlaying)
#else
if (mAlphaFrameID != frameID)
#endif
{
mAlphaFrameID = frameID;
UpdateFinalAlpha(frameID);
}
return finalAlpha;
}
///
/// Force-calculate the final alpha value.
///
protected void UpdateFinalAlpha (int frameID)
{
if (!mIsVisibleByAlpha || !mIsInFront)
{
finalAlpha = 0f;
}
else
{
UIRect pt = parent;
finalAlpha = (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
}
///
/// Update the widget's visibility and final alpha.
///
public override void Invalidate (bool includeChildren, bool onlyAlphaChange = false)
{
if(!onlyAlphaChange)
{
mChanged = true;
}
mAlphaFrameID = -1;
if (panel != null)
{
bool vis = (hideIfOffScreen || panel.hasCumulativeClipping) ? panel.IsVisible(this) : true;
UpdateVisibility(CalculateCumulativeAlpha(Time.frameCount) > 0.001f, vis);
UpdateFinalAlpha(Time.frameCount);
if (includeChildren) base.Invalidate(true, onlyAlphaChange);
}
}
///
/// Same as final alpha, except it doesn't take own visibility into consideration. Used by panels.
///
public float CalculateCumulativeAlpha (int frameID)
{
UIRect pt = parent;
return (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
///
/// Set the widget's rectangle.
///
public override void SetRect (float x, float y, float width, float height)
{
Vector2 po = pivotOffset;
float fx = Mathf.Lerp(x, x + width, po.x);
float fy = Mathf.Lerp(y, y + height, po.y);
int finalWidth = Mathf.FloorToInt(width + 0.5f);
int finalHeight = Mathf.FloorToInt(height + 0.5f);
if (po.x == 0.5f) finalWidth = ((finalWidth >> 1) << 1);
if (po.y == 0.5f) finalHeight = ((finalHeight >> 1) << 1);
Transform t = cachedTransform;
Vector3 pos = t.localPosition;
pos.x = Mathf.Floor(fx + 0.5f);
pos.y = Mathf.Floor(fy + 0.5f);
if (finalWidth < minWidth) finalWidth = minWidth;
if (finalHeight < minHeight) finalHeight = minHeight;
t.localPosition = pos;
this.width = finalWidth;
this.height = finalHeight;
if (isAnchored)
{
t = t.parent;
if (leftAnchor.target) leftAnchor.SetHorizontal(t, x);
if (rightAnchor.target) rightAnchor.SetHorizontal(t, x + width);
if (bottomAnchor.target) bottomAnchor.SetVertical(t, y);
if (topAnchor.target) topAnchor.SetVertical(t, y + height);
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
///
/// Adjust the widget's collider size to match the widget's dimensions.
///
public void ResizeCollider () { if (this.enabled) NGUITools.UpdateWidgetCollider(gameObject); }
///
/// Static widget comparison function used for depth sorting.
///
[System.Diagnostics.DebuggerHidden]
[System.Diagnostics.DebuggerStepThrough]
static public int FullCompareFunc (UIWidget left, UIWidget right)
{
int val = UIPanel.CompareFunc(left.panel, right.panel);
return (val == 0) ? PanelCompareFunc(left, right) : val;
}
///
/// Static widget comparison function used for inter-panel depth sorting.
///
[System.Diagnostics.DebuggerHidden]
[System.Diagnostics.DebuggerStepThrough]
static public int PanelCompareFunc (UIWidget left, UIWidget right)
{
if (left.mDepth < right.mDepth) return -1;
if (left.mDepth > right.mDepth) return 1;
Material leftMat = left.material;
Material rightMat = right.material;
if (leftMat == rightMat) return 0;
if (leftMat == null) return 1;
if (rightMat == null) return -1;
#if FUNCELL_MODIFIED
bool leftGray = left.IsGray;
bool rightGray = right.IsGray;
if (leftGray == rightGray) return 0;
if (leftGray) return 1;
if (rightGray) return -1;
bool leftFlow = left.IsFlowLight;
bool rightFlow = right.IsFlowLight;
if (leftFlow == rightFlow) return 0;
if (leftFlow) return 1;
if (rightFlow) return -1;
bool leftOverlay = left.IsOverlay;
bool rightOverlay = right.IsOverlay;
if (leftOverlay == rightOverlay) return 0;
if (leftOverlay) return 1;
if (rightOverlay) return -1;
#endif
return (leftMat.GetHashCode() < rightMat.GetHashCode()) ? -1 : 1;
}
///
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
///
public Bounds CalculateBounds () { return CalculateBounds(null); }
///
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
///
public Bounds CalculateBounds (Transform relativeParent)
{
if (relativeParent == null)
{
Vector3[] corners = localCorners;
Bounds b = new Bounds(corners[0], Vector3.zero);
for (int j = 1; j < 4; ++j) b.Encapsulate(corners[j]);
return b;
}
else
{
Matrix4x4 toLocal = relativeParent.worldToLocalMatrix;
Vector3[] corners = worldCorners;
Bounds b = new Bounds(toLocal.MultiplyPoint3x4(corners[0]), Vector3.zero);
for (int j = 1; j < 4; ++j) b.Encapsulate(toLocal.MultiplyPoint3x4(corners[j]));
return b;
}
}
///
/// Mark the widget as changed so that the geometry can be rebuilt.
///
public void SetDirty ()
{
if (drawCall != null)
{
drawCall.isDirty = true;
}
else if (isVisible && hasVertices)
{
CreatePanel();
}
}
///
/// Remove this widget from the panel.
///
public void RemoveFromPanel ()
{
if (panel != null)
{
panel.RemoveWidget(this);
panel = null;
_panelInstID = 0;
}
drawCall = null;
#if UNITY_EDITOR
mOldTex = null;
mOldShader = null;
#endif
}
#if UNITY_EDITOR
[System.NonSerialized] Texture mOldTex;
[System.NonSerialized] Shader mOldShader;
///
/// This callback is sent inside the editor notifying us that some property has changed.
///
protected override void OnValidate()
{
if (NGUITools.GetActive(this))
{
base.OnValidate();
// Prior to NGUI 2.7.0 width and height was specified as transform's local scale
if ((mWidth == 100 || mWidth == minWidth) &&
(mHeight == 100 || mHeight == minHeight) && cachedTransform.localScale.magnitude > 8f)
{
UpgradeFrom265();
cachedTransform.localScale = Vector3.one;
}
if (mWidth < minWidth) mWidth = minWidth;
if (mHeight < minHeight) mHeight = minHeight;
if (autoResizeBoxCollider) ResizeCollider();
// If the texture is changing, we need to make sure to rebuild the draw calls
if (mOldTex != mainTexture || mOldShader != shader)
{
mOldTex = mainTexture;
mOldShader = shader;
}
aspectRatio = (keepAspectRatio == AspectRatioSource.Free) ?
(float)mWidth / mHeight : Mathf.Max(0.01f, aspectRatio);
if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
{
mWidth = Mathf.RoundToInt(mHeight * aspectRatio);
}
else if (keepAspectRatio == AspectRatioSource.BasedOnWidth)
{
mHeight = Mathf.RoundToInt(mWidth / aspectRatio);
}
if (!Application.isPlaying)
{
if (panel != null)
{
panel.RemoveWidget(this);
panel = null;
_panelInstID = 0;
}
CreatePanel();
}
}
else
{
if (mWidth < minWidth) mWidth = minWidth;
if (mHeight < minHeight) mHeight = minHeight;
}
}
#endif
///
/// Tell the panel responsible for the widget that something has changed and the buffers need to be rebuilt.
///
public virtual void MarkAsChanged ()
{
if (NGUITools.GetActive(this))
{
mChanged = true;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
// If we're in the editor, update the panel right away so its geometry gets updated.
if (panel != null && enabled && NGUITools.GetActive(gameObject) && !mPlayMode)
{
SetDirty();
CheckLayer();
#if UNITY_EDITOR
// Mark the panel as dirty so it gets updated
if (material != null) NGUITools.SetDirty(panel.gameObject);
#endif
}
}
}
#if FUNCELL_MODIFIED
///
/// Ensure we have a panel referencing this widget.
///
public bool CreatePanel ()
{
try
{
bool isActive = false;
try
{
isActive = NGUITools.GetActive(gameObject);
}
catch (System.Exception ex0)
{
Debug.LogError("NGUITools.GetActive::::::" + ID.ToString() + "::::" + ex0.Message);
Debug.LogException(ex0);
return false;
}
if (mStarted && panel == null && enabled && isActive)
{
panel = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != null)
{
mParentFound = false;
panel.AddWidget(this);
CheckLayer();
Invalidate(true);
}
}
}
catch (System.Exception ex1)
{
Debug.LogError("CreatePanel::::::" + ID.ToString() + "::::" + ex1.Message);
Debug.LogException(ex1);
return false;
}
_panelInstID = 0;
if (panel) _panelInstID = panel.GetInstanceID();
return true;
}
#else
///
/// Ensure we have a panel referencing this widget.
///
public UIPanel CreatePanel ()
{
if (mStarted && panel == null && enabled && NGUITools.GetActive(gameObject))
{
panel = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != null)
{
mParentFound = false;
panel.AddWidget(this);
CheckLayer();
Invalidate(true);
}
}
if (panel) _panelInstID = panel.GetInstanceID();
return panel;
}
#endif
///
/// Check to ensure that the widget resides on the same layer as its panel.
///
public void CheckLayer ()
{
if (panel != null && panel.gameObject.layer != gameObject.layer)
{
#if UNITY_EDITOR
Debug.LogWarning("You can't place widgets on a layer different than the UIPanel that manages them.\n" +
"If you want to move widgets to a different layer, parent them to a new panel instead.", this);
#endif
gameObject.layer = panel.gameObject.layer;
}
}
///
/// Checks to ensure that the widget is still parented to the right panel.
///
public override void ParentHasChanged ()
{
base.ParentHasChanged();
if (panel != null)
{
UIPanel p = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != p)
{
RemoveFromPanel();
CreatePanel();
}
}
}
///
/// Remember whether we're in play mode.
///
protected override void Awake ()
{
base.Awake();
mPlayMode = Application.isPlaying;
}
///
/// Mark the widget and the panel as having been changed.
///
protected override void OnInit ()
{
base.OnInit();
RemoveFromPanel();
mMoved = true;
// Prior to NGUI 2.7.0 width and height was specified as transform's local scale
if (mWidth == 100 && mHeight == 100 && cachedTransform.localScale.magnitude > 8f)
{
UpgradeFrom265();
cachedTransform.localScale = Vector3.one;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
#if FUNCELL_MODIFIED
SelfUpdate();
#else
Update();
#endif
}
///
/// Facilitates upgrading from NGUI 2.6.5 or earlier versions.
///
protected virtual void UpgradeFrom265 ()
{
Vector3 scale = cachedTransform.localScale;
mWidth = Mathf.Abs(Mathf.RoundToInt(scale.x));
mHeight = Mathf.Abs(Mathf.RoundToInt(scale.y));
NGUITools.UpdateWidgetCollider(gameObject, true);
}
///
/// Virtual Start() functionality for widgets.
///
protected override void OnStart ()
{
#if UNITY_EDITOR
if (GetComponent() != null)
{
Debug.LogError("Widgets and panels should not be on the same object! Widget must be a child of the panel.", this);
}
else if (!Application.isPlaying && GetComponents().Length > 1)
{
Debug.LogError("You should not place more than one widget on the same object. Weird stuff will happen!", this);
}
#endif
CreatePanel();
}
///
/// Update the anchored edges and ensure the widget is registered with a panel.
///
protected override void OnAnchor ()
{
float lt, bt, rt, tt;
Transform trans = cachedTransform;
Transform parent = trans.parent;
Vector3 pos = trans.localPosition;
Vector2 pvt = pivotOffset;
// Attempt to fast-path if all anchors match
if (leftAnchor.target == bottomAnchor.target &&
leftAnchor.target == rightAnchor.target &&
leftAnchor.target == topAnchor.target)
{
Vector3[] sides = leftAnchor.GetSides(parent);
if (sides != null)
{
lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute;
rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute;
bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute;
tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute;
mIsInFront = true;
}
else
{
// Anchored to a single transform
Vector3 lp = GetLocalPos(leftAnchor, parent);
lt = lp.x + leftAnchor.absolute;
bt = lp.y + bottomAnchor.absolute;
rt = lp.x + rightAnchor.absolute;
tt = lp.y + topAnchor.absolute;
mIsInFront = (!hideIfOffScreen || lp.z >= 0f);
}
}
else
{
mIsInFront = true;
// Left anchor point
if (leftAnchor.target)
{
Vector3[] sides = leftAnchor.GetSides(parent);
if (sides != null)
{
lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute;
}
else
{
lt = GetLocalPos(leftAnchor, parent).x + leftAnchor.absolute;
}
}
else lt = pos.x - pvt.x * mWidth;
// Right anchor point
if (rightAnchor.target)
{
Vector3[] sides = rightAnchor.GetSides(parent);
if (sides != null)
{
rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute;
}
else
{
rt = GetLocalPos(rightAnchor, parent).x + rightAnchor.absolute;
}
}
else rt = pos.x - pvt.x * mWidth + mWidth;
// Bottom anchor point
if (bottomAnchor.target)
{
Vector3[] sides = bottomAnchor.GetSides(parent);
if (sides != null)
{
bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute;
}
else
{
bt = GetLocalPos(bottomAnchor, parent).y + bottomAnchor.absolute;
}
}
else bt = pos.y - pvt.y * mHeight;
// Top anchor point
if (topAnchor.target)
{
Vector3[] sides = topAnchor.GetSides(parent);
if (sides != null)
{
tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute;
}
else
{
tt = GetLocalPos(topAnchor, parent).y + topAnchor.absolute;
}
}
else tt = pos.y - pvt.y * mHeight + mHeight;
}
// Calculate the new position, width and height
Vector3 newPos = new Vector3(Mathf.Lerp(lt, rt, pvt.x), Mathf.Lerp(bt, tt, pvt.y), pos.z);
newPos.x = Mathf.Round(newPos.x);
newPos.y = Mathf.Round(newPos.y);
int w = Mathf.FloorToInt(rt - lt + 0.5f);
int h = Mathf.FloorToInt(tt - bt + 0.5f);
// Maintain the aspect ratio if requested and possible
if (keepAspectRatio != AspectRatioSource.Free && aspectRatio != 0f)
{
if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
{
w = Mathf.RoundToInt(h * aspectRatio);
}
else h = Mathf.RoundToInt(w / aspectRatio);
}
// Don't let the width and height get too small
if (w < minWidth) w = minWidth;
if (h < minHeight) h = minHeight;
// Update the position if it has changed
if (Vector3.SqrMagnitude(pos - newPos) > 0.001f)
{
cachedTransform.localPosition = newPos;
if (mIsInFront) mChanged = true;
}
// Update the width and height if it has changed
if (mWidth != w || mHeight != h)
{
mWidth = w;
mHeight = h;
if (mIsInFront) mChanged = true;
if (autoResizeBoxCollider) ResizeCollider();
}
if (AnchorChanged != null)
{
AnchorChanged(cachedTransform);
}
}
///
/// Ensure we have a panel to work with.
///
protected override bool OnUpdate ()
{
bool result = true;
#if FUNCELL_MODIFIED
//updatealpha
if (_oldAlpha != mColor.a)
{
var frame = Time.frameCount;
_oldAlpha = mColor.a;
var parentAlpha = 1f;
if(parent != null)
{
parentAlpha = parent.finalAlpha;
}
SelfCalculateFinalAlpha(parentAlpha);
}
#endif
/*
if (panel == null) result = CreatePanel();
#if UNITY_EDITOR
else if (!mPlayMode) ParentHasChanged();
#endif
*/
//这里把panel是否为空的判断,修改为通过InstID和异常来判断
try
{
if (_panelInstID == 0)
{
result = CreatePanel();
}
if (_panelInstID != 0)
{
_panelInstID = panel.GetInstanceID();
}
#if UNITY_EDITOR
if (!mPlayMode) ParentHasChanged();
#endif
}
catch
{
result = CreatePanel();
}
return result;
}
#if !UNITY_EDITOR
///
/// Mark the UI as changed when returning from paused state.
///
void OnApplicationPause (bool paused) { if (!paused) MarkAsChanged(); }
#endif
///
/// Clear references.
///
protected override void OnDisable ()
{
geometry.FreeCache();
RemoveFromPanel();
base.OnDisable();
}
///
/// Unregister this widget.
///
//void OnDestroy () { RemoveFromPanel(); }
#if FUNCELL_MODIFIED
//优化NGUI GC
protected override void OnDestroy()
{
geometry.FreeCache();
RemoveFromPanel();
base.OnDestroy();
}
#else
void OnDestroy ()
{
geometry.FreeCache();
RemoveFromPanel();
}
#endif
//优化NGUI GC
//void OnDestroy() { RemoveFromPanel(); }
#if UNITY_EDITOR
static int mHandles = -1;
///
/// Whether widgets will show handles with the Move Tool, or just the View Tool.
///
static public bool showHandlesWithMoveTool
{
get
{
if (mHandles == -1)
{
mHandles = UnityEditor.EditorPrefs.GetInt("NGUI Handles", 1);
}
return (mHandles == 1);
}
set
{
int val = value ? 1 : 0;
if (mHandles != val)
{
mHandles = val;
UnityEditor.EditorPrefs.SetInt("NGUI Handles", mHandles);
}
}
}
///
/// Whether the widget should have some form of handles shown.
///
static public bool showHandles
{
get
{
#if UNITY_4_3 || UNITY_4_5
if (showHandlesWithMoveTool)
{
return UnityEditor.Tools.current == UnityEditor.Tool.Move;
}
return UnityEditor.Tools.current == UnityEditor.Tool.View;
#else
return UnityEditor.Tools.current == UnityEditor.Tool.Rect;
#endif
}
}
///
/// Draw some selectable gizmos.
///
void OnDrawGizmos ()
{
if (isVisible && NGUITools.GetActive(this))
{
if (UnityEditor.Selection.activeGameObject == gameObject && showHandles) return;
Color outline = new Color(1f, 1f, 1f, 0.2f);
float adjustment = (root != null) ? 0.05f : 0.001f;
Vector2 offset = pivotOffset;
Vector3 center = new Vector3(mWidth * (0.5f - offset.x), mHeight * (0.5f - offset.y), -mDepth * adjustment);
Vector3 size = new Vector3(mWidth, mHeight, 1f);
// Draw the gizmo
Gizmos.matrix = cachedTransform.localToWorldMatrix;
Gizmos.color = (UnityEditor.Selection.activeGameObject == cachedTransform) ? Color.white : outline;
Gizmos.DrawWireCube(center, size);
// Make the widget selectable
size.z = 0.01f;
Gizmos.color = Color.clear;
Gizmos.DrawCube(center, size);
}
}
#endif // UNITY_EDITOR
///
/// Update the widget's visibility state.
///
public bool UpdateVisibility (bool visibleByAlpha, bool visibleByPanel)
{
if (mIsVisibleByAlpha != visibleByAlpha || mIsVisibleByPanel != visibleByPanel)
{
mChanged = true;
mIsVisibleByAlpha = visibleByAlpha;
mIsVisibleByPanel = visibleByPanel;
return true;
}
return false;
}
int mMatrixFrame = -1;
Vector3 mOldV0;
Vector3 mOldV1;
///
/// Check to see if the widget has moved relative to the panel that manages it
///
public bool UpdateTransform (int frame)
{
Transform trans = cachedTransform;
#if UNITY_EDITOR
mPlayMode = Application.isPlaying;
if (mMoved || !mPlayMode)
#else
mPlayMode = true;
if (mMoved)
#endif
{
mMoved = true;
mMatrixFrame = -1;
trans.hasChanged = false;
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
mOldV0 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x0, y0, 0f));
mOldV1 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x1, y1, 0f));
}
else if (!panel.widgetsAreStatic && trans.hasChanged)
{
mMatrixFrame = -1;
trans.hasChanged = false;
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
Vector3 v0 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x0, y0, 0f));
Vector3 v1 = panel.worldToLocal.MultiplyPoint3x4(trans.TransformPoint(x1, y1, 0f));
if (Vector3.SqrMagnitude(mOldV0 - v0) > 0.000001f ||
Vector3.SqrMagnitude(mOldV1 - v1) > 0.000001f)
{
mMoved = true;
mOldV0 = v0;
mOldV1 = v1;
}
}
// Notify the listeners
if (mMoved && onChange != null) onChange();
return mMoved || mChanged;
}
///
/// Update the widget and fill its geometry if necessary. Returns whether something was changed.
///
public bool UpdateGeometry (int frame)
{
// Has the alpha changed?
#if FUNCELL_MODIFIED
float finalAlpha = this.finalAlpha;
#else
float finalAlpha = CalculateFinalAlpha(frame);
#endif
var alphaChanged = false;
if (mIsVisibleByAlpha && mLastAlpha != finalAlpha)
{
alphaChanged = true;
}
mLastAlpha = finalAlpha;
if (mChanged)
{
if (mIsVisibleByAlpha && finalAlpha > 0.001f && shader != null)
{
bool hadVertices = geometry.hasVertices;
if (fillGeometry)
{
geometry.Clear();
OnFill(geometry.verts, geometry.uvs, geometry.cols);
}
if (geometry.hasVertices)
{
// Want to see what's being filled? Uncomment this line.
//Debug.Log("Fill " + name + " (" + Time.frameCount + ")");
if (mMatrixFrame != frame)
{
mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
mMatrixFrame = frame;
}
geometry.ApplyTransform(mLocalToPanel, panel.generateNormals,height,width);
mMoved = false;
mChanged = false;
return true;
}
mChanged = false;
return hadVertices;
}
else if (geometry.hasVertices)
{
if (fillGeometry) geometry.Clear();
mMoved = false;
mChanged = false;
return true;
}
}
else if (mMoved && geometry.hasVertices)
{
// Want to see what's being moved? Uncomment this line.
//Debug.Log("Moving " + name + " (" + Time.frameCount + ")");
if (mMatrixFrame != frame)
{
mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
mMatrixFrame = frame;
}
geometry.ApplyTransform(mLocalToPanel, panel.generateNormals,height,width);
mMoved = false;
mChanged = false;
return true;
}
else if(alphaChanged && geometry.hasVertices)
{
mMoved = false;
mChanged = false;
return true;
}
mMoved = false;
mChanged = false;
return false;
}
///
/// Append the local geometry buffers to the specified ones.
///
public void WriteToBuffers (BetterList v, BetterList u, BetterList c, BetterList n, BetterList t)
{
geometry.WriteToBuffers(v, u, c, n, t, finalAlpha);
}
///
/// Make the widget pixel-perfect.
///
virtual public void MakePixelPerfect ()
{
Vector3 pos = cachedTransform.localPosition;
pos.z = Mathf.Round(pos.z);
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
cachedTransform.localPosition = pos;
Vector3 ls = cachedTransform.localScale;
cachedTransform.localScale = new Vector3(Mathf.Sign(ls.x), Mathf.Sign(ls.y), 1f);
}
///
/// Minimum allowed width for this widget.
///
virtual public int minWidth { get { return 2; } }
///
/// Minimum allowed height for this widget.
///
virtual public int minHeight { get { return 2; } }
///
/// Dimensions of the sprite's border, if any.
///
virtual public Vector4 border { get { return Vector4.zero; } set { } }
///
/// Virtual function called by the UIPanel that fills the buffers.
///
virtual public void OnFill(BetterList verts, BetterList uvs, BetterList cols)
{
// Call this in your derived classes:
//if (onPostFill != null)
// onPostFill(this, verts.size, verts, uvs, cols);
}
}