/*--------------------------------------------------------------------
- Author Name: DXL
- Creation Time: 2018.10.26
- File Describe: ScrollRect面板实现简单的横向动态有限数量内的循环
- ------------------------------------------------------------------*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class InfinityLoop : MonoBehaviour
{
#region 变量
//注意,最后的实际数量为 minAmount + 2,因为Grid下面原来就有一个
public int minAmount = 10; //一次加载最少的物体数量,从0开始计数,
RectTransform rectTrans; //当前Grid的RectTransform
HorizontalLayoutGroup layoutGroup; //Grid上挂载的网格控件
ContentSizeFitter fitter; //Grid挂载的自适应控件
ScrollRect scroll; //滚动面板控件
List<RectTransform> children = new List<RectTransform>(); //存放子物体位置
Vector2 startPosition; //存放拖动时开始位置
int amount; //生成物体的总数量,打开界面一瞬间就固定好了,就是按钮总数量
int realIndex = -1; //物体序号
float gridChildWidth; //Grid下要实例化的物体宽度
float gridChildHeight; //Grid下要实例化的物体高度
//拖动结束后的委托
public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);
public UpdateChildrenCallbackDelegate updateChildrenCallback = null;
#endregion
#region 方法
void InitComponent()
{
rectTrans = GetComponent<RectTransform>();
layoutGroup = GetComponent<HorizontalLayoutGroup>();
fitter = GetComponent<ContentSizeFitter>();
scroll = transform.parent.GetComponent<ScrollRect>();
gridChildWidth = transform.GetChild(0).GetComponent<RectTransform>().sizeDelta.x;
gridChildHeight = transform.GetChild(0).GetComponent<RectTransform>().sizeDelta.y;
//注册ScrollRect滚动回调;
scroll = transform.parent.GetComponent<ScrollRect>();
scroll.onValueChanged.AddListener((data) => { ScrollCallback(data); });
}
IEnumerator InitChildren()
{
//等一帧
yield return 0;
InitComponent();
//获取对应子物体的信息
for (int i = 0; i < transform.childCount; ++i)
{
Transform trans = transform.GetChild(i);
trans.gameObject.SetActive(true);
children.Add(transform.GetChild(i).GetComponent<RectTransform>());
//初始化前面几个;
UpdateChildrenCallback(children.Count - 1, transform.GetChild(i));
}
//获取初始位置
startPosition = rectTrans.anchoredPosition;
realIndex = children.Count - 1;
//显示规定数量以内的物体
for (int i = 0; i < minAmount; ++i)
{
children[i].gameObject.SetActive(i < amount);
}
}
void ScrollCallback(Vector2 data)
{
UpdateChildren();
}
//更新物体信息,参数 当前物体生成的序号,对应物体的位置信息
void UpdateChildrenCallback(int index, Transform trans)
{
if (updateChildrenCallback != null)
{
updateChildrenCallback(index, trans);
}
}
//更新物体(核心)
void UpdateChildren()
{
if (transform.childCount < minAmount)
{
return;
}
Vector2 currentPos = rectTrans.anchoredPosition;
//原来是一个一个移动,现在要4个一起移动,这里就先增加3个物体的宽度
float offsetWidth = gridChildWidth * 3;
//Grid位置最左边的位置超过移动刚开始的位置偏移了多少
float offsetX = currentPos.x - startPosition.x;
//float offsetX = currentPos.x - startPosition.x + offsetWidth;
//向左滑动,向右扩展(物体从屏幕右边向中间移动)
if (offsetX < 0)
{
//如果当前生成的物体数量已经达到规定上限,就不再继续生成(向右扩展时到头了)
if (realIndex >= amount - 1)
{
startPosition = currentPos;
return;
}
//将scroll的位置转换成世界坐标,得到移动面板(固定scroll)的左边界点位置
//float scrollLeft = scroll.transform.TransformPoint(Vector3.zero).x;
float scrollLeft = Camera.main.WorldToScreenPoint(scroll.transform.position).x;
//检测的子物体的序号,原来是0,即第一个(最左边那个),现在先换成3,第4个试试
int checkChildIndex = 0;
//children[0].anchoredPosition 就是实例化后的物体第一个物体的相对坐标
//1、
//Vector3 childBottomRight = new Vector3(children[checkChildIndex].anchoredPosition.x + gridChildWidth,
// children[checkChildIndex].anchoredPosition.y, 0);
//2、
Vector3 childBottomRight = new Vector3(children[checkChildIndex].gameObject.transform.position.x + gridChildWidth,
children[checkChildIndex].gameObject.transform.position.y, 0);
//3、
//RectTransformUtility.WorldToScreenPoint()
//Canvas canvas = scroll.transform.parent.parent.GetComponent<Canvas>();
//GameObject obj = children[checkChildIndex].gameObject;
//Vector3 scr = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, obj.transform.position);
//scr.z = 0;
//scr.z = Mathf.Abs(Camera.main.transform.position.z - transform.position.z);
//Vector3 changeVec = Camera.main.ScreenToWorldPoint(scr);
//4、
//Vector3[] vecs = new Vector3[4];
//children[checkChildIndex].GetWorldCorners(vecs);
//5、
//Vector3 childBottomRight = new Vector3(children[checkChildIndex].sizeDelta.x + gridChildWidth,
// children[checkChildIndex].sizeDelta.y, 0);
//将最左边的子物体x的坐标转换成世界坐标,(用于比较,判断是否跑到屏幕外面(左边)去了)
//float childRight = transform.TransformPoint(childBottomRight).x;
float childRight = Camera.main.WorldToScreenPoint(childBottomRight).x;
//Debug.Log("child width " + gridChildWidth);
//Debug.Log("scroll pos " + scroll.transform.position);
//Debug.Log("0: " + vecs[0] + ", 1: " + vecs[1] + ", 2: " + vecs[2] + ", 3: " + vecs[3]);
//Debug.Log("child1 pos " + children[checkChildIndex].gameObject.transform.position);
//Debug.Log("child2 pos " + children[1].gameObject.transform.position);
//Debug.Log("new child world pos " + changeVec);
//Debug.Log("scroll " + scrollLeft + " , childRight is " + childRight);
//最左边的物体已经被完全被拉到屏幕左边去了,就是最左边的这个物体已经看不见了
if (childRight <= scrollLeft)
{
//要将看不见的这个物体移动到最右边去,如果是多行,这里要有个for循环,将每一行都进行这种操作
//将首个物体的循序放到路径的最下面
children[checkChildIndex].SetAsLastSibling();
//将它的位置移动到当前子物体最后一个物体的右边,y值保持不变间隔为0,就不加
children[checkChildIndex].anchoredPosition = new Vector2(children[children.Count - 1].anchoredPosition.x
+ gridChildWidth, children[checkChildIndex].anchoredPosition.y);
//相当于新增了一个物体,最新的物体序号+1
realIndex++;
//更新最新的物体信息
UpdateChildrenCallback(realIndex, children[checkChildIndex]);
}
//生成物体后,HorizontalLayoutGroup右边要加成对应长度
rectTrans.sizeDelta += new Vector2(gridChildWidth, 0);
//更新child数组
for (int i = 0; i < children.Count; ++i)
{
children[i] = transform.GetChild(i).GetComponent<RectTransform>();
}
}
//向右滑动,向左扩展,(物体从屏幕左边向中间移动)
else
{
//如果向左移动到头了,就停止移动了
if (realIndex + 1 <= children.Count)
{
startPosition = currentPos;
return;
}
RectTransform scrollRect = scroll.GetComponent<RectTransform>();
Vector3 scrollAnchorRight = new Vector3(scrollRect.rect.width, 0, 0);
float scrollRight = scrollRect.transform.TransformPoint(scrollAnchorRight).x;
Vector3 childLeftPos = new Vector3(children[children.Count - 1].anchoredPosition.x,
children[children.Count - 1].anchoredPosition.y, 0);
float childLeft = transform.TransformPoint(childLeftPos).x;
if (childLeft >= scrollRight) //物体已经被滑动到屏幕最右边看不见的地方了
{
//将最后一个物体移动到子物体首个位置上
children[children.Count - 1].SetAsFirstSibling();
children[children.Count - 1].anchoredPosition = new Vector2(children[0].anchoredPosition.x - gridChildWidth,
children[0].anchoredPosition.y);
children[children.Count - 1].gameObject.SetActive(true);
UpdateChildrenCallback(realIndex - children.Count, children[children.Count - 1]);
}
rectTrans.sizeDelta -= new Vector2(gridChildWidth, 0);
for (int i = 0; i < children.Count; ++i)
{
children[i] = transform.GetChild(i).GetComponent<RectTransform>();
}
realIndex--;
}
//每次移动之后,更新一下起始点的位置信息
startPosition = currentPos;
}
public void SetAmount(int count)
{
amount = count;
StartCoroutine(InitChildren());
}
#endregion
}