NGUI无限滑动
http://www.unity蛮牛.com/blog-9383-1391.html
最近由于工作需要,就开始研究NGUI滑动。刚开始参考NGUI自带的循环滑动,利用隐藏和显示,提高GPU的渲染,但是效果依旧不是很理想。在同事提醒下,添加缓存列表,不断刷新当前裁剪区域里的数据,最总完成了需求。在网上也参考了很多资料,今天恰好闲下来,就拿出来大家分享下,哈哈。代码附上:
主要分为三部分
1.重写UIScrollView和UICustomDragScrollView两个脚本
////////////////////////////////////////////////////////////////////////////////
修改 UIScrollView 为 UICustomScrollView
////////////////////////////////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
/// <summary>
/// 修改 UIScrollView 为 UICustomScrollView
/// </summary>
public class UICustomDragScrollView : MonoBehaviour
{
/// <summary>
/// Reference to the scroll view that will be dragged by the script.
/// </summary>
public UICustomScrollView scrollView;
// Legacy functionality, kept for backwards compatibility. Use 'scrollView' instead.
[HideInInspector][SerializeField] UICustomScrollView draggablePanel;
Transform mTrans;
UICustomScrollView mScroll;
bool mAutoFind = false;
bool mStarted = false;
/// <summary>
/// Automatically find the scroll view if possible.
/// </summary>
void OnEnable ()
{
mTrans = transform;
// Auto-upgrade
if (scrollView == null && draggablePanel != null)
{
scrollView = draggablePanel;
draggablePanel = null;
}
if (mStarted && (mAutoFind || mScroll == null))
FindScrollView();
}
/// <summary>
/// Find the scroll view.
/// </summary>
void Start ()
{
mStarted = true;
FindScrollView();
}
/// <summary>
/// Find the scroll view to work with.
/// </summary>
void FindScrollView ()
{
// If the scroll view is on a parent, don't try to remember it (as we want it to be dynamic in case of re-parenting)
UICustomScrollView sv = NGUITools.FindInParents<UICustomScrollView>(mTrans);
if (scrollView == null)
{
scrollView = sv;
mAutoFind = true;
}
else if (scrollView == sv)
{
mAutoFind = true;
}
mScroll = scrollView;
}
/// <summary>
/// Create a plane on which we will be performing the dragging.
/// </summary>
void OnPress (bool pressed)
{
// If the scroll view has been set manually, don't try to find it again
if (mAutoFind && mScroll != scrollView)
{
mScroll = scrollView;
mAutoFind = false;
}
if (scrollView && enabled && NGUITools.GetActive(gameObject))
{
scrollView.Press(pressed);
if (!pressed && mAutoFind)
{
scrollView = NGUITools.FindInParents<UICustomScrollView>(mTrans);
mScroll = scrollView;
}
}
}
/// <summary>
/// Drag the object along the plane.
/// </summary>
void OnDrag (Vector2 delta)
{
if (scrollView && NGUITools.GetActive(this))
scrollView.Drag();
}
/// <summary>
/// If the object should support the scroll wheel, do it.
/// </summary>
void OnScroll (float delta)
{
if (scrollView && NGUITools.GetActive(this))
scrollView.Scroll(delta);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
重写UICustomDragScrollView
////////////////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2014 Tasharen Entertainment
//----------------------------------------------
using UnityEngine;
/// <summary>
/// This script, when attached to a panel turns it into a scroll view.
/// You can then attach UIDragScrollView to colliders within to make it draggable.
/// </summary>
public class UICustomScrollView : MonoBehaviour
{
static public BetterList<UICustomScrollView> list = new BetterList<UICustomScrollView>();
public enum Movement
{
Horizontal,
Vertical,
Unrestricted,
Custom,
}
public enum DragEffect
{
None,
Momentum,
MomentumAndSpring,
}
public enum ShowCondition
{
Always,
OnlyIfNeeded,
WhenDragging,
}
public delegate void OnDragFinished ();
/// <summary>
/// Type of movement allowed by the scroll view.
/// </summary>
public Movement movement = Movement.Horizontal;
/// <summary>
/// Effect to apply when dragging.
/// </summary>
public DragEffect dragEffect = DragEffect.MomentumAndSpring;
/// <summary>
/// Whether the dragging will be restricted to be within the scroll view's bounds.
/// </summary>
public bool restrictWithinPanel = true;
/// <summary>
/// Whether dragging will be disabled if the contents fit.
/// </summary>
public bool disableDragIfFits = false;
/// <summary>
/// Whether the drag operation will be started smoothly, or if if it will be precise (but will have a noticeable "jump").
/// </summary>
public bool smoothDragStart = true;
/// <summary>
/// Whether to use iOS drag emulation, where the content only drags at half the speed of the touch/mouse movement when the content edge is within the clipping area.
/// </summary>
public bool iOSDragEmulation = true;
/// <summary>
/// Effect the scroll wheel will have on the momentum.
/// </summary>
public float scrollWheelFactor = 0.25f;
/// <summary>
/// How much momentum gets applied when the press is released after dragging.
/// </summary>
public float momentumAmount = 35f;
/// <summary>
/// Horizontal scrollbar used for visualization.
/// </summary>
public UIProgressBar horizontalScrollBar;
/// <summary>
/// Vertical scrollbar used for visualization.
/// </summary>
public UIProgressBar verticalScrollBar;
/// <summary>
/// Condition that must be met for the scroll bars to become visible.
/// </summary>
public ShowCondition showScrollBars = ShowCondition.OnlyIfNeeded;
/// <summary>
/// Custom movement, if the 'movement' field is set to 'Custom'.
/// </summary>
public Vector2 customMovement = new Vector2(1f, 0f);
/// <summary>
/// Content's pivot point -- where it originates from by default.
/// </summary>
public UIWidget.Pivot contentPivot = UIWidget.Pivot.TopLeft;
/// <summary>
/// Event callback to trigger when the drag process finished. Can be used for additional effects, such as centering on some object.
/// </summary>
public OnDragFinished onDragFinished;
// Deprecated functionality. Use 'movement' instead.
[HideInInspector][SerializeField] Vector3 scale = new Vector3(1f, 0f, 0f);
// Deprecated functionality. Use 'contentPivot' instead.
[SerializeField][HideInInspector] Vector2 relativePositionOnReset = Vector2.zero;
protected Transform mTrans;
protected UIPanel mPanel;
protected Plane mPlane;
protected Vector3 mLastPos;
protected bool mPressed = false;
protected Vector3 mMomentum = Vector3.zero;
protected float mScroll = 0f;
protected Bounds mBounds;
protected bool mCalculatedBounds = false;
protected bool mShouldMove = false;
protected bool mIgnoreCallbacks = false;
protected int mDragID = -10;
protected Vector2 mDragStartOffset = Vector2.zero;
protected bool mDragStarted = false;
/// <summary>
/// Panel that's being dragged.
/// </summary>
public UIPanel panel { get { return mPanel; } }
/// <summary>
/// Whether the scroll view is being dragged.
/// </summary>
public bool isDragging { get { return mPressed && mDragStarted; } }
/// <summary>
/// Calculate the bounds used by the widgets.
/// </summary>
public virtual Bounds bounds
{
get
{
//if (!mCalculatedBounds)
//{
// mCalculatedBounds = true;
// mTrans = transform;
// mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans, true);
//}
return mBounds;
}
set
{
mBounds = value;
}
}
/// <summary>
/// Whether the scroll view can move horizontally.
/// </summary>
public bool canMoveHorizontally
{
get
{
return movement == Movement.Horizontal ||
movement == Movement.Unrestricted ||
(movement == Movement.Custom && customMovement.x != 0f);
}
}
/// <summary>
/// Whether the scroll view can move vertically.
/// </summary>
public bool canMoveVertically
{
get
{
return movement == Movement.Vertical ||
movement == Movement.Unrestricted ||
(movement == Movement.Custom && customMovement.y != 0f);
}
}
/// <summary>
/// Whether the scroll view should be able to move horizontally (contents don't fit).
/// </summary>
public virtual bool shouldMoveHorizontally
{
get
{
float size = bounds.size.x;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.x * 2f;
return Mathf.RoundToInt(size - mPanel.width) > 0;
}
}
/// <summary>
/// Whether the scroll view should be able to move vertically (contents don't fit).
/// </summary>
public virtual bool shouldMoveVertically
{
get
{
float size = bounds.size.y;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.y * 2f;
return Mathf.RoundToInt(size - mPanel.height) > 0;
}
}
/// <summary>
/// Whether the contents of the scroll view should actually be draggable depends on whether they currently fit or not.
/// </summary>
protected virtual bool shouldMove
{
get
{
if (!disableDragIfFits) return true;
if (mPanel == null) mPanel = GetComponent<UIPanel>();
Vector4 clip = mPanel.finalClipRegion;
Bounds b = bounds;
float hx = (clip.z == 0f) ? Screen.width : clip.z * 0.5f;
float hy = (clip.w == 0f) ? Screen.height : clip.w * 0.5f;
if (canMoveHorizontally)
{
if (b.min.x < clip.x - hx) return true;
if (b.max.x > clip.x + hx) return true;
}
if (canMoveVertically)
{
if (b.min.y < clip.y - hy) return true;
if (b.max.y > clip.y + hy) return true;
}
return false;
}
}
/// <summary>
/// Current momentum, exposed just in case it's needed.
/// </summary>
public Vector3 currentMomentum { get { return mMomentum; } set { mMomentum = value; mShouldMove = true; } }
/// <summary>
/// Cache the transform and the panel.
/// </summary>
void Awake ()
{
mTrans = transform;
mPanel = GetComponent<UIPanel>();
if (mPanel.clipping == UIDrawCall.Clipping.None)
mPanel.clipping = UIDrawCall.Clipping.ConstrainButDontClip;
// Auto-upgrade
if (movement != Movement.Custom && scale.sqrMagnitude > 0.001f)
{
if (scale.x == 1f && scale.y == 0f)
{
movement = Movement.Horizontal;
}
else if (scale.x == 0f && scale.y == 1f)
{
movement = Movement.Vertical;
}
else if (scale.x == 1f && scale.y == 1f)
{
movement = Movement.Unrestricted;
}
else
{
movement = Movement.Custom;
customMovement.x = scale.x;
customMovement.y = scale.y;
}
scale = Vector3.zero;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
// Auto-upgrade
if (contentPivot == UIWidget.Pivot.TopLeft && relativePositionOnReset != Vector2.zero)
{
contentPivot = NGUIMath.GetPivot(new Vector2(relativePositionOnReset.x, 1f - relativePositionOnReset.y));
relativePositionOnReset = Vector2.zero;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
void OnEnable () { list.Add(this); }
void OnDisable () { list.Remove(this); }
/// <summary>
/// Set the initial drag value and register the listener delegates.
/// </summary>
protected virtual void Start ()
{
//UpdatePosition();
if (Application.isPlaying)
{
if (horizontalScrollBar != null)
{
EventDelegate.Add(horizontalScrollBar.onChange, OnScrollBar);
horizontalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveHorizontally) ? 1f : 0f;
}
if (verticalScrollBar != null)
{
EventDelegate.Add(verticalScrollBar.onChange, OnScrollBar);
verticalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveVertically) ? 1f : 0f;
}
}
}
/// <summary>
/// Restrict the scroll view's contents to be within the scroll view's bounds.
/// </summary>
public bool RestrictWithinBounds (bool instant) { return RestrictWithinBounds(instant, true, true); }
/// <summary>
/// Restrict the scroll view's contents to be within the scroll view's bounds.
/// </summary>
public bool RestrictWithinBounds (bool instant, bool horizontal, bool vertical)
{
Bounds b = bounds;
Vector3 constraint = mPanel.CalculateConstrainOffset(b.min, b.max);
if (!horizontal) constraint.x = 0f;
if (!vertical) constraint.y = 0f;
if (constraint.sqrMagnitude > 1f)
{
if (!instant && dragEffect == DragEffect.MomentumAndSpring)
{
// Spring back into place
Vector3 pos = mTrans.localPosition + constraint;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
SpringPanel.Begin(mPanel.gameObject, pos, 13f);
}
else
{
// Jump back into place
MoveRelative(constraint);
mMomentum = Vector3.zero;
mScroll = 0f;
}
return true;
}
return false;
}
/// <summary>
/// Disable the spring movement.
/// </summary>
public void DisableSpring ()
{
SpringPanel sp = GetComponent<SpringPanel>();
if (sp != null) sp.enabled = false;
}
/// <summary>
/// Update the values of the associated scroll bars.
/// </summary>
public void UpdateScrollbars () { UpdateScrollbars(true); }
/// <summary>
/// Update the values of the associated scroll bars.
/// </summary>
public virtual void UpdateScrollbars (bool recalculateBounds)
{
if (mPanel == null) return;
if (horizontalScrollBar != null || verticalScrollBar != null)
{
if (recalculateBounds)
{
mCalculatedBounds = false;
mShouldMove = shouldMove;
}
Bounds b = bounds;
Vector2 bmin = b.min;
Vector2 bmax = b.max;
if (horizontalScrollBar != null && bmax.x > bmin.x)
{
Vector4 clip = mPanel.finalClipRegion;
int intViewSize = Mathf.RoundToInt(clip.z);
if ((intViewSize & 1) != 0) intViewSize -= 1;
float halfViewSize = intViewSize * 0.5f;
halfViewSize = Mathf.Round(halfViewSize);
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
halfViewSize -= mPanel.clipSoftness.x;
float contentSize = bmax.x - bmin.x;
float viewSize = halfViewSize * 2f;
float contentMin = bmin.x;
float contentMax = bmax.x;
float viewMin = clip.x - halfViewSize;
float viewMax = clip.x + halfViewSize;
contentMin = viewMin - contentMin;
contentMax = contentMax - viewMax;
UpdateScrollbars(horizontalScrollBar, contentMin, contentMax, contentSize, viewSize, false);
}
if (verticalScrollBar != null && bmax.y > bmin.y)
{
Vector4 clip = mPanel.finalClipRegion;
int intViewSize = Mathf.RoundToInt(clip.w);
if ((intViewSize & 1) != 0) intViewSize -= 1;
float halfViewSize = intViewSize * 0.5f;
halfViewSize = Mathf.Round(halfViewSize);
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
halfViewSize -= mPanel.clipSoftness.y;
float contentSize = bmax.y - bmin.y;
float viewSize = halfViewSize * 2f;
float contentMin = bmin.y;
float contentMax = bmax.y;
float viewMin = clip.y - halfViewSize;
float viewMax = clip.y + halfViewSize;
contentMin = viewMin - contentMin;
contentMax = contentMax - viewMax;
UpdateScrollbars(verticalScrollBar, contentMin, contentMax, contentSize, viewSize, true);
}
}
else if (recalculateBounds)
{
mCalculatedBounds = false;
}
}
/// <summary>
/// Helper function used in UpdateScrollbars(float) function above.
/// </summary>
protected void UpdateScrollbars (UIProgressBar slider, float contentMin, float contentMax, float contentSize, float viewSize, bool inverted)
{
if (slider == null) return;
mIgnoreCallbacks = true;
{
float contentPadding;
if (viewSize < contentSize)
{
contentMin = Mathf.Clamp01(contentMin / contentSize);
contentMax = Mathf.Clamp01(contentMax / contentSize);
contentPadding = contentMin + contentMax;
slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);
}
else
{
contentMin = Mathf.Clamp01(-contentMin / contentSize);
contentMax = Mathf.Clamp01(-contentMax / contentSize);
contentPadding = contentMin + contentMax;
slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);
if (contentSize > 0)
{
contentMin = Mathf.Clamp01(contentMin / contentSize);
contentMax = Mathf.Clamp01(contentMax / contentSize);
contentPadding = contentMin + contentMax;
}
}
UIScrollBar sb = slider as UIScrollBar;
if (sb != null) sb.barSize = 1f - contentPadding;
}
mIgnoreCallbacks = false;
}
/// <summary>
/// Changes the drag amount of the scroll view to the specified 0-1 range values.
/// (0, 0) is the top-left corner, (1, 1) is the bottom-right.
/// </summary>
public virtual void SetDragAmount (float x, float y, bool updateScrollbars)
{
if (mPanel == null) mPanel = GetComponent<UIPanel>();
DisableSpring();
Bounds b = bounds;
if (b.min.x == b.max.x || b.min.y == b.max.y) return;
Vector4 clip = mPanel.finalClipRegion;
float hx = clip.z * 0.5f;
float hy = clip.w * 0.5f;
float left = b.min.x + hx;
float right = b.max.x - hx;
float bottom = b.min.y + hy;
float top = b.max.y - hy;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
{
left -= mPanel.clipSoftness.x;
right += mPanel.clipSoftness.x;
bottom -= mPanel.clipSoftness.y;
top += mPanel.clipSoftness.y;
}
// Calculate the offset based on the scroll value
float ox = Mathf.Lerp(left, right, x);
float oy = Mathf.Lerp(top, bottom, y);
// Update the position
if (!updateScrollbars)
{
Vector3 pos = mTrans.localPosition;
if (canMoveHorizontally) pos.x += clip.x - ox;
if (canMoveVertically) pos.y += clip.y - oy;
mTrans.localPosition = pos;
}
if (canMoveHorizontally) clip.x = ox;
if (canMoveVertically) clip.y = oy;
// Update the clipping offset
Vector4 cr = mPanel.baseClipRegion;
mPanel.clipOffset = new Vector2(clip.x - cr.x, clip.y - cr.y);
// Update the scrollbars, reflecting this change
if (updateScrollbars) UpdateScrollbars(mDragID == -10);
}
/// <summary>
/// Reset the scroll view's position to the top-left corner.
/// It's recommended to call this function before AND after you re-populate the scroll view's contents (ex: switching window tabs).
/// Another option is to populate the scroll view's contents, reset its position, then call this function to reposition the clipping.
/// </summary>
[ContextMenu("Reset Clipping Position")]
public void ResetPosition()
{
if (NGUITools.GetActive(this))
{
// Invalidate the bounds
mCalculatedBounds = false;
Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);
// First move the position back to where it would be if the scroll bars got reset to zero
SetDragAmount(pv.x, 1f - pv.y, false);
// Next move the clipping area back and update the scroll bars
SetDragAmount(pv.x, 1f - pv.y, true);
}
}
/// <summary>
/// Call this function after you adjust the scroll view's bounds if you want it to maintain the current scrolled position
/// </summary>
public void UpdatePosition ()
{
if (!mIgnoreCallbacks && (horizontalScrollBar != null || verticalScrollBar != null))
{
mIgnoreCallbacks = true;
mCalculatedBounds = false;
Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);
float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : pv.x;
float y = (verticalScrollBar != null) ? verticalScrollBar.value : 1f - pv.y;
SetDragAmount(x, y, false);
UpdateScrollbars(true);
mIgnoreCallbacks = false;
}
}
/// <summary>
/// Triggered by the scroll bars when they change.
/// </summary>
public void OnScrollBar ()
{
if (!mIgnoreCallbacks)
{
mIgnoreCallbacks = true;
float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : 0f;
float y = (verticalScrollBar != null) ? verticalScrollBar.value : 0f;
SetDragAmount(x, y, false);
mIgnoreCallbacks = false;
}
}
/// <summary>
/// Move the scroll view by the specified amount.
/// </summary>
public virtual void MoveRelative (Vector3 relative)
{
mTrans.localPosition += relative;
Vector2 co = mPanel.clipOffset;
co.x -= relative.x;
co.y -= relative.y;
mPanel.clipOffset = co;
// Update the scroll bars
UpdateScrollbars(false);
}
/// <summary>
/// Move the scroll view by the specified amount.
/// </summary>
public void MoveAbsolute (Vector3 absolute)
{
Vector3 a = mTrans.InverseTransformPoint(absolute);
Vector3 b = mTrans.InverseTransformPoint(Vector3.zero);
MoveRelative(a - b);
}
/// <summary>
/// Create a plane on which we will be performing the dragging.
/// </summary>
public void Press (bool pressed)
{
if (smoothDragStart && pressed)
{
mDragStarted = false;
mDragStartOffset = Vector2.zero;
}
if (enabled && NGUITools.GetActive(gameObject))
{
if (!pressed && mDragID == UICamera.currentTouchID) mDragID = -10;
mCalculatedBounds = false;
mShouldMove = shouldMove;
if (!mShouldMove) return;
mPressed = pressed;
if (pressed)
{
// Remove all momentum on press
mMomentum = Vector3.zero;
mScroll = 0f;
// Disable the spring movement
DisableSpring();
// Remember the hit position
mLastPos = UICamera.lastHit.point;
// Create the plane to drag along
mPlane = new Plane(mTrans.rotation * Vector3.back, mLastPos);
// Ensure that we're working with whole numbers, keeping everything pixel-perfect
Vector2 co = mPanel.clipOffset;
co.x = Mathf.Round(co.x);
co.y = Mathf.Round(co.y);
mPanel.clipOffset = co;
Vector3 v = mTrans.localPosition;
v.x = Mathf.Round(v.x);
v.y = Mathf.Round(v.y);
mTrans.localPosition = v;
}
else
{
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None && dragEffect == DragEffect.MomentumAndSpring)
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
if (onDragFinished != null)
onDragFinished();
}
}
}
/// <summary>
/// Drag the object along the plane.
/// </summary>
public void Drag ()
{
if (enabled && NGUITools.GetActive(gameObject) && mShouldMove)
{
if (mDragID == -10) mDragID = UICamera.currentTouchID;
UICamera.currentTouch.clickNotification = UICamera.ClickNotification.BasedOnDelta;
// Prevents the drag "jump". Contributed by 'mixd' from the Tasharen forums.
if (smoothDragStart && !mDragStarted)
{
mDragStarted = true;
mDragStartOffset = UICamera.currentTouch.totalDelta;
}
Ray ray = smoothDragStart ?
UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos - mDragStartOffset) :
UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos);
float dist = 0f;
if (mPlane.Raycast(ray, out dist))
{
Vector3 currentPos = ray.GetPoint(dist);
Vector3 offset = currentPos - mLastPos;
mLastPos = currentPos;
if (offset.x != 0f || offset.y != 0f || offset.z != 0f)
{
offset = mTrans.InverseTransformDirection(offset);
if (movement == Movement.Horizontal)
{
offset.y = 0f;
offset.z = 0f;
}
else if (movement == Movement.Vertical)
{
offset.x = 0f;
offset.z = 0f;
}
else if (movement == Movement.Unrestricted)
{
offset.z = 0f;
}
else
{
offset.Scale((Vector3)customMovement);
}
offset = mTrans.TransformDirection(offset);
}
// Adjust the momentum
mMomentum = Vector3.Lerp(mMomentum, mMomentum + offset * (0.01f * momentumAmount), 0.67f);
// Move the scroll view
if (!iOSDragEmulation || dragEffect != DragEffect.MomentumAndSpring)
{
MoveAbsolute(offset);
}
else
{
Vector3 constraint = mPanel.CalculateConstrainOffset(bounds.min, bounds.max);
if (constraint.magnitude > 1f)
{
MoveAbsolute(offset * 0.5f);
mMomentum *= 0.5f;
}
else
{
MoveAbsolute(offset);
}
}
// We want to constrain the UI to be within bounds
if (restrictWithinPanel &&
mPanel.clipping != UIDrawCall.Clipping.None &&
dragEffect != DragEffect.MomentumAndSpring)
{
RestrictWithinBounds(true, canMoveHorizontally, canMoveVertically);
}
}
}
}
/// <summary>
/// If the object should support the scroll wheel, do it.
/// </summary>
public void Scroll (float delta)
{
if (enabled && NGUITools.GetActive(gameObject) && scrollWheelFactor != 0f)
{
DisableSpring();
mShouldMove = shouldMove;
if (Mathf.Sign(mScroll) != Mathf.Sign(delta)) mScroll = 0f;
mScroll += delta * scrollWheelFactor;
}
}
/// <summary>
/// Apply the dragging momentum.
/// </summary>
void LateUpdate ()
{
if (!Application.isPlaying) return;
float delta = RealTime.deltaTime;
// Fade the scroll bars if needed
if (showScrollBars != ShowCondition.Always && (verticalScrollBar || horizontalScrollBar))
{
bool vertical = false;
bool horizontal = false;
if (showScrollBars != ShowCondition.WhenDragging || mDragID != -10 || mMomentum.magnitude > 0.01f)
{
vertical = shouldMoveVertically;
horizontal = shouldMoveHorizontally;
}
if (verticalScrollBar)
{
float alpha = verticalScrollBar.alpha;
alpha += vertical ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (verticalScrollBar.alpha != alpha) verticalScrollBar.alpha = alpha;
}
if (horizontalScrollBar)
{
float alpha = horizontalScrollBar.alpha;
alpha += horizontal ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (horizontalScrollBar.alpha != alpha) horizontalScrollBar.alpha = alpha;
}
}
// Apply momentum
if (mShouldMove && !mPressed)
{
if (movement == Movement.Horizontal)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, 0f, 0f));
}
else if (movement == Movement.Vertical)
{
mMomentum -= mTrans.TransformDirection(new Vector3(0f, mScroll * 0.05f, 0f));
}
else if (movement == Movement.Unrestricted)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, mScroll * 0.05f, 0f));
}
else
{
mMomentum -= mTrans.TransformDirection(new Vector3(
mScroll * customMovement.x * 0.05f,
mScroll * customMovement.y * 0.05f, 0f));
}
if (mMomentum.magnitude > 0.0001f)
{
mScroll = NGUIMath.SpringLerp(mScroll, 0f, 20f, delta);
// Move the scroll view
Vector3 offset = NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
MoveAbsolute(offset);
// Restrict the contents to be within the scroll view's bounds
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
if (mMomentum.magnitude < 0.0001f && onDragFinished != null)
onDragFinished();
return;
}
else
{
mScroll = 0f;
mMomentum = Vector3.zero;
}
}
else mScroll = 0f;
// Dampen the momentum
NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
}
#if UNITY_EDITOR
/// <summary>
/// Draw a visible orange outline of the bounds.
/// </summary>
void OnDrawGizmos ()
{
if (mPanel != null)
{
if (!Application.isPlaying) mCalculatedBounds = false;
Bounds b = bounds;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = new Color(1f, 0.4f, 0f);
Gizmos.DrawWireCube(new Vector3(b.center.x, b.center.y, b.min.z), new Vector3(b.size.x, b.size.y, 0f));
}
}
#endif
}
////////////////////////////////////////////////////////////////////////////////
修改 UIScrollView 为 UICustomScrollView
////////////////////////////////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
/// <summary>
/// 修改 UIScrollView 为 UICustomScrollView
/// </summary>
public class UICustomDragScrollView : MonoBehaviour
{
/// <summary>
/// Reference to the scroll view that will be dragged by the script.
/// </summary>
public UICustomScrollView scrollView;
// Legacy functionality, kept for backwards compatibility. Use 'scrollView' instead.
[HideInInspector][SerializeField] UICustomScrollView draggablePanel;
Transform mTrans;
UICustomScrollView mScroll;
bool mAutoFind = false;
bool mStarted = false;
/// <summary>
/// Automatically find the scroll view if possible.
/// </summary>
void OnEnable ()
{
mTrans = transform;
// Auto-upgrade
if (scrollView == null && draggablePanel != null)
{
scrollView = draggablePanel;
draggablePanel = null;
}
if (mStarted && (mAutoFind || mScroll == null))
FindScrollView();
}
/// <summary>
/// Find the scroll view.
/// </summary>
void Start ()
{
mStarted = true;
FindScrollView();
}
/// <summary>
/// Find the scroll view to work with.
/// </summary>
void FindScrollView ()
{
// If the scroll view is on a parent, don't try to remember it (as we want it to be dynamic in case of re-parenting)
UICustomScrollView sv = NGUITools.FindInParents<UICustomScrollView>(mTrans);
if (scrollView == null)
{
scrollView = sv;
mAutoFind = true;
}
else if (scrollView == sv)
{
mAutoFind = true;
}
mScroll = scrollView;
}
/// <summary>
/// Create a plane on which we will be performing the dragging.
/// </summary>
void OnPress (bool pressed)
{
// If the scroll view has been set manually, don't try to find it again
if (mAutoFind && mScroll != scrollView)
{
mScroll = scrollView;
mAutoFind = false;
}
if (scrollView && enabled && NGUITools.GetActive(gameObject))
{
scrollView.Press(pressed);
if (!pressed && mAutoFind)
{
scrollView = NGUITools.FindInParents<UICustomScrollView>(mTrans);
mScroll = scrollView;
}
}
}
/// <summary>
/// Drag the object along the plane.
/// </summary>
void OnDrag (Vector2 delta)
{
if (scrollView && NGUITools.GetActive(this))
scrollView.Drag();
}
/// <summary>
/// If the object should support the scroll wheel, do it.
/// </summary>
void OnScroll (float delta)
{
if (scrollView && NGUITools.GetActive(this))
scrollView.Scroll(delta);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
重写UICustomDragScrollView
////////////////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2014 Tasharen Entertainment
//----------------------------------------------
using UnityEngine;
/// <summary>
/// This script, when attached to a panel turns it into a scroll view.
/// You can then attach UIDragScrollView to colliders within to make it draggable.
/// </summary>
public class UICustomScrollView : MonoBehaviour
{
static public BetterList<UICustomScrollView> list = new BetterList<UICustomScrollView>();
public enum Movement
{
Horizontal,
Vertical,
Unrestricted,
Custom,
}
public enum DragEffect
{
None,
Momentum,
MomentumAndSpring,
}
public enum ShowCondition
{
Always,
OnlyIfNeeded,
WhenDragging,
}
public delegate void OnDragFinished ();
/// <summary>
/// Type of movement allowed by the scroll view.
/// </summary>
public Movement movement = Movement.Horizontal;
/// <summary>
/// Effect to apply when dragging.
/// </summary>
public DragEffect dragEffect = DragEffect.MomentumAndSpring;
/// <summary>
/// Whether the dragging will be restricted to be within the scroll view's bounds.
/// </summary>
public bool restrictWithinPanel = true;
/// <summary>
/// Whether dragging will be disabled if the contents fit.
/// </summary>
public bool disableDragIfFits = false;
/// <summary>
/// Whether the drag operation will be started smoothly, or if if it will be precise (but will have a noticeable "jump").
/// </summary>
public bool smoothDragStart = true;
/// <summary>
/// Whether to use iOS drag emulation, where the content only drags at half the speed of the touch/mouse movement when the content edge is within the clipping area.
/// </summary>
public bool iOSDragEmulation = true;
/// <summary>
/// Effect the scroll wheel will have on the momentum.
/// </summary>
public float scrollWheelFactor = 0.25f;
/// <summary>
/// How much momentum gets applied when the press is released after dragging.
/// </summary>
public float momentumAmount = 35f;
/// <summary>
/// Horizontal scrollbar used for visualization.
/// </summary>
public UIProgressBar horizontalScrollBar;
/// <summary>
/// Vertical scrollbar used for visualization.
/// </summary>
public UIProgressBar verticalScrollBar;
/// <summary>
/// Condition that must be met for the scroll bars to become visible.
/// </summary>
public ShowCondition showScrollBars = ShowCondition.OnlyIfNeeded;
/// <summary>
/// Custom movement, if the 'movement' field is set to 'Custom'.
/// </summary>
public Vector2 customMovement = new Vector2(1f, 0f);
/// <summary>
/// Content's pivot point -- where it originates from by default.
/// </summary>
public UIWidget.Pivot contentPivot = UIWidget.Pivot.TopLeft;
/// <summary>
/// Event callback to trigger when the drag process finished. Can be used for additional effects, such as centering on some object.
/// </summary>
public OnDragFinished onDragFinished;
// Deprecated functionality. Use 'movement' instead.
[HideInInspector][SerializeField] Vector3 scale = new Vector3(1f, 0f, 0f);
// Deprecated functionality. Use 'contentPivot' instead.
[SerializeField][HideInInspector] Vector2 relativePositionOnReset = Vector2.zero;
protected Transform mTrans;
protected UIPanel mPanel;
protected Plane mPlane;
protected Vector3 mLastPos;
protected bool mPressed = false;
protected Vector3 mMomentum = Vector3.zero;
protected float mScroll = 0f;
protected Bounds mBounds;
protected bool mCalculatedBounds = false;
protected bool mShouldMove = false;
protected bool mIgnoreCallbacks = false;
protected int mDragID = -10;
protected Vector2 mDragStartOffset = Vector2.zero;
protected bool mDragStarted = false;
/// <summary>
/// Panel that's being dragged.
/// </summary>
public UIPanel panel { get { return mPanel; } }
/// <summary>
/// Whether the scroll view is being dragged.
/// </summary>
public bool isDragging { get { return mPressed && mDragStarted; } }
/// <summary>
/// Calculate the bounds used by the widgets.
/// </summary>
public virtual Bounds bounds
{
get
{
//if (!mCalculatedBounds)
//{
// mCalculatedBounds = true;
// mTrans = transform;
// mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans, true);
//}
return mBounds;
}
set
{
mBounds = value;
}
}
/// <summary>
/// Whether the scroll view can move horizontally.
/// </summary>
public bool canMoveHorizontally
{
get
{
return movement == Movement.Horizontal ||
movement == Movement.Unrestricted ||
(movement == Movement.Custom && customMovement.x != 0f);
}
}
/// <summary>
/// Whether the scroll view can move vertically.
/// </summary>
public bool canMoveVertically
{
get
{
return movement == Movement.Vertical ||
movement == Movement.Unrestricted ||
(movement == Movement.Custom && customMovement.y != 0f);
}
}
/// <summary>
/// Whether the scroll view should be able to move horizontally (contents don't fit).
/// </summary>
public virtual bool shouldMoveHorizontally
{
get
{
float size = bounds.size.x;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.x * 2f;
return Mathf.RoundToInt(size - mPanel.width) > 0;
}
}
/// <summary>
/// Whether the scroll view should be able to move vertically (contents don't fit).
/// </summary>
public virtual bool shouldMoveVertically
{
get
{
float size = bounds.size.y;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip) size += mPanel.clipSoftness.y * 2f;
return Mathf.RoundToInt(size - mPanel.height) > 0;
}
}
/// <summary>
/// Whether the contents of the scroll view should actually be draggable depends on whether they currently fit or not.
/// </summary>
protected virtual bool shouldMove
{
get
{
if (!disableDragIfFits) return true;
if (mPanel == null) mPanel = GetComponent<UIPanel>();
Vector4 clip = mPanel.finalClipRegion;
Bounds b = bounds;
float hx = (clip.z == 0f) ? Screen.width : clip.z * 0.5f;
float hy = (clip.w == 0f) ? Screen.height : clip.w * 0.5f;
if (canMoveHorizontally)
{
if (b.min.x < clip.x - hx) return true;
if (b.max.x > clip.x + hx) return true;
}
if (canMoveVertically)
{
if (b.min.y < clip.y - hy) return true;
if (b.max.y > clip.y + hy) return true;
}
return false;
}
}
/// <summary>
/// Current momentum, exposed just in case it's needed.
/// </summary>
public Vector3 currentMomentum { get { return mMomentum; } set { mMomentum = value; mShouldMove = true; } }
/// <summary>
/// Cache the transform and the panel.
/// </summary>
void Awake ()
{
mTrans = transform;
mPanel = GetComponent<UIPanel>();
if (mPanel.clipping == UIDrawCall.Clipping.None)
mPanel.clipping = UIDrawCall.Clipping.ConstrainButDontClip;
// Auto-upgrade
if (movement != Movement.Custom && scale.sqrMagnitude > 0.001f)
{
if (scale.x == 1f && scale.y == 0f)
{
movement = Movement.Horizontal;
}
else if (scale.x == 0f && scale.y == 1f)
{
movement = Movement.Vertical;
}
else if (scale.x == 1f && scale.y == 1f)
{
movement = Movement.Unrestricted;
}
else
{
movement = Movement.Custom;
customMovement.x = scale.x;
customMovement.y = scale.y;
}
scale = Vector3.zero;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
// Auto-upgrade
if (contentPivot == UIWidget.Pivot.TopLeft && relativePositionOnReset != Vector2.zero)
{
contentPivot = NGUIMath.GetPivot(new Vector2(relativePositionOnReset.x, 1f - relativePositionOnReset.y));
relativePositionOnReset = Vector2.zero;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
void OnEnable () { list.Add(this); }
void OnDisable () { list.Remove(this); }
/// <summary>
/// Set the initial drag value and register the listener delegates.
/// </summary>
protected virtual void Start ()
{
//UpdatePosition();
if (Application.isPlaying)
{
if (horizontalScrollBar != null)
{
EventDelegate.Add(horizontalScrollBar.onChange, OnScrollBar);
horizontalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveHorizontally) ? 1f : 0f;
}
if (verticalScrollBar != null)
{
EventDelegate.Add(verticalScrollBar.onChange, OnScrollBar);
verticalScrollBar.alpha = ((showScrollBars == ShowCondition.Always) || shouldMoveVertically) ? 1f : 0f;
}
}
}
/// <summary>
/// Restrict the scroll view's contents to be within the scroll view's bounds.
/// </summary>
public bool RestrictWithinBounds (bool instant) { return RestrictWithinBounds(instant, true, true); }
/// <summary>
/// Restrict the scroll view's contents to be within the scroll view's bounds.
/// </summary>
public bool RestrictWithinBounds (bool instant, bool horizontal, bool vertical)
{
Bounds b = bounds;
Vector3 constraint = mPanel.CalculateConstrainOffset(b.min, b.max);
if (!horizontal) constraint.x = 0f;
if (!vertical) constraint.y = 0f;
if (constraint.sqrMagnitude > 1f)
{
if (!instant && dragEffect == DragEffect.MomentumAndSpring)
{
// Spring back into place
Vector3 pos = mTrans.localPosition + constraint;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
SpringPanel.Begin(mPanel.gameObject, pos, 13f);
}
else
{
// Jump back into place
MoveRelative(constraint);
mMomentum = Vector3.zero;
mScroll = 0f;
}
return true;
}
return false;
}
/// <summary>
/// Disable the spring movement.
/// </summary>
public void DisableSpring ()
{
SpringPanel sp = GetComponent<SpringPanel>();
if (sp != null) sp.enabled = false;
}
/// <summary>
/// Update the values of the associated scroll bars.
/// </summary>
public void UpdateScrollbars () { UpdateScrollbars(true); }
/// <summary>
/// Update the values of the associated scroll bars.
/// </summary>
public virtual void UpdateScrollbars (bool recalculateBounds)
{
if (mPanel == null) return;
if (horizontalScrollBar != null || verticalScrollBar != null)
{
if (recalculateBounds)
{
mCalculatedBounds = false;
mShouldMove = shouldMove;
}
Bounds b = bounds;
Vector2 bmin = b.min;
Vector2 bmax = b.max;
if (horizontalScrollBar != null && bmax.x > bmin.x)
{
Vector4 clip = mPanel.finalClipRegion;
int intViewSize = Mathf.RoundToInt(clip.z);
if ((intViewSize & 1) != 0) intViewSize -= 1;
float halfViewSize = intViewSize * 0.5f;
halfViewSize = Mathf.Round(halfViewSize);
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
halfViewSize -= mPanel.clipSoftness.x;
float contentSize = bmax.x - bmin.x;
float viewSize = halfViewSize * 2f;
float contentMin = bmin.x;
float contentMax = bmax.x;
float viewMin = clip.x - halfViewSize;
float viewMax = clip.x + halfViewSize;
contentMin = viewMin - contentMin;
contentMax = contentMax - viewMax;
UpdateScrollbars(horizontalScrollBar, contentMin, contentMax, contentSize, viewSize, false);
}
if (verticalScrollBar != null && bmax.y > bmin.y)
{
Vector4 clip = mPanel.finalClipRegion;
int intViewSize = Mathf.RoundToInt(clip.w);
if ((intViewSize & 1) != 0) intViewSize -= 1;
float halfViewSize = intViewSize * 0.5f;
halfViewSize = Mathf.Round(halfViewSize);
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
halfViewSize -= mPanel.clipSoftness.y;
float contentSize = bmax.y - bmin.y;
float viewSize = halfViewSize * 2f;
float contentMin = bmin.y;
float contentMax = bmax.y;
float viewMin = clip.y - halfViewSize;
float viewMax = clip.y + halfViewSize;
contentMin = viewMin - contentMin;
contentMax = contentMax - viewMax;
UpdateScrollbars(verticalScrollBar, contentMin, contentMax, contentSize, viewSize, true);
}
}
else if (recalculateBounds)
{
mCalculatedBounds = false;
}
}
/// <summary>
/// Helper function used in UpdateScrollbars(float) function above.
/// </summary>
protected void UpdateScrollbars (UIProgressBar slider, float contentMin, float contentMax, float contentSize, float viewSize, bool inverted)
{
if (slider == null) return;
mIgnoreCallbacks = true;
{
float contentPadding;
if (viewSize < contentSize)
{
contentMin = Mathf.Clamp01(contentMin / contentSize);
contentMax = Mathf.Clamp01(contentMax / contentSize);
contentPadding = contentMin + contentMax;
slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);
}
else
{
contentMin = Mathf.Clamp01(-contentMin / contentSize);
contentMax = Mathf.Clamp01(-contentMax / contentSize);
contentPadding = contentMin + contentMax;
slider.value = inverted ? ((contentPadding > 0.001f) ? 1f - contentMin / contentPadding : 0f) :
((contentPadding > 0.001f) ? contentMin / contentPadding : 1f);
if (contentSize > 0)
{
contentMin = Mathf.Clamp01(contentMin / contentSize);
contentMax = Mathf.Clamp01(contentMax / contentSize);
contentPadding = contentMin + contentMax;
}
}
UIScrollBar sb = slider as UIScrollBar;
if (sb != null) sb.barSize = 1f - contentPadding;
}
mIgnoreCallbacks = false;
}
/// <summary>
/// Changes the drag amount of the scroll view to the specified 0-1 range values.
/// (0, 0) is the top-left corner, (1, 1) is the bottom-right.
/// </summary>
public virtual void SetDragAmount (float x, float y, bool updateScrollbars)
{
if (mPanel == null) mPanel = GetComponent<UIPanel>();
DisableSpring();
Bounds b = bounds;
if (b.min.x == b.max.x || b.min.y == b.max.y) return;
Vector4 clip = mPanel.finalClipRegion;
float hx = clip.z * 0.5f;
float hy = clip.w * 0.5f;
float left = b.min.x + hx;
float right = b.max.x - hx;
float bottom = b.min.y + hy;
float top = b.max.y - hy;
if (mPanel.clipping == UIDrawCall.Clipping.SoftClip)
{
left -= mPanel.clipSoftness.x;
right += mPanel.clipSoftness.x;
bottom -= mPanel.clipSoftness.y;
top += mPanel.clipSoftness.y;
}
// Calculate the offset based on the scroll value
float ox = Mathf.Lerp(left, right, x);
float oy = Mathf.Lerp(top, bottom, y);
// Update the position
if (!updateScrollbars)
{
Vector3 pos = mTrans.localPosition;
if (canMoveHorizontally) pos.x += clip.x - ox;
if (canMoveVertically) pos.y += clip.y - oy;
mTrans.localPosition = pos;
}
if (canMoveHorizontally) clip.x = ox;
if (canMoveVertically) clip.y = oy;
// Update the clipping offset
Vector4 cr = mPanel.baseClipRegion;
mPanel.clipOffset = new Vector2(clip.x - cr.x, clip.y - cr.y);
// Update the scrollbars, reflecting this change
if (updateScrollbars) UpdateScrollbars(mDragID == -10);
}
/// <summary>
/// Reset the scroll view's position to the top-left corner.
/// It's recommended to call this function before AND after you re-populate the scroll view's contents (ex: switching window tabs).
/// Another option is to populate the scroll view's contents, reset its position, then call this function to reposition the clipping.
/// </summary>
[ContextMenu("Reset Clipping Position")]
public void ResetPosition()
{
if (NGUITools.GetActive(this))
{
// Invalidate the bounds
mCalculatedBounds = false;
Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);
// First move the position back to where it would be if the scroll bars got reset to zero
SetDragAmount(pv.x, 1f - pv.y, false);
// Next move the clipping area back and update the scroll bars
SetDragAmount(pv.x, 1f - pv.y, true);
}
}
/// <summary>
/// Call this function after you adjust the scroll view's bounds if you want it to maintain the current scrolled position
/// </summary>
public void UpdatePosition ()
{
if (!mIgnoreCallbacks && (horizontalScrollBar != null || verticalScrollBar != null))
{
mIgnoreCallbacks = true;
mCalculatedBounds = false;
Vector2 pv = NGUIMath.GetPivotOffset(contentPivot);
float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : pv.x;
float y = (verticalScrollBar != null) ? verticalScrollBar.value : 1f - pv.y;
SetDragAmount(x, y, false);
UpdateScrollbars(true);
mIgnoreCallbacks = false;
}
}
/// <summary>
/// Triggered by the scroll bars when they change.
/// </summary>
public void OnScrollBar ()
{
if (!mIgnoreCallbacks)
{
mIgnoreCallbacks = true;
float x = (horizontalScrollBar != null) ? horizontalScrollBar.value : 0f;
float y = (verticalScrollBar != null) ? verticalScrollBar.value : 0f;
SetDragAmount(x, y, false);
mIgnoreCallbacks = false;
}
}
/// <summary>
/// Move the scroll view by the specified amount.
/// </summary>
public virtual void MoveRelative (Vector3 relative)
{
mTrans.localPosition += relative;
Vector2 co = mPanel.clipOffset;
co.x -= relative.x;
co.y -= relative.y;
mPanel.clipOffset = co;
// Update the scroll bars
UpdateScrollbars(false);
}
/// <summary>
/// Move the scroll view by the specified amount.
/// </summary>
public void MoveAbsolute (Vector3 absolute)
{
Vector3 a = mTrans.InverseTransformPoint(absolute);
Vector3 b = mTrans.InverseTransformPoint(Vector3.zero);
MoveRelative(a - b);
}
/// <summary>
/// Create a plane on which we will be performing the dragging.
/// </summary>
public void Press (bool pressed)
{
if (smoothDragStart && pressed)
{
mDragStarted = false;
mDragStartOffset = Vector2.zero;
}
if (enabled && NGUITools.GetActive(gameObject))
{
if (!pressed && mDragID == UICamera.currentTouchID) mDragID = -10;
mCalculatedBounds = false;
mShouldMove = shouldMove;
if (!mShouldMove) return;
mPressed = pressed;
if (pressed)
{
// Remove all momentum on press
mMomentum = Vector3.zero;
mScroll = 0f;
// Disable the spring movement
DisableSpring();
// Remember the hit position
mLastPos = UICamera.lastHit.point;
// Create the plane to drag along
mPlane = new Plane(mTrans.rotation * Vector3.back, mLastPos);
// Ensure that we're working with whole numbers, keeping everything pixel-perfect
Vector2 co = mPanel.clipOffset;
co.x = Mathf.Round(co.x);
co.y = Mathf.Round(co.y);
mPanel.clipOffset = co;
Vector3 v = mTrans.localPosition;
v.x = Mathf.Round(v.x);
v.y = Mathf.Round(v.y);
mTrans.localPosition = v;
}
else
{
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None && dragEffect == DragEffect.MomentumAndSpring)
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
if (onDragFinished != null)
onDragFinished();
}
}
}
/// <summary>
/// Drag the object along the plane.
/// </summary>
public void Drag ()
{
if (enabled && NGUITools.GetActive(gameObject) && mShouldMove)
{
if (mDragID == -10) mDragID = UICamera.currentTouchID;
UICamera.currentTouch.clickNotification = UICamera.ClickNotification.BasedOnDelta;
// Prevents the drag "jump". Contributed by 'mixd' from the Tasharen forums.
if (smoothDragStart && !mDragStarted)
{
mDragStarted = true;
mDragStartOffset = UICamera.currentTouch.totalDelta;
}
Ray ray = smoothDragStart ?
UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos - mDragStartOffset) :
UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos);
float dist = 0f;
if (mPlane.Raycast(ray, out dist))
{
Vector3 currentPos = ray.GetPoint(dist);
Vector3 offset = currentPos - mLastPos;
mLastPos = currentPos;
if (offset.x != 0f || offset.y != 0f || offset.z != 0f)
{
offset = mTrans.InverseTransformDirection(offset);
if (movement == Movement.Horizontal)
{
offset.y = 0f;
offset.z = 0f;
}
else if (movement == Movement.Vertical)
{
offset.x = 0f;
offset.z = 0f;
}
else if (movement == Movement.Unrestricted)
{
offset.z = 0f;
}
else
{
offset.Scale((Vector3)customMovement);
}
offset = mTrans.TransformDirection(offset);
}
// Adjust the momentum
mMomentum = Vector3.Lerp(mMomentum, mMomentum + offset * (0.01f * momentumAmount), 0.67f);
// Move the scroll view
if (!iOSDragEmulation || dragEffect != DragEffect.MomentumAndSpring)
{
MoveAbsolute(offset);
}
else
{
Vector3 constraint = mPanel.CalculateConstrainOffset(bounds.min, bounds.max);
if (constraint.magnitude > 1f)
{
MoveAbsolute(offset * 0.5f);
mMomentum *= 0.5f;
}
else
{
MoveAbsolute(offset);
}
}
// We want to constrain the UI to be within bounds
if (restrictWithinPanel &&
mPanel.clipping != UIDrawCall.Clipping.None &&
dragEffect != DragEffect.MomentumAndSpring)
{
RestrictWithinBounds(true, canMoveHorizontally, canMoveVertically);
}
}
}
}
/// <summary>
/// If the object should support the scroll wheel, do it.
/// </summary>
public void Scroll (float delta)
{
if (enabled && NGUITools.GetActive(gameObject) && scrollWheelFactor != 0f)
{
DisableSpring();
mShouldMove = shouldMove;
if (Mathf.Sign(mScroll) != Mathf.Sign(delta)) mScroll = 0f;
mScroll += delta * scrollWheelFactor;
}
}
/// <summary>
/// Apply the dragging momentum.
/// </summary>
void LateUpdate ()
{
if (!Application.isPlaying) return;
float delta = RealTime.deltaTime;
// Fade the scroll bars if needed
if (showScrollBars != ShowCondition.Always && (verticalScrollBar || horizontalScrollBar))
{
bool vertical = false;
bool horizontal = false;
if (showScrollBars != ShowCondition.WhenDragging || mDragID != -10 || mMomentum.magnitude > 0.01f)
{
vertical = shouldMoveVertically;
horizontal = shouldMoveHorizontally;
}
if (verticalScrollBar)
{
float alpha = verticalScrollBar.alpha;
alpha += vertical ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (verticalScrollBar.alpha != alpha) verticalScrollBar.alpha = alpha;
}
if (horizontalScrollBar)
{
float alpha = horizontalScrollBar.alpha;
alpha += horizontal ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (horizontalScrollBar.alpha != alpha) horizontalScrollBar.alpha = alpha;
}
}
// Apply momentum
if (mShouldMove && !mPressed)
{
if (movement == Movement.Horizontal)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, 0f, 0f));
}
else if (movement == Movement.Vertical)
{
mMomentum -= mTrans.TransformDirection(new Vector3(0f, mScroll * 0.05f, 0f));
}
else if (movement == Movement.Unrestricted)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, mScroll * 0.05f, 0f));
}
else
{
mMomentum -= mTrans.TransformDirection(new Vector3(
mScroll * customMovement.x * 0.05f,
mScroll * customMovement.y * 0.05f, 0f));
}
if (mMomentum.magnitude > 0.0001f)
{
mScroll = NGUIMath.SpringLerp(mScroll, 0f, 20f, delta);
// Move the scroll view
Vector3 offset = NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
MoveAbsolute(offset);
// Restrict the contents to be within the scroll view's bounds
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
if (mMomentum.magnitude < 0.0001f && onDragFinished != null)
onDragFinished();
return;
}
else
{
mScroll = 0f;
mMomentum = Vector3.zero;
}
}
else mScroll = 0f;
// Dampen the momentum
NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
}
#if UNITY_EDITOR
/// <summary>
/// Draw a visible orange outline of the bounds.
/// </summary>
void OnDrawGizmos ()
{
if (mPanel != null)
{
if (!Application.isPlaying) mCalculatedBounds = false;
Bounds b = bounds;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = new Color(1f, 0.4f, 0f);
Gizmos.DrawWireCube(new Vector3(b.center.x, b.center.y, b.min.z), new Vector3(b.size.x, b.size.y, 0f));
}
}
#endif
}
2.核心脚本NGUIDynamicScrollBase ,计算滑动和播放位移动画
[code]csharpcode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | using UnityEngine; using System.Collections; using System.Collections.Generic; /// <summary> /// 扩展NGUIScroll滑动类,需要继承该类进行开发 /// </summary> public abstract class NGUIDynamicScrollBase : MonoBehaviour { //每个列表项数据初始化 abstract protected void ResetItemData(GameObject go, int index); public UIPanel panel; public UIGrid grid; //目标prefab public GameObject prefab; //宽度 public int cellHeight = 60; //高度 public int cellWidth = 700; //裁剪区的高度 private float m_height; //裁剪区的宽度 private int m_maxLine; //当前滑动的列表 protected GameObject[] m_cellList; //当前需要滑动的列表总数 private int m_dataListCount; //自定义滑动 private UICustomScrollView mDrag; //最后一次的滑动位置 private float lastY = -1; private Vector3 defaultVec; // Use this for initialization protected void BaseOnEnable() { GetConfiguration(); } // Update is called once per frame protected void BaseUpdate() { if (panel.transform.localPosition.y != lastY) { Validate(); lastY = panel.transform.localPosition.y; } } //设置当前列表中的 protected int DataListCount { get { return m_dataListCount; } set { m_dataListCount = value; AddItem(m_dataListCount); PlayMoveAnimation(m_cellList); } } #region private Functions //初始化配置数据 private void GetConfiguration() { //物体默认位置 defaultVec = new Vector3(0, cellHeight, 0); //裁剪区域的高度 m_height = panel.height; //裁剪区域中最多显示的cellItem数量 m_maxLine = Mathf.CeilToInt(m_height / cellHeight) + 1; //初始化CellList m_cellList = new GameObject[m_maxLine]; //创建Item,默认为不可显示状态 CreateItem(); } //创建Item private void CreateItem() { for ( int i = 0; i < m_maxLine; i++) { GameObject go = null ; go = (GameObject)Instantiate(prefab); go.gameObject.SetActive( false ); go.GetComponent<UICustomDragScrollView>().scrollView = panel.GetComponent<UICustomScrollView>(); AddChild(grid.gameObject, go); go.transform.localScale = Vector3.one; go.transform.localPosition = new Vector3(cellWidth * AllScale.ResolutionScale, -i * cellHeight, 0); m_cellList[i] = go; go.gameObject.SetActive( false ); } } //验证当前区域中的需要显示的CellItem private void Validate() { Vector3 position = panel.transform.localPosition; float _ver = Mathf.Max(position.y, 0); int startIndex = Mathf.FloorToInt(_ver / cellHeight); int endIndex = Mathf.Min(DataListCount, startIndex + m_maxLine); GameObject cell; int index = 0; for ( int i = startIndex; i < startIndex + m_maxLine; i++) { cell = m_cellList[index]; if (i < endIndex) { //开始渲染 cell.gameObject.SetActive( true ); //重新填充数据 ResetItemData(cell, i); //改变位置 cell.transform.localPosition = new Vector3(cell.transform.localPosition.x, i * -cellHeight, 0); cell.name = "Item_" + index; } else { cell.transform.localPosition = defaultVec; // cell.gameObject.SetActive(false); } index++; } } //重新计算包围合的大小 private void UpdateBounds( int count) { Vector3 vMin = new Vector3(); vMin.x = -grid.transform.localPosition.x; vMin.y = grid.transform.localPosition.y - count * cellHeight; vMin.z = grid.transform.localPosition.z; Bounds b = new Bounds(vMin, Vector3.one); b.Encapsulate(grid.transform.localPosition); if (mDrag == null ) mDrag = panel.GetComponent<UICustomScrollView>(); mDrag.bounds = b; mDrag.UpdateScrollbars( true ); mDrag.RestrictWithinBounds( true ); } //根据新的数量来重新绘制 private void AddItem( int count) { Validate(); UpdateBounds(count); } //增加孩子节点 void AddChild(GameObject parent, GameObject go) { Transform t = go.transform; t.parent = parent.transform; t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; go.layer = parent.layer; } //播放开始加载的位移动画 void PlayMoveAnimation(GameObject[] list) { Vector3 to; Vector3 from ; for ( int i = 0; i < list.Length; i++) { from = list[i].transform.localPosition; from = new Vector3(cellWidth*AllScale.ResolutionScale, from .y, 0); to = new Vector3(0, from .y, 0); list[i].transform.localPosition = from ; TweenPosition tp = TweenPosition.Begin(list[i], 0.8f, to); tp.delay = 0.1f; tp. from = from ; tp.to = to; tp.duration = (i + 2) * 0.1f; tp.method = UITweener.Method.EaseIn; } } #endregion } |
3.将列表中的Prefab设置为向上对齐
分类:
Unity&NGUI
, Unity&Script
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决