概述
造个轮子,Unity UGUI 循环对象池ScrollView
只用Mask做显示切割,不依赖其他任何UGUI控件,一个纯粹的轮子。
轮子纯粹,兼容性就高。方便各种改造。欢迎交流讨论。
使用到的MonoPool参考我的另一片文章链接: 留个档,MonoBehaviour对象池
核心代码:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ScrollViewEX : MonoBehaviour
{
public enum Type : byte
{
Horizontal, // 横向
Vertical // 纵向
}
[Header("Base")]
[SerializeField] Type m_eDirection = Type.Vertical;
[SerializeField] ScrollViewEX_Item m_prefabItem = null;
[SerializeField] Image m_PanelRect = null;
[SerializeField] float m_nDragScale = 0; // 拖动距离的缩放;
// data
private object[] m_arrDatas = null; // 所有数据
private MonoPool<ScrollViewEX_Item> m_poolItems = null; // 对象池
private float m_nPanelSize = 0; // 限时范围
private float m_nItemSize = 0; // 单个cell尺寸
private float m_nPosMax = 0; // 滚动坐标上限
// runtime
private Dictionary<int, ScrollViewEX_Item> m_dicShowingItems = new Dictionary<int, ScrollViewEX_Item>(); // 显示中的内容
private float m_nPos = 0; // 当前坐标
private float m_nPosCache = 0; // 拖拽过程中的坐标
private void Awake()
{
GameObject go = m_PanelRect.gameObject;
UIEventListener.Get(go).onBeginDrag = OnDragBegin;
UIEventListener.Get(go).onDrag = OnDraging;
UIEventListener.Get(go).onEndDrag = OnDragEnd;
m_poolItems = new MonoPool<ScrollViewEX_Item>(m_prefabItem, this.transform, null, true);
Vector2 v2PanelSize = m_PanelRect.rectTransform.sizeDelta;
m_nItemSize = m_eDirection == Type.Horizontal ? m_prefabItem.Size.x : m_prefabItem.Size.y;
m_nPanelSize = m_eDirection == Type.Horizontal ? v2PanelSize.x : v2PanelSize.y;
}
private void Start()
{
// 这是一个测试
Init(new object[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 });
}
public void Init(object[] arrDatas)
{
m_arrDatas = arrDatas;
m_nPosMax = m_arrDatas.Length * m_nItemSize - m_nPanelSize;
if (m_eDirection == Type.Horizontal)
{
m_nPosMax = 0 - m_nPosMax;
}
Reposition();
}
public void Reposition()
{
m_nPos = 0;
MathViewIndexs(0);
SyncItemsPos(0);
}
/// <summary>
/// 刷新范围内需要显示的内容
/// </summary>
private void MathViewIndexs(float nPos)
{
if (m_eDirection == Type.Horizontal)
{
nPos = 0 - nPos;
}
float ne = nPos + m_nPanelSize;
int nCountFrom = (int)(nPos / m_nItemSize);
int nCountTo = (int)(ne / m_nItemSize);
if (nCountFrom < 0)
{
nCountFrom = 0 - nCountFrom;
}
if (nCountTo < 0)
{
nCountTo = 0 - nCountTo;
}
RefreshList(nCountFrom, nCountTo);
}
private void RefreshList(int nFrom, int nTo)
{
List<int> listShowing = new List<int>(m_dicShowingItems.Keys);
for (int i = nFrom; i <= nTo; i++)
{
listShowing.Remove(i);
}
for (int i = 0; i < listShowing.Count; i++)
{
int nKey = listShowing[i];
var one = m_dicShowingItems[nKey];
m_dicShowingItems.Remove(nKey);
one.Release();
m_poolItems.FreeOne(one);
}
for (int i = nFrom; i <= nTo; i++)
{
if (i >= m_arrDatas.Length)
{
break;
}
if (!m_dicShowingItems.ContainsKey(i))
{
var item = m_poolItems.GetOne();
item.m_nDefaultPos = m_nItemSize * 0.5f + i * m_nItemSize;
item.Init(m_arrDatas[i]);
m_dicShowingItems.Add(i, item);
}
}
}
/// <summary>
/// 刷新所有内容坐标
/// </summary>
private void SyncItemsPos(float nPos = 0)
{
foreach (var item in m_dicShowingItems)
{
SyncItemPos(item.Value, nPos);
}
}
/// <summary>
/// 同步偏移坐标
/// </summary>
private void SyncItemPos(ScrollViewEX_Item item, float nPos)
{
Vector3 pos = item.transform.localPosition;
if (m_eDirection == Type.Horizontal)
{
pos.x = item.m_nDefaultPos - m_nPanelSize * 0.5f + nPos;
}
else
{
pos.y = -item.m_nDefaultPos + m_nPanelSize * 0.5f + nPos;
}
item.transform.localPosition = pos;
}
private void OnDragBegin(PointerEventData e)
{
m_nPosCache = m_nPos;
m_nPosLastFrame = m_nPosCurFrame = m_nPos;
m_isPlaySpring = false;
}
private void OnDraging(PointerEventData e)
{
Vector2 v2Move = e.position - e.pressPosition;
var movepos = (m_eDirection == Type.Horizontal ? v2Move.x : v2Move.y) * m_nDragScale;
m_nPosCache = FilterPosInRange(m_nPos + movepos);
MathViewIndexs(m_nPosCache);
SyncItemsPos(m_nPosCache);
m_nPosLastFrame = m_nPosCurFrame;
m_nPosCurFrame = m_nPosCache;
}
private void OnDragEnd(PointerEventData e)
{
m_nPos = m_nPosCache;
PlaySpring();
}
[Header("Base-Spring")]
[SerializeField] float m_nSpringLenScale = 0; // spring距离倍数
[SerializeField] float m_nSpringSmoothTime = 0; // spring时间
[SerializeField] float m_nSpringStopThreshold = 0; // 判断是否缓动静止
private bool m_isPlaySpring = false;
private float m_nPosLastFrame = 0; // 记录两帧之间的差异
private float m_nPosCurFrame = 0; // 记录两帧之间的差异
private float m_nPosTarget = 0; // 缓动 目标位置
private void PlaySpring()
{
m_nPosTarget = m_nPos + (m_nPosCurFrame - m_nPosLastFrame) * m_nSpringLenScale;
nVelocity = 0;
m_isPlaySpring = true;
}
private float nVelocity = 0.0f; // SmoothDamp 的必要参数
void Update()
{
if (!m_isPlaySpring)
{
return;
}
m_nPos = Mathf.SmoothDamp(m_nPos, m_nPosTarget, ref nVelocity, m_nSpringSmoothTime);
m_nPos = FilterPosInRange(m_nPos);
MathViewIndexs(m_nPos);
SyncItemsPos(m_nPos);
float yv = nVelocity < 0 ? (0 - nVelocity) : nVelocity;
if (yv <= m_nSpringStopThreshold)
{
m_isPlaySpring = false;
}
}
private float FilterPosInRange(float nPos)
{
float nResult =
Mathf.Clamp
(
nPos,
m_eDirection == Type.Horizontal ? m_nPosMax : 0,
m_eDirection == Type.Horizontal ? 0 : m_nPosMax
);
return nResult;
}
}
单个内容对象基类
using UnityEngine;
public class ScrollViewEX_Item : MonoBehaviour
{
[SerializeField] Vector2 m_size = Vector2.zero;
public virtual Vector2 Size
{
get
{
return m_size;
}
}
[HideInInspector] public float m_nDefaultPos = 0;
public virtual void Init(object data)
{
}
public virtual void Release()
{
}
}
测试用内容对象脚本
using UnityEngine;
using UnityEngine.UI;
public class TestRoundItem : ScrollViewEX_Item
{
[SerializeField] Text m_txt = null;
public override void Init(object data)
{
m_txt.text = ((int)data).ToString();
}
public override void Release()
{
m_txt.text = "";
}
}
unity里的面板:
程序学无止尽。
欢迎大家沟通,有啥不明确的,或者不对的,也可以和我私聊
我的QQ 334524067 神一般的狄狄
最后
以上就是爱笑电灯胆为你收集整理的造个轮子,Unity UGUI 循环对象池ScrollView的全部内容,希望文章能够帮你解决造个轮子,Unity UGUI 循环对象池ScrollView所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复