NGUI上下滑动教程
近期因工作需求,我开始研究 NGUI 滑动功能。起初,我参考 NGUI 自带的循环滑动方法,通过隐藏和显示元素来提高 GPU 渲染效率,但效果并不理想。在同事的提醒下,我添加了缓存列表,并不断刷新当前裁剪区域内的数据,最终满足了需求。期间,我也在网上查阅了大量资料,今天恰好有空,便将此经验分享给大家。以下是具体实现步骤及代码。
主要实现步骤
本教程的实现主要分为三部分:
- 重写
UIScrollView
和UICustomDragScrollView
两个脚本。 - 核心脚本
NGUIDynamicScrollBase
,用于计算滑动并播放位移动画。
核心脚本代码
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 = (GameObject)Instantiate(prefab);
go.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.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.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.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
}
以上代码实现了 NGUI 的上下滑动功能,通过重写相关脚本和使用核心脚本 NGUIDynamicScrollBase
,实现了数据的动态加载和滑动效果。希望本教程能对大家有所帮助。