I'm trying to get a good animation with constant fps, but nothing works. I'm using threejs, webgl to render the scene and for the animation loop I found two ways (is there a third?), which is either requestAnimationFrame(...) or by setTimeOut(). Both don't guarantee that the fps is constant, but I'm fixing it by updating the object position by the timedelta of window.performance.now(). But I still have lagspikes which one can clearly see. So how can I fix this? It's obviously possibly because there are games like doom which don't lag.
My example with full src-code can be found here:
http://sc2tube.com:8080/test/three.html
the relevant code:
function animate() {
requestAnimationFrame( animate );
// calculate how long the last frame was
var timefix = (window.performance.now() - last)/(1000/30);
last = window.performance.now();
var oldX = object.position.x;
// calculate updateX including the timefix
var updateX = oldX + (10 / 30 * 100) * dx * timefix;
// update the position of the object
object.position.x = updateX;
// render the scene
renderer.render(scene, camera);
}
worker.js:
self.addEventListener('message', function(e) {
setInterval(function(){
now = self.performance.now()
timefix = (now - last)/(1000/100);
last = now;
x += 5*timefix*dx;
self.postMessage(x);
}, 1000/100);
}, false);
var test;
var dx = 1, dy = 0;
var speed = 0.5;
var activeKey = 0;
// Set up the scene, camera, and renderer as global variables.
var scene, camera, renderer;
init();
animate();
// Sets up the scene.
function init() {
// Create the scene and set the scene size.
scene = new THREE.Scene();
var WIDTH = window.innerWidth - 50,
HEIGHT = 500;
// Create a renderer and add it to the DOM.
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(WIDTH, HEIGHT);
document.body.appendChild(renderer.domElement);
camera = new THREE.OrthographicCamera( 0, WIDTH, 200, -HEIGHT, 1, 1000 );
camera.position.set(0,0,100);
scene.add(camera);
console.log(WIDTH);
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth - 50,
HEIGHT = window.innerHeight - 50;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
renderer.setClearColor();
var loader = new THREE.ObjectLoader();
loader.parse({
"metadata" : {
"type" : "Object",
"version" : 4.3,
"generator" : "Blender Script"
},
"object" : {
"name" : "red_cube.Material",
"type" : "Mesh",
"uuid" : "6071e8f2-79ae-5660-8d2b-aa675c566703",
"matrix" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
"geometry" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
"material" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561"
},
"geometries" : [{
"name" : "red_cube.Material",
"type" : "BufferGeometry",
"uuid" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
"data" : {
"attributes" : {
"position" : {
"type" : "Float32Array",
"itemSize" : 3,
"array" : [0.79906648,-0.73424673,-0.87263167,0.79906648,-0.73424661,1.1273682,-1.2009337,-0.73424661,1.1273681,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,-1.2009335,1.2657533,-0.87263179,-1.2009339,1.2657533,1.1273677,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424673,-0.87263167,0.79906696,1.2657533,-0.87263131,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424661,1.1273682,0.79906648,-0.73424661,1.1273682,0.79906583,1.2657533,1.1273688,-1.2009339,1.2657533,1.1273677,-1.2009337,-0.73424661,1.1273681,-1.2009337,-0.73424661,1.1273681,-1.2009339,1.2657533,1.1273677,-1.2009335,1.2657533,-0.87263179,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,0.79906648,-0.73424673,-0.87263167,-1.2009332,-0.73424673,-0.87263215,-1.2009335,1.2657533,-0.87263179]
},
"normal" : {
"type" : "Float32Array",
"itemSize" : 3,
"array" : [-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,0,1,0,0,1,0,0,1,0,0,1,0,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1]
},
"index" : {
"type" : "Uint32Array",
"itemSize" : 1,
"array" : [0,1,2,2,3,0,4,5,6,6,7,4,8,9,10,10,11,8,12,13,14,14,15,12,16,17,18,18,19,16,20,21,22,22,23,20]
}
}
}
}],
"materials" : [{
"name" : "Material",
"type" : "MeshBasicMaterial",
"uuid" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561",
"transparent" : false,
"opacity" : 1,
"color" : 10682379
}]
}, function(object){
test = object;
object.scale.set(50,50,50);
scene.add(object)
});
document.addEventListener('keydown', function(e) {
if (activeKey == e.keyCode) return;
activeKey = e.keyCode;
//left
if (e.keyCode == 37) {
dx = -1;
}
//top
else if (e.keyCode == 38) {
dy = 1;
}
//right
else if (e.keyCode == 39) {
dx = 1;
}
//bottom
else if (e.keyCode == 40) {
dy = -1;
}
});
document.addEventListener('keyup', function(e) {
switch (e.keyCode) {
case 37: // left
case 39: // right
dx = 0;
break;
case 38: // up
case 40: // down
dy = 0;
break;
}
activeKey = 0;
});
}
var start;
var last;
function animate() {
requestAnimationFrame( animate );
if(start == null) {
start = window.performance.now();
last = start;
}
var timefix = (window.performance.now() - last)/(1000/30);
last = window.performance.now();
if(test != null) {
var oldX = test.position.x;
var oldY = test.position.y;
var updateX = oldX + (10 / 30 * 100) * dx * speed * timefix;
var updateY = oldY + (10 / 30 * 100) * dy * speed * timefix;
if(updateX > 1800 ) {
dx = -1;
} else if(updateX < 100) {
dx = 1;
}
test.position.x = updateX;
test.position.y = oldY + (10 / 30 * 100) * dy * speed * timefix;
var text = document.getElementById('panel');
text.innerHTML = timefix;
renderer.render(scene, camera);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js"></script>
<body style="margin: 0;">
<div id="panel">TEST
</div>
<br>
</body>