如何重置三.js时钟

How to reset three.js clock?

本文关键字:时钟 js 何重置      更新时间:2023-09-26

我想重置时钟,以便clock.getElapsedTime()从重置时钟时开始给我一个新的时间(例如,在第二次重新启动游戏关卡/场景时很有用)。

我在init()启动clock = new THREE.Clock();,在我的游戏循环update()中,我正在使用这个时钟。但是当游戏结束时,我想重置时钟(我没有再次启动关卡,只是将玩家定位回起点,所以我不会启动新的时钟)。

我怎样才能做到这一点?

坏消息:从 2015 年 10 月发布的 r73 开始,不可能将THREE.Clock重置为零时间。解释如下,唯一可能的解决方法是本答案的末尾。

深入探讨三时钟设计存在的问题

时钟的设计很危险...

——Mrdoob,GitHub问题评论

要了解THREE.Clock中的缺陷,我们必须检查源代码以了解其工作原理。我们可以看到,在 Clock 的构造函数中,实例化了一些变量,乍一看,我们可以覆盖Clock实例以重置为零:

this.startTime = 0;
this.oldTime = 0;
this.elapsedTime = 0;
但是,深入

挖掘,我们需要弄清楚调用getElapsedTime()时会发生什么。在引擎盖下,它调用getDelta(),它变异this.elapsedTime,这意味着getDeltagetElapsedTime是危险的、冲突的函数,但我们仍然需要仔细研究getDelta

var newTime = self.performance.now();
diff = 0.001 * ( newTime - this.oldTime );
this.oldTime = newTime;
this.elapsedTime += diff;

此函数引用一些未知的隐式全局变量 self并调用一些奇数函数 self.performance.now() 。红旗!但是让我们继续挖掘...

事实证明,Three 定义了一个全局变量 self ,其属性performance与"main"Three.js 文件中now()的方法。

退后一步THREE.Clock片刻,看看它是如何计算this.elapsedTime的。它基于 self.performance.now() 返回的值。从表面上看,这似乎足够无辜,但真正的问题出现在这里。我们可以看到,self.performance.now()在闭包中创建了一个真正的"私有"变量,这意味着外界没有人可以看到/访问它:

( function () {
    var start = Date.now();
    self.performance.now = function () {
        return Date.now() - start;
    }
} )();

start变量是应用的开始时间,以毫秒为单位从 Date.now() 返回。

这意味着Three.js库加载时,start将设置为值 Date.now()澄清一下,简单地包含Three.js脚本会对计时器产生全局的、不可逆的副作用。回顾时钟,我们可以看到 self.performance.now() 的返回值用于计算一个Clock的当前经过时间。因为这是基于Three.js的私有的、不可访问的"开始时间",所以它将始终与包含Three.js脚本的时间有关。

解决方法 #1

startTime = clock.getElapsedTime()存储在您的游戏/关卡开始时,并与之相关进行所有计算。类似于var currentTime = clock.getElapsedTime() - startTime,这是从场景加载/启动中获取真实绝对时间的唯一方法。

解决方法 #2

引擎盖下的Three.Clock实际上只是围绕Date.now()的薄包装,这是一种IE9+可用的方法。你最好围绕这个创建你自己的小抽象,以获得一个理智的时钟,并有一个易于实现的reset()方法。如果我找到具有此功能的npm包,或者自己制作,我将更新此答案。

解决方法 #3

等待 Three.js Three.Clock 源代码更新,可能是我更新的。由于它有一个开放的票证,因此可能会接受修复它的拉取请求。

在游戏开始时节省时间:var gameStartTime = clock.performance.now(),并通过从游戏过程中任何其他时间点(包括游戏结束时)从clock.performance.now()中减去gameStartTime来计算此后的时间运行。例如:gameStartTime - gameStartTime等于零,clock.performance.now() - gameStartTime会给你自游戏开始以来的秒或分钟。

下面是对 JavaScript 中 performance.now() 计时器函数的引用。

这不会重置时钟,但它会生成一个新的时钟对象,并将新值分配给每 5 秒一次的 elapsedTime 变量:

let clock = new THREE.Clock();
const animate = () => {
  let elapsedTime = clock.getElapsedTime();
  if (elapsedTime > 5) {
    clock = new THREE.Clock();
  }
  window.requestAnimationFrame(animate);
};

这是我用来start pausereset动画系统运行时的简单解决方法。

let offsetTime = 0; // in milliseconds
let start = false;
function reset() {
    offsetTime = 0;    
    start = false;
}
function start() {
    
    if ( start ) return;
    offsetTime += performance.now();    
    start = true;
}
function pause() {
    
    if ( start ) return;
    offsetTime -= performance.now();    
    start = false;
}
const animationRuntime = performance.now() - offsetTime;