Unity - 无限滚动
今天学习:Unity - UGUI - 无限滚动
目录
版本:
1、Unity 2020.3.10f1
时隔多周,在这里分享出来一点最近的功能吧。
借助UGUI - ScrollView,进行更改,并且实现无限滑动!!!!!!!!
一:思路分享(Share idea)
大概就是下边这个巨作的思路,希望对大家有个思想考量方向,不过不能因为我而放弃了自己的想法,适合自己的才是最好的。说不定你自己的思路比我的还好,不过功能要的快,所以我就百度了,发现早就有大佬们实现了,所以找了一篇下来,刚好满足需求。不过我还是要把自己思路分享出来!!!
画画技术水平有限,见谅。。。。。。。
1、上来获取最上一排的位置信息和宽高度;
2、设置一个触发高度/宽度位置;
3、当再移动的过程中每排最指定节点的高度大于/小于,指定触发点时,重新设置位置到第一排/最后一排的位置,反复如此。
4、在更改位置时,对要被更改的子节点进行设置相关索引或者其他一系列操作。
二:脚本编写(Scripts)
1、既然是要实现无限滑动,那我们就先来编写第一个相关脚本:InfinityGridLayoutGroup
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace ThisXHGame
{
/// <summary>
/// 无限滑动
/// </summary>
[RequireComponent(typeof(GridLayoutGroup))]
[RequireComponent(typeof(ContentSizeFitter))]
public class InfinityGridLayoutGroup : MonoBehaviour
{
/* 要确定当前所属canvas的rect下方scale缩放比例,要与之保持一样。 */
float _scale = 0.061f;
/* 实现无限滚动,需要的最少的child数量。
* 屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 4 行,每一行 3 个。
* 则这个值为 4行 * 3个 + 1行 * 3个 = 15个。*/
int childrenAmount = 0;
#region Private Attribute
ScrollRect scrollRect;
RectTransform rectTransform;
GridLayoutGroup gridLayoutGroup;
ContentSizeFitter contentSizeFitter;
List<RectTransform> children = new List<RectTransform>();
int amount = 0;
int constraintCount;
int realIndex = -1;
float childHeight;
bool hasInit = false;
Vector2 startPosition;
Vector2 gridLayoutSize;
Vector2 gridLayoutPos;
Dictionary<Transform, Vector2> childsAnchoredPosition = new Dictionary<Transform, Vector2>();
Dictionary<Transform, int> childsSiblingIndex = new Dictionary<Transform, int>();
#endregion
public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);
public UpdateChildrenCallbackDelegate updateChildrenCallback = null;
void Start() => childrenAmount = transform.childCount;
IEnumerator InitChildren()
{
yield return 0;
if (!hasInit)
{
//获取Grid的宽度;
rectTransform = GetComponent<RectTransform>();
gridLayoutGroup = GetComponent<GridLayoutGroup>();
gridLayoutGroup.enabled = false;
constraintCount = gridLayoutGroup.constraintCount;
childHeight = gridLayoutGroup.cellSize.y;
contentSizeFitter = GetComponent<ContentSizeFitter>();
contentSizeFitter.enabled = false;
gridLayoutPos = rectTransform.anchoredPosition;
gridLayoutSize = rectTransform.sizeDelta;
//注册ScrollRect滚动回调;
scrollRect = transform.parent.GetComponent<ScrollRect>();
scrollRect.onValueChanged.AddListener((data) => { ScrollCallback(data); });
//获取所有child anchoredPosition 以及 SiblingIndex;
for (int index = 0; index < childrenAmount; index++)
{
Transform child = transform.GetChild(index);
RectTransform childRectTrans = child.GetComponent<RectTransform>();
childsAnchoredPosition.Add(child, childRectTrans.anchoredPosition);
childsSiblingIndex.Add(child, child.GetSiblingIndex());
}
}
else
{
rectTransform.anchoredPosition = gridLayoutPos;
rectTransform.sizeDelta = gridLayoutSize;
children.Clear();
realIndex = -1;
//children重新设置上下顺序;
foreach (var info in childsSiblingIndex)
{
info.Key.SetSiblingIndex(info.Value);
}
//children重新设置anchoredPosition;
for (int index = 0; index < childrenAmount; index++)
{
Transform child = transform.GetChild(index);
RectTransform childRectTrans = child.GetComponent<RectTransform>();
if (childsAnchoredPosition.ContainsKey(child))
{
childRectTrans.anchoredPosition = childsAnchoredPosition[child];
}
else
{
Debug.LogError("Unity Error Log : childs Anchored Position are no contain " + child.name);
}
}
}
//int needCount = (minAmount < amount) ? minAmount : amount;
//获取所有child;
for (int _idx = 0; _idx < childrenAmount; _idx++)
{
Transform child = transform.GetChild(_idx);
child.gameObject.SetActive(true);
RectTransform rect = child.GetComponent<RectTransform>();
children.Add(rect);
//初始化前面几个;
if (_idx < amount)
{
UpdateChildrenInfoCallback(_idx, child);
}
}
startPosition = rectTransform.anchoredPosition;
realIndex = children.Count - 1;
//Debug.Log( scrollRect.transform.TransformPoint(Vector3.zero));
//Debug.Log(transform.TransformPoint(children[0].localPosition));
hasInit = true;
//如果需要显示的个数小于设定的个数;
for (int index = 0; index < childrenAmount; index++)
{
children[index].gameObject.SetActive(index < amount);
}
if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
//如果小了一行,则需要把GridLayout的高度减去一行的高度;
int row = (childrenAmount - amount) / constraintCount;
//Debug.Log($"---------minAmount = {minAmount}----amount = {amount}-----constraintCount = {constraintCount}-------row = {row}--- ");
if (row > 0)
{
rectTransform.sizeDelta -= new Vector2(0, (gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y) * row);
}
}
else
{
//如果小了一列,则需要把GridLayout的宽度减去一列的宽度;
int column = (childrenAmount - amount) / constraintCount;
if (column > 0)
{
rectTransform.sizeDelta -= new Vector2((gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x) * column, 0);
}
}
}
/// <summary>
/// 设置总的个数;
/// </summary>
/// <param name="count">总个数</param>
public void InitSetAmount(int count)
{
amount = count;
StartCoroutine(InitChildren());
}
/// <summary>
/// 滑动回调
/// </summary>
void ScrollCallback(Vector2 data)
{
if (data.y >= 1.0f)
return;
UpdateChildrenInfo();
}
/// <summary>
/// 子物体的更改
/// </summary>
void UpdateChildrenInfo()
{
if (childrenAmount < transform.childCount)
return;
Vector2 currentPos = rectTransform.anchoredPosition;
if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
float offsetY = currentPos.y - startPosition.y;
//Debug.Log("offsetY is " + (offsetY > 0.0f));
if (offsetY > 0)
{
//向上拉,向下扩展;
{
if (realIndex >= amount - 1)
{
startPosition = currentPos;
return;
}
float scrollRectUp = scrollRect.transform.TransformPoint(Vector3.zero).y;
Vector3 childBottomLeft = new Vector3(children[0].anchoredPosition.x, children[0].anchoredPosition.y - gridLayoutGroup.cellSize.y, 0f);
float childBottom = transform.TransformPoint(childBottomLeft).y;
if (childBottom >= scrollRectUp + childHeight * _scale)
{
//移动到底部;
for (int index = 0; index < constraintCount; index++)
{
children[index].SetAsLastSibling();
children[index].anchoredPosition = new Vector2(children[index].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y - gridLayoutGroup.cellSize.y - gridLayoutGroup.spacing.y);
realIndex++;
if (realIndex > amount - 1)
{
children[index].gameObject.SetActive(false);
}
else
{
UpdateChildrenInfoCallback(realIndex, children[index]);
}
}
//GridLayoutGroup 底部加长;
rectTransform.sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
}
}
}
else
{
//向下拉,下面收缩;
if (realIndex + 1 <= children.Count)
{
startPosition = currentPos;
return;
}
RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
Vector3 scrollRectAnchorBottom = new Vector3(0, -scrollRectTransform.rect.height - gridLayoutGroup.spacing.y, 0f);
float scrollRectBottom = scrollRect.transform.TransformPoint(scrollRectAnchorBottom).y;
Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);
float childUp = transform.TransformPoint(childUpLeft).y;
if (childUp < scrollRectBottom)
{
//把底部的一行 移动到顶部
for (int index = 0; index < constraintCount; index++)
{
children[children.Count - 1 - index].SetAsFirstSibling();
children[children.Count - 1 - index].anchoredPosition = new Vector2(children[children.Count - 1 - index].anchoredPosition.x, children[0].anchoredPosition.y + gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
children[children.Count - 1 - index].gameObject.SetActive(true);
UpdateChildrenInfoCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
}
realIndex -= constraintCount;
//GridLayoutGroup 底部缩短;
rectTransform.sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
}
}
}
else
{
float offsetX = currentPos.x - startPosition.x;
if (offsetX < 0)
{
//向左拉,向右扩展;
{
if (realIndex >= amount - 1)
{
startPosition = currentPos;
return;
}
float scrollRectLeft = scrollRect.transform.TransformPoint(Vector3.zero).x;
Vector3 childBottomRight = new Vector3(children[0].anchoredPosition.x + gridLayoutGroup.cellSize.x, children[0].anchoredPosition.y, 0f);
float childRight = transform.TransformPoint(childBottomRight).x;
if (childRight <= scrollRectLeft)
{
//移动到右边;
for (int index = 0; index < constraintCount; index++)
{
children[index].SetAsLastSibling();
children[index].anchoredPosition = new Vector2(children[children.Count - 1].anchoredPosition.x + gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, children[index].anchoredPosition.y);
realIndex++;
if (realIndex > amount - 1)
{
children[index].gameObject.SetActive(false);
}
else
{
UpdateChildrenInfoCallback(realIndex, children[index]);
}
}
//GridLayoutGroup 右侧加长;
rectTransform.sizeDelta += new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
}
}
}
else
{
//向右拉,右边收缩;
if (realIndex + 1 <= children.Count)
{
startPosition = currentPos;
return;
}
RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
Vector3 scrollRectAnchorRight = new Vector3(scrollRectTransform.rect.width + gridLayoutGroup.spacing.x, 0, 0f);
float scrollRectRight = scrollRect.transform.TransformPoint(scrollRectAnchorRight).x;
Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);
float childLeft = transform.TransformPoint(childUpLeft).x;
if (childLeft >= scrollRectRight)
{
//把右边的一行 移动到左边;
for (int index = 0; index < constraintCount; index++)
{
children[children.Count - 1 - index].SetAsFirstSibling();
children[children.Count - 1 - index].anchoredPosition = new Vector2(children[0].anchoredPosition.x - gridLayoutGroup.cellSize.x - gridLayoutGroup.spacing.x, children[children.Count - 1 - index].anchoredPosition.y);
children[children.Count - 1 - index].gameObject.SetActive(true);
UpdateChildrenInfoCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
}
//GridLayoutGroup 右侧缩短;
rectTransform.sizeDelta -= new Vector2(gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x, 0);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
realIndex -= constraintCount;
}
}
}
startPosition = currentPos;
}
/// <summary>
/// 更新回调
/// </summary>
/// <param name="index">当前索引</param>
/// <param name="trans">当前物体</param>
void UpdateChildrenInfoCallback(int index, Transform trans)
{
updateChildrenCallback?.Invoke(index, trans);
}
}
}
2、那我们有了相关脚本,我们就需要去编写相关控制脚本咯:TheInfinityScrollController
using UnityEngine;
using UnityEngine.UI;
namespace ThisXHGame
{
/// <summary>
/// 无限滑动控制器
/// </summary>
public class TheInfinityScrollController : MonoBehaviour
{
InfinityGridLayoutGroup infinityGridLayoutGroup;
void Start()
{
初始化数据列表;
infinityGridLayoutGroup =
GameObject.FindObjectOfType<InfinityGridLayoutGroup>();
infinityGridLayoutGroup.updateChildrenCallback = UpdateChildrenCallback;
for (int i = 0; i < infinityGridLayoutGroup.transform.childCount; i++)
{
Transform child = infinityGridLayoutGroup.transform.GetChild(i);
child.GetComponent<Button>().onClick.AddListener(() =>
{
OnClickButtonWithIndex(child.GetComponentInChildren<Text>());
});
}
infinityGridLayoutGroup.InitSetAmount(100);
}
/// <summary>
/// 通过当前缩略图索引从ios相册获取原图
/// </summary>
void OnClickButtonWithIndex(Text tex)
{
Debug.Log($" Unity log: index is {tex.text} in your click
button...");
}
/// <summary>
/// 上下翻滚更新函数
/// </summary>
void UpdateChildrenCallback(int indx, Transform trans)
{
Text tex = trans.Find("Text").GetComponent<Text>();
tex.text = indx.ToString();
}
}
}
至此,我们全部代码已经编写完了。
三:场景面板布置(Hierarchy)
1、那既然是借助了ScrollView,那我们就先看相关设置。这里我们把ScrollView下的Viewport、Scrollbar Horizontal、Scrollbar Vertical全部删除,因为我们不需要他们。
2、既然Viewport已经删除,那我们接着是整Content,首先增加脚本InfinityGridLayoutGroup,接着要注意:在GridLayoutGroup下的Constraint要设置为FixedColumnCount。★★★★★
3、好,我们接下来就是设置他的子节点了,具体要设置多少个,之前代码里有说明,不过这里还是再说一下。
/*
* 实现无限滚动,需要的最少的child数量。
* 屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 4 行,每一行 3 个。
* 则这个值为 4行 * 3个 + 1行 * 3个 = 15个。
*/
那么根据这个结果,我们就创建15个子节点。
四:运行结果(Running Result)
1、刚运行
2、运行中
至此,圆满结束。
希望大家:点赞,留言,关注咯~
唠家常
- 小黑的今日分享结束啦,小伙伴们你们get到了么,你们有没有更好的办法呢,可以评论区留言分享,也可以加小黑的QQ:841298494,大家一起进步。
今日有推荐
参考:https://blog.csdn.net/huutu/article/details/51549762
- 客官,看完get之后记得点赞哟!
- 小伙伴你还想要别的知识?好的呀,分享给你们
- 小黑的杂货铺,想要什么都有,客官不进来喝杯茶么?
Unity - 无限滚动的更多相关文章
- [Unity3D插件]2dtoolkit系列二 动画精灵的创建以及背景图的无限滚动
经过昨天2dtoolkit系列教程一的推出,感觉对新手还有有一定的启发作用,引导学习使用unity 2dToolKit插件的使用过程,今天继续系列二——动画精灵的创建,以及背景图的无限循环滚动,在群里 ...
- iOScollectionView广告无限滚动(Swift实现)
今天公司里的实习生跑过来问我一般App上广告的无限滚动是怎么实现的,刚好很久没写博客了,就决定写下了,尽量帮助那些处于刚学iOS的程序猿. 做一个小demo,大概实现效果如下图所示: 基本实现思路: ...
- Infinite Scroll - jQuery & WP 无限滚动插件
无限滚动(Infinite Scroll)也称为自动分页.滚动分页和无限分页.常用在图片.文章或其它列表形式的网页中,用来在滚动网页的时候自动加载下一页的内容.Infinite Scroll 这款 ...
- 基于HTML5+CSS3的图片旋转、无限滚动、文字跳动特效
本文分享几种基于HTML5+CSS3实现的一些动画特效:图片旋转.无限滚动.文字跳动;实现起来均比较容易,动手来试试! 一.图片旋转 效果图如下: 这个效果实现起来其实并不困难.代码清单如下: < ...
- LoopBar – Tap酒吧与无限滚动
相约 LoopBar – 标签栏与无限滚动为Android由Cleveroad 在Cleveroad我们最近认识到通过使用任何一个应用程序类别的导航,导航面板是很无聊和琐碎.这就是为什么我们的设计师的 ...
- Android 高级UI设计笔记09:Android如何实现无限滚动列表
ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们提供了一个良好,整洁的用户体验 ...
- 无限滚动 --demo
<!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content=&q ...
- 10 个 jQuery 的无限滚动的插件:
很多社交网站都使用了一些新技术来提高用户体验,而无限滚动的翻页技术就是其中一项,当你页面滑到列表底部时候无需点击就自动加载更多的内容. 下面为你推荐 10 个 jQuery 的无限滚动的插件: 1. ...
- 10款无限滚动自动翻页jquery插件
2012年3月29日 无限滚动自动翻页可以说是web2.0时代的一项堪称伟大的技术,它让我们在浏览页面的时候只需要把滚动条拉到网页底部就能自动显示下一页的 结果,改变了一直以来只能通过点击下一页来翻页 ...
- masonry结合json 制作无限滚动的瀑布流
做前端这行的 能直接贴代码就直接贴代码了,不用多说什么别的 效果需要引入jquery和jquery.masonry.min.js这两个JS JS代码如下: $(document).ready(func ...
随机推荐
- c语言中 -> 的用法
->是一个整体,它是用于指向结构体. 1.换种说法,如果我们在C语言中定义了一个结构体,然后申明一个指针指向这个结构体,那么我们要用指针取出结构体中的数据,就要用到"->&quo ...
- HTML5和CSS3新特性
1.HTML5新标签和属性 1.1 兼容性前缀与语义化 兼容低版本的写法.比较新的浏览器,可以直接写.兼容性前缀,是每个浏览器私有的. 内核 兼容性前缀 浏览器 Gecko -moz- Firefox ...
- ES6 学习笔记(五)基本类型Boolean
Boolean 1.需要注意的地方: 取值:true false 对于值为空字符串,0,-0,NaN,Null,undefined,false的布尔对象,它都会有一个初始值false.对于其它的值如& ...
- 可编程渲染管线(Scriptable Render Pipeline, SRP)
原文链接 可编程渲染管线处理数据的流程可分为以下3大阶段 1. 应用阶段 这个阶段大概会由CPU处理4件事情.首先会对模型数据进行可见性判断.模型数据由顶点位置.法线方向.顶点颜色.纹理坐标等构成.然 ...
- 带你了解NLP的词嵌入
摘要:今天带领大家学习自然语言处理中的词嵌入的内容. 本文分享自华为云社区<[MindSpore易点通]深度学习系列-词嵌入>,作者:Skytier. 1 特征表示 在自然语言处理中,有一 ...
- vulnhub靶场之DEATHNOTE: 1
准备: 攻击机:虚拟机kali.本机win10. 靶机:DEATHNOTE: 1,网段地址我这里设置的桥接,所以与本机电脑在同一网段,下载地址:https://download.vulnhub.com ...
- 08 | 白话容器基础(四):重新认识Docker容器
你好,我是张磊.今天我和你分享的主题是:白话容器基础之重新认识Docker容器. 在前面的三次分享中,我分别从Linux Namespace的隔离能力.Linux Cgroups的限制能力,以及基于r ...
- 关于vlc"编解码器暂不支持: VLC 无法解码格式“MIDI” (MIDI Audio)"解决
解决办法 sudo apt install vlc-plugin-fluidsynth
- 过压保护芯片,高输入电压(OVP)
PW2606是一种前端过电压和过电流保护装置.它实现了广泛的输入电压范围从2.5V到40V.过电压阈值可在外部编程或设置为内部默认设置.集成功率路径nFET开关的超低电阻确保了更好的性能电池充电系统应 ...
- (小白向)2020-12-18 中国大学MOOC第十二讲-动态变量应用
1创建单向链表(10分) 问题描述:根据随机输入的若干非零整数,以数字0结束:建立一个新链表. 输入:随机输入若干个整数,以数字0结束 输出:新建链表中个节点的值,数字间没有间隔字符. 样例:输入 5 ...