NGUI 类皇室战争(CR)的滚动列表效果完整实现(一)

前言:CR作为SuperCell的热卖大作,受到很多用户的追捧.
玩法新颖,游戏性突出这些都不说了.它的UI体验也是别具一格令人感觉整个UI交互就像一个辅助生活的APP一样十分好用.

本系列最终效果展示:

[底部Button变换以及控制ScrollView翻页]

[水平滑动具有相册翻页效果同时底部Button响应]

[支持垂直滑动与水平滑动]

今天就来说说如何用NGUI实现CR的横向竖向都支持的滚动列表.
仔细观察CR的UI的滚动效果会发现,当你成功垂直滑动后按住不放,发现只能垂直滑动了,同样当你成功水平滑动后按住不放,后续也只能水平滑动.

那么思路便有了,检测到用户首次按下之后记录下射线检测到的位置,记为:mLastPos,然后当用户企图Drag时,分析onDrag函数传入的参数:Vector2 delta,如果delta.x的绝对值大于delta.y的绝对值那么就启用横向滑动列表,反之则启用垂直滑动列表.

下面搭建一下基础场景:
NGUI 类皇室战争(CR)的滚动列表效果完整实现(一)_第1张图片

开始我在每个ScrollView上Add了NGUI的UIGrid以及UIScrollView,为了响应拖动又在每个UISprite添加了UIDragScrollView.
然而这还不能实现我们想要的效果.

观察UIDragScrollView.cs中的源码:

    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<UIScrollView>(mTrans);
                mScroll = scrollView;
            }
        }
    }

注意到,在检测到按入或按出时即onPress函数会调用UIScrollView的Press函数,并把是否按入\按出的信息传入.

再观察UIScrollView.cs中的对应源码,在Press函数中会有这么一段:

            if (pressed)
            {
                // Remove all momentum on press
                mMomentum = Vector3.zero;
                mScroll = 0f;

                // Disable the spring movement
                DisableSpring();

                // Remember the hit position
                mLastPos = UICamera.lastWorldPosition;

                // 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;

                if (!smoothDragStart)
                {
                    mDragStarted = true;
                    mDragStartOffset = Vector2.zero;
                    if (onDragStarted != null) onDragStarted();
                }
            }

注意这里

                // Remember the hit position
                mLastPos = UICamera.lastWorldPosition;

这是最关键的,我们希望在按入时相关的逻辑在分析出究竟使用场景中的HScrollView还是VScrollView后再进行处理.
因此我们需要把if(pressed){…}花括号里面的代码剪切走,延后执行.

为了不扰乱NGUI的正常功能,我分别对UIScrollView.cs以及UIDragScrollView.cs做了复制,重命名以及修改.

为了方便,我直接把全部代码放在这里:
(注:剪切走的代码放到了这里:剪切代码)

//----------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//----------------------------------------------

using UnityEngine;

/// 
/// 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.
/// 

[ExecuteInEditMode]
[RequireComponent(typeof(UIPanel))]
[AddComponentMenu("NGUI/Custom/Custom Scroll View")]
public class UICustomScrollView : MonoBehaviour
{
    static public BetterList list = new BetterList();

    public enum Movement
    {
        Horizontal,
        Vertical,
        Unrestricted,
        Custom,
    }

    public enum DragEffect
    {
        None,
        Momentum,
        MomentumAndSpring,
    }

    public enum ShowCondition
    {
        Always,
        OnlyIfNeeded,
        WhenDragging,
    }

    public delegate void OnDragNotification ();

    /// 
    /// Type of movement allowed by the scroll view.
    /// 

    public Movement movement = Movement.Horizontal;

    /// 
    /// Effect to apply when dragging.
    /// 

    public DragEffect dragEffect = DragEffect.MomentumAndSpring;

    /// 
    /// Whether the dragging will be restricted to be within the scroll view's bounds.
    /// 

    public bool restrictWithinPanel = true;

    /// 
    /// Whether dragging will be disabled if the contents fit.
    /// 

    public bool disableDragIfFits = false;

    /// 
    /// Whether the drag operation will be started smoothly, or if if it will be precise (but will have a noticeable "jump").
    /// 

    public bool smoothDragStart = true;

    /// 
    /// 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.
    ///   

    public bool iOSDragEmulation = true;

    /// 
    /// Effect the scroll wheel will have on the momentum.
    /// 

    public float scrollWheelFactor = 0.25f;

    /// 
    /// How much momentum gets applied when the press is released after dragging.
    /// 

    public float momentumAmount = 35f;

    /// 
    /// Strength of the spring dampening effect.
    /// 

    public float dampenStrength = 9f;

    /// 
    /// Horizontal scrollbar used for visualization.
    /// 

    public UIProgressBar horizontalScrollBar;

    /// 
    /// Vertical scrollbar used for visualization.
    /// 

    public UIProgressBar verticalScrollBar;

    /// 
    /// Condition that must be met for the scroll bars to become visible.
    /// 

    public ShowCondition showScrollBars = ShowCondition.OnlyIfNeeded;

    /// 
    /// Custom movement, if the 'movement' field is set to 'Custom'.
    /// 

    public Vector2 customMovement = new Vector2(1f, 0f);

    /// 
    /// Content's pivot point -- where it originates from by default.
    /// 

    public UIWidget.Pivot contentPivot = UIWidget.Pivot.TopLeft;

    /// 
    /// Event callback to trigger when the drag process begins.
    /// 

    public OnDragNotification onDragStarted;

    /// 
    /// Event callback to trigger when the drag process finished. Can be used for additional effects, such as centering on some object.
    /// 

    public OnDragNotification onDragFinished;

    /// 
    /// Event callback triggered when the scroll view is moving as a result of momentum in between of OnDragFinished and OnStoppedMoving.
    /// 

    public OnDragNotification onMomentumMove;

    /// 
    /// Event callback to trigger when the scroll view's movement ends.
    /// 

    public OnDragNotification onStoppedMoving;

    // 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;

    /// 
    /// Panel that's being dragged.
    /// 

    public UIPanel panel { get { return mPanel; } }

    /// 
    /// Whether the scroll view is being dragged.
    /// 

    public bool isDragging { get { return mPressed && mDragStarted; } }

    /// 
    /// Calculate the bounds used by the widgets.
    /// 

    public virtual Bounds bounds
    {
        get
        {
            if (!mCalculatedBounds)
            {
                mCalculatedBounds = true;
                mTrans = transform;
                mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans);
            }
            return mBounds;
        }
    }

    /// 
    /// Whether the scroll view can move horizontally.
    /// 

    public bool canMoveHorizontally
    {
        get
        {
            return movement == Movement.Horizontal ||
                movement == Movement.Unrestricted ||
                (movement == Movement.Custom && customMovement.x != 0f);
        }
    }

    /// 
    /// Whether the scroll view can move vertically.
    /// 

    public bool canMoveVertically
    {
        get
        {
            return movement == Movement.Vertical ||
                movement == Movement.Unrestricted ||
                (movement == Movement.Custom && customMovement.y != 0f);
        }
    }

    /// 
    /// Whether the scroll view should be able to move horizontally (contents don't fit).
    /// 

    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;
        }
    }

    /// 
    /// Whether the scroll view should be able to move vertically (contents don't fit).
    /// 

    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;
        }
    }

    /// 
    /// Whether the contents of the scroll view should actually be draggable depends on whether they currently fit or not.
    /// 

    protected virtual bool shouldMove
    {
        get
        {
            if (!disableDragIfFits) return true;

            if (mPanel == null) mPanel = GetComponent();
            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;
        }
    }

    /// 
    /// Current momentum, exposed just in case it's needed.
    /// 

    public Vector3 currentMomentum
    {
        get
        {
            return mMomentum;
        }
        set
        {
            mMomentum = value;
            mShouldMove = true;
        }
    }

    /// 
    /// Cache the transform and the panel.
    /// 

    void Awake ()
    {
        mTrans = transform;
        mPanel = GetComponent();

        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
        }
    }

    [System.NonSerialized] bool mStarted = false;

    void OnEnable ()
    {
        list.Add(this);
        if (mStarted && Application.isPlaying) CheckScrollbars();
    }

    void Start ()
    {
        mStarted = true;
        if (Application.isPlaying) CheckScrollbars();
    }

    void CheckScrollbars ()
    {
        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;
        }
    }

    void OnDisable () { list.Remove(this); }

    /// 
    /// Restrict the scroll view's contents to be within the scroll view's bounds.
    /// 

    public bool RestrictWithinBounds (bool instant) { return RestrictWithinBounds(instant, true, true); }

    /// 
    /// Restrict the scroll view's contents to be within the scroll view's bounds.
    /// 

    public bool RestrictWithinBounds (bool instant, bool horizontal, bool vertical)
    {
        if (mPanel == null) return false;

        Bounds b = bounds;
        Vector3 constraint = mPanel.CalculateConstrainOffset(b.min, b.max);

        if (!horizontal) constraint.x = 0f;
        if (!vertical) constraint.y = 0f;

        if (constraint.sqrMagnitude > 0.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).strength = 8f;
            }
            else
            {
                // Jump back into place
                MoveRelative(constraint);

                // Clear the momentum in the constrained direction
                if (Mathf.Abs(constraint.x) > 0.01f) mMomentum.x = 0;
                if (Mathf.Abs(constraint.y) > 0.01f) mMomentum.y = 0;
                if (Mathf.Abs(constraint.z) > 0.01f) mMomentum.z = 0;
                mScroll = 0f;
            }
            return true;
        }
        return false;
    }

    /// 
    /// Disable the spring movement.
    /// 

    public void DisableSpring ()
    {
        SpringPanel sp = GetComponent();
        if (sp != null) sp.enabled = false;
    }

    /// 
    /// Update the values of the associated scroll bars.
    /// 

    public void UpdateScrollbars () { UpdateScrollbars(true); }

    /// 
    /// Update the values of the associated scroll bars.
    /// 

    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;
        }
    }

    /// 
    /// Helper function used in UpdateScrollbars(float) function above.
    /// 

    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;
    }

    /// 
    /// 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.
    /// 

    public virtual void SetDragAmount (float x, float y, bool updateScrollbars)
    {
        if (mPanel == null) mPanel = GetComponent();

        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);
    }

    /// 
    /// Manually invalidate the scroll view's bounds so that they update next time.
    /// 

    public void InvalidateBounds () { mCalculatedBounds = false; }

    /// 
    /// 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.
    /// 

    [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);
        }
    }

    /// 
    /// Call this function after you adjust the scroll view's bounds if you want it to maintain the current scrolled position
    /// 

    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;
        }
    }

    /// 
    /// Triggered by the scroll bars when they change.
    /// 

    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;
        }
    }

    /// 
    /// Move the scroll view by the specified local space amount.
    /// 

    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);
    }

    /// 
    /// Move the scroll view by the specified world space amount.
    /// 

    public void MoveAbsolute (Vector3 absolute)
    {
        Vector3 a = mTrans.InverseTransformPoint(absolute);
        Vector3 b = mTrans.InverseTransformPoint(Vector3.zero);
        MoveRelative(a - b);
    }

    /// 
    /// Create a plane on which we will be performing the dragging.
    /// 

    public void Press (bool pressed)
    {
        if (UICamera.currentScheme == UICamera.ControlScheme.Controller) return;

        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)
            {

            }
            else if (centerOnChild)
            {
                centerOnChild.Recenter();
            }
            else
            {
                if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
                    RestrictWithinBounds(dragEffect == DragEffect.None, canMoveHorizontally, canMoveVertically);

                if (mDragStarted && onDragFinished != null) onDragFinished();
                if (!mShouldMove && onStoppedMoving != null)
                    onStoppedMoving();
            }
        }
    }

    public void OnDelayPressEnter(Vector3 lastPos)
    {
        // Remove all momentum on press
        mMomentum = Vector3.zero;
        mScroll = 0f;

        // Disable the spring movement
        DisableSpring();

        // Remember the hit position
        mLastPos = lastPos;

        // 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;

        if (!smoothDragStart)
        {
            mDragStarted = true;
            mDragStartOffset = Vector2.zero;
            if (onDragStarted != null) onDragStarted();
        }
    }


    /// 
    /// Drag the object along the plane.
    /// 

    public void Drag ()
    {
        if (UICamera.currentScheme == UICamera.ControlScheme.Controller) return;

        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;
                if (onDragStarted != null) onDragStarted();
            }

            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
                if (dragEffect == DragEffect.None) mMomentum = Vector3.zero;
                else 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);
                }
            }
        }
    }

    [HideInInspector]
    public UICenterOnChild centerOnChild = null;

    /// 
    /// If the object should support the scroll wheel, do it.
    /// 

    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;
        }
    }

    /// 
    /// Apply the dragging momentum.
    /// 

    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;
            }
        }

        if (!mShouldMove) return;

        // Apply momentum
        if (!mPressed)
        {
            if (mMomentum.magnitude > 0.0001f || mScroll != 0f)
            {
                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));
                }
                mScroll = NGUIMath.SpringLerp(mScroll, 0f, 20f, delta);

                // Move the scroll view
                Vector3 offset = NGUIMath.SpringDampen(ref mMomentum, dampenStrength, delta);
                MoveAbsolute(offset);

                // Restrict the contents to be within the scroll view's bounds
                if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
                {
                    if (NGUITools.GetActive(centerOnChild))
                    {
                        if (centerOnChild.nextPageThreshold != 0f)
                        {
                            mMomentum = Vector3.zero;
                            mScroll = 0f;
                        }
                        else centerOnChild.Recenter();
                    }
                    else
                    {
                        RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
                    }
                }

                if (onMomentumMove != null)
                    onMomentumMove();
            }
            else
            {
                mScroll = 0f;
                mMomentum = Vector3.zero;

                SpringPanel sp = GetComponent();
                if (sp != null && sp.enabled) return;

                mShouldMove = false;
                if (onStoppedMoving != null)
                    onStoppedMoving();
            }
        }
        else
        {
            // Dampen the momentum
            mScroll = 0f;
            NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
        }
    }

#if UNITY_EDITOR

    /// 
    /// Draw a visible orange outline of the bounds.
    /// 

    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
}

另外一个响应脚本:

//----------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//----------------------------------------------

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System;

/// 
/// Allows dragging of the specified scroll view by mouse or touch.
/// 

[AddComponentMenu("NGUI/Custom/HV Drag Scroll View")]
public class UI_HV_DragScrollView : MonoBehaviour
{
    /// 
    /// Reference to the scroll view that will be dragged by the script.
    /// 

    public UICustomScrollView scrollView;

    // Legacy functionality, kept for backwards compatibility. Use 'scrollView' instead.
    [HideInInspector][SerializeField] UICustomScrollView draggablePanel;

    Transform mTrans;
    UICustomScrollView mScroll;
    UICustomScrollView hScroll;//水平滚动的ScrollView
    UICustomScrollView vScroll;//垂直滚动的ScrollView
    UICustomScrollView currentScroll = null;
    bool mAutoFind = false;
    bool mStarted = false;

    Vector3 mLastPos;

    /// 
    /// Automatically find the scroll view if possible.
    /// 

    void OnEnable ()
    {
//      mTrans = transform;
//
//      // Auto-upgrade
//      if (scrollView == null && draggablePanel != null)
//      {
//          scrollView = draggablePanel;
//          draggablePanel = null;
//      }
//
//      if (mStarted && (mAutoFind || mScroll == null))
//          FindScrollView();
    }

    /// 
    /// Find the scroll view.
    /// 

    void Start ()
    {
        mStarted = true;
        Initialize ();
    }

    void Initialize()
    {
        vScroll = transform.parent.GetComponent ();
        hScroll = transform.parent.parent.GetComponent ();
    }

    /// 
    /// Find the scroll view to work with.
    /// 



    /// 
    /// Create a plane on which we will be performing the dragging.
    /// 

    void OnPress (bool pressed)
    {
        hScroll.Press (pressed);
        vScroll.Press (pressed);

        if (pressed) 
        {
            mLastPos = UICamera.lastWorldPosition;
        }
        else
        {
            currentScroll = null;
        }


    }

    /// 
    /// Drag the object along the plane.
    /// 

    void OnDrag (Vector2 delta)
    {
        if (currentScroll == null) 
        {
            //Debug.LogFormat ("delta.x == {0} delta.y == {1}",delta.x,delta.y);
            if (Math.Abs (delta.x) > Math.Abs (delta.y)) {
                currentScroll = hScroll;
            } else 
            {
                currentScroll = vScroll;
            }
            currentScroll.OnDelayPressEnter (mLastPos);
        }
        currentScroll.Drag ();
    }

    /// 
    /// If the object should support the scroll wheel, do it.
    /// 

    void OnScroll (float delta)
    {
        if (scrollView && NGUITools.GetActive(this))
            scrollView.Scroll(delta);
    }
}

今天先到这里,接下来我会继续对效果进行补充和完善.

你可能感兴趣的:(NGUI,Unity)