Unity实现滑动更换界面效果
作者:罗小c 发布时间:2021-10-06 19:56:40
在做2048这个游戏时,因为菜单页面还能查看游戏规则,而这些规则又不在同一个页上,所以需要滑动页面实现页面切换,但是仅仅使用unity提供的组件做出的效果仅有一个切换的意思,交互感很差,所以在组件的基础上又写了一个控制页面切换的类。而界面切换就是实现一个滚动的视图。
在unity编辑器中实现滚动视图的基本操作:需要用Scroll Rect组件
首先可以看看官方用户手册中关于Scroll Rect组件的讲解,说的很明白。最精辟的描述就是用于使子 RectTransform 滚动的组件。
滚动视图中的重要元素包括视口、滚动内容以及可选的一个或两个滚动条。
ScrollView是滚动视图,Viewport是视口,Content是滚动内容的集合(在其他地方可能就是一张大图),这些都是panel。Viewport会显示Content的一部分内容。
注意ScrollView、Viewport的大小都是和画布一样的,而Content大小应该是其下所有内容大小的和,如下图。
下面是Content中对应的内容。看到图也应该知道实现滑动更换界面功能的原理了,正是把该显示的放在视口下。
Scroll Rect组件的配置:
为ScrollView添加Scroll Rect组件,并把Viewport拖给Scroll Rect组件的Viewport属性,再把Content拖给Scroll Rect组件的Content属性,滚动条看需要加吧,我加了一个水平滚动条,滚动条并不用加以过多的控制,unity已经把滚动条和Scroll Rect的组合使用做的很好了。其他属性的配置在官方手册中讲的很清楚。
有一个节省Content性能的组件——Mask:
因为我们只能看到视口下的内容,由于Content可能有很多界面组成,所以我们可以采取遮罩的方式来不渲染我们看不到的东西,也就是只渲染视口,来提高效率,unity给我们提供了这样一个组件帮助我们实现这个功能:
这个组件当然要给到视口上,可以看到除了一开始在视口上的菜单界面,其余规则界面都没有渲染。
这个组件在全屏的切换时确实只能节省性能,但是在某一部分实现滑动更换界面时却可以遮住Content中不应该显示的部分,很方便,也很必要。
到此,unity中可以帮我们的最大限度就到这了,现在运行会发现确实可以移动,但是到达边界是会出现瑕疵、卡动,并且滑到一半不滑了也不会自动弹回,所以我们仍需要自己编写脚本给与更好的控制。
用代码控制灵敏度、弹回速度。。。:
需要了解的:
当然少不了用到Scroll Rect组件对应的API:ScrollRect,此外,因为要自己写代码控制滑动界面,必须要用到Event事件的实现接口,这里我用的是拖拽类的IBeginDragHandler, IEndDragHandler。
基本的原理:
就是记录每一次开始拖拽到结束拖拽的距离,求出现在的位置,和每个界面的位置比较,显示最近的界面即可。虽然这看起来很简单,但是将交互的效果提升了1000000000000000000000000个档次。
值得注意的是,尽管在代码中注释了很多次,但是还是提一下,就是在ScrollRect中有horizontalNormalizedPosition属性,用来记录水平滚动位置,以 0 到 1 之间的值表示,0 表示位于左侧。所以我们在记录每个界面的位置时,应当记录每个界面的临界比例,且在0-1之间,比如一共有四页,那么第一页和第二页之间的临界应在0.333333.
代码实现:
代码中用到的API均可在ScrollRect找到,不难理解。并且基本所有地方都加了注释。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using System;
public class PageView : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
// 1)Smooting表示停止滑动后,当前页码归正的速率
// 2)sensitivity 滑动的敏感度,如果数值过大会导致翻多页
// 3)OnPageChanged 当前页码改变时回调
// 4)方法pageTo 直接跳转到某一页
//注意点:ScrollView下的Content的长度是每页的宽度* 页数,每页的宽度与ScrollView的宽度相同
private ScrollRect rect; //滑动组件
private float targethorizontal = 0; //滑动的起始坐标
private bool isDrag = false; //是否拖拽结束
private List<float> posList = new List<float>(); //求出每页的临界值(0-1)
//private int currentPageIndex = -1; //记录当前是第几页,页索引从0开始 ,这里不需要显示,有需求可以自己显示
//public Action<int> OnPageChanged;
private bool stopMove = true; //是否停止移动
public float smooting = 4; //滑动速度
public float sensitivity = 0; //灵敏度
private float startTime; //从开始拖动到结束的时间
private float startDragHorizontal;//记录当前开始滑动的位置(0-1)
void Awake()
{
rect = transform.GetComponent<ScrollRect>();
//Content水平宽度的大小减去视口大小
float horizontalLength = rect.content.rect.width - GetComponent<RectTransform>().rect.width;
//print(horizontalLength);
posList.Add(0);
for (int i = 1; i < rect.content.transform.childCount - 1; i++)
{//求出每页的临界值(0-1)
posList.Add(GetComponent<RectTransform>().rect.width * i / horizontalLength);
//print(GetComponent<RectTransform>().rect.width * i / horizontalLength);
}
posList.Add(1);
}
void Update()
{
if (!isDrag && !stopMove)//如果拖动没有结束并且界面还没停止移动就继续移动
{
startTime += Time.deltaTime;
float t = startTime * smooting;
//水平滚动位置,以 0 到 1 之间的值表示,0 表示位于左侧。用lerp可以实现平缓过渡
rect.horizontalNormalizedPosition = Mathf.Lerp(rect.horizontalNormalizedPosition, targethorizontal, t);
if (t >= 1)
stopMove = true;
}
}
/// <summary>
/// 移动到第几页,如果需要可在界面进行交互
/// </summary>
/// <param name="index"></param>
//public void pageTo(int index)
//{
// if (index >= 0 && index < posList.Count)
// {
// rect.horizontalNormalizedPosition = posList[index];
// SetPageIndex(index);
// }
// else
// {
// Debug.LogWarning("页码不存在");
// }
}
//private void SetPageIndex(int index)
//{
// if (currentPageIndex != index)
// {
// currentPageIndex = index;
// if (OnPageChanged != null)
// OnPageChanged(index);
// }
//}
//下面就是拖拽事件,eventData存拖拽的信息
public void OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
startDragHorizontal = rect.horizontalNormalizedPosition;//开始滑动位置(0-1)
}
public void OnEndDrag(PointerEventData eventData)
{
float posX = rect.horizontalNormalizedPosition;//结束拖拽的位置(0-1)
posX += ((posX - startDragHorizontal) * sensitivity);//拖拽距离乘以灵敏度
posX = posX < 1 ? posX : 1;
posX = posX > 0 ? posX : 0;
//计算当前拖拽到的位置到哪个界面最近就显示哪个界面
int index = 0;
float offset = Mathf.Abs(posList[index] - posX);
for (int i = 1; i < posList.Count; i++)
{
float temp = Mathf.Abs(posList[i] - posX);
if (temp < offset)
{
index = i;
offset = temp;
}
}
//SetPageIndex(index);
targethorizontal = posList[index]; //设置当前坐标,更新函数进行插值
isDrag = false;
startTime = 0;
stopMove = false;
}
}
一开始还在自己用移动函数和SetActive去控制界面的切换,但把自己搞蒙了,搜了半天才知道还有这么好用的组件,搞了两个小时终于搞明白了,多看手册积累经验很重要!
来源:https://blog.csdn.net/derbi123123/article/details/107732244
猜你喜欢
- 将SpringBoot项目部署到腾讯云注意:1、如果已经下载好MySql和JDK,可以直接跳过1、3步骤。但是不要忘记步骤2哦。2、如果已经
- 相信大家肯定都在电商网站买过东西,当我们看中一件喜欢又想买的东西时,这时候你又不想这么快结账,这时候你就可以放入购物车;就像我们平时去超市买
- 前言在 C# 项目中通过链接方式引入文件可以让我们在项目中使用这些文件中的代码。常见的比如链接 AssemblyInfo.cs 文件,这样我
- SpringBoot项目中新增脱敏功能项目背景目前正在开发一个SpringBoot项目,此项目有Web端和微信小程序端。web端提供给工作人
- idea中的Maven导包失败问题解决总结1.先确定idea和Maven 的配置文件settings 没有问题找到我们本地的maven仓库,
- 开始 在本文中,我将展示如何使用各种不同的 Java 技术构建一些简单的 Comet 风格的 Web 应
- 定义可理解为 适配广泛的类型,即参数化类型,可以把类型像方法的参数那样进行传递。// 以ArrayList为示例// 泛型T可以是任意类pu
- 前言:对于一个程序员来说,尤其是在java web端开发的程序员,三大框架:Struts+Hibernate+Spring是必须要掌握熟透的
- 参数和返回值得加密目的为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性。具体实现方
- 一、新时间日期API常用、重要对象介绍ZoneId: 时区ID,用来确定Instant和LocalDateTime互相转换的规则Instan
- 在客户机和服务器之间建立单一的双向连接,这就意味着客户只需要发送一个请求到服务端,那么服务端则会进行处理,处理好后则将其返回给客户端,客户端
- 前言本来没有计划这一篇文章的,只是在看完SpringBoot核心原理后,突然想到之前开发中遇到的MVC自动失效的问题,虽然网上有很多文章以及
- 这篇文章主要介绍了基于SPRINGBOOT配置文件占位符过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 面向对象有封装、继承、多态这三个特性,面向对象编程按照现实世界的特点来管理复杂的事物,把它们抽象为对象,具有自己的状态和行为,通过对消息的反
- 1、右值1.1 简介首先区分一下左右值:左值是指存储在内存中、有明确存储地址(可取地址)的数据;右值是指可以提供数据值的数据(不可取地址)如
- 一、FeignClient注解FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient
- C#之继承继承、封装和多态是面向对象编程的重要特性。其成员被继承的类叫基类也称父类,继承其成员的类叫派生类也称子类。派生类隐式获得基类的除构
- 使用范围synchronized使用上用于同步方法或者同步代码块在锁实现上是基于对象去实现使用中用于对static修饰的便是class类锁使
- 什么是WebSocket WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间
- 目录一、System.out.println(最简单)二、java.util.logging(相对简单)三、log4j(最强大)四、comm