目录
[TOC]
六、优化版滚动视图
1.常规滚动视图Scroll View
需要用到的UI组件:
UI组件的详细介绍可见博客:UGUI–基础_基础组件_中
-
Scroll View滚动视图;
-
Vertical Layout Group垂直布局组件;(添加在Content上)
-
Content Size Fitter内容大小适配器;(添加在Content上)
Vertical Layout Group垂直布局组件用于将待显示的子项合理得平铺在Content下;Content Size Fitter内容大小适配器用于使Content根据子项来调节自身大小。
最终效果:
2.常规滚动视图的缺陷
常规滚动视图会在一开始就将所有的待显示的子项(Content下的各个Image)生成出来,即使Viewport可视区域并不能同一时间同时显现所有子项。
此时就会存在暂时不显示的子项无端占用内存的现象,如果子项不多还没什么关系,如果子项数量成千上万,就会导致巨大的内存消耗。
所以需要自制一个滚动视图来优化此项。
3.优化的滚动视图
效果展示:
优化的滚动视图核心原理:
- 1.生成的子项数量恒定,使用滚动视图的时候,只是动态为子项赋值数据。(不会频繁删除、生成对象)
- 2.子项动态改变数据核心在于动态改变数据索引,数据索引在初始化的时候确定下来。
- 3.数据索引的改变策略:
假设Viewport同一时间可以显示3个子项,则此滚动视图共生成4个子项。
首先定义3个变量:
- self:自身的数据索引
- start:开始的数据索引
- end:结束的数据索引
代码:
挂载在ScrollView上的CustomScrollView
:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace StudyUGUIExample
{
public class CustomScrollView : MonoBehaviour
{
[Tooltip("子项间距")]
public float Spacing;
private float mItemHeight;
private int mSpawnNum;
private RectTransform mContent;
private List<CustomScrollViewItem> mItems;//子项List
private List<CustomScrollViewItemModel> mModels;//子项数据List
private void Start()
{
//初始化的时候注意逻辑时序
mItems = new List<CustomScrollViewItem>();
mModels = new List<CustomScrollViewItemModel>();
GetModels();
mContent = transform.Find("Viewport/Content").GetComponent<RectTransform>();
GameObject itemPrefab = Resources.Load<GameObject>("CustomScrollViewItem");
mItemHeight = itemPrefab.GetComponent<RectTransform>().rect.height;
SetContentSize();
mSpawnNum = GetSpawnNum(mContent, mItemHeight);
SpawnItem(mSpawnNum, itemPrefab, Spacing);
transform.GetComponent<ScrollRect>().onValueChanged.AddListener(ValueChanged);
}
//当滚动视图Viewport中显示项改变时调用一次
private void ValueChanged(Vector2 data)
{
for (int i = 0; i < mItems.Count; i++)
{
mItems[i].OnValueChanged();
}
}
private int GetSpawnNum(RectTransform content, float itemHeight)
{
float scrollViewHeight = GetComponent<RectTransform>().rect.height;
return Mathf.CeilToInt(scrollViewHeight / (itemHeight + Spacing)) + 1;
}
private void SpawnItem(int count, GameObject prefab, float spacing)
{
CustomScrollViewItem item;
for (int i = 0; i < count; i++)
{
item = Instantiate(prefab, mContent).AddComponent<CustomScrollViewItem>();
item.AddGetModelListener(GetModel);//添加获取数据方法,要在Init之前
item.Init(i, count, spacing);
mItems.Add(item);
}
}
private CustomScrollViewItemModel GetModel(int index)
{
//对数据索引进行合理性校验,不合理的索引值返回空的数据
if (index < 0 || index >= mModels.Count)
return new CustomScrollViewItemModel();
return mModels[index];
}
private void GetModels()
{
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Icon"))
{
mModels.Add(new CustomScrollViewItemModel(sprite, "描述:" + sprite.name));
}
}
private void SetContentSize()
{
//设置Content大小(与数据项个数相关)
float y = mModels.Count * (mItemHeight + Spacing) - Spacing;
mContent.sizeDelta = new Vector2(mContent.sizeDelta.x, y);
}
}
}
处理自定义滚动视图子项相关逻辑的脚本CustomScrollViewItem
:
using System;
using UnityEngine;
using UnityEngine.UI;
namespace StudyUGUIExample
{
public class CustomScrollViewItem : MonoBehaviour
{
private RectTransform mRect;
private Image mImage;
private Text mText;
private RectTransform mContent;
private int mIndex;//序号
private int mSpawnNum;//生成子项数量
private CustomScrollViewItemModel mModel;//数据
private Func<int, CustomScrollViewItemModel> mGetModel;
private float mSpacing;
public RectTransform Rect
{
get
{
if (mRect == null)
mRect = GetComponent<RectTransform>();
return mRect;
}
}
public Image Image
{
get
{
if (mImage == null)
mImage = transform.Find("Image").GetComponent<Image>();
return mImage;
}
}
public Text Text
{
get
{
if (mText == null)
mText = transform.Find("Text").GetComponent<Text>();
return mText;
}
}
public void Init(int index, int spawnNum, float spacing)
{
mContent = transform.parent.GetComponent<RectTransform>();
mIndex = -1;
mSpawnNum = spawnNum;
mSpacing = spacing;
ChangeIndex(index);
}
//当滚动视图Viewport中显示项改变时调用一次
public void OnValueChanged()
{
int start = 0;
int end = 0;
UpdateIndexRange(out start, out end);
CheckSelfIndex(mIndex, start, end);
}
private void UpdateIndexRange(out int start, out int end)
{
//开始点的索引值=Content的Y轴坐标/(子项高度+间距)
start = Mathf.FloorToInt(mContent.anchoredPosition.y / (Rect.rect.height + mSpacing));
end = start + mSpawnNum - 1;
//Debug.LogFormat("start+end+self=={0}+{1}+{2}", start, end, mIndex);
}
private void CheckSelfIndex(int self, int start, int end)
{
int offset = 0;
if (self < start)
{
offset = start - 1 - self;//self向上多移动的格子数
ChangeIndex(end - offset);
Debug.LogFormat("start+end+self=={0}+{1}+{2}", start, end, end - offset);
}
else if (self > end)
{
offset = self - (end + 1);//self向下多移动的格子数
ChangeIndex(start + offset);
//Debug.LogFormat("start+end+self=={0}+{1}+{2}", start, end, start + offset);
}
}
private void ChangeIndex(int newIndex)
{
//index发生改变且改变的值合法才进行index修改
if (mIndex != newIndex && ValidIndex(newIndex))
{
mIndex = newIndex;
mModel = mGetModel(mIndex);
SetModel(mModel);
//设置新位置
SetPos(mIndex);
}
}
private bool ValidIndex(int index)
{
return !mGetModel(index).Equals(new CustomScrollViewItemModel());
}
private void SetPos(int index)
{
//索引值与Y轴坐标相关
Rect.anchoredPosition = new Vector2(0, -index * (Rect.rect.height + mSpacing));
}
private void SetModel(CustomScrollViewItemModel model)
{
Image.sprite = model.Sprite;
Text.text = model.Describe;
}
public void AddGetModelListener(Func<int, CustomScrollViewItemModel> onGetModel)
{
mGetModel = onGetModel;
}
}
}
自定义滚动视图子项的数据CustomScrollViewItemModel
:
using UnityEngine;
namespace StudyUGUIExample
{
public struct CustomScrollViewItemModel
{
public Sprite Sprite;
public string Describe;
public CustomScrollViewItemModel(Sprite sprite, string describe)
{
Sprite = sprite;
Describe = describe;
}
}
}
七、血条
(受需求影响,此处暂略)
八、UGUI特效层级问题
在粒子特效的Particle System组件中的Renderer下有Order in Layer可以用来控制特效层级:
创建两个Canvas,设置Render Mode为Screen Space - Camera,并设置好渲染摄像机Render Camera;分别为两个Canvas设置不同的渲染层级,一个的Sorting Layer设置为0,一个设置为10。
因为创建多个Canvas会产生渲染上的性能消耗,所以尽可能创建较少的Canvas,每一个负责一组渲染层级,将相同渲染层级的UI元素放在同一个Canvas下,如此一来可节省性能。
效果如下:(Order大的会覆盖小的进行渲染)
九、裁切粒子特效
有时候会有在指定范围内显示粒子特效的需求,此时需要一个遮罩,创建一个空物体,为其添加Sprite Renderer组件、Sprite Mask组件,为组件指定一个遮罩图片。
其中Sprite Mask组件遮罩依据的是图片的透明度Alpha值,Alpha Cutoff即为透明度裁剪阈值,高于此透明度的像素点会视作遮罩内部,反之为遮罩外部。
然后为粒子特效组件Particle System设置遮罩:(内部会自动搜寻场景中存在Sprite Mask组件的物体)
Hierarchy面板如下:
效果如下:
十、互动照片墙
(暂略)