在游戏中,无论是显示活动或者是任务,分页滚动显示都是一个常见的功能.使用UGUI的Scroll view来实现一个这样的功能是在unity中比较常见的实现方式.
Scroll view的设置
- 在Scroll view的content中添加如下俩个组件,可以通过调节grid layout group组件中的cell size来调节没一个分页里的元素的大小.
- 如果只需要水平分页移动,可以将Scroll View组件的vertic垂直选项关闭
- 在content下添加image作为每一个页面,这样就可以通过手动移动的方式实现了一个简单的滑动页面.
实现自我滚动的功能
- 上述实现,由于Scroll view的设置,滑动过程中其可能会处在两个页面的中间,在实际游戏中,对于这中功能,当滑动结束时,只能处于一个界面,不会出现如下情况,如下:
- 实现滚动可以通过Scroll view下的Scroll Rect组件来实现.
通过得到Scroll view下的Scroll Rect组件,打印scrollRect.horizontalNormalizedPosition属性,发现当存在两个页面时,处于第一个界面的时候scrollRect.horizontalNormalizedPosition=0,处于第二个界面的时scrollRect.horizontalNormalizedPosition=1.
- 当存在三个页面的时候,第一个页面为0,第二个页面为0.5,第三个页面为1
我们可以同过这个属性来实现移动到哪个界面的功能. - 假设总共有n个页面,则第n个为1,前n-1个平分1,则第i个的scrollRect.horizontalNormalizedPosition为 i*1/n-1.可以通过修改scrollRect.horizontalNormalizedPosition的值来修改移动.
- 如下代码添加到scroll view 控件上,即可实现一个简单的分页滚动功能
包括,自动滚动,以及移动页面的时候,跳转到最近的页面.
public class PageScrollView : MonoBehaviour,IBeginDragHandler,IEndDragHandler
{
// Start is called before the first frame update
#region
protected ScrollRect scrollRect;
private int pageCount;
//每个页面的horizontalNormalizedPosition值
public float[] pageHorizontal;
//是否自动跳转
public bool isau=true;
//自动跳转的时间间隔,以及计数器
public float auTime = 2;
private float auTimer = 0;
//是否跳转
private bool isjump = false;
//当前的页面下标
private int currentpageindex;
//开始的页面位置
private float startHorizontal;
#endregion
//跳转需要的时间
private float jumpTime = 0.3f;
private float jumpTimer=0;
//在拖拽的时候,不进行自动滚动
private bool isdraging = false;
//跳转到具体页面的时候,进行相应的事件回调
public Action<int> pagehandle;
void Start()
{
scrollRect = GetComponent<ScrollRect>();
if(scrollRect==null)
{
throw new System.Exception("没有该组件");
}
pageCount = transform.Find("Viewport/Content").childCount;
pageHorizontal = new float[pageCount];
for(int i=0;i<pageCount;i++)
{
pageHorizontal[i] = i * 1.0f / (float)(pageCount - 1);
}
}
// Update is called once per frame
void Update()
{
listenJump();
Listenantojump();
}
//处理自动跳转逻辑
void Listenantojump()
{
if (isdraging == true)
return;
if(isau)
{
auTimer += Time.deltaTime;
if(auTimer>=auTime)
{
auTimer = 0;
currentpageindex++;
currentpageindex %= pageCount;
pagejump(currentpageindex);
}
}
}
void pagejump(int pageindex)
{
isjump = true;
jumpTimer = 0;
this.currentpageindex = pageindex;
startHorizontal = scrollRect.horizontalNormalizedPosition;
if (pagehandle != null)
pagehandle(currentpageindex);
}
void listenJump()
{
if(isjump)
{
jumpTimer += Time.deltaTime * (1 / jumpTime);
//通过线性插值的方式修改horizontalNormalizedPosition的值来达到移动的效果
scrollRect.horizontalNormalizedPosition = Mathf.Lerp(startHorizontal, pageHorizontal[currentpageindex], jumpTimer);
if(jumpTimer>=1)
{
isjump = false;
}
}
}
//通过监听拖拽事件,跳转到松开手的时候离得最近的页面
public void OnEndDrag(PointerEventData eventData)
{
int minpageindex = 0;
//float nowhe = scrollRect.horizontalNormalizedPosition;
for(int i=1;i<pageCount;i++)
{
if(Mathf.Abs(scrollRect.horizontalNormalizedPosition - pageHorizontal[i])< Mathf.Abs(pageHorizontal[minpageindex] - scrollRect.horizontalNormalizedPosition))
{
minpageindex = i;
}
}
pagejump(minpageindex);
isdraging = false;
//结束拖曳后,要过2s才会进行页面滚动
auTimer = 0;
}
public void OnBeginDrag(PointerEventData eventData)
{
isdraging = true;
}
}
对代码进行修改,让其变得通用
- 上述代码实现,只能在水平方向进行滚动,再垂直方向上不能滚动.稍作修改可以实现在垂直方向上也进行分页滚动.
public enum ScrllViewType
{
Horizontal,
Vertical
}
public class PageScrollView : MonoBehaviour,IBeginDragHandler,IEndDragHandler
{
// Start is called before the first frame update
#region
protected ScrollRect scrollRect;
private int pageCount;
//每个页面的horizontalNormalizedPosition值
public float[] pageHorizontal;
//是否自动跳转
public bool isau=true;
//自动跳转的时间间隔,以及计数器
public float auTime = 2;
private float auTimer = 0;
//是否跳转
private bool isjump = false;
//当前的页面下标
private int currentpageindex;
//开始的页面位置
private float startHorizontal;
#endregion
//跳转需要的时间
private float jumpTime = 0.3f;
private float jumpTimer=0;
//在拖拽的时候,不进行自动滚动
private bool isdraging = false;
//跳转到具体页面的时候,进行相应的事件回调
public Action<int> pagehandle;
public ScrllViewType scrllViewType = ScrllViewType.Horizontal;
void Start()
{
scrollRect = GetComponent<ScrollRect>();
if(scrollRect==null)
{
throw new System.Exception("没有该组件");
}
pageCount = transform.Find("Viewport/Content").childCount;
pageHorizontal = new float[pageCount];
switch (scrllViewType)
{
case ScrllViewType.Horizontal:
for (int i = 0; i < pageCount; i++)
{
pageHorizontal[i] = i * 1.0f / (float)(pageCount - 1);
}
break;
//对于垂直移动来说,假设有三个页面,第一个的scrollRect.verticalNormalizedPosition是1,第二个是0.5,第三个是0
case ScrllViewType.Vertical:
for (int i = 0; i < pageCount; i++)
{
pageHorizontal[i] = 1-i * 1.0f / (float)(pageCount - 1);
}
break;
}
}
// Update is called once per frame
void Update()
{
listenJump();
Listenantojump();
}
//处理自动跳转逻辑
void Listenantojump()
{
if (isdraging == true)
return;
if(isau)
{
auTimer += Time.deltaTime;
if(auTimer>=auTime)
{
auTimer = 0;
currentpageindex++;
currentpageindex %= pageCount;
pagejump(currentpageindex);
}
}
}
void pagejump(int pageindex)
{
isjump = true;
jumpTimer = 0;
this.currentpageindex = pageindex;
switch (scrllViewType)
{
case ScrllViewType.Horizontal:
startHorizontal = scrollRect.horizontalNormalizedPosition;
break;
case ScrllViewType.Vertical:
startHorizontal = scrollRect.verticalNormalizedPosition;
break;
}
if (pagehandle != null)
pagehandle(currentpageindex);
}
void listenJump()
{
if(isjump)
{
jumpTimer += Time.deltaTime * (1 / jumpTime);
//通过线性插值的方式修改horizontalNormalizedPosition的值来达到移动的效果
switch (scrllViewType)
{
case ScrllViewType.Horizontal:
scrollRect.horizontalNormalizedPosition = Mathf.Lerp(startHorizontal, pageHorizontal[currentpageindex], jumpTimer);
break;
case ScrllViewType.Vertical:
scrollRect.verticalNormalizedPosition = Mathf.Lerp(startHorizontal, pageHorizontal[currentpageindex], jumpTimer);
break;
}
if(jumpTimer>=1)
{
isjump = false;
}
}
}
//通过监听拖拽事件,跳转到松开手的时候离得最近的页面
public void OnEndDrag(PointerEventData eventData)
{
int minpageindex = 0;
//float nowhe = scrollRect.horizontalNormalizedPosition;
switch (scrllViewType)
{
case ScrllViewType.Horizontal:
for (int i=1;i<pageCount;i++)
{
if(Mathf.Abs(scrollRect.horizontalNormalizedPosition - pageHorizontal[i])< Mathf.Abs(pageHorizontal[minpageindex] - scrollRect.horizontalNormalizedPosition))
{
minpageindex = i;
}
}
break;
case ScrllViewType.Vertical:
for (int i = 1; i < pageCount; i++)
{
if (Mathf.Abs(scrollRect.verticalNormalizedPosition - pageHorizontal[i]) < Mathf.Abs(pageHorizontal[minpageindex] - scrollRect.verticalNormalizedPosition))
{
minpageindex = i;
}
}
break;
}
pagejump(minpageindex);
isdraging = false;
//结束拖曳后,要过2s才会进行页面滚动
auTimer = 0;
}
public void OnBeginDrag(PointerEventData eventData)
{
isdraging = true;
}
}