Files
2025-01-25 04:38:09 +08:00

1959 lines
46 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//----------------------------------------------
#define FUNCELL_MODIFIED
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// Base class for all UI components that should be derived from when creating new widget types.
/// </summary>
[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<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols);
/// <summary>
/// Notification triggered when the widget's dimensions or position changes.
/// </summary>
public OnDimensionsChanged onChange;
/// <summary>
/// Notification triggered after the widget's buffer has been filled.
/// </summary>
public OnPostFillCallback onPostFill;
/// <summary>
/// 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.
/// </summary>
public UIDrawCall.OnRenderCallback mOnRender;
/// <summary>
/// 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.
/// </summary>
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;
}
}
}
/// <summary>
/// If set to 'true', the box collider's dimensions will be adjusted to always match the widget whenever it resizes.
/// </summary>
public bool autoResizeBoxCollider = false;
/// <summary>
/// Hide the widget if it happens to be off-screen.
/// </summary>
public bool hideIfOffScreen = false;
public enum AspectRatioSource
{
Free,
BasedOnWidth,
BasedOnHeight,
}
/// <summary>
/// Whether the rectangle will attempt to maintain a specific aspect ratio.
/// </summary>
public AspectRatioSource keepAspectRatio = AspectRatioSource.Free;
/// <summary>
/// If you want the anchored rectangle to keep a specific aspect ratio, set this value.
/// </summary>
public float aspectRatio = 1f;
public delegate bool HitCheck (Vector3 worldPos);
/// <summary>
/// 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.
/// </summary>
public HitCheck hitCheck;
/// <summary>
/// Panel that's managing this widget.
/// </summary>
[System.NonSerialized] public UIPanel panel;
//当前Panel的ID
private int _panelInstID = 0;
/// <summary>
/// Widget's generated geometry.
/// </summary>
[System.NonSerialized] public UIGeometry geometry = new UIGeometry();
/// <summary>
/// If set to 'false', the widget's OnFill function will not be called, letting you define custom geometry at will.
/// </summary>
[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;
/// <summary>
/// Internal usage -- draw call that's drawing the widget.
/// </summary>
[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
/// <summary>
/// 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.
/// </summary>
public Vector4 drawRegion
{
get
{
return mDrawRegion;
}
set
{
if (mDrawRegion != value)
{
mDrawRegion = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
/// <summary>
/// Pivot offset in relative coordinates. Bottom-left is (0, 0). Top-right is (1, 1).
/// </summary>
public Vector2 pivotOffset { get { return NGUIMath.GetPivotOffset(pivot); } }
/// <summary>
/// Widget's width in pixels.
/// </summary>
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);
}
}
}
/// <summary>
/// Widget's height in pixels.
/// </summary>
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);
}
}
}
/// <summary>
/// Color used by the widget.
/// </summary>
public Color color
{
get
{
return mColor;
}
set
{
if (mColor != value)
{
bool alphaChange = (mColor.a != value.a);
mColor = value;
Invalidate(alphaChange);
}
}
}
/// <summary>
/// Widget's alpha -- a convenience method.
/// </summary>
public override float alpha
{
get
{
return mColor.a;
}
set
{
float val = Mathf.Clamp01(value);
if (mColor.a != val)
{
mColor.a = val;
Invalidate(true, true);
}
}
}
/// <summary>
/// Whether the widget is currently visible.
/// </summary>
public bool isVisible { get { return mIsVisibleByPanel && mIsVisibleByAlpha && mIsInFront && finalAlpha > 0.001f && NGUITools.GetActive(this); } }
/// <summary>
/// Whether the widget has vertices to draw.
/// </summary>
public bool hasVertices { get { return geometry != null && geometry.hasVertices; } }
/// <summary>
/// Change the pivot point and do not attempt to keep the widget in the same place by adjusting its transform.
/// </summary>
public Pivot rawPivot
{
get
{
return mPivot;
}
set
{
if (mPivot != value)
{
mPivot = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
/// <summary>
/// Set or get the value that specifies where the widget's pivot point should be.
/// </summary>
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;
}
}
}
/// <summary>
/// Depth controls the rendering order -- lowest to highest.
/// </summary>
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
}
}
}
/// <summary>
/// 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.
/// </summary>
public int raycastDepth
{
get
{
if (panel == null) CreatePanel();
return (panel != null) ? mDepth + panel.depth * 10000 : mDepth;
}
}
/// <summary>
/// Local space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
/// </summary>
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;
}
}
/// <summary>
/// Local width and height of the widget in pixels.
/// </summary>
public virtual Vector2 localSize
{
get
{
Vector3[] cr = localCorners;
return cr[2] - cr[0];
}
}
/// <summary>
/// Widget's center in local coordinates. Don't forget to transform by the widget's transform.
/// </summary>
public Vector3 localCenter
{
get
{
Vector3[] cr = localCorners;
return Vector3.Lerp(cr[0], cr[2], 0.5f);
}
}
/// <summary>
/// World-space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
/// </summary>
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;
}
}
/// <summary>
/// World-space center of the widget.
/// </summary>
public Vector3 worldCenter { get { return cachedTransform.TransformPoint(localCenter); } }
/// <summary>
/// Local space region where the actual drawing will take place.
/// X = left, Y = bottom, Z = right, W = top.
/// </summary>
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));
}
}
/// <summary>
/// Material used by the widget.
/// </summary>
public virtual Material material
{
get
{
return null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no material setter");
}
}
/// <summary>
/// Texture used by the widget.
/// </summary>
public virtual Texture mainTexture
{
get
{
Material mat = material;
return (mat != null) ? mat.mainTexture : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no mainTexture setter");
}
}
/// <summary>
/// Shader is used to create a dynamic material if the widget has no material to work with.
/// </summary>
public virtual Shader shader
{
get
{
Material mat = material;
return (mat != null) ? mat.shader : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no shader setter");
}
}
/// <summary>
/// Do not use this, it's obsolete.
/// </summary>
[System.Obsolete("There is no relative scale anymore. Widgets now have width and height instead")]
public Vector2 relativeSize { get { return Vector2.one; } }
/// <summary>
/// Convenience function that returns 'true' if the widget has a box collider.
/// </summary>
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<Collider>() as BoxCollider;
#endif
if (box != null) return true;
return GetComponent<BoxCollider2D>() != null;
}
}
/// <summary>
/// Adjust the widget's dimensions without going through the anchor validation logic.
/// </summary>
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();
}
}
/// <summary>
/// Get the sides of the rectangle relative to the specified transform.
/// The order is left, top, right, bottom.
/// </summary>
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
/// <summary>
/// Widget's final alpha, after taking the panel's alpha into account.
/// </summary>
///
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;
}
/// <summary>
/// Force-calculate the final alpha value.
/// </summary>
protected void UpdateFinalAlpha (int frameID)
{
if (!mIsVisibleByAlpha || !mIsInFront)
{
finalAlpha = 0f;
}
else
{
UIRect pt = parent;
finalAlpha = (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
}
/// <summary>
/// Update the widget's visibility and final alpha.
/// </summary>
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);
}
}
/// <summary>
/// Same as final alpha, except it doesn't take own visibility into consideration. Used by panels.
/// </summary>
public float CalculateCumulativeAlpha (int frameID)
{
UIRect pt = parent;
return (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
/// <summary>
/// Set the widget's rectangle.
/// </summary>
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
}
}
/// <summary>
/// Adjust the widget's collider size to match the widget's dimensions.
/// </summary>
public void ResizeCollider () { if (this.enabled) NGUITools.UpdateWidgetCollider(gameObject); }
/// <summary>
/// Static widget comparison function used for depth sorting.
/// </summary>
[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;
}
/// <summary>
/// Static widget comparison function used for inter-panel depth sorting.
/// </summary>
[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;
}
/// <summary>
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
/// </summary>
public Bounds CalculateBounds () { return CalculateBounds(null); }
/// <summary>
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
/// </summary>
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;
}
}
/// <summary>
/// Mark the widget as changed so that the geometry can be rebuilt.
/// </summary>
public void SetDirty ()
{
if (drawCall != null)
{
drawCall.isDirty = true;
}
else if (isVisible && hasVertices)
{
CreatePanel();
}
}
/// <summary>
/// Remove this widget from the panel.
/// </summary>
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;
/// <summary>
/// This callback is sent inside the editor notifying us that some property has changed.
/// </summary>
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
/// <summary>
/// Tell the panel responsible for the widget that something has changed and the buffers need to be rebuilt.
/// </summary>
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
/// <summary>
/// Ensure we have a panel referencing this widget.
/// </summary>
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
/// <summary>
/// Ensure we have a panel referencing this widget.
/// </summary>
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
/// <summary>
/// Check to ensure that the widget resides on the same layer as its panel.
/// </summary>
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;
}
}
/// <summary>
/// Checks to ensure that the widget is still parented to the right panel.
/// </summary>
public override void ParentHasChanged ()
{
base.ParentHasChanged();
if (panel != null)
{
UIPanel p = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != p)
{
RemoveFromPanel();
CreatePanel();
}
}
}
/// <summary>
/// Remember whether we're in play mode.
/// </summary>
protected override void Awake ()
{
base.Awake();
mPlayMode = Application.isPlaying;
}
/// <summary>
/// Mark the widget and the panel as having been changed.
/// </summary>
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
}
/// <summary>
/// Facilitates upgrading from NGUI 2.6.5 or earlier versions.
/// </summary>
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);
}
/// <summary>
/// Virtual Start() functionality for widgets.
/// </summary>
protected override void OnStart ()
{
#if UNITY_EDITOR
if (GetComponent<UIPanel>() != 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<UIWidget>().Length > 1)
{
Debug.LogError("You should not place more than one widget on the same object. Weird stuff will happen!", this);
}
#endif
CreatePanel();
}
/// <summary>
/// Update the anchored edges and ensure the widget is registered with a panel.
/// </summary>
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);
}
}
/// <summary>
/// Ensure we have a panel to work with.
/// </summary>
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
/// <summary>
/// Mark the UI as changed when returning from paused state.
/// </summary>
void OnApplicationPause (bool paused) { if (!paused) MarkAsChanged(); }
#endif
/// <summary>
/// Clear references.
/// </summary>
protected override void OnDisable ()
{
geometry.FreeCache();
RemoveFromPanel();
base.OnDisable();
}
/// <summary>
/// Unregister this widget.
/// </summary>
//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;
/// <summary>
/// Whether widgets will show handles with the Move Tool, or just the View Tool.
/// </summary>
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);
}
}
}
/// <summary>
/// Whether the widget should have some form of handles shown.
/// </summary>
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
}
}
/// <summary>
/// Draw some selectable gizmos.
/// </summary>
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
/// <summary>
/// Update the widget's visibility state.
/// </summary>
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;
/// <summary>
/// Check to see if the widget has moved relative to the panel that manages it
/// </summary>
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;
}
/// <summary>
/// Update the widget and fill its geometry if necessary. Returns whether something was changed.
/// </summary>
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;
}
/// <summary>
/// Append the local geometry buffers to the specified ones.
/// </summary>
public void WriteToBuffers (BetterList<Vector3> v, BetterList<Vector2> u, BetterList<Color32> c, BetterList<Vector3> n, BetterList<Vector4> t)
{
geometry.WriteToBuffers(v, u, c, n, t, finalAlpha);
}
/// <summary>
/// Make the widget pixel-perfect.
/// </summary>
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);
}
/// <summary>
/// Minimum allowed width for this widget.
/// </summary>
virtual public int minWidth { get { return 2; } }
/// <summary>
/// Minimum allowed height for this widget.
/// </summary>
virtual public int minHeight { get { return 2; } }
/// <summary>
/// Dimensions of the sprite's border, if any.
/// </summary>
virtual public Vector4 border { get { return Vector4.zero; } set { } }
/// <summary>
/// Virtual function called by the UIPanel that fills the buffers.
/// </summary>
virtual public void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
{
// Call this in your derived classes:
//if (onPostFill != null)
// onPostFill(this, verts.size, verts, uvs, cols);
}
}