Unity -UGUI Scroll View 停在正中間

Unity 內建的 UGUI Scroll View 介面,要怎樣讓裡面的項目滑動之後停在正中央呢?

環境
Unity 2021.1.10f1 (64-bit)

我做了一些測試,來看看吧!

會寫一個 WndScrollCenterVertical.cs 來處理這件事,先建立測試環境!

一個簡單的 Scroll View,垂直移動的,裡面有 6 個 200 x 200 的小圖片。其他設定都照以下圖片。

WndScrollCenterVertical.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class WndScrollCenterVertical : MonoBehaviour, IEndDragHandler
{
    public float contentItemHeight = 200.0f;    //content 裡面項目的尺寸

    private GameObject _goContent;
    private RectTransform _rcContent;
    private RectTransform _rcScrollView;

    private float _fStart1;
    private float _fStart2;

    private float _fNewY;

    void Start()
    {
        RectTransform rcContent = this.GetComponent<ScrollRect>().content;
        _goContent = rcContent.gameObject;
        _rcContent = _goContent.GetComponent<RectTransform>();

        _rcScrollView = GetComponent<RectTransform>();

        //_rcScrollView.sizeDelta.y 是 ScrollView 的高度
        _fStart1 = contentItemHeight - (_rcScrollView.sizeDelta.y / 2);
        _fStart2 = contentItemHeight - ((_rcScrollView.sizeDelta.y - contentItemHeight) / 2);
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        int index = GetIndex();
        _fNewY = _fStart2 + ((index - 1) * contentItemHeight);
        _rcContent.localPosition = new Vector2(_rcContent.localPosition.x, _fNewY);
    }
    private int GetIndex()
    {
        //_rcContent.localPosition.y 是 content 的位置
        //_rcContent.sizeDelta.y 是 content 的高度

        float y = (_rcContent.localPosition.y) - _fStart1;
        if (y <= 0)
            return 0;

        int maxIndex = (int)(_rcContent.sizeDelta.y / contentItemHeight) - 1;

        int index = (int)(y / contentItemHeight) + 1;

        if (index >= maxIndex)
            index = maxIndex;

        return index;
    }
}

執行結果如下,只要超過標示在中間的紅線,就會自動設在中間。

加入滑動效果

直接設定位置雖然簡單,但看起來就是太突然,所以加入一點滑動效果

簡單寫一個控制滑動的 script

WndTween.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;

public class WndTween : MonoBehaviour
{
    private float _fSec = 0.2f;  //在幾秒內完成滑動

    private float _timeInterval = 0.04f;
    private float _timeNow;
    private float _timeTrigger;
    private bool _bUpdate = false;

    private Vector3 _vStart;
    private Vector3 _vEnd;
    private Vector3 _vDelta;

    private GameObject _goMove;
    private RectTransform _rcMove;

    private int _nCount;

    [SerializeField]
    protected UnityEvent m_OnTweenEnd = new UnityEvent();

    [ContextMenu("TweenEnd")]
    public void TweenEnd()
    {
        m_OnTweenEnd.Invoke();
    }
    public UnityEvent onTweenEnd
    {
        get { return m_OnTweenEnd; }
        set { m_OnTweenEnd = value; }
    }

    void Start()
    {
        
    }
    void FixedUpdate()
    {
        OnFixedUpdate();
    }
    
    private void OnFixedUpdate()
    {
        if (!_bUpdate)
            return;

        _timeNow = Time.time;
        if (_timeNow >= _timeTrigger)
        {
            _timeTrigger = _timeNow + _timeInterval;

            Move();
        }
    }
    public void SetMoveObject(GameObject goMove)
    {
        _goMove = goMove;
        _rcMove = _goMove.GetComponent<RectTransform>();
    }
    public void SetStart(Vector3 vStart)
    {
        _vStart = vStart;
    }
    public void SetEnd(Vector3 vEnd)
    {
        _vEnd = vEnd;
    }
    public void SetMoveTime(float fSec)
    {
        _fSec = fSec;
    }
    public void StartMove()
    {
        _timeNow = Time.time;
        _timeTrigger = Time.time;

        _vDelta = (_vEnd - _vStart) / (_fSec / _timeInterval);

        _nCount = GetCount();

        _bUpdate = true;

        OnFixedUpdate();
    }
    public void StopCounting()
    {
        _bUpdate = false;
    }
    private void Move()
    {
        _rcMove.localPosition = _rcMove.localPosition + _vDelta;

        _nCount = _nCount - 1;

        if (_nCount <= 0)
        {
            _rcMove.localPosition = _vEnd;

            StopCounting();

            TweenEnd();
        }
    }
    private int GetCount()
    {
        Vector3 vDistance = _vEnd - _vStart;

        int nCountX = (int)(vDistance.x / _vDelta.x) + 1;
        int nCountY = (int)(vDistance.y / _vDelta.y) + 1;
        int nCountZ = (int)(vDistance.z / _vDelta.z) + 1;

        List<int> listCount = new List<int>();
        listCount.Add(nCountX);
        listCount.Add(nCountY);
        listCount.Add(nCountZ);
        listCount.Sort();
        listCount.Reverse();

        return listCount[0];
    }
}

因為加入了滑動效果,所以修改一下 WndScrollCenterVertical.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class WndScrollCenterVertical : MonoBehaviour, IEndDragHandler
{
    public float contentItemHeight = 200.0f;    //content 裡面項目的尺寸

    private GameObject _goContent;
    private RectTransform _rcContent;
    private RectTransform _rcScrollView;

    private float _fStart1;
    private float _fStart2;

    private float _fNewY;

    private WndTween _tween;

    void Start()
    {
        RectTransform rcContent = this.GetComponent<ScrollRect>().content;
        _goContent = rcContent.gameObject;
        _rcContent = _goContent.GetComponent<RectTransform>();

        _rcScrollView = GetComponent<RectTransform>();

        //_rcScrollView.sizeDelta.y 是 ScrollView 的高度
        _fStart1 = contentItemHeight - (_rcScrollView.sizeDelta.y / 2);
        _fStart2 = contentItemHeight - ((_rcScrollView.sizeDelta.y - contentItemHeight) / 2);

        _tween = this.GetComponent<WndTween>();
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        int index = GetIndex();
        _fNewY = _fStart2 + ((index - 1) * contentItemHeight);
        //_rcContent.localPosition = new Vector2(_rcContent.localPosition.x, _fNewY);

        _tween.SetMoveObject(_goContent);
        _tween.SetStart(new Vector3(_rcContent.localPosition.x, _rcContent.localPosition.y, _rcContent.localPosition.z));
        _tween.SetEnd(new Vector3(_rcContent.localPosition.x, _fNewY, _rcContent.localPosition.z));
        _tween.SetMoveTime(0.15f);
        _tween.StartMove();
    }
    private int GetIndex()
    {
        //_rcContent.localPosition.y 是 content 的位置
        //_rcContent.sizeDelta.y 是 content 的高度

        float y = (_rcContent.localPosition.y) - _fStart1;
        if (y <= 0)
            return 0;

        int maxIndex = (int)(_rcContent.sizeDelta.y / contentItemHeight) - 1;

        int index = (int)(y / contentItemHeight) + 1;

        if (index >= maxIndex)
            index = maxIndex;

        return index;
    }
}

執行結果如下,是不是好多了?

參考網址
https://medium.com/@szuwenchen/unity-ugui%E8%A3%BD%E4%BD%9Cpageview-f8a8aa72576d
https://newgoodlooking.pixnet.net/blog/post/110496729

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *