Unity-保卫萝卜-翻书效果的实现(基于Scroll View组件)

前言:(需求:鼠标一次滑动一个页面 或者 多个页面)
在我们的游戏制作(虚拟仿真)的过程中,有的时候会使用翻书的一个效果进行控制页面的

首先上公式: UI 宽度 = 原宽度 + (单元格长度 + 间隙) * (单元格数量 - 1)

第一种方法:使用比例进行控制滑动页数
图-1首先将Scroll View 将Content的长度 单位化变成了 0-1 的区间(这里使用到 Unity ScrollRect组件中的一个API,scrollrect.horizontalNormalizedPosition);先将物体的中心点取到并且计算滑动到的位置,Camera中心点距离当前那个单元格(物体的中心点)中心点最近就将物体固定在中心点。根据鼠标滚动的一个距离进行计算滑动所占的比例,根据比例来进行调整位置。接着往下面走的话就是我们书写脚本,首先是定义成员变量。然后就是拿到 Scroll View 组件来完成翻书效果;

成员变量:
Content:容器的总长度 后面进行单位化(0-1)
图-2
StartMouseX:鼠标的开始位置、要知道用户开始滑动的位置(使用接口事件的方式)
EndMouseX:鼠标结束位置、松开的位置(使用接口事件的方式)
注:因为作者左边做的是水平方向的移动,所以只需取 X 的位置即可
Lerp:开始位置-结束位置>0。结束的X坐标大于开始的X坐标=右滑
           开始位置-结束位置<0。左滑动
           根据插值决定滑动几个格子。
Distance:
                (1)移动一个单元用户鼠标鼠标需要多少距离?
                 这里的话作者自己测试的是 : 左偏移(如下图 Left)+ 一个单元格的数量
                 图-3
                 大家可以需要根据自己项目进行测试
              (lerp > 移动一个单元格需要的距离进行比较 )移动一个单元格
                         <                                                               不进行移动
                 (2)移动多个单元格需要多少距离?
                 这里的公式是:左偏移 + 单元格长度
                                          后面每一个单元格的长度都是 : 一个单元格 + 间隔(当前单元格与上一单元格的距离)
由上面总结出:
比例计算公式 : 一个单元格中心点(物体) / Content 长度(容器的长度)
考虑到一直滑动的情况,所以需要两个目标上限值,Max 与 Min,当往右边进行滑动的时候直接进行归 1,往左边进行滑动的时候直接将 值归零。如果没有这一步操作的话。可能你就会口吐芬芳了。因为不论你左滑还是右滑的时候你都会看不到物体的。
Max:上限值
Min:下限值
接下来上代码----------------------------------------------

//==========================
// - FileName:      SliderCanMoveScrollView.cs         
// - Created:       true.	
// - CreateTime:    2020/02/27 15:43:01	
// - Email:         [email protected]		
// - 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:         [email protected]		
// - 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);
    }
}
发布了29 篇原创文章 · 获赞 2 · 访问量 787

猜你喜欢

转载自blog.csdn.net/zhanxxiao/article/details/104535851