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
}

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设置为向上对齐
posted @   ing...  阅读(3519)  评论(2编辑  收藏  举报
编辑推荐:
· 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 重磅开源!
· 字符编码:从基础到乱码解决
点击右上角即可分享
微信分享提示