前言

   该文档为《Unity游戏开发文档(3):Dancing Line》的附属文档,亦可看作是单独的技术总结文档。

目录

  • 综述
  • 对话框的非匀速滑动
  • 对话框动画的异步运行
  • 最终效果
  • 参考资料


综述

无论是在游戏中还是在其他应用程序中,我们都经常使用到 “点击按钮 — 弹出对话框” 这一功能。这个功能最简单的形式就是点击某个按钮,然后对话框直接出现在用户的屏幕上,点击关闭按钮,对话框又直接从屏幕上消失。

在游戏中我们会希望往这一个交互动作中再加入一些特效,例如让对话框从屏幕的边缘滑动到指定的位置,同时辅佐以一些互动音效。此外还会在对话框的滑动上做一些手脚,例如让对话框做一个非匀速运动,刚出现时速度是比较快的,但随着对话框离指定位置越来越近,滑动的速度也随之降低。从而使这一互动显得更加生动有趣。


对话框的非匀速滑动

我们可以使用Untiy中的 动画曲线(Animation Curve) 组件,来实现上述的功能。

动画曲线是一条可由用户自由编辑形状的的曲线,动画曲线的定于域与值域均为 [0,1][0,1][0,1]。用户可以通过在曲线上右键 “add key” 来添加控制曲线形状的键值点。一般情况下,我们会把动画曲线当作是一个连续的 字典(Dictionary) 来使用(标准库提供的字典是离散的),即通过 XXX 值来获取我们所希望得到的 YYY 值。

那么我们如何使用动画曲线来为UI实现非匀速的滑动效果呢?在这里我们可以借助Unity 线性插值函数(Lerp Function) 的力量来实现这一个功能。线性插值函数的数学含义是通过用户提供的起始数值 aaa 和末端数值 bbb 以及插值范围 ttt,计算并返回一个大于 aaa 小于 bbb 且与 ttt 呈对应比例的数值,其数学表达式如下:

Lerp(a,b,t)=a+(b−a)∗tt∈[0,1]Lerp(a, b, t) = a + (b-a)*t \qquad t\in[0,1] Lerp(a,b,t)=a+(b−a)∗tt∈[0,1]

如果我们把UI的初始位置设为 aaa,UI的目标位置设为 bbb,那么通过令 ttt 从 000 递增至 111 便可实现UI从其初始位置滑动到某个特定位置的的效果。如果我们进一步调整 ttt 的递增速度,便可实现UI的非匀速滑动效果。例如令 ttt 以指数形式递增,UI便会以初始慢、结尾快的速度滑动;令 ttt 以对数形式递增,UI则会以初始快、结尾慢的速度滑动。再结合我们刚刚介绍过的动画曲线。如果我们令 ttt 值的变化,与动画曲线 YYY 值的变化一致,便可自由地调整UI滑动的速度了。

以下是具体的实现代码:

private float anime_time = 0.5f;
public AnimationCurve anime_curve;void PopOutAnime(GameObject panel) {float timer = 0.0f;Vector3 origin_pos = panel.transform.position;Vector3 target_pos = panel_pos - new Vector3(0.0f, panel.transform.position.y, 0.0f);while (timer < anime_time) {timer += Time.deltaTime;if (timer > anime_time)timer = anime_time;float lerp_ration = timer / anime_time;panel.transform.position = Vector3.Lerp(origin_pos, target_pos, anime_curve.Evaluate(lerp_ration));}
}

在上述代码中,我们手动设定了面板滑出动画的时间,然后令动画进行时间与动画曲线的 XXX 值呈映射关系,并获取曲线上相对应的 YYY 值来算出面板位置的插值,从而得到了非匀速的滑动效果。

面板消失的实现逻辑与面板弹出是一致的,相当于面板弹出的逆过程。


对话框动画的异步运行

接下来让我们理一理弹窗特效在游戏中的运行逻辑。一般来说我们希望呈现给用户的的效果是:

  • 用户点击按钮或其他交互组件。
  • 交互组件接收到交互指令,或是被交互指令触发。
  • 交互组件唤醒弹窗,并命令弹窗播放特效。
  • 用户看到弹窗出现。

顺着上述的思路,并代入到Unity的框架下构思代码实现逻辑,我们首先能想到的是玩家点击按钮后程序立即调用上文中的 PopOutAnime() 函数来播放弹窗特效。但这样存在一个问题是,PopOutAnime() 被调用后会在一帧内执行完毕。也就是说在玩家点击按钮后的下一帧里,对话框会在一帧的时间内走完整个滑动流程。玩家所能看到的是自己点击按钮后,对话框立马凭空出现在屏幕上。这明显是不对的。

我们希望的是玩家点击按钮后,PopOutAnime() 能够“缓慢地”执行,这样玩家才能完整地看到整个弹窗特效。在这种情况下,异步(Asynchronous) 运行的强大之处就得以体现了。 在Unity中,我们可以通过创建 协程(Coroutines) 来异步运行任务。

从定义上来讲,Unity的协程是一个能够暂停执行,在暂停结束后又能立即恢复执行的函数。当我们开启了一个协程后,主函数会开始执行协程中的内容,直到协程被暂停。协程被暂停后会立即返回到主函数,并继续执行主函数的剩余部分,直到暂停状态结束,协程恢复执行。通过协程的这种特性,我们便可以实现 PopOutAnime() 的“缓慢执行”。先上代码:

private float anime_time = 0.5f;
private Button help_button_;
private GameObject help_panel_;
public AnimationCurve anime_curve;void Start() {help_button_.onClick.AddListener(() => {StartCoroutine(PopOutAnime(help_panel_));});
}IEnumerator PopOutAnime(GameObject panel) {float timer = 0.0f;Vector3 panel_pos = panel.transform.position;Vector3 target_pos = panel_pos - new Vector3(0.0f, panel_distance_, 0.0f);while (timer < anime_time) {timer += Time.deltaTime;if (timer > anime_time)timer = anime_time;float lerp_ration = timer / anime_time;panel.transform.position = Vector3.Lerp(panel_pos, target_pos, anime_curve.Evaluate(lerp_ration));yield return null;}yield break;
}

在上述代码中,我们为按钮添加了监听器。当按钮监听到了玩家的点击事件,会就为 PopOutAnime() 开启一个协程。在协程内部,当 while() 循环每执行完一次,协程就会通过 yield return null 指令被暂停,直到当前帧结束,下一帧开始后,协程才会继续执行 while() 循环的剩余部分。当循环结束后,即对话框已经到达了指定位置,协程通过 yield break 结束掉自己,以释放系统资源。

通过这样 PopOutAnime() 函数在每一帧只会令对话框移动一小段距离。最终呈现在玩家面前的就是正常的滑出效果了。


最终效果


参考资料

Unity中协程(IEnumerator)的使用方法介绍: https://blog.csdn.net/beihuanlihe130/article/details/76098844?spm=1001.2014.3001.5502
Unity UGUI 按钮绑定事件的 4 种方式:https://www.cnblogs.com/isayes/p/6370168.html
关于unity中AddListener的传参问题的研究: https://blog.csdn.net/qq_42097011/article/details/103712333?spm=1001.2014.3001.5501

原创博客,不得转载、抄袭

Unity游戏开发文档(3.1.1):弹窗效果相关推荐

  1. Unity游戏开发文档(3.1.3):滚动式关卡选择菜单

    前言      该文档为<Unity游戏开发文档(3):Dancing Line>的附属文档,亦可看作是单独的技术总结文档. 目录 综述 构建滚动菜单 读取关卡信息 填充菜单选项 选项自动 ...

  2. Unity游戏开发文档(3.1.2):下拉式音乐选择菜单

    前言      该文档为<Unity游戏开发文档(3):Dancing Line>的附属文档,亦可看作是单独的技术总结文档. 目录 综述 构建下拉菜单 填充下拉菜单 切换背景音乐 最终效果 ...

  3. Unity游戏开发文档(1):飞行模拟

    前言     本篇的代码是基于Unity3D 系列课程 "Create with Code" 第一章 "Player Control" 改进而来 目录 背景 设 ...

  4. python飞机大战概要设计_飞机大战游戏开发文档(Android版)

    飞机大战游戏 开发文档 (Android版) 课程名称:飞机大战游戏 课程类型:Android游戏编程精彩内容,尽在百度攻略:https://gl.baidu.com 姓名:苏均灿 学号:131342 ...

  5. 微信小程序游戏开发文档以及开发工具地址

    微信小程序开发交流qq群   581478349    承接微信小程序开发.扫码加微信. 正文: 微信官方于 2017 - 12 - 28 日 开发微信小程序 开发小游戏 , 微信小程序小游戏开发官方 ...

  6. java小组坦克大战游戏开发文档开发日志_java实现坦克大战游戏

    本文实例为大家分享了java实现坦克大战游戏的具体代码,供大家参考,具体内容如下 一.实现的功能 1.游戏玩法介绍 2.自定义游戏(选择游戏难度.关卡等) 3.自定义玩家姓名 4.数据的动态显示 二. ...

  7. php赛车游戏开发文档,React 开发一款简单的赛车游戏

    写在开始之前 最近研究egret引擎时,在论坛看到了用egret引擎写的一款赛车游戏 玩法很简单,左右控制赛车躲避来车,碰撞即游戏失败 下面将为大家一步步讲解,如何用React写出一款纯 javasc ...

  8. 微信小游戏开发文档(4)

    微信小游戏系统API: wx.onTouchEnd wx.offTouchEnd wx.onTouchCancel wx.offTouchCancel Touch 触点 微信小游戏数据缓存接口: wx ...

  9. 火力篮球游戏源码完整版-带游戏开发文档

    火力篮球,通过模拟现实中的投篮游戏机,而投篮游戏机又是源于街头篮球,街头篮球起源于美国,现在已经流行于世界的体育竞技项目,将投篮部分独立出来做成投篮游戏机.成为了专门的投篮类游戏设备.而本游戏就是将该 ...

最新文章

  1. Mysql分页order by数据错乱重复
  2. 每日命令:(13)more
  3. Oracle中默认创建的表
  4. Nginx核心要领五:worker_processes、worker_connections设置
  5. 视频直播点播nginx-rtmp开发手册中文版
  6. [html] 如何优化页面的渲染过程?
  7. mysql 1000万数据读取_插入1000万条数据到mysql数据库表
  8. Linux下vim常用操作
  9. Skywalking微服务监控分析
  10. Java编程基础 - 泛型
  11. C# WinFrom 对字符进行UTF-8编码
  12. 机器学习——seaborn可视化
  13. 1023. Have Fun with Numbers (20)
  14. fiddler 的AutoRespoder的使用(手动添加测试桩)
  15. 浅析Linux下的task_struct结构体
  16. 视频测试皮肤的软件,皮肤检测仪(LEIM魔镜仪)安装及操作视频
  17. Kaptcha 使用
  18. Map-Based Indoor Pedestrian Navigation Using an Auxiliary Particle Filter
  19. 新加坡国立大学计算机系访学,关于选拔本科生2019年春季学期赴新加坡国立大学访学的通知...
  20. 别让学历限制你,你可能是AI领域的下一个巨星

热门文章

  1. Linux运维学习历程-第十五天-磁盘管理(二)Raid与LVM逻辑卷
  2. 【mysql学习篇】为什么mysql用B+Tree?
  3. LightOJ 1340 Story of Tomisu Ghost
  4. esxi查看许可过期_解决Vsphere Client 60天过期问题
  5. 2764和6264地址范围
  6. 纵横网络靶场社区前四题wp
  7. android json html标签,Android: Parsing HTML tags in JSON
  8. Nagios Core/Icinga 基于栈的缓冲区溢出漏洞
  9. 深入分析Docker镜像原理 (转载)
  10. C++ 将二叉树叶子结点从左往右顺序串连