测试环境


操作系统:Windows8.1

开发工具:Unity5.5.2


1、问题描述,在实际开发过程中经常会使用ScrollRect实现滚动列表,当初次加载数据比较多的情形时,Unity3D会出现比较严重的卡顿,降低帧率,其原因主要为 a、集中式的申请ItemRenderer对象大量堆内存,b、帧Draw Call增加。

2、解决方案,主要逻辑根据Viewport即Mask Visual区域计算当前上下文显示ItemRenderer个数,同时滚动的时候会动态计算scrollLineIndex行数,来重新计算每一个ItemRenderer渲染的位置,从而复用ItemRenderer。

   如图所示:当前数据已经有31个,但是ItemRenderer的实例只有21个,即当前满屏情况下最大的显示个数。

3、完成代码

UIWrapItem 用来作为数据、ItemRenderer prefab的 具体关联类。

using UnityEngine;

/// <summary>
/// Wrapper Item for user data index.
/// </summary>
public class UIWrapItem : MonoBehaviour { /// <summary>
/// User data index
/// </summary>
private int dataIndex = ; /// <summary>
/// Item container
/// </summary>
private UIWrapContainer container = null; private void OnDestroy()
{
container = null;
} /// <summary>
/// Container setter
/// </summary>
public UIWrapContainer Container
{
set
{
container = value;
}
} /// <summary>
/// DataIndex getter & setter
/// </summary>
public int DataIndex
{
set
{
dataIndex = value; gameObject.name = dataIndex.ToString(); if (dataIndex >= )
{
transform.localPosition = container.GetLocalPositionByDataIndex(dataIndex); if (container.onInitializeItem != null)
{
container.onInitializeItem(gameObject, dataIndex);
}
}
} get { return dataIndex; }
}
}

UIWrapContainer作用为UIWrapItem的容器,提供基本的数据判定逻辑。

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic; /// <summary>
/// This script makes it possible for a scroll view to wrap its item, creating scroll views.
/// Usage: simply attach this script underneath your scroll view where you would normally place a container:
///
/// + Scroll View
/// |- UIWrapContainer
/// |-- Item 1
/// |-- Item 2
/// |-- Item 3
/// </summary>
[DisallowMultipleComponent]
public class UIWrapContainer : MonoBehaviour
{
public delegate void OnInitializeItem(GameObject go, int dataIndex); public OnInitializeItem onInitializeItem = null; public enum Arraygement
{
Horizontal,
Vertical
} #region public variables /// <summary>
/// Type of arragement
/// </summary>
public Arraygement arrangement = Arraygement.Horizontal; /// <summary>
/// Maximum item per line.
/// If the arrangement is horizontal, this denotes the number of columns.
/// If the arrangement is vertical, this stands for the number of rows.
/// </summary>
public int maxPerLine = ; /// <summary>
/// The width of each of the items.
/// </summary>
public float itemWidth = 100f; /// <summary>
/// The height of each of the items.
/// </summary>
public float itemHeight = 100f; /// <summary>
/// The Horizontal space of each of the items.
/// </summary>
public float itemHorizontalSpace = 0f; /// <summary>
/// The vertical space of each of the items.
/// </summary>
public float itemVerticalSpace = 0f; public ScrollRect scrollRect = null; public RectTransform viewport = null; public RectTransform container = null; public GameObject itemPrefab = null; #endregion #region private variables private int dataCount = ; private int maximumVisualVerticalItemCount = ; private int maximumVisualHorizontalItemCount = ; private int currentScrollLineIndex = -; private IList<UIWrapItem> activeItems = null; private Queue<UIWrapItem> deactiveItems = null; #endregion void Awake()
{
activeItems = new List<UIWrapItem>();
deactiveItems = new Queue<UIWrapItem>(); maximumVisualVerticalItemCount = Mathf.CeilToInt(viewport.rect.height / (itemHeight + itemVerticalSpace));
maximumVisualHorizontalItemCount = Mathf.CeilToInt(viewport.rect.width / (itemWidth + itemHorizontalSpace));
} void Start()
{ } public void Initialize(int dataCount)
{
if (scrollRect == null || container == null || itemPrefab == null)
{
Debug.LogError("Not attach scrollRect or container or itemPrefab instance here, please check.");
return;
} if (dataCount <= )
{
return;
} setDataCount(dataCount); scrollRect.onValueChanged.RemoveAllListeners();
scrollRect.onValueChanged.AddListener(OnValueChanged); deactiveItems.Clear();
activeItems.Clear(); UpdateItems();
} public void RemoveItem(int dataIndex)
{
if (dataIndex < || dataIndex >= dataCount)
{
return;
} bool isDestroy = activeItems.Count >= dataCount;
setDataCount(dataCount - ); for (int index = activeItems.Count - ; index >= ; index--)
{
UIWrapItem item = activeItems[index];
int itemDataIndex = item.DataIndex; if (itemDataIndex == dataIndex)
{
activeItems.Remove(item);
if (isDestroy)
{
GameObject.Destroy(item.gameObject);
}
else
{
item.DataIndex = -;
item.gameObject.SetActive(false);
deactiveItems.Enqueue(item);
}
} if (itemDataIndex > dataIndex)
{
item.DataIndex = itemDataIndex - ;
}
} UpdateItems(GetCurrentScrollLineIndex());
} public void AddItem(int dataIndex)
{
if (dataIndex < || dataIndex > dataCount)
{
return;
} bool isNew = false;
for (int index = activeItems.Count - ; index >= ; index--)
{
UIWrapItem item = activeItems[index];
if (item.DataIndex >= (dataCount - ))
{
isNew = true;
break;
}
} setDataCount(dataCount + ); if (isNew)
{
for (int index = , length = activeItems.Count; index < length; index++)
{
UIWrapItem item = activeItems[index];
int itemDataIndex = item.DataIndex;
if (itemDataIndex >= dataIndex)
{
item.DataIndex = itemDataIndex + ;
}
} UpdateItems(GetCurrentScrollLineIndex());
}
else
{
for (int index = , length = activeItems.Count; index < length; index++)
{
UIWrapItem item = activeItems[index];
int itemDataIndex = item.DataIndex;
if (itemDataIndex >= dataIndex)
{
item.DataIndex = itemDataIndex;
}
}
}
} public Vector3 GetLocalPositionByDataIndex(int dataIndex)
{
float x = 0f;
float y = 0f;
float z = 0f; switch (arrangement)
{
case Arraygement.Horizontal:
{
x = (dataIndex / maxPerLine) * (itemWidth + itemHorizontalSpace);
y = -(dataIndex % maxPerLine) * (itemHeight + itemVerticalSpace);
break;
}
case Arraygement.Vertical:
{
x = (dataIndex % maxPerLine) * (itemWidth + itemHorizontalSpace);
y = -(dataIndex / maxPerLine) * (itemHeight + itemVerticalSpace);
break;
}
} return new Vector3(x, y, z);
} private void setDataCount(int count)
{
if (count != dataCount)
{
dataCount = count;
UpdateContainerSize();
}
} private void OnValueChanged(Vector2 value)
{
switch (arrangement)
{
case Arraygement.Vertical:
{
float y = value.y;
if (y >= 1.0f || y <= 0.0f)
{
return;
}
break;
}
case Arraygement.Horizontal:
{
float x = value.x;
if (x <= 0.0f || x >= 1.0f)
{
return;
}
break;
}
} int scrollPerLineIndex = GetCurrentScrollLineIndex(); if (scrollPerLineIndex == currentScrollLineIndex) { return; } UpdateItems(scrollPerLineIndex);
} private void UpdateItems(int scrollLineIndex)
{
if (scrollLineIndex < )
{
return;
} currentScrollLineIndex = scrollLineIndex; int startDataIndex = currentScrollLineIndex * maxPerLine;
int endDataIndex = (currentScrollLineIndex + (arrangement == Arraygement.Vertical ? maximumVisualVerticalItemCount : maximumVisualHorizontalItemCount)) * maxPerLine; for (int index = activeItems.Count - ; index >= ; index--)
{
UIWrapItem item = activeItems[index];
int itemDataIndex = item.DataIndex;
if (itemDataIndex < startDataIndex || itemDataIndex >= endDataIndex)
{
item.DataIndex = -;
activeItems.Remove(item);
item.gameObject.SetActive(false);
deactiveItems.Enqueue(item);
}
} for (int dataIndex = startDataIndex; dataIndex < endDataIndex; dataIndex++)
{
if (dataIndex >= dataCount)
{
continue;
} if (Exists(dataIndex))
{
continue;
} CreateItem(dataIndex);
}
} private void CreateItem(int dataIndex)
{
UIWrapItem item = null;
if (deactiveItems.Count > )
{
item = deactiveItems.Dequeue();
item.gameObject.SetActive(true);
}
else
{
item = AddChild(itemPrefab, container).AddComponent<UIWrapItem>();
} item.Container = this;
item.DataIndex = dataIndex;
activeItems.Add(item);
} private bool Exists(int dataIndex)
{
if (activeItems == null || activeItems.Count <= )
{
return false;
} for (int index = , length = activeItems.Count; index < length; index++)
{
if (activeItems[index].DataIndex == dataIndex)
{
return true;
}
} return false;
} private GameObject AddChild(GameObject prefab, Transform parent)
{
if (prefab == null || parent == null)
{
Debug.LogError("Could not add child with null of prefab or parent.");
return null;
} GameObject go = GameObject.Instantiate(prefab) as GameObject;
go.layer = parent.gameObject.layer;
go.transform.SetParent(parent, false); return go;
} private void OnDestroy()
{
scrollRect = null;
container = null;
itemPrefab = null;
onInitializeItem = null; activeItems.Clear();
deactiveItems.Clear(); activeItems = null;
deactiveItems = null;
}
}

4、未完待续

实际在开中主要使用数组集合作为参数及动态的Add or Rmove操作,所以接下来将会迭代掉采用数组下标的方式提供数据的初始化,增加删除等操作。

最后实现仓促难免存在bug,请与指正。

UGUI ScrollRect 性能优化的更多相关文章

  1. UGUI batch 规则和性能优化

    UGUI batch 规则和性能优化 (基础) Unity 绘图性能优化 - Draw Call Batching : http://docs.unity3d.com/Manual/DrawCallB ...

  2. UGUI性能优化

    http://www.cnblogs.com/suoluo/p/5417152.html http://blog.csdn.net/uwa4d/article/details/54344423 htt ...

  3. [转] 擎天哥as3教程系列第二回——性能优化

    所谓性能优化主要是让游戏loading和运行的时候不卡. 一  优化fla导出的swf的体积? 1,  在flash中,舞台上的元件最多,生成的swf越大,库里面有连接名的元件越多,swf越大.当舞台 ...

  4. Unity技术支持团队性能优化经验分享

    https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247490321&idx=1&sn=f9f34407ee5c5d ...

  5. U3D开发性能优化笔记(待增加版本.x)

    http://blog.csdn.net/kaitiren/article/details/45071997 此总结由自己经验及网上收集整理优化内容 包括: .代码方面: .函数使用方面: .ui注意 ...

  6. Unity 性能优化(力荐)

    开始之前先分享几款性能优化的插件: 1.SimpleLOD : 除了同样拥有Mesh Baker所具有的Mesh合并.Atlas烘焙等功能,它还能提供Mesh的简化,并对动态蒙皮网格进行了很好的支持. ...

  7. 01.SQLServer性能优化之----强大的文件组----分盘存储

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 文章内容皆自己的理解,如有不足之处欢迎指正~谢谢 前天有学弟问逆天:“逆天,有没有一种方 ...

  8. 03.SQLServer性能优化之---存储优化系列

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 概  述:http://www.cnblogs.com/dunitian/p/60413 ...

  9. Web性能优化:What? Why? How?

    为什么要提升web性能? Web性能黄金准则:只有10%~20%的最终用户响应时间花在了下载html文档上,其余的80%~90%时间花在了下载页面组件上. web性能对于用户体验有及其重要的影响,根据 ...

随机推荐

  1. 在IIS上部署你的ASP.NET Core项目

    概述 与ASP.NET时代不同,ASP.NET Core不再是由IIS工作进程(w3wp.exe)托管,而是使用自托管Web服务器(Kestrel)运行,IIS则是作为反向代理的角色转发请求到Kest ...

  2. 4月5日--课堂笔记--JS内置对象

    JavaScript 4.5 一.    JS内置对象 1.数组Array a)创建语法1:var arr=new Array(参数); i.       没有参数:创建一个初始容量为0的数组 ii. ...

  3. POST与GET

    面试如果被问到这个问题,相信很多人都是会心一笑,答案随口而来: 1.GET在浏览器回退时是无害的,而POST会再次提交请求. 2.GET请求会被浏览器主动cache,而POST不会,除非手动设置. 3 ...

  4. python 面向对象编程(一)

    一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...

  5. Sql日期时间格式转换大全

    简介:我们经常会用到sql日期转换,这里列出了日期输出为字符串的所有代码 输出格式 2014-06-12 ), ) 输出格式 2014-06-12 22:31:18 ), ) 以下是各种转换日期代码号 ...

  6. Android -- 从源码解析Handle+Looper+MessageQueue机制

    1,今天和大家一起从底层看看Handle的工作机制是什么样的,那么在引入之前我们先来了解Handle是用来干什么的 handler通俗一点讲就是用来在各个线程之间发送数据的处理对象.在任何线程中,只要 ...

  7. spine动画融合与动画叠加

    spine动画融合与动画叠加 一.动画融合setMix 1.概述:两个动作之间的平滑过渡 参数duration为需要多少时间从fromAnimation过渡到toAnimation,过渡时间为动画重叠 ...

  8. 在线恶意软件和URL分析集成框架 – MalSub

    malsub是一个基于Python 3.6.x的框架,它的设计遵循了当前最流行的互联网软件架构RESTful架构,并通过其RESTful API应用程序编程接口(API),封装了多个在线恶意软件和UR ...

  9. 给上传文件的input控件"美容"

    作为一名前端程序猿呢,在工作中经常会遇到form表单这种东西.然而表单的其他input控件样式还是很好改变的.但是,唯独input类型是file的文件上传控件可能就没那么好打扮的漂亮.刚好菜鸟我最近工 ...

  10. 在Debian 8 上安装自动化工具Ansible

    如果你是新手,就不要犹豫了,ansible是你最好的选择,本人菜鸟一个.废话少说,开始安装! 实验环境: 192.168.3.190 192.168.3.191 192.168.3.192 192.1 ...