如何解决如何使用纯 JavaScript 为游戏制作循环画布背景? 运动模糊演示CPU/GPU 负载非常重
这让我发疯了哈哈。我正在制作一个简单的侧跑游戏,并希望让背景中的草在循环中真正快速地放大(草是一个可以拉伸画布宽度的精灵)。我明白如何实现我需要的所有精灵功能,但我似乎无法让这个细节发挥作用。我正在尝试只使用基本的 JavaScript。任何想法将不胜感激。
解决方法
无休止地滚动重复图像
要绘制无限滚动的背景,您需要一个重复的图像。
以下基本演示中使用的重复草图像 256 x 72 像素的示例。
当您将图像移动到与其宽度相等的距离时向一侧移动图像时,您将其向相反方向移动相同的宽度。
使用余数运算符 %
const worldSpeed = -20; // images moves from right to left
var w = image.width;
var worldPos = w;
然后为每次更新移动图像位置并绘制图像
worldPos = ((worldPos + worldSpeed) % w + w) % w;
ctx.drawImage(image,worldPos,ctx.canvas.height - image.height);
图片的宽度可能不足以覆盖整个显示屏的宽度。要解决只需通过向左和向右绘制副本来重复图像,以便它们始终覆盖所有显示(或使用图案)
无尽的滚动演示
-
该示例将草纹理显示为无限滚动。
-
图像被绘制 3 次以适合整个画布。
-
使用键盘和鼠标点击画布聚焦,使用左右箭头移动。
-
在没有键盘的情况下等待 5 秒,它就会启动。
requestAnimationFrame(renderLoop);
const W = canvas.width,H = canvas.height;
const ctx = canvas.getContext("2d");
const grass = new Image;
grass.src = "https://i.stack.imgur.com/X8GdJ.png";
const sky = new Image;
sky.src = "https://i.stack.imgur.com/MfmBf.png";
const maxSpeed = 100;
var worldPos = 0,speed = 0,acceleration = 0.5;
var skyPos = 0
function drawImageRepeat(x,img) {
const ih = img.naturalHeight
ctx.drawImage(img,x,H - ih);
ctx.drawImage(img,x + 256,x - 256,H - ih);
}
function move() {
if (grass.complete && sky.complete) {
if (keys.left) { speed-= acceleration }
if (keys.right) { speed+= acceleration }
speed = speed< -maxSpeed ? -maxSpeed : speed > maxSpeed ? maxSpeed : speed;
speed *= 0.99;
worldPos = ((worldPos + speed) % 256 + 256) % 256;
skyPos = ((skyPos + speed / 16) % 256 + 256) % 256;
drawImageRepeat(skyPos,sky);
drawImageRepeat(worldPos,grass);
}
}
function renderLoop() {
ctx.setTransform(1,1,0);
ctx.clearRect(0,W,H);
move();
speed && (info.textContent = "Speed: " + (speed * 60).toFixed(0) + "Px per second");
requestAnimationFrame(renderLoop);
}
const K = {ArrowLeft: "left",ArrowRight: "right"};
const keys = {left: false,right: false};
const listen = (cb,...ns) => ns.forEach(n => addEventListener(n,cb));
const keyEvent = (e,n = K[e.code]) => n && (keys[n] = (e.preventDefault(),e.type === "keydown"));
listen(keyEvent,"keydown","keyup");
setTimeout(() => speed === 0 && (keys.left = true),5000);
#info {
position: absolute;
top: 10px;
left: 10px;
font-family: arial;
font-size: 24;
}
canvas { border: 1px solid black; }
<div id="info">Click to focuse then Left Right Arrow keys to move</div>
<canvas id="canvas" width="400" height="150"></canvas>
问题
当速度开始变得非常高时,滚动图像看起来不自然,如果允许继续加速,它甚至会开始看起来变慢。
运动模糊
为了避免这种效果,我们需要引入一些运动模糊。
下面的演示与上面相同,但不是每帧绘制一次重复的图像,而是为它在帧之间移动的每个像素绘制一次。例如,如果速度为每帧 100 像素,则每次绘制将绘制 100 次移动 1 个像素。
必须对每个步骤做出贡献的图像数量(如果速度为每帧 100 像素)将约为第 100。我们使用 alpha 来减少图像每帧添加的数量。
这个方法还有一些其他问题,演示通过改变模糊的传播并在它变慢时停止它来克服。
运动模糊演示
注意天空没有应用运动模糊。
const W = canvas.width,H = canvas.height;
requestAnimationFrame(renderLoop);
const ctx = canvas.getContext("2d");
const grass = new Image;
grass.src = "https://i.stack.imgur.com/X8GdJ.png";
const sky = new Image;
sky.src = "https://i.stack.imgur.com/MfmBf.png";
const maxSpeed = 100;
var worldPos = 256,skyPos = 256,acceleration = 0.5
function drawImageRepeat(x,H - ih);
}
function move() {
if (grass.complete && sky.complete) {
if (keys.left) { speed -= acceleration }
if (keys.right) { speed += acceleration }
speed = speed < -maxSpeed ? -maxSpeed : speed > maxSpeed ? maxSpeed : speed;
speed *= 0.99;
worldPos = ((worldPos + speed) % 256 + 256) % 256;
skyPos = ((skyPos + speed / 16) % 256 + 256) % 256;
var dir = Math.sign(speed);
var i = Math.abs(speed) + 1 | 0;
drawImageRepeat(skyPos,sky);
if (i < 2) {
drawImageRepeat(worldPos,grass);
} else {
if (i < 20) {
ctx.globalAlpha = 1 - (i / 20);
dir *= i / 20;
drawImageRepeat((worldPos + i * dir) % 256,grass);
}
ctx.globalAlpha = (1 / i) ** 0.95;
while (i--) { drawImageRepeat((worldPos + i * dir) % 256,grass) }
ctx.globalAlpha = 1;
}
}
}
function renderLoop() {
ctx.setTransform(1,H);
move();
speed && (info.textContent = "Speed: " + (speed * 60).toFixed(0) + "Px per second");
requestAnimationFrame(renderLoop);
}
const K = {ArrowLeft: "left",5000);
#info {
position: absolute;
top: 10px;
left: 10px;
font-family: arial;
font-size: 24;
}
canvas { border: 1px solid black; }
<div id="info">Click to focuse then Left Right Arrow keys to move</div>
<canvas id="canvas" width="400" height="150"></canvas>
CPU/GPU 负载非常重
这显然是非常昂贵的 CPU/GPU(以 100 的速度需要绘制 300 次图像)。
如果您以几种不同的速度渲染带有运动模糊的图像(使用 photoshop、blender 或使用上面的演示代码通过画布),这可以克服
然后,不是为移动的每个像素绘制图像,而是随着速度的增加混合预渲染的运动模糊图像。然后,您最多只需要渲染每个图像两次(从一个预渲染的模糊混合到下一个),而不是 100 次。
,如果您的问题是移动背景,您可以使用 CSS。
例如:
background_sprite.animate([
{"left": "0px"},{"left": "100px"}
],{
"duration": 7000 // In milliseconds
})
为了让它移动得更快,你可以减少持续时间。
这种方法的唯一缺点是 left
位置在动画完成后重置。
这可以通过异步运行超时函数来绕过。
例如:
setTimeout(() => {
background_sprite.style.left = "100px"
},7000)
所以我们完整的运动代码可能看起来像这样
background_sprite.animate([
{"left": "0px"},{"left": background_sprite.style.width}
],{
"duration": 500 // In milliseconds
})
setTimeout(() => {
background_sprite.style.left = background_sprite.style.width
},500)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。