我基本上希望能够在准确的时间内将一个对象从开始位置移动到结束位置 ,例如,将一个立方体从{x:0,y:0, z:0}到{x:5,y:-3,z:10}恰好在750毫秒内。我需要能够计算精确到毫秒的运动,以便正确地将其与音频对齐,因此我需要能够计算从一帧到下一帧所花费的时间,并将其计入我的方程式中。我几乎可以正常工作,我不想粘贴我的整个项目,因为它依赖于几个文件,但是我会引用到目前为止的关键计算,但是请记住,下面的代码是不可测试,因为它取决于很多其他事情,但是我只想分享通用方法。在具有所有辅助功能的主文件中,我在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”,当到达它时(从“到达”函数中调用),它将tween.first重置为旧的me.ko(也就是将新的开始位置更改为旧的结束位置),并将tween.second设置为新的随机生成的位置,它会不断重复和console.log-loging从一个到另一个的时间。 (您可能已经猜到“ totalTime”是它应该在一个补间和另一个补间之间花费的时间)。
它几乎可以完美地精确地工作,除了通常约16毫秒太少或太多(大约1000/60),或者有时少了几毫秒或几毫秒。这是一个问题,因为我需要精确到毫秒。
如果以上所有代码都太复杂而无法获取,则整个代码的关键部分是:
stepAmount = (
((curDist) /
(
this.totalTime * (
1
)
)) * diff
*deltaTween * deltaTime
/*
(
(originalTotalTime - this.totalTime) /
(this.totalTime)
)*/
)
正确计算补间中每帧要拍摄的正确stepAmount。
不用说,我对使用诸如tween.js等之类的任何库不感兴趣。