JavaScript Three.js /逻辑-如何仅使用本机JS在给定的时间内将对象从一个位置补间到另一个位置

问题描述 投票:0回答:1

我基本上希望能够将一个对象从开始位置移动到结束位置在精确的时间内,例如,将立方体从{x:0,y:0,z:0}移动精确到750毫秒内的{x:5,y:-3,z:10}。我需要能够计算精确到毫秒的运动,以便正确地将其与音频对齐,因此我需要能够计算从一帧到下一帧所花费的时间,并将其计入我的方程式中。我几乎可以正常工作,我不想粘贴我的整个项目,因为它依赖于几个文件,但是我会引用到目前为止的关键计算,但是请记住,下面的代码是无法测试,因为它取决于很多其他事情,但是我只想分享通用方法。在带有所有辅助函数的主文件中,我在setInterval(update,1000 / FPS)中设置了update()函数,并将FPS设置为60。

您可能已经猜到了,它每秒不会[发生60次,因为浏览器的性能有时会使它降低几十毫秒,甚至更少,但仍然如此。因此,在我的更新函数中,它是这样的(我不确定要捕获哪些变量,无论是一次帧到下一帧的字面差异还是差异的比率,所以我现在都捕获了这两个变量):var lastFrame = Date.now(); var difference = 0; var deltaTime = 0; function update() { renderer.render(scene, camera); raycaster.setFromCamera( mouse.position3D, camera ); things.forEach(x => { if(x.update) { x.update(x); } }); //other general update things which take some time etc. deltaTime = Date.now() / lastFrame; difference = Date.now() - lastFrame; lastFrame = Date.now() }

因此,您将我的所有THREE.js对象嵌套在称为“事物”的包装对象数组中,并且每个“事物”都有一个从主update函数调用的update函数。我们将在几秒钟后回到这个问题,但首先是另一个主要辅助函数(这是问题的重点),补间函数(仍在此通用主辅助函数文件中):

Object.defineProperties(Object, { copy: { get() { return function(other, other2) { if(!(typeof other2 == "object")) { var tmp = {}; for(var k in other) { if(typeof other == "object") { tmp[k] = other[k]; } } return tmp; } else { for(var k in other2) { other[k] = other2[k] } var o2a; if(t(other2, String)) { o2a = other2.split(","); } else if(t(other2, Array)) { o2a = other2 } // console.log(o2a) if(t(o2a, Array)) { var r = {}; o2a.forEach(k => { r[k] = other[k]; }) return r } } }; } } }); function myTween(opts) { var th = this; if(!opts) opts = {}; this.changeObj = opts.changeObj || {}; var first = opts.first || {}; this.second = opts.second || {}; var originalTotalTime = opts.totalTime || 1000; this.totalTime = originalTotalTime Object.defineProperties(this, { first: { get() { return first; }, set(v) { first = v; Object.copy(obj, v); th.totalTime = originalTotalTime; } } }); var rezs = {}, rez = "going", obj = this.changeObj, hasReached = false, curTotal = this.totalTime; Object.copy(obj, first) this.reached = () => { if(t(opts.reached, Function)) { opts.reached(this); } if(hasReached) { curTotal = Date.now() - timeOfLast console.log( originalTotalTime - curTotal, curTotal, diff ); timeOfLast = Date.now(); hasReached = false; } }; var lastF = Date.now(), diff = 1, timeOfLast = lastF, totalTime = 0; this.update = () => { if(!hasReached) { // timeOfLast = Date.now() hasReached = true; } diff = Date.now() - lastF; rezs = {}; rez = "" Object.keys(this.first).forEach(k => { var distanceNeeded = Math.abs( this.first[k] - this.second[k] ), curDist = Math.abs(obj[k] - this.second[k]), stepAmount = ( ((curDist) / ( this.totalTime * ( 1 ) )) * diff *deltaTween * deltaTime /* ( (originalTotalTime - this.totalTime) / (this.totalTime) )*/ ), leeway = stepAmount //console.log(stepAmount) this.totalTime -= diff / Object.keys(this.first).length; if(this.totalTime < 0) { // this.reached() /// this.totalTime = originalTotalTime; } var ox = this.second[k]; if(ox < obj[k] + leeway) { stepAmount = -Math.abs(stepAmount); } else if (ox > obj[k] - leeway) { stepAmount = Math.abs(stepAmount); } if(curDist < leeway) { obj[k] = this.second[k]; stepAmount = 0; rezs[k] = "there"; } obj[k] += stepAmount; }); var count = 0; for(var r in rezs) { if(rezs[r] == "there") count++; } if(count > 0) { rez = "there"; } else { rez = "going" } if(rez == "there") { this.reached(); } lastF = Date.now(); }; }

因此,此函数中发生了很多事情,但是希望一旦我在另一个文件中展示了如何使用这些细节,这些细节就会变得显而易见。基本上,当在“事物”数组中创建新事物时,每个“事物”都具有一个更新函数(从主更新循环中更早调用)和一个启动函数,因此,其用法如下:

new main.thing({ color:"blue", position: { x:-3, y:-1 }, start(me) { me.ok = { x:0, z:-2, y:4, }; me.ko = { y:-2, x:3, z:3 } me.done = [] me.averages = []; me.notsoaverage = []; me.lolabuy = new main.myTween({ changeObj: me.position, first: me.ok, second: me.ko, totalTime: 200, reached(tween) { tween.first = Object.copy(tween.second, "x,y,z"); tween.second = { x: Math.random() * 8 - 4, z: Math.random() * 8 - 6, y: Math.random() * 8 - 4, } } }); }, update(me) { me.lolabuy.update() } });

所以基本上,以前的函数myTween实际上是构造函数,并且从一开始就将新的myTween设置为“ me”对象,该对象引用“ things”(THREE.js对象的包装器)本身。补间的“第一个”设置为代表起始位置的“ me.ok”,而“第二个”则表示要补间到的位置,设置为变量“ me.ko”,当到达它时(如从函数“ reached”中调用的那样),它将tween.first重置为旧的me.ko(也就是将新的开始位置更改为旧的结束位置),并将tween.second设置为新的随机生成位置,并不断重复和记录从一个到另一个的时间。 (并且您可能已经猜到“ totalTime”是应该在一个补间和另一个补间之间花费的时间)。

它几乎可以完美地精确地工作,除了它通常约16毫秒太少或太多(大约1000/60),或者有时少了几毫秒或更多毫秒。这是一个问题,因为我需要精确到毫秒。

如果上面的所有代码太复杂而无法获取,则整个代码的关键部分是这样的:

stepAmount = ( ((curDist) / ( this.totalTime * ( 1 ) )) * diff *deltaTween * deltaTime /* ( (originalTotalTime - this.totalTime) / (this.totalTime) )*/ )

以正确计算补间中每帧要采取的正确stepAmount。

而且不用说,我

not

对使用任何库(例如tween.js等感兴趣)>>我基本上希望能够在准确的时间内将一个对象从开始位置移动到结束位置,例如将一个立方体从{x:0,y:0,z:0}移动到{x: 5,y:-3,z:10}恰好在750 ...
javascript math logic tween
1个回答
0
投票
大多数补间引擎不使用stepAmount,也不保留任何状态(或不多的状态)。它们只有一个startTime和持续时间(或endTime和计算持续时间)。

因此,首先将其转换为归一化的(0到1)数字。

© www.soinside.com 2019 - 2024. All rights reserved.