今天来说说一直都让我在项目中头疼的其中一个问题,NGUI 的scrollView 列表性能问题,实现循环使用item减少性能上的开销。
希望能够给其他同学们使用和提供一个我个人的思路,这个写的不是太完美,目前我在项目中使用了,希望大神能够给更多的建议来优化scrollView.
思 路:通过调整item位置来实现item循环使用,在通过delegate来实现数据刷新
功 能:
SetGrid(int imax, ScrollGridSetItem sc)
NextIndex()
PreIndex()
GotoIndex(int index)
ClearChild()
上代码:
/*** ****************** scrollView 循环 item 减少性能消耗 ************ *@2015/03/31 *@author fastHro * *基于NGUI Version 3.7.7 *使用说明:在scrollView 下新建空物体,然后把此脚本挂在上面然后设置相关属性. *注意:scrollGrid 下不需要有item ,由脚本自动创建关联的item * 刷新item数据通过 委托函数scrollGridSetItem 来设置 * */ using UnityEngine; using System.Collections; public class ScrollGrid : MonoBehaviour { public delegate void ScrollGridSetItem(Transform[] trans, int start, int end); /** @index -1 start, 0 start and end, 1 end, 2 center */ public delegate void ScrollGridMessage(int index); private ScrollGridSetItem scrollGridSetItem; private ScrollGridMessage scrollGridMessage; #region 外部数据 /** 最多显示几个 */ public int defaultShowMaxLine; /** 默认生成几个 */ public int defaultMaxLine; /** 每一行的间距 */ public int space; /** 每一行的size */ public Vector2 size; /** gotoIndex 速率 */ public float autoVelocity = 8f; /** 当数据个数小于defaultMaxLine 列表是否可以移动 */ public bool moveScrollViewLessMaxLine = true; /** 每次滑动之后是否使item显示在中心 */ public bool centerScrollViewOnChild = false; #endregion #region 内部数据 private int indexStar = 0; private int indexEnd = 0; private int indexMax = 0; private Vector3 panelPos = Vector3.zero; private int lastIndex = 0; private int showStartIndex = 0; #endregion #region scrollView private UIPanel panel; private UIScrollView sView; /** 列表的 cliping size */ public Vector2 vSize; /** 列表移动方式 */ private UIScrollView.Movement movement; private Transform panelTransfrom; #endregion #region item public GameObject ItemPrefab; private Transform[] itemTrans; #endregion void Awake() { sView = transform.GetComponentInParent<UIScrollView> (); panel = sView.transform.GetComponentInParent<UIPanel> (); panelTransfrom = panel.gameObject.transform; } #region init /** 初始化设置 */ public void SetGrid(int imax, ScrollGridSetItem sc) { if (!moveScrollViewLessMaxLine) { if(imax < defaultShowMaxLine) { sView.enabled = false; } } if (defaultMaxLine > imax) { defaultMaxLine = imax; } initGrid (); indexMax = imax; scrollGridSetItem = sc; if (scrollGridSetItem != null) { scrollGridSetItem(itemTrans, indexStar, indexEnd); } SetMessage (); } public void SetGrid(int imax, ScrollGridSetItem sc, ScrollGridMessage sm) { scrollGridMessage = sm; SetGrid (imax, sc); } /** 向后滑动一格 */ public void NextIndex() { int ix = GetShowStartIndex (); //Debug.Log ("next = " + ix); GotoIndex (ix + 1); } /** 向前滑动一格 */ public void PreIndex() { int ix = GetShowStartIndex (); //Debug.Log ("pre = " + ix); GotoIndex (ix - 1); } /**直接跳到指定位置*/ public void GotoIndex(int index) { if (index < 0) { return; } if (index > indexMax - defaultShowMaxLine) { return; } float ip = GetPanelPositionByIndex (index); ip = Mathf.Abs (ip); Vector3 v3 = Vector3.zero; Vector2 v2 = Vector2.zero; if(movement == UIScrollView.Movement.Horizontal) { v3.x = panel.clipSoftness.x - ip; v2.x = ip + panel.clipSoftness.x; if(panelTransfrom.localPosition.x == ip) { return; } } else if(movement == UIScrollView.Movement.Vertical){ v3.y = ip - panel.clipSoftness.y; v2.y = -ip + panel.clipSoftness.y; if(panelTransfrom.localPosition.y == ip) { return; } } SpringPanel.Begin (sView.panel.cachedGameObject, v3, autoVelocity); } /** 初始化 */ void initGrid() { ClearChild (); itemTrans = new Transform[defaultMaxLine]; for (int i = 0; i < defaultMaxLine; i++) { itemTrans[i] = CreatItemTransfrom(i); } movement = sView.movement; sView.onDragStarted = onDragStarted; sView.onDragFinished = onDragFinished; sView.onMomentumMove = onMomentumMove; sView.onStoppedMoving = onStoppedMoving; /** 初始化位置 */ ResetPostion (); /** 初始化数据 */ indexStar = 0; indexEnd = defaultMaxLine - 1; indexMax = 0; panelPos = Vector3.zero; lastIndex = 0; showStartIndex = 0; if (scrollGridSetItem != null) { scrollGridSetItem(itemTrans, indexStar, indexEnd); } SetMessage(); ResetScrollPanelPosition (); } void ResetPostion() { float fristCoord = 0; bool hasFristCoord = false; Vector3 np = Vector3.zero; float vp = 0; float ip = 0; float fp = 0; if(movement == UIScrollView.Movement.Horizontal) { vp = vSize.x; ip = size.x; }else if(movement == UIScrollView.Movement.Vertical) { vp = vSize.y; ip = size.y; } for(int i = 0; i < itemTrans.Length; i++) { if(!hasFristCoord) { fristCoord = vp / 2.0f - ip / 2.0f; if(fristCoord > vp / 2.0f) { fristCoord = vp / 2.0f; } hasFristCoord = true; fp = fristCoord; }else{ fp = fristCoord - (ip + space) * i; } if(movement == UIScrollView.Movement.Horizontal) { np.x = -fp; }else if(movement == UIScrollView.Movement.Vertical) { np.y = fp; } itemTrans[i].localPosition = np; } } #endregion #region switch Item void SwitchItem(int id) { indexStar = id; //Debug.Log("indexStar : " + indexStar.ToString()); if(movement == UIScrollView.Movement.Horizontal) { //Debug.Log("indexStar : " + indexStar.ToString()); //Debug.Log("stop : " + (indexMax - defaultMaxLine).ToString()); if(indexStar > 0) { indexStar = 0; return; } if(indexStar < -(indexMax - defaultMaxLine)) { indexStar = indexMax - defaultMaxLine; return; } indexStar = Mathf.Abs(indexStar); }else if(movement == UIScrollView.Movement.Vertical) { /** star */ if(indexStar < 0) { indexStar = 0; return; } /**end*/ if(indexStar > indexMax - defaultMaxLine) { indexStar = indexMax - defaultMaxLine; return; } } if (lastIndex != indexStar) { if (lastIndex < indexStar) { Transform t = itemTrans[0]; for(int i = 0; i < itemTrans.Length - 1; i++) { itemTrans[i] = itemTrans[i+1]; } float fy = 0; Vector3 v3 = t.localPosition; if(movement == UIScrollView.Movement.Horizontal) { fy = itemTrans[itemTrans.Length - 2].localPosition.x + size.x + space; v3.x = fy; }else if(movement == UIScrollView.Movement.Vertical) { fy = itemTrans[itemTrans.Length - 2].localPosition.y - size.y - space; v3.y = fy; } t.localPosition = v3; itemTrans[itemTrans.Length - 1] = t; }else{ Transform t = itemTrans[itemTrans.Length - 1]; for(int i = itemTrans.Length - 1; i > 0; i--) { itemTrans[i] = itemTrans[i-1]; } float fy = 0; Vector3 v3 = t.localPosition; if(movement == UIScrollView.Movement.Horizontal) { fy = itemTrans[1].localPosition.x - size.x - space; v3.x = fy; }else if(movement == UIScrollView.Movement.Vertical) { fy = itemTrans[1].localPosition.y + size.y + space; v3.y = fy; } t.localPosition = v3; itemTrans[0] = t; } lastIndex = indexStar; if (scrollGridSetItem != null) { scrollGridSetItem(itemTrans, indexStar, indexEnd); } sView.UpdatePosition(); } } /** 重置scroll panel */ void ResetScrollPanelPosition() { if(movement == UIScrollView.Movement.Horizontal) { if (indexStar == 0) { ResetPostion (); Vector3 v3 = Vector3.zero; v3.x = panel.clipSoftness.x; Vector2 v2 = Vector2.zero; v2.x = -panel.clipSoftness.x; panelTransfrom.localPosition = v3; panel.clipOffset = v2; } } else if(movement == UIScrollView.Movement.Vertical){ if (indexStar == 0) { ResetPostion (); Vector3 v3 = Vector3.zero; v3.y = -panel.clipSoftness.y; Vector2 v2 = Vector2.zero; v2.y = panel.clipSoftness.y; panelTransfrom.localPosition = v3; panel.clipOffset = v2; } } } void SetItemPostion(){ float fristCoord = GetPositionByIndex(indexStar); //Debug.Log ("fristCoord = " + fristCoord + " indexStar " + indexStar); Vector3 np = Vector3.zero; float ip = 0; float fp = 0; if(movement == UIScrollView.Movement.Horizontal) { ip = size.x; }else if(movement == UIScrollView.Movement.Vertical) { ip = size.y; } for(int i = 0; i < itemTrans.Length; i++) { fp = fristCoord - (ip + space) * i; if(movement == UIScrollView.Movement.Horizontal) { np.x = -fp; }else if(movement == UIScrollView.Movement.Vertical) { np.y = fp; } itemTrans[i].localPosition = np; } } /** 通过index 获得item位置 */ public float GetPositionByIndex(int index) { float fristCoord = 0; bool hasFristCoord = false; Vector3 np = Vector3.zero; float vp = 0; float ip = 0; float fp = 0; if(movement == UIScrollView.Movement.Horizontal) { vp = vSize.x; ip = size.x; }else if(movement == UIScrollView.Movement.Vertical) { vp = vSize.y; ip = size.y; } fristCoord = vp / 2.0f - ip / 2.0f; if(fristCoord > vp / 2.0f) { fristCoord = vp / 2.0f; } fp = fristCoord - (ip + space) * index; return fp; } /** 通过index 获得panel位置 */ public float GetPanelPositionByIndex(int index) { if(movement == UIScrollView.Movement.Horizontal) { return (size.x + space) * index; }else if(movement == UIScrollView.Movement.Vertical) { return (size.y + space) * index; } return 0; } /** 获得列表显示的第一个的index */ int GetShowStartIndex() { Vector3 v3 = panelTransfrom.localPosition; if(movement == UIScrollView.Movement.Horizontal) { if(indexStar > 0) { int index = (int)((v3.x - panel.clipSoftness.x) / (size.x + space)); return Mathf.Abs(index); } }else if(movement == UIScrollView.Movement.Vertical) { if(indexStar > 0) { int index = (int)((v3.y + panel.clipSoftness.y) / (size.y + space)); return index; } } return indexStar; } #endregion #region 创建 和 清空Item Transform CreatItemTransfrom(int index) { GameObject go = Instantiate (ItemPrefab, Vector3.zero, Quaternion.identity) as GameObject; if (go != null) { go.transform.parent = transform; go.transform.localScale = Vector3.one; go.transform.localPosition = Vector3.zero; go.transform.localRotation = Quaternion.identity; go.name = "ScrollGrid Item " + index.ToString(); return go.transform; } Debug.LogError ("scrollGrid creat item prefab "); return null; } public void ClearChild() { if (transform.childCount < 1) { return; } Transform t = transform.GetChild (0); t.parent = null; Destroy (t.gameObject); ClearChild (); } #endregion #region scroll delegate void onDragStarted() { //Debug.Log ("onDragStarted"); } void onDragFinished() { //Debug.Log ("onDragFinished"); SetItemPostion(); if (centerScrollViewOnChild) { GotoIndex(GetShowStartIndex()); } } void onMomentumMove() { //Debug.Log ("onMomentumMove"); } void onStoppedMoving() { //Debug.Log ("onStoppedMoving"); ResetScrollPanelPosition (); } #endregion #region update and LateUpdate int stepIndex = 0; void LateUpdate (){ panelPos = panel.gameObject.transform.localPosition; if(movement == UIScrollView.Movement.Horizontal) { stepIndex = (int)(panelPos.x / (size.x + space)); }else if(movement == UIScrollView.Movement.Vertical) { stepIndex = (int)(panelPos.y / (size.y + space)); } SwitchItem (stepIndex); SetMessage(); } #endregion #region message /** 通知是否到两头 */ void SetMessage() { if (scrollGridMessage != null) { int index = GetShowStartIndex(); if(index == 0) { if(indexMax <= defaultShowMaxLine) { scrollGridMessage(0); }else{ scrollGridMessage(-1); } }else{ if(indexMax - defaultShowMaxLine > index) { if(movement == UIScrollView.Movement.Horizontal) { if(index + 1 == indexMax - defaultShowMaxLine) { if(Mathf.Abs(itemTrans[itemTrans.Length - 1].localPosition.x) == Mathf.Abs(GetPositionByIndex(indexMax - 1))) { scrollGridMessage(1); }else{ scrollGridMessage(2); } }else{ scrollGridMessage(2); } }else if(movement == UIScrollView.Movement.Vertical) { if(index + 1 == indexMax - defaultShowMaxLine) { if(Mathf.Abs(itemTrans[itemTrans.Length - 1].localPosition.y) == Mathf.Abs(GetPositionByIndex(indexMax - 1))) { scrollGridMessage(1); }else{ scrollGridMessage(2); } }else{ scrollGridMessage(2); } } }else{ scrollGridMessage(1); } } } } #endregion }
*********************************** 测试 **********************************
在unity里首先需要按照:
List为scrollView 的父类(空GameObject)
ScrollView为NGUI自带的组件
ScrollGrid为我们自己的脚本
最后在用测试代码来测试
using UnityEngine; using System.Collections; public class UITest : MonoBehaviour { public ScrollGrid sg; void Update() { if (Input.GetKeyDown (KeyCode.A)) { sg.SetGrid(20, setItem); } else if (Input.GetKeyDown (KeyCode.S)) { sg.GotoIndex(5); } } void Start() { sg.SetGrid(20, setItem); } void setItem(Transform[] t, int start, int end) { for (int i = 0; i < t.Length; i++) { t[i].GetComponent<ItemTest>().Name = "item " + (start + i).ToString(); } } }
******************** 测试结果 ***********************
*******************************************************
希望我写的还算详细,就写到这里了!!!!!!
支持原创转载请注明出处http://home.cnblogs.com/u/fastHro/