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