我试图制作一款平台游戏并需要在4帧之间制作动画。我有一些代码,但这会产生不稳定的动画。这是我的玩家对象:
app.config(function($routeProvider){
$routeProvider.when("/home",{
templateUrl:"home.html",
})
.otherwise('/home')
})
有更好的方法吗? player.physics()方法是我的动画代码。它未完成,因为我无法对帧进行动画处理。
答案 0 :(得分:1)
哦,是的,有更好的方法可以做到这一点......
src
。设置HTMLImage的src
始终是异步*。这意味着当您要求绘制它时,它可能仍然是上一个图像被加载,并且您最终实际上绘制了错误的图像。
*实际上,在某些浏览器中,此操作是在并行线程中完成的,可能已在下一行代码执行完成,但您不能依赖它,太多参数可能会干扰。
相反,为每个文件创建一个HTMLImage实例,在启动时加载它们并存储这些HTMLImages。存储这些的便捷方式是Array
。
// load images from an Array of URLs, returns an Array of HTMLImages
// accepts an callback which will fire when all images will have loaded
function loadImages(arrayOfURLs, onloadAll){
let loaded = 0;
function onloadSingle(e){
if(++loaded >= arrayOfURLs.length){
onloadAll();
}
}
return arrayOfURLs.map(function(url) {
let img = new Image();
img.onload = onloadSingle;
img.src = url;
return img;
});
}
setTimeout
和setInterval
不是精确的计时功能,它们只告诉浏览器至少等待你传递给它的时间参数,但你不知道它有多晚,并且嵌套这个你正在添加越来越多的不精确。最后,您最后可能会在下一个setTimeout
之后调用上次setInterval
。再加上你现在修复的图像加载的异步性,你创建了一个漂亮的随机机器。
相反,创建一个动画循环,并检查此独特循环中的当前时间以相应地更新您的逻辑。
处理图像渲染动画(即哪一端是屏幕)时,创建此类动画循环的最佳方法是使用requestAnimationFrame
( rAF )方法。
此方法接受一个回调函数,该函数将在下一次屏幕刷新之前调用(大多数计算机上约为@ 60fps)。这允许您仅在确实需要时以恒定的帧速率进行更新
传递给它的回调将有一个参数,一个HighResTimeStamp,它允许您根据精确的时序建立逻辑。
一个非常基本的动画循环可能如下所示:
function animLoop(time){
logicUpdate(time); // do your logics update based on time + external user gestures
draw(); // separate your drawing operations
requestAnimationFrame(animLoop); // call again at next screen refresh
}
然后你需要做的就是存储你的图像数组的currentIndex,并在每个间隔增加它,模数为数组的长度 - 1
// at every interval that you may decide based on rAF time
currentIndex = (currentIndex + 1) % (images.length - 1);
currentImage = images[ currentIndex ];
// that code could be better arranged with Classes, please don't use as is
// I only tried to make it simple to understand how everything works independently
const urls = [
'x8hwadksz3bz7za/banana-1.png',
'hl7d2kzr4yvoi37/banana-3.png',
'tqrlzaujru03e9w/banana-5.png',
'd1gbyfyiz90w152/banana-7.png'
].map(u=>'https://dl.dropboxusercontent.com/s/'+u);
const images = loadImages(urls, startAnimation);
images.currentIndex = 0;
function startAnimation(){
requestAnimationFrame(function(time){
images.lastUpdate = time; // set the initial time
animLoop(time); // start the animation
});
canvas.width = images[0].width;
canvas.height = images[1].height;
}
function imageUpdate(time){
// only if we passed the interval
if(time - images.lastUpdate > image_interval.value){
images.lastUpdate = time; // save the curent time
// update the image to be drawn
images.currentIndex = (images.currentIndex + 1) % (images.length - 1);
}
}
function logicUpdate(time){
imageUpdate(time);
// other logic updates
}
function draw(){
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(images[images.currentIndex], 0, 0);
}
function animLoop(time){
logicUpdate(time); // do your logic updates based on time + external user gestures
draw(); // separate your drawing operations
requestAnimationFrame(animLoop); // call again at next screen refresh
}
// load images from an Array of URLs, returns an Array of HTMLImages
// accepts an callback which will fire when all images will have loaded
function loadImages(arrayOfURLs, onloadAll){
let loaded = 0;
function onloadSingle(e){
if(++loaded >= arrayOfURLs.length){
onloadAll();
}
}
return arrayOfURLs.map(function(url) {
let img = new Image();
img.onload = onloadSingle;
img.src = url;
return img;
});
}
const ctx = canvas.getContext('2d');
<label>set the image interval</label>
<input type="range" id="image_interval" min="0" max="2000" step="100" value="200"></label><br>
<canvas id="canvas"></canvas>