首先贴效果:
1.动态设置gridLayout的大小
核心组件为rectTransform,核心属性一个为rectTransform.anchoredPosition,用来设置和锚点的距离,否则只设置宽高会导致grid只以锚点(一般为居中)为中心扩展。第二个核心属性为rectTransform.sizeDelta,用于设置控件的宽度和高度。实现起来很简单,只要获得放进去的图片的宽高然后乘以总数就能得到滑动框的总宽高。
boardBtns.GetComponent<RectTransform>().anchoredPosition = new Vector2(posX*(num-1),0);
boardBtns.GetComponent<RectTransform>().sizeDelta = new Vector2(width*num, boardBtns.GetComponent<RectTransform>().rect.height);
boardBtns为含有gridLayout组件的控件,即所有公告的父物体。
2.定时滑动公告板的实现
实现这个核心组件为scrollrect,核心属性scrollrect.horizontalNormalizedPosition
这个值返回一个0-1的浮点数,实验一下就知道这个能用来控制滑动框划到哪里,值越小越接近起点。
以及需要继承Unity事件的拖动接口,分别为IBeginDragHandler和IEndDragHandler(IDragHandler用于拖动时监测事件,不过感觉它没什么用就没有实现)
同时需要设置一个核心变量,表示现在滑动到第几页,我取名为currIndex,滑动到currIndex页(从0开始)时上述scrollrect.horizontalNormalizedPosition的值为currIndex * ((float)1 / (num - 1)))
public void OnBeginDrag(PointerEventData eventData)//记录起始位置
{
lastX = sr.horizontalNormalizedPosition;
}
public void OnEndDrag(PointerEventData eventData)//滑动结束,根据判断的方向设置index的加减然后根据index的值朝某个方向滑动
{
if (Mathf.Abs(lastX- sr.horizontalNormalizedPosition)>=0.01f)
{
if (lastX < sr.horizontalNormalizedPosition)//向左划
{
if (currIndex<num-1)
{
currIndex++;
StopAllCoroutines();//一定要停止协程!!否则会导致协程重复执行,滑动框会乱划
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
}
else if (lastX > sr.horizontalNormalizedPosition)//向右划
{
if (currIndex >0)
{
currIndex--;
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
}
}
}
接下来实现利用协程平滑移动公告板的功能,主要使用Mathf.Lerp插值函数。
IEnumerator MoveTo(float targetX)
{
while (true)
{
if (Mathf.Abs(sr.horizontalNormalizedPosition - targetX) >= 0.01f)//未到时滑动
{
sr.horizontalNormalizedPosition = Mathf.Lerp(
sr.horizontalNormalizedPosition, targetX,
Time.deltaTime * (Mathf.Abs(currIndex - lastIndex)*10));
}
else//停止
{
sr.horizontalNormalizedPosition = targetX;
lastIndex = currIndex;
toggles[currIndex].isOn = true;
timer = 0;
canSlide = true;
break;
}
yield return null;
}
}
接下来实现点击toggle跳转到对应的页面,场景中公告板上需要添加toggleGroup组件然后设置每个toggle的group为这个toggleGroup,然后为toggle的值改变事件添加监听事件,主要改变currIndex的值,和toggle数组的i一一对应。
bt.onValueChanged.AddListener((bool isOn)=> {
OnboardToggleClick(isOn,bt); });
void OnboardToggleClick(bool isOn,Toggle t)
{
for (int i = 0; i < toggles.Length; i++)
{
if (toggles[i].isOn) {
currIndex = i; break; }
}
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
最后实现定时的功能,其实也就是设置一个定时器定时使currIndex循环增加,然后开启移动协程。
void Update () {
timer += Time.deltaTime;
if (timer>=6 && canSlide)//6s滑动一次
{
canSlide = false;
if (currIndex < num-1) currIndex++;
else currIndex = 0;
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num-1))));
}
}
最后贴上整个脚本的代码,我写了两个场景,可复用。别的操作大多是给Image和Text赋值还有给Button添加监听事件,这个可以自由发挥。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
public class OnBoard : MonoBehaviour,IBeginDragHandler,IEndDragHandler{
float posX;//board偏移量
float width;//board宽度
float height;//board高度
Transform boardBtns;
ScrollRect sr;
Transform boardToggles;
int num;//the count of boards
Toggle[] toggles;
string[] boardTexts;
Sprite[] boardImages;
int lastIndex;//上次索引(toggle)
int currIndex;//现在索引
float lastX;//上次sr位置
float timer;
bool canSlide = true;
//timer to auto slide
// Use this for initialization
void Start () {
switch (SceneManager.GetActiveScene().name)
{
case "Main":
num = 4;
boardImages = new Sprite[num];
boardImages = Resources.LoadAll<Sprite>("Background");
boardTexts = new string[]{
"Battle", "Heroes", "Room", "Summon" };
break;
case "Summon":
num = 3;
boardImages = new Sprite[num];
boardImages = Resources.LoadAll<Sprite>("Summon");
boardTexts = new string[num];
break;
default:
break;
}
toggles = new Toggle[num];
boardBtns = transform.Find("boardBtns");
sr = GetComponent<ScrollRect>();
boardToggles = transform.Find("boardToggles");
width = boardBtns.GetComponent<RectTransform>().sizeDelta.x;
height= boardBtns.GetComponent<RectTransform>().sizeDelta.y;
posX = width / 2;
boardBtns.GetComponent<RectTransform>().anchoredPosition = new Vector2(posX*(num-1),0);
boardBtns.GetComponent<RectTransform>().sizeDelta = new Vector2(width*num, boardBtns.GetComponent<RectTransform>().rect.height);
//instantiate btns and toggles
for (int i = 0; i < num; i++)
{
//btns
GameObject boardBtn = Instantiate((GameObject)GameFuncs.GetResource("Prefabs/boardBtn"));
boardBtn.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height);
boardBtn.transform.SetParent(boardBtns);
boardBtn.GetComponent<Image>().sprite = boardImages[i];
boardBtn.GetComponentInChildren<Text>().text = boardTexts[i];
boardBtn.GetComponent<Button>().onClick.AddListener(()=> {
OnboardBtnClick(boardBtn.GetComponentInChildren<Text>().text); });
//toggles
GameObject boardToggle = Instantiate((GameObject)GameFuncs.GetResource("Prefabs/boardToggle"));
boardToggle.transform.SetParent(boardToggles);
Toggle bt = boardToggle.GetComponent<Toggle>();
bt.group = boardToggles.GetComponent<ToggleGroup>();
if (i == 0) bt.isOn = true;
bt.onValueChanged.AddListener((bool isOn)=> {
OnboardToggleClick(isOn,bt); });
toggles[i] = bt;
}
}
// 定时滚动
void Update () {
timer += Time.deltaTime;
if (timer>=6 && canSlide)//6s滑动一次
{
canSlide = false;
if (currIndex < num-1) currIndex++;
else currIndex = 0;
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num-1))));
}
}
void OnboardBtnClick(string boardStr)
{
switch (SceneManager.GetActiveScene().name)
{
case "Main":
GameFuncs.GoToSceneAsync(boardStr);
break;
case "Summon":
break;
default:
break;
}
}
void OnboardToggleClick(bool isOn,Toggle t)
{
for (int i = 0; i < toggles.Length; i++)
{
if (toggles[i].isOn) {
currIndex = i; break; }
}
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
public void OnBeginDrag(PointerEventData eventData)
{
lastX = sr.horizontalNormalizedPosition;
}
public void OnEndDrag(PointerEventData eventData)//toggle change ison
{
if (Mathf.Abs(lastX- sr.horizontalNormalizedPosition)>=0.01f)
{
if (lastX < sr.horizontalNormalizedPosition)//向左划
{
if (currIndex<num-1)
{
currIndex++;
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
}
else if (lastX > sr.horizontalNormalizedPosition)//向右划
{
if (currIndex >0)
{
currIndex--;
StopAllCoroutines();
StartCoroutine(MoveTo(currIndex * ((float)1 / (num - 1))));
}
}
}
}
IEnumerator MoveTo(float targetX)
{
while (true)
{
if (Mathf.Abs(sr.horizontalNormalizedPosition - targetX) >= 0.01f)
{
sr.horizontalNormalizedPosition = Mathf.Lerp(
sr.horizontalNormalizedPosition, targetX,
Time.deltaTime * (Mathf.Abs(currIndex - lastIndex)*10));
}
else
{
sr.horizontalNormalizedPosition = targetX;
lastIndex = currIndex;
toggles[currIndex].isOn = true;
timer = 0;
canSlide = true;
break;
}
yield return null;
}
}
}