Unity 扩展UGUI ScrollRect 实现水平循环拖动。

本文介绍了如何扩展Unity的UGUI ScrollRect组件,使其支持水平循环拖动。通过检测内容的边界,判断滑动方向,并在达到边界时将元素循环移动,确保视觉上的无缝滚动效果。提供了具体的实现代码,使用方式类似原生ScrollRect,只需设置相应枚举。
摘要由CSDN通过智能技术生成

目前UGUI的ScrollRect不支持循环滑动,这里提供一种水平循环滑动的方法


简单描述一下思路:

(1)检测第一个物件的左边界和右边界,和Viewport的左边界比较。

(2)检测当前是向左滑动还是向右滑动

(3)修改Content的中心和边界,使Content的矩形随着具体内容的变化而变化

(4)左循环时把左边第一个元素放置到最右边

(5)右循环时把右边第一个元素放置到最左边。


直接放代码,使用方式和ScrollRect基本一致,注意设置枚举为HorizonLoop即可。

using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace UnityEngine.UI
{
    /// <summary>
    /// Modified By HawkWang at 2017-04-07
    /// All right not reserverd.
    /// Copy and distribute and modify and delete at free will.
    /// Too laze to support vertical loop scroll(and infact it is not as useful as horizon loop scroll).
    /// </summary>
    [AddComponentMenu("UI/Scroll Rect", 37)]
    [SelectionBase]
    [ExecuteInEditMode]
    [DisallowMultipleComponent]
    [RequireComponent(typeof(RectTransform))]
    public class ScrollRectExt : UIBehaviour, IInitializePotentialDragHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler, ICanvasElement, ILayoutElement, ILayoutGroup
    {
        /// <summary>
        /// Question One:研究哪里真正改变了Content的位置!
        /// </summary>
        #region ################  AddOn Content  ##########################################
        public enum EScrollType
        {
            HorizonLoop,
            Horizon,
            Vertical,
        }

        [SerializeField]
        private EScrollType m_scrollType = EScrollType.HorizonLoop;

        public enum EHorizonScrollDirection 
        {
            Left,
            Still,
            Right,
        }
        EHorizonScrollDirection m_horizonDirection = EHorizonScrollDirection.Still;

        public enum EMoveSiblingType 
        {
            MoveFirstToLast,
            MoveLastToFirst,
        }
        EMoveSiblingType m_moveSiblingType = EMoveSiblingType.MoveFirstToLast;

        RectTransform FirstChild
        {
            get 
            {
                return m_Content.GetChild(0) as RectTransform;
            }
        }

        RectTransform LastChild 
        {
            get 
            {
                return m_Content.GetChild(m_Content.childCount - 1) as RectTransform;
            }
        }

        float m_halfSize;
        Vector2 m_viewPortWorldBound;//x 表示左边界,下边界,y表示右边界,上边界。
        
        void InitExtContent() 
        {
            if( horizontal && vertical == false )
            {
                HorizontalLayoutGroup hlg = m_Content.gameObject.GetComponent<HorizontalLayoutGroup>();
                if( hlg != null )
                {
                    m_halfSize = ( FirstChild.sizeDelta.x + hlg.spacing ) * 0.5f;
                    m_viewPortWorldBound = RectTransformUtility.WorldToScreenPoint(Camera.main, viewport.position);
                    m_viewPortWorldBound.y = m_viewPortWorldBound.x + viewport.sizeDelta.x;
                }
            }
            if(vertical && horizontal == false)
            {
                VerticalLayoutGroup vlg = m_Content.gameObject.GetComponent<VerticalLayoutGroup>();
                if( vlg != null )
                {
                    //默认左上角对齐,所以下边界,采用的是x,上边界才是y
                    m_halfSize = (FirstChild.sizeDelta.y + vlg.spacing) * 0.5f;
                    m_viewPortWorldBound = RectTransformUtility.WorldToScreenPoint(Camera.main, viewport.position);
                    m_viewPortWorldBound.x = m_viewPortWorldBound.y - viewport.sizeDelta.y;
                }
            }                
        }
        
        public bool horizontalLoop
        {
            get
            {
                return m_scrollType == EScrollType.HorizonLoop;
            }
        }

        public bool horizontal
        {
            get
            {
                return m_scrollType == EScrollType.HorizonLoop || m_scrollType == EScrollType.Horizon;
            }
        }

        public bool vertical
        {
            get
            {
                return m_scrollType == EScrollType.Vertical;
            }
        }
        #endregion ########################################################################

        public enum MovementType
        {
            Unrestricted, // Unrestricted movement -- can scroll forever
            Elastic, // Restricted but flexible -- can go past the edges, but springs back in place
            Clamped, // Restricted movement where it's not possible to go past the edges
        }

        public enum ScrollbarVisibility
        {
            Permanent,
            AutoHide,
            AutoHideAndExpandViewport,
        }

        [Serializable]
        public class ScrollRectEvent : UnityEvent<Vector2> { }

        [SerializeField]
        private RectTransform m_Content;
        public RectTransform content { get { return m_Content; } set { m_Content = value; } }


        [SerializeField]
        private MovementType m_MovementType = MovementType.Elastic;
        public MovementType movementType { get { return m_MovementType; } set { m_MovementType = value; } }

        [SerializeField]
        private float m_Elasticity = 0.1f; // Only used for MovementType.Elastic
        public float elasticity { get { return m_Elasticity; } set { m_Elasticity = value; } }

        [SerializeField]
        private bool m_Inertia = true;
        public bool inertia { get { return m_Inertia; } set { m_Inertia = value; } }

        [SerializeField]
        private float m_DecelerationRate = 0.135f; // Only used when inertia is enabled
        public float decelerationRate { get { return m_DecelerationRate; } set { m_DecelerationRate = value; } }

        [SerializeField]
        private float m_ScrollSensitivity = 1.0f;
        public float scrollSensitivity { get { return m_ScrollSensitivity; } set { m_ScrollSensitivity = value; } }

        [SerializeField]
        private RectTransform m_Viewport;
        public RectTransform viewport { get { return m_Viewport; } set { m_Viewport = value; SetDirtyCaching(); } }

        [SerializeField]
        private Scrollbar m_HorizontalScrollbar;
        public Scrollbar horizontalScrollbar
        {
            get
            {
                return m_HorizontalScrollbar;
            }
            set
            {
                if (m_HorizontalScrollbar)
                    m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
                m_HorizontalScrollbar = value;
                if (m_HorizontalScrollbar)
                    m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
                SetDirtyCaching();
            }
        }

        [SerializeField]
        private Scrollbar m_VerticalScrollbar;
        public Scrollbar verticalScrollbar
        {
            get
            {
                return m_VerticalScrollbar;
            }
            set
            {
                if (m_VerticalScrollbar)
                    m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
                m_VerticalScrollbar = value;
                if (m_VerticalScrollbar)
                    m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
                SetDirtyCaching();
            }
        }

        [SerializeField]
        private ScrollbarVisibility m_HorizontalScrollbarVisibility;
        public ScrollbarVisibility horizontalScrollbarVisibility { get { return m_HorizontalScrollbarVisibility; } set { m_HorizontalScrollbarVisibility = value; SetDirtyCaching(); } }

        [SerializeField]
        private ScrollbarVisibility m_VerticalScrollbarVisibility;
    
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值