我对游戏开发的兴趣使我产生了在没有引擎的情况下从头开始实现逻辑的想法。
最后,我开始优化 RPG 2D 游戏中背景的运动序列。基本上,由于帮助功能众多,实现并不是特别复杂,但我并不完全满意。
到目前为止,我只在有限的范围内使用画布,这就是为什么我在这里提供的代码当然可以优化。然而,我感兴趣的是如何优化 8 向系统。
最终感动的不是用户,而是背景。由于移动速度和必须克服的指定像素数量,这会导致巨大的抖动。
基本上,我有一个逻辑可以让我标准化向量,但我注意到的优化指南不是很有帮助。
为了更清楚地说明,我最小化了移动速度。我已经尝试过一些增量值或尝试对某些值进行舍入,但这在一些模糊的像素艺术中以非常糟糕的方式结束。
对如何优化这个演示有什么想法吗?
const game = document.querySelector('#game');
const context = game.getContext('2d');
const background = new Image();
background.src = 'https://i.imgur.com/Kjrpd2v.png';
const player = new Image();
player.src = 'https://i.imgur.com/PIT6zqs.png';
const initialGameWidth = 640;
const initialGameHeight = 360;
const step = 1 / 24;
let x = 3360 / 2;
let y = 1920 / 2;
let speed = 1; // 7
let keymap = [];
let previousMs;
game.width = initialGameWidth;
game.height = initialGameHeight;
const handleScreens = () => {
let scale = window.innerHeight / initialGameHeight;
game.style.scale = scale < 1 ? 1 : scale;
console.log(game.style.scale)
game.height = window.innerHeight % 2 ? window.innerHeight - 1 : window.innerHeight;
game.width = window.innerWidth % 2 ? window.innerWidth - 1 : window.innerWidth;
};
window.addEventListener('resize', () => handleScreens());
handleScreens();
window.addEventListener('keydown', event => {
let index = keymap.indexOf(event.key);
if (index > -1) {
keymap.splice(index, 1);
}
keymap.push(event.key);
});
window.addEventListener('keyup', event => {
let index = keymap.indexOf(event.key);
if (index > -1) {
keymap.splice(index, 1);
}
});
const handleWork = (delta) => {
let tempChange = { x: 0, y: 0 };
[...keymap].forEach(direction => {
switch (direction) {
case 'ArrowRight':
tempChange.x = 1;
break;
case 'ArrowLeft':
tempChange.x = -1;
break;
case 'ArrowUp':
tempChange.y = -1;
break;
case 'ArrowDown':
tempChange.y = 1;
break;
}
});
let angle = Math.atan2(tempChange.y, tempChange.x);
if (tempChange.x !== 0) {
x += Math.cos(angle) * speed;
}
if (tempChange.y !== 0) {
y += Math.sin(angle) * speed;
}
// const mapX = Math.floor((game.width / 2) - 24 - x) + delta;
// const mapY = Math.floor((game.height / 2) - 32 - y) + delta;
const mapX = (game.width / 2) - 24 - x;
const mapY = (game.height / 2) - 32 - y;
context.clearRect(0, 0, game.width, game.height);
context.drawImage(background, 0, 0, 3360, 1920, mapX, mapY, 3360, 1920);
context.drawImage(player, 0, 0, 48, 64, (game.width / 2) - 24, (game.height / 2) - 32, 48, 64);
};
const main = (timestampMs) => {
if (previousMs == undefined) {
previousMs = timestampMs;
}
let delta = (timestampMs - previousMs) / 1000;
while (delta >= step) {
handleWork(delta);
delta -= step;
}
previousMs = timestampMs - delta * 1000;
requestAnimationFrame(main);
};
requestAnimationFrame(main);
* {
margin: 0;
padding: 0;
}
body {
background-color: #222;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
height: 100vh;
width: 100vw;
}
#game {
-ms-interpolation-mode: nearest-neighbor;
image-rendering: -moz-crisp-edges;
image-rendering: pixelated;
}
<canvas id="game"></canvas>
如果速度为 7,地图将一次移动 7 个像素,在 24FPS 的帧速率下看起来会有点卡顿。因此,您可以做的第一件事就是将 FPS 设置为 60,并将速度设置得低一点。
const step = 1 / 60; const speed = 3;
它仍然有点卡顿,但这也可能是由于浏览器/CPU 执行其他操作延迟了
requestAnimationframe
调用而引起的。
const game = document.querySelector('#game');
const context = game.getContext('2d');
const background = new Image();
background.src = 'https://i.imgur.com/Kjrpd2v.png';
const player = new Image();
player.src = 'https://i.imgur.com/PIT6zqs.png';
const initialGameWidth = 640;
const initialGameHeight = 360;
const step = 1 / 60;
let x = 3360 / 2;
let y = 1920 / 2;
let speed = 3;
let keymap = [];
let previousMs;
game.width = initialGameWidth;
game.height = initialGameHeight;
const handleScreens = () => {
let scale = window.innerHeight / initialGameHeight;
game.style.scale = scale < 1 ? 1 : scale;
console.log(game.style.scale)
game.height = window.innerHeight % 2 ? window.innerHeight - 1 : window.innerHeight;
game.width = window.innerWidth % 2 ? window.innerWidth - 1 : window.innerWidth;
};
window.addEventListener('resize', () => handleScreens());
handleScreens();
window.addEventListener('keydown', event => {
let index = keymap.indexOf(event.key);
if (index > -1) {
keymap.splice(index, 1);
}
keymap.push(event.key);
});
window.addEventListener('keyup', event => {
let index = keymap.indexOf(event.key);
if (index > -1) {
keymap.splice(index, 1);
}
});
const handleWork = (delta) => {
let tempChange = { x: 0, y: 0 };
[...keymap].forEach(direction => {
switch (direction) {
case 'ArrowRight':
tempChange.x = 1;
break;
case 'ArrowLeft':
tempChange.x = -1;
break;
case 'ArrowUp':
tempChange.y = -1;
break;
case 'ArrowDown':
tempChange.y = 1;
break;
}
});
let angle = Math.atan2(tempChange.y, tempChange.x);
if (tempChange.x !== 0) {
x += Math.cos(angle) * speed;
}
if (tempChange.y !== 0) {
y += Math.sin(angle) * speed;
}
// const mapX = Math.floor((game.width / 2) - 24 - x) + delta;
// const mapY = Math.floor((game.height / 2) - 32 - y) + delta;
const mapX = (game.width / 2) - 24 - x;
const mapY = (game.height / 2) - 32 - y;
context.clearRect(0, 0, game.width, game.height);
context.drawImage(background, 0, 0, 3360, 1920, mapX, mapY, 3360, 1920);
context.drawImage(player, 0, 0, 48, 64, (game.width / 2) - 24, (game.height / 2) - 32, 48, 64);
};
const main = (timestampMs) => {
if (previousMs == undefined) {
previousMs = timestampMs;
}
let delta = (timestampMs - previousMs) / 1000;
while (delta >= step) {
handleWork(delta);
delta -= step;
}
previousMs = timestampMs - delta * 1000;
requestAnimationFrame(main);
};
requestAnimationFrame(main);
* {
margin: 0;
padding: 0;
}
body {
background-color: #222;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
height: 100vh;
width: 100vw;
}
#game {
-ms-interpolation-mode: nearest-neighbor;
image-rendering: -moz-crisp-edges;
image-rendering: pixelated;
}
<canvas id="game"></canvas>