一、Unity插值函数Lerp()

通过官方文档简单了解插值函数(https://docs.unity3d.com/ScriptReference/index.html),可以看到插值函数有很多

Mathf.Lerp()

从最简单的数学插值来看,插值函数接收三个参数a,b,t,在ab之间,以t作为比例来插值。

例如,Lerp(0, 10, 0.4f),此时返回4,即 a + (b - a) * t

注意,第三个参数要小于1,如果大于1,则还是返回b。

其他的插值也类似于此,只不过插值的对象变为向量,颜色等等。

二、增量时间Time.deltatime

游戏都是一帧一帧显示的,平时说的60FPS就是1秒60帧;帧率越高,游戏运行就越流畅。

增量时间deltatime,就是从上一帧到现在所经过的时间。如果游戏稳定在60FPS,那么增量时间就是1/60s,当然实际游戏运行帧数肯定在不断变化,所以Time.delta的值也在不断地变化。简单来说,Time.deltatime就是运行每一帧所花的时间。

三、插值函数的作用

在看官方文档以及其他的代码时,经常发现插值函数的第三个参数往往和增量时间有关,如:

using UnityEngine;

public class Example : MonoBehaviour
{
Transform target;
float speed = 0.1f;
void Update()
{
transform.position = Vector3.Lerp(transform.positon, target.position, Time.deltatime * speed);
}
}

那么为什么线性插值的第三个参数要用到增量时间呢?

首先,需要理解为什么要利用插值函数。

不妨想象以下游戏场景,在主人身后有一只宠物,它会时刻跟在主人身后,如果主人走远了,它也会快速跟上。

可以发现两点,第一,宠物不是瞬间移动到主人身后的,而是一点一点走过来的,第二,宠物和主人离得越远,宠物跟随的速度就越快,离主人越近,宠物的速度就越慢,而Lerp()函数就可以实现弹性跟随的效果。

transform.position = Vector3.Lerp(transform.positon, target.position, Time.deltatime * speed);

这行代码中,Lerp()函数返回一个自身位置和target位置之间的一个位置,比例是Time.deltatime * speed,然后再把这个值作为自己的新位置

如果第三个参数是0.5,自身位置为0,目标位置为10,那么第一次自身从0移动到5,第二次从5移动到7.5,即每次都会移动(目标位置 - 自身位置)* 0.5的位置,由于自己越来越接近目标位置,这个值也会越来越小,所以每次往前移动的距离也就越来越小,这样就实现了弹性的效果。

当然,从数学的角度来看,只能无限接近目标位置,永远也到达不了目标位置,但是玩家是感觉不出这一点的差距的。

四、使用增量时间进行插值

最后回到本篇的重点,为什么在插值函数中要使用增量时间作为参数呢?

还是以最简单的数学插值为例

a = Mathf.Lerp(a, b, 0.1f);

每次运行后,a都会以10%的速度向b靠近。假设a和b之间相差1个单位,第一帧后,a和b之间剩余的距离变为0.9,第二帧过后,a和b之间剩余的距离变为0.81 = 0.9 * 0.9。以此类推,第n帧过后,a和b之间的剩余距离变为0.9^n

如果FPS=10,那么一秒后a和b之间的剩余距离变为0.9^10,如果如果FPS=20,那么一秒后a和b之间的剩余距离变为0.9^20,也就是说插值效果和电脑运行的帧率有关。

下面考虑使用增量时间的情况(一般会用speed来乘以增量时间,这里为了简化就假设speed为1)

a = Mathf.Lerp(a, b, Time.deltatime);

类似的,在n帧过后,a和b之间的剩余距离变为 (1 - Time.deltatime) ^ n,如果假设电脑帧率稳定的话,根据增量时间的定义,Time.deltatime表示每帧运行的事件,即Time.deltatime= 1/n

那么,a和b之间的剩余距离变为 (1 - 1/n) ^ n。可以看出,插值效果仍然和帧率有关,但是如果带入数字详细计算的话,会发现实际差距已经很小。

例如,n = 30,即FPS=30,那么结果大致是0.36166,如果 n = 60,结果大致是0.36479

这种写法比较简便,而且不同帧率下的插值效果相差很小,这也是为什么大部分代码都会这么写的原因。

最后,给出如下写法:

a = Mathf.Lerp(a, b, 1 - speed ^ Time.deltatime);

通过同样的计算可得,a和b之间的剩余距离变为 [1 - (1 - speed ^ (1/n) ] ^ n = speed,这种写法做到了完全和帧率无关,即无论在哪一台电脑上运行,在相同的时间后都会有相同的插值效果

五、总结

在不苛求完美的情况下,一般采取第二种写法,就可以取得较好的插值效果效果

参考文章:https://www.construct.net/en/blogs/ashleys-blog-2/using-lerp-delta-time-924

Unity插值函数Lerp()与增量时间Time.deltatime的更多相关文章

  1. Time.deltaTime 增量时间

    static var deltaTime : float Description描述 The time in seconds it took to complete the last frame (R ...

  2. Unity学习疑问记录之时间变量

    1.Time.deltaTime 以秒计算,完成最后一帧的时间 放在Update()函数中的代码是以帧来执行的.如果我们需要物体的移动以秒来执行.我们需要将物体移动的值乘以Time.deltaTime ...

  3. Unity的Lerp函数实现缓动

    在Unity里面Lerp函数可以实现缓动效果 下面例子实现点光源的移动 在场景中创建好一个平面,一个点光源,我在这里随便放了一个模型. 然后新建c#脚本,代码如下: using UnityEngine ...

  4. Time.fixedDeltaTime 固定增量时间

    static var fixedDeltaTime : float Description描述 The interval in seconds at which physics and other f ...

  5. Unity通过NTP获取网络时间

    最初通过qq时间服务器获得时间,经常出现有网络也获取失败的情况. 后面寻找解决办法,查找资料终于发现通过ntp时间服务器获取网络时间的方法.   首先游戏开始获得初始化网络时间,通常只获取一次,其他时 ...

  6. Unity 新手入门 如何理解协程 IEnumerator yield

    Unity 新手入门 如何理解协程 IEnumerator 本文包含两个部分,前半部分是通俗解释一下Unity中的协程,后半部分讲讲C#的IEnumerator迭代器 协程是什么,能干什么? 为了能通 ...

  7. Unity中Time.deltaTime的含义及其应用

    The time in scenes it took to complete the last frame.这是使用此函数的时候给出的提示 一般我们会在设置速度的时候看到这个函数.先写出我对Time. ...

  8. 如何正确的使用Lerp In Unity

    摘要 本文探讨如何用lerp实现近似的匀速旋转,当然如果运用本文给出的方法,使用slerp则可以实现匀速旋转,并指出Unity官方lerp示例代码的一些缺陷. 现有问题 比如四元数Lerp API: ...

  9. kettle 6.1 按时间循环增量抽取数据

    场景:假设有一张表数据量很大,需要按一个时间来循环增量抽取 方法:主要是通过JOB自身调用,实现循环调用,类似于 函数自调用 的循环. 1.JOB全图: 2.获取增量时间,并设置增量时间环境变量 3. ...

随机推荐

  1. OC小实例关于init 方法不小心的错误

    OC小实例关于init 方法不小心的错误  正视遇到的每一个错误 在一个遥控器类操控小车玩具的小实例项目中,我采用组合的方式,将遥控器拥有小汽车对象(has a)关系,而不是继承(is a)关系. 想 ...

  2. Mysql 数据库中间件

    读写分离:简单的说是把对数据库读和写的操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力.主数据库提供写操作,从数据库提供读操作,其实在很多系统中,主要是读的操作.当主数据库 ...

  3. PandoraBox 支持3G无线上网卡(电信卡3G卡)(三)

    笔者采用的是华为EC122无线上网卡 一:编辑/etc/modules.d/60-usb-serial usbserial vendor=0x12d1   product=0x1505 二:编辑/et ...

  4. hadoop reduce 阶段遍历 Iterable 的 2 个“坑”

    01 package com.test; 02   03 import java.util.ArrayList; 04 import java.util.Iterator; 05 import jav ...

  5. 启用了不安全的HTTP方法解决办法 IBM APPSCAN

    启用了不安全的HTTP方法解决办法  IBM APPSCAN     安全风险:       可能会在Web 服务器上上载.修改或删除Web 页面.脚本和文件. 可能原因:       Web 服务器 ...

  6. intellij IDEA 更新java后不用重启tomcat

    最近项目大了,每次修改后重启都要等和很久,那个煎熬…… 为了解决这个问题,万能的Google  装了这个  JREBEL 5.63最新的 安装步骤: 一.IDEA在线搜索 jrebel  安装 二.破 ...

  7. Record is locked by another user

    Oracle修改表中记录时出现record is locked by another user的问题 在操作表时没有commit,导致表被锁,只要执行下面两行语句,就可以了将行锁解锁了. Select ...

  8. iconfont的图文混排

    最近在使用iconfont排版,但是发现完全没法混到textarea中. 希望借助 contentEditable 解决这个问题

  9. vue开发购物车,解决全选单选问题

    实现全选单选,在vue中无法通过this获取input中的checkbox的checked属性,但是可以通过vue对input的特殊方式v-model来实现对应数据的绑定,同样也可以通过这种方式实现购 ...

  10. MYSQL初级学习笔记二:数据表相关操作及MySQL存储引擎!(视频序号:初级_5,7-22|6)

    知识点三:数据表相关操作(5,7-22) --------------------------------整型--------------------------------- --测试整型 CREA ...