NGUI------UIWrapContent无限循环

之前做项目就想写一篇关于UI无限循环的总结,但一直没写过,一是自己也没理解清楚,二是项目忙,也没时间。现在由于在等待新项目的启动,终于有时间去总结一下UI方面的相关知识。

无限循环:主要应用在游戏排行榜,邮件,国家成员等需要大量创建Gameobj的场景,因为如果每多一条数据,就创建一个Gameobj去显示的话,那么游戏当中会存在大量的Obj,消耗性能,造成卡帧的现象,影响玩家游戏体验。这时候,就需要无限循环的功能了,因为游戏视窗大小是固定的,只显示一定数量的UI,这样,就可以用一定数量的UI去模拟所有数据的显示。

UIWrapContent就是NGUI封装好,用来实现这套功能的脚本。

接下来,就来详解这个脚本。

protected virtual void Start ()
{
	SortBasedOnScrollMovement();
	WrapContent();    //核心函数
	if (mScroll != null) mScroll.GetComponent().onClipMove = OnMove;    //由UIPanel的驱动,来更新对应的数据
	mFirstTime = false;
}

public void SortBasedOnScrollMovement ()    //这段函数主要用来将他的子物体排序,代替了UIGrid的功能
{
	if (!CacheScrollView()) return;

	// Cache all children and place them in order
	mChildren.Clear();
	for (int i = 0; i < mTrans.childCount; ++i)
		mChildren.Add(mTrans.GetChild(i));

	// Sort the list of children so that they are in order
	if (mHorizontal) mChildren.Sort(UIGrid.SortHorizontal);
	else mChildren.Sort(UIGrid.SortVertical);
	ResetChildPositions();
}
NGUI------UIWrapContent无限循环_第1张图片

//上图对wrapcontent的实现,大致画了一下,以便于理解
public void WrapContent ()    //这个函数,是核心函数,用来实现无限循环的功能
{
	float extents = itemSize * mChildren.Count * 0.5f; //计算所有的UI距离,并分为一半(后面用来判断用)
	Vector3[] corners = mPanel.worldCorners;
		
	for (int i = 0; i < 4; ++i)        //将panel的世界坐标转化为本地坐标(具体哪些点,可以参考上图)
	{
		Vector3 v = corners[i];
		v = mTrans.InverseTransformPoint(v);
			corners[i] = v;
	}
		
	Vector3 center = Vector3.Lerp(corners[0], corners[2], 0.5f); //插值计算出中心点
	bool allWithinRange = true;
	float ext2 = extents * 2f;    //ui真正的范围大小

	if (mHorizontal)    //横向
	{
		float min = corners[0].x - itemSize;     //最小范围
		float max = corners[2].x + itemSize;     //最大范围

		for (int i = 0, imax = mChildren.Count; i < imax; ++i) //遍历整个子物体
		{
			Transform t = mChildren[i];
			float distance = t.localPosition.x - center.x; //判断移动超出的距离

			if (distance < -extents) //距离超出左边范围
			{
				Vector3 pos = t.localPosition;
				pos.x += ext2;    //ui由最左边移动到最右边
				distance = pos.x - center.x;
				int realIndex = Mathf.RoundToInt(pos.x / itemSize);

				if (minIndex == maxIndex || (minIndex <= realIndex && realIndex <= maxIndex)) //如果满足设置的数量,就进行位置变换
				{
					t.localPosition = pos;
					UpdateItem(t, i);
				}
				else allWithinRange = false;
			}
			else if (distance > extents) //距离超出右边范围
			{
				Vector3 pos = t.localPosition;
				pos.x -= ext2;
				distance = pos.x - center.x;
				int realIndex = Mathf.RoundToInt(pos.x / itemSize);

				if (minIndex == maxIndex || (minIndex <= realIndex && realIndex <= maxIndex))
				{
					t.localPosition = pos;
					UpdateItem(t, i);
				}
				else allWithinRange = false;
			}
			else if (mFirstTime) UpdateItem(t, i);

			if (cullContent)
			{
				distance += mPanel.clipOffset.x - mTrans.localPosition.x;
				if (!UICamera.IsPressed(t.gameObject))
					NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
			}
		}
	}
	else    //纵向
	{
		float min = corners[0].y - itemSize;
		float max = corners[2].y + itemSize;

		for (int i = 0, imax = mChildren.Count; i < imax; ++i)
		{
			Transform t = mChildren[i];
			float distance = t.localPosition.y - center.y;

			if (distance < -extents)
			{
				Vector3 pos = t.localPosition;
				pos.y += ext2;
				distance = pos.y - center.y;
				int realIndex = Mathf.RoundToInt(pos.y / itemSize);

				if (minIndex == maxIndex || (minIndex <= realIndex && realIndex <= maxIndex))
				{
					t.localPosition = pos;
					UpdateItem(t, i);
				}
				else allWithinRange = false;
			}
			else if (distance > extents)
			{
				Vector3 pos = t.localPosition;
				pos.y -= ext2;
				distance = pos.y - center.y;
				int realIndex = Mathf.RoundToInt(pos.y / itemSize);

				if (minIndex == maxIndex || (minIndex <= realIndex && realIndex <= maxIndex))
				{
					t.localPosition = pos;
					UpdateItem(t, i);
				}
				else allWithinRange = false;
			}
			else if (mFirstTime) UpdateItem(t, i);

			if (cullContent)
			{
				distance += mPanel.clipOffset.y - mTrans.localPosition.y;
				if (!UICamera.IsPressed(t.gameObject))
					NGUITools.SetActive(t.gameObject, (distance > min && distance < max), false);
			}
		}
	}
	mScroll.restrictWithinPanel = !allWithinRange; //用来控制ScrollView是否可以继续滑动
}
以上是NGUI实现无限循环功能的核心代码。因为无限循环在项目中经常需要用到,所以自己写了一套简易的框架,如有错误,请指正。
NGUI------UIWrapContent无限循环_第2张图片    
    private List mLisData = new List() { "自己","天地", "众生", "白骨", "杨戬", "悟空", "大圣", "贪狼", "破军", "百花", "大鹏" }; //数据
    private UIGridContainer mGrid; //项目代码(用来生成子物体,并负责排序,其实就是UIGrid的扩展)
    private UIScrollView mScrollView;
    private UIWrapContent mWrapContent;
    private UIPanel mPanel;

    private int nInterval = 20; //Item之间的间隔
    private int nItemSize = 60; //Item的大小(这个笔者用的是60*60的正方形,如果不是正方形的话,size需要区分高和宽)
    private int nMaxIndex = 0; 
    private int nMinIndex = 0;

    private void Start()
    {
        mGrid = Utility.Get(this.transform, "Scroll View/grid");
        mScrollView = Utility.Get(this.transform, "Scroll View");
        mWrapContent = Utility.Get(this.transform, "Scroll View/grid");
        mPanel = mScrollView.panel;
        mWrapContent.onInitializeItem = OnInitializeItem;
        //根据上图坐标系,横向的话,如果向右查看,索引是越来越大,向左是越来越小,同理,纵向的话,向上索引越来越大,向下索引越来越小
        if (mScrollView.movement == UIScrollView.Movement.Horizontal)
        {
            nMinIndex = 0;
            nMaxIndex = mLisData.Count - 1; 
        }
        else
        {
            nMinIndex = 0;
            nMaxIndex = mLisData.Count - 1;
        }

        InitHorizontalLoop();
        //InitVerticalLoop();
    }

    private void InitHorizontalLoop()
    {
        if (mGrid == null || mWrapContent == null) return;
        int contentSize = nItemSize + nInterval;
        int nInitItemCount = (int)mPanel.GetViewSize().x / contentSize;

        mGrid.CellWidth = contentSize;
        mGrid.transform.localPosition = new Vector3(-119, 0, 0);
        mGrid.MaxCount = nInitItemCount + 1;   
     //下面这些参数,是无限循环计算所需要的
   mWrapContent.minIndex = nMinIndex; //最小索引
   mWrapContent.maxIndex = nMaxIndex; //最大索引
   mWrapContent.itemSize = contentSize; //item的size
}

private void InitVerticalLoop()
{
        if (mGrid == null || mWrapContent == null) return;
        int contentSize = nItemSize + nInterval;
        int nInitItemCount = (int)mPanel.GetViewSize().y / contentSize;

        mGrid.CellHeight = contentSize;
        mGrid.transform.localPosition = new Vector3(0, 70, 0);
        mGrid.MaxCount = nInitItemCount + 1;

        mWrapContent.minIndex = -nMaxIndex;
        mWrapContent.maxIndex = nMinIndex;
        mWrapContent.itemSize = contentSize;
 }
   
   //无限循环以后的回调函数  warpIndex:子物体Item在父物体下的位置     realIndex:当先刷新的真实数据索引
    private void OnInitializeItem(GameObject go, int wrapIndex, int realIndex)
    {
        Debug.Log("wrapIndex ----:" + wrapIndex);
        Debug.Log("realIndex ----:" + realIndex);
        int nIndex = Mathf.Abs(realIndex);
        if (nIndex > (mLisData.Count - 1)) return;
        UILabel lab = Utility.Get(go.transform, "Label");
        lab.text = mLisData[nIndex];
    }
    //如果有要求,特定显示某一个元素,可以用下面的代码来实现,横向(以0为界,右边是正,左边是负)纵向(以0为界,下面是负,上面是正)因为这是无限循环,所以无法用ScrollView自带的通过SetDragAmount来实现
    //index: -num ---0--- +num 之间取值
    public void ShowElementByIndex(int index)
    {
        if (mScrollView.movement == UIScrollView.Movement.Horizontal)
        {
            Vector2 offset = new Vector2(index * nItemSize, 0);
            mScrollView.transform.localPosition = new Vector3(-offset.x, 0, 0);
            mScrollView.panel.clipOffset = offset;
        }
        else
        {
            Vector2 offset = new Vector2(0, -index * nItemSize);
            mScrollView.transform.localPosition = new Vector3(0, offset.y, 0);
            mScrollView.panel.clipOffset = offset;
        }
    }



你可能感兴趣的:(Unity,Ngui)