Unity-保卫萝卜-翻书效果的实现(基于Scroll View组件)
前言:(需求:鼠标一次滑动一个页面 或者 多个页面)
在我们的游戏制作(虚拟仿真)的过程中,有的时候会使用翻书的一个效果进行控制页面的
首先上公式: UI 宽度 = 原宽度 + (单元格长度 + 间隙) * (单元格数量 - 1)
第一种方法:使用比例进行控制滑动页数
首先将Scroll View 将Content的长度 单位化变成了 0-1 的区间(这里使用到 Unity ScrollRect组件中的一个API,scrollrect.horizontalNormalizedPosition);先将物体的中心点取到并且计算滑动到的位置,Camera中心点距离当前那个单元格(物体的中心点)中心点最近就将物体固定在中心点。根据鼠标滚动的一个距离进行计算滑动所占的比例,根据比例来进行调整位置。接着往下面走的话就是我们书写脚本,首先是定义成员变量。然后就是拿到 Scroll View 组件来完成翻书效果;
成员变量:
Content:容器的总长度 后面进行单位化(0-1)
StartMouseX:鼠标的开始位置、要知道用户开始滑动的位置(使用接口事件的方式)
EndMouseX:鼠标结束位置、松开的位置(使用接口事件的方式)
注:因为作者左边做的是水平方向的移动,所以只需取 X 的位置即可
Lerp:开始位置-结束位置>0。结束的X坐标大于开始的X坐标=右滑
开始位置-结束位置<0。左滑动
根据插值决定滑动几个格子。
Distance:
(1)移动一个单元用户鼠标鼠标需要多少距离?
这里的话作者自己测试的是 : 左偏移(如下图 Left)+ 一个单元格的数量
大家可以需要根据自己项目进行测试
(lerp > 移动一个单元格需要的距离进行比较 )移动一个单元格
< 不进行移动
(2)移动多个单元格需要多少距离?
这里的公式是:左偏移 + 单元格长度
后面每一个单元格的长度都是 : 一个单元格 + 间隔(当前单元格与上一单元格的距离)
由上面总结出:
比例计算公式 : 一个单元格中心点(物体) / Content 长度(容器的长度)
考虑到一直滑动的情况,所以需要两个目标上限值,Max 与 Min,当往右边进行滑动的时候直接进行归 1,往左边进行滑动的时候直接将 值归零。如果没有这一步操作的话。可能你就会口吐芬芳了。因为不论你左滑还是右滑的时候你都会看不到物体的。
Max:上限值
Min:下限值
接下来上代码----------------------------------------------
//==========================
// - FileName: SliderCanMoveScrollView.cs
// - Created: true.
// - CreateTime: 2020/02/27 15:43:01
// - Email: aaa@qq.com
// - Region: China WUHAN
// - Description:
//==========================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
public class SliderCanMoveScrollView : MonoBehaviour ,IBeginDragHandler,IEndDragHandler{
private ScrollRect scrollRect;
//容器的长度
private float contentLength;
//鼠标开始位置
private float beginMousePositonX;
//结束位置
private float endMousePositionX;
//上一位置的比例(单元格的中心点)
private float lastProportion;
//上下限
private float Max;
private float Min;
//第一个单元格的距离
private float firstItemLength;
//滑动一个单元格的距离
private float oneItemLength;
//滑动一个单元格比例
private float oneItemProportion;
//单元格索引
private int currentIndex;
//单元格长度
public int cellLength;
//间隔
public int spacing;
//左偏移
public int LeftOffset;
//单元格个数
public int totalItemNum;
private void Awake()
{
scrollRect = GetComponent<ScrollRect>();
//拿到容器长度
contentLength = scrollRect.content.rect.xMax - 2 * LeftOffset - cellLength;
//移动第一个单元格
firstItemLength = cellLength / 2 + LeftOffset;
//其他单元格
oneItemLength = cellLength + spacing;
//移动一个单元格的比例(移动一个单元格长度除以总长度)
oneItemProportion = oneItemLength / contentLength;
//上下限
Max = 1- firstItemLength / contentLength;
Min = firstItemLength / contentLength;//移动第一个单元格的长度 除以 容器的总长度
//索引赋值
currentIndex = 1;
//从最左边开始
scrollRect.horizontalNormalizedPosition = 0;
}
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("开始拖拽");
beginMousePositonX = Input.mousePosition.x;
//Debug.Log("开始位置" + beginMousePositonX);
}
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("结束拖拽");
float offset = 0;
endMousePositionX = Input.mousePosition.x;
//Debug.Log("结束位置" + endMousePositionX);
//插值
//UI(世界坐标) 与鼠标(视口坐标)会发生一个偏差
//Debug.Log("偏移" + offset);
//Debug.Log("firstItemLength" + firstItemLength);
offset = (beginMousePositonX - endMousePositionX) * 2;
//偏移大于第一个单元格的距离的时候
if (Mathf.Abs(offset)>firstItemLength)
{
//右滑 等于 0 的情况不考虑。使用 float
if (offset > 0)
{
//当前单元格判断是否到达最大值
if (currentIndex >= totalItemNum)
{
return;
}
//移动几个单元格,可以移动的数目
int moveCount = (int)((offset - firstItemLength) / oneItemLength) + 1;
//更新数值
currentIndex += moveCount;
//大于总数
if (currentIndex>=totalItemNum)
{
currentIndex = totalItemNum;
}
//当前的比例进行累加 需要移动的比例
lastProportion += oneItemProportion * moveCount;
//如果比例大于最大值的时候
if (lastProportion >= Max)
{
//停止进行滑动
lastProportion = 1;
}
}
else //左滑
{
//累减
if (currentIndex <= 1)
{
return;
}
//移动几个单元格,可以移动的数目
int moveCount = (int)((offset + firstItemLength) / oneItemLength) - 1;
//更新数值
currentIndex += moveCount;
//大于总数
if (currentIndex <= 1)
{
currentIndex = 1;
}
//当前的比例进行累加 需要移动的比例
lastProportion += oneItemProportion * moveCount;
//如果比例大于最大值的时候
if (lastProportion <= Min)
{
//停止进行滑动
lastProportion = 0;
}
}
}
//使用 Dowtenn 缓动函数进行视觉改变
DOTween.To(() =>
scrollRect.horizontalNormalizedPosition, lerpValue =>
scrollRect.horizontalNormalizedPosition = lerpValue, lastProportion, 0.3f).SetEase(Ease.OutQuart);
}
}
第二种方法:使用Conten进行:
在Unity中Content 在移动的时候进行计算距离,比如当前鼠标滑动的是100,第二次的时候就是200,第三次的时候是300,依次进行累加。如果说这边能够得到某一个值的时候,直接让物体固定在移动的位置即可。
方法二和方法一有相同的特点:所以这里直接上代码就行了。
//==========================
// - FileName: SliderScrollView.cs
// - Created: true.
// - CreateTime: 2020/02/27 17:07:11
// - Email: aaa@qq.com
// - Region: China WUHAN
// - Description:
//==========================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
public class SliderScrollView : MonoBehaviour ,IBeginDragHandler,IEndDragHandler{
private ScrollRect scrollRect;
//容器
private RectTransform contentTrans;
//起始、重点位置
private float beginMousePosition;
private float endMousePosition;
//当前的物体的局部坐标,记录每次的位置
private Vector3 currentContentLocalPos;
//移动一个单元格的距离
private float moveOneItemLength;
//第几个单元格
private int currentIndex;
//单元格长度
public int cellLength;
//间隔
public int spacing;
//左偏移
public int leftOffset;
//单元格个数
public int totalItemNum;
private void Awake()
{
scrollRect = GetComponent<ScrollRect>();
//拿到容器的 位置信息
contentTrans = scrollRect.content;
//移动一个单元格的距离
moveOneItemLength = cellLength + spacing;
//容器的当前位置
currentContentLocalPos = contentTrans.localPosition;
currentIndex = 1;
}
public void OnBeginDrag(PointerEventData eventData)
{
beginMousePosition = Input.mousePosition.x;
}
public void OnEndDrag(PointerEventData eventData)
{
endMousePosition = Input.mousePosition.x;
//插值
float offSetX = 0;
//需要滑动的距离
float moveDistance = 0;
//偏移的位置
offSetX = beginMousePosition - endMousePosition;
//右滑
if (offSetX > 0)
{
//到达最右了
if (currentIndex >= totalItemNum)
{
return;
}
//没有到的时候就让它到达
moveDistance = -moveOneItemLength;
//单元格加加
currentIndex++;
}
else //左滑
{
if (currentIndex <= 1)
{
return;
}
moveDistance = moveOneItemLength;
//每次只滑动一次
currentIndex--;
}
//缓动函数
DOTween.To(() => contentTrans.localPosition,lerpValue => contentTrans.localPosition = lerpValue, currentContentLocalPos + new Vector3(moveDistance, 0, 0), 0.5f).SetEase(Ease.OutQuart);
//累加局部坐标
currentContentLocalPos += new Vector3(moveDistance, 0, 0);
Debug.Log(currentContentLocalPos);
}
}
下一篇: unity3D学习8