HTML5 Canvas 滚动延迟
HTML5 Canvas scroll lag
我正在制作 HTML5 canvas 平台游戏。玩家的位置更新后,玩家的视图(相机)每帧都会更新。这会导致相机稍微抖动(如果您查看相机轮廓的最左侧)。我不能让相机的位置像那样起伏不定
问题:如何固定相机,使其位置随玩家的位置更新,但又流畅? (无缝移动,不波涛汹涌,不拖沓)
//setting everything up.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
wrapper = document.getElementById("wrapper"),
requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame,
then = Date.now(),
now,
framesPerSecond = 30,
counter = 1000/framesPerSecond,
delta = 0,
//for smooth movement.
friction = 0.9,
//to track key presses.
keyDown = {};
var main = function(){
now = Date.now();
delta += now - then;
then = now;
if(delta >= counter){
delta = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
tick();
render();
}
requestAnimationFrame(main);
}
var player = {
x:0,
y:115,
w:20,
h:20,
velX:0,
speed:3,
color:"maroon",
camX:0,
camY:0,
camOffsetX:250,
camOffsetY:125,
tick:function(){
this.velX *= friction
this.x += 2*this.velX;
//left arrow key.
if(37 in keyDown){
if(this.velX > -this.speed){
this.velX--;
}
}
//right arrow key.
if(39 in keyDown){
if(this.velX < this.speed){
this.velX++;
}
}
//update new camera position after the player's position got updated.
this.updateCamera();
},
render:function(){
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
},
updateCamera:function(){
//sets the center of the camera view to the center of the player
this.camX = this.x + this.w/2 - this.camOffsetX;
this.camY = this.y + this.h/2 - this.camOffsetY;
//scrolls canvas with the camera
wrapper.scrollLeft = this.camX;
}
};
var tick = function(){
player.tick();
}
var render = function(){
player.render();
//arrow pointing to the problem
ctx.fillText("<---", player.camX + 10, player.y);
//camera bounderies
ctx.strokeRect(player.x + player.w/2 - player.camOffsetX, player.y + player.h/2 - player.camOffsetY, 2*player.camOffsetX, 2*player.camOffsetY);
//sets markers so you can tell your're scrolling.
ctx.fillText("250 pixels", 250, 10);
ctx.fillText("500 pixels", 500, 10);
ctx.fillText("750 pixels", 750, 10);
ctx.fillText("1000 pixels", 1000, 10);
ctx.fillText("1250 pixels", 1250, 10);
ctx.fillText("1500 pixels", 1500, 10);
ctx.fillText("1750 pixels", 1750, 10);
}
//adds or removes keys from keyDown on keydown or keyup
document.addEventListener("keydown", function(e){
keyDown[e.keyCode] = true;
});
document.addEventListener("keyup", function(e){
delete keyDown[e.keyCode];
});
requestAnimationFrame(main);
#wrapper {
width:250px;
height:250px;
overflow:hidden;
border:1px solid navy;
}
<!-- div is so the canvas can scroll. -->
<div id="wrapper" style="width:500px; height:250px; border:1px solid navy; overflow:hidden;">
<canvas id="canvas" width="2000" height="250"></canvas>
</div>
问题出在您使用 canvas 的方式上。您不应创建大于屏幕显示的 canvas。如果你这样做,你只会过度使用你的渲染代码。
闪烁是由于您试图将 canvas 像素调整为像素的一部分。如果将相机位置设置为 x = 100.5 像素,则显示器无法将显示像素物理移动 1/2,因此它会尽力插入 canvas 像素以适合显示像素。结果忽明忽暗
但是您不应该移动 canvas,而应该移动 canvas 变换。这可确保边界矩形始终与显示像素对齐,即使滚动像素的一小部分也是如此。
我对你的代码做了一些修改。在 main
函数中,我更改了 canvas 变换以跟随相机,而不是在 DOM.
中移动 canvas
//adds or removes keys from keyDown on keydown or keyup
function keyEvent(e) {
keyDown[e.keyCode] = e.type === "keydown"
}
document.addEventListener("keydown", keyEvent);
document.addEventListener("keyup", keyEvent);
requestAnimationFrame(main);
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const wrapper = document.getElementById("wrapper");
var then;
const framesPerSecond = 30;
var counter = 1000 / framesPerSecond;
var delta = 0;
var friction = 0.9;
const keyDown = {};
function main(time) {
if (then === undefined) {
then = time
}
delta += time - then;
then = time;
if (delta >= counter) {
delta = 0;
ctx.setTransform(1, 0, 0, 1, 0, 0); // set default transform
ctx.clearRect(0, 0, canvas.width, canvas.height);
player.update();
// set the view to the camera
ctx.setTransform(1, 0, 0, 1, -player.camX, -player.camY);
render();
}
requestAnimationFrame(main);
}
var player = {
x: 0,
y: 115,
w: 20,
h: 20,
velX: 0,
speed: 3,
color: "maroon",
camX: 0,
camY: 0,
camOffsetX: 250,
camOffsetY: 125,
update() {
this.velX *= friction
this.x += 2 * this.velX;
if (keyDown["37"]) {
if (this.velX > -this.speed) {
this.velX--
}
}
if (keyDown["39"]) {
if (this.velX < this.speed) {
this.velX++
}
}
this.updateCamera();
},
render() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
},
updateCamera() {
this.camX = this.x + this.w / 2 - this.camOffsetX;
this.camY = this.y + this.h / 2 - this.camOffsetY;
}
};
var render = function() {
player.render();
//arrow pointing to the problem
ctx.fillText("<---", player.camX + 10, player.y);
ctx.fillText("Player X pos : " + player.camX.toFixed(3), player.x, 100);
//camera bounderies
ctx.strokeRect(player.x + player.w / 2 - player.camOffsetX, player.y + player.h / 2 - player.camOffsetY, 2 * player.camOffsetX, 2 * player.camOffsetY);
//sets markers so you can tell your're scrolling.
ctx.fillText("250 pixels", 250, 10);
ctx.fillText("500 pixels", 500, 10);
ctx.fillText("750 pixels", 750, 10);
ctx.fillText("1000 pixels", 1000, 10);
ctx.fillText("1250 pixels", 1250, 10);
ctx.fillText("1500 pixels", 1500, 10);
ctx.fillText("1750 pixels", 1750, 10);
}
#wrapper {
width: 500px;
height: 250px;
overflow: hidden;
border: 1px solid cyan;
}
<!-- div is so the canvas can scroll. -->
<div id="wrapper">
<canvas id="canvas" width="500" height="250"></canvas>
</div>
我正在制作 HTML5 canvas 平台游戏。玩家的位置更新后,玩家的视图(相机)每帧都会更新。这会导致相机稍微抖动(如果您查看相机轮廓的最左侧)。我不能让相机的位置像那样起伏不定
问题:如何固定相机,使其位置随玩家的位置更新,但又流畅? (无缝移动,不波涛汹涌,不拖沓)
//setting everything up.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
wrapper = document.getElementById("wrapper"),
requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame,
then = Date.now(),
now,
framesPerSecond = 30,
counter = 1000/framesPerSecond,
delta = 0,
//for smooth movement.
friction = 0.9,
//to track key presses.
keyDown = {};
var main = function(){
now = Date.now();
delta += now - then;
then = now;
if(delta >= counter){
delta = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
tick();
render();
}
requestAnimationFrame(main);
}
var player = {
x:0,
y:115,
w:20,
h:20,
velX:0,
speed:3,
color:"maroon",
camX:0,
camY:0,
camOffsetX:250,
camOffsetY:125,
tick:function(){
this.velX *= friction
this.x += 2*this.velX;
//left arrow key.
if(37 in keyDown){
if(this.velX > -this.speed){
this.velX--;
}
}
//right arrow key.
if(39 in keyDown){
if(this.velX < this.speed){
this.velX++;
}
}
//update new camera position after the player's position got updated.
this.updateCamera();
},
render:function(){
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
},
updateCamera:function(){
//sets the center of the camera view to the center of the player
this.camX = this.x + this.w/2 - this.camOffsetX;
this.camY = this.y + this.h/2 - this.camOffsetY;
//scrolls canvas with the camera
wrapper.scrollLeft = this.camX;
}
};
var tick = function(){
player.tick();
}
var render = function(){
player.render();
//arrow pointing to the problem
ctx.fillText("<---", player.camX + 10, player.y);
//camera bounderies
ctx.strokeRect(player.x + player.w/2 - player.camOffsetX, player.y + player.h/2 - player.camOffsetY, 2*player.camOffsetX, 2*player.camOffsetY);
//sets markers so you can tell your're scrolling.
ctx.fillText("250 pixels", 250, 10);
ctx.fillText("500 pixels", 500, 10);
ctx.fillText("750 pixels", 750, 10);
ctx.fillText("1000 pixels", 1000, 10);
ctx.fillText("1250 pixels", 1250, 10);
ctx.fillText("1500 pixels", 1500, 10);
ctx.fillText("1750 pixels", 1750, 10);
}
//adds or removes keys from keyDown on keydown or keyup
document.addEventListener("keydown", function(e){
keyDown[e.keyCode] = true;
});
document.addEventListener("keyup", function(e){
delete keyDown[e.keyCode];
});
requestAnimationFrame(main);
#wrapper {
width:250px;
height:250px;
overflow:hidden;
border:1px solid navy;
}
<!-- div is so the canvas can scroll. -->
<div id="wrapper" style="width:500px; height:250px; border:1px solid navy; overflow:hidden;">
<canvas id="canvas" width="2000" height="250"></canvas>
</div>
问题出在您使用 canvas 的方式上。您不应创建大于屏幕显示的 canvas。如果你这样做,你只会过度使用你的渲染代码。
闪烁是由于您试图将 canvas 像素调整为像素的一部分。如果将相机位置设置为 x = 100.5 像素,则显示器无法将显示像素物理移动 1/2,因此它会尽力插入 canvas 像素以适合显示像素。结果忽明忽暗
但是您不应该移动 canvas,而应该移动 canvas 变换。这可确保边界矩形始终与显示像素对齐,即使滚动像素的一小部分也是如此。
我对你的代码做了一些修改。在 main
函数中,我更改了 canvas 变换以跟随相机,而不是在 DOM.
//adds or removes keys from keyDown on keydown or keyup
function keyEvent(e) {
keyDown[e.keyCode] = e.type === "keydown"
}
document.addEventListener("keydown", keyEvent);
document.addEventListener("keyup", keyEvent);
requestAnimationFrame(main);
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const wrapper = document.getElementById("wrapper");
var then;
const framesPerSecond = 30;
var counter = 1000 / framesPerSecond;
var delta = 0;
var friction = 0.9;
const keyDown = {};
function main(time) {
if (then === undefined) {
then = time
}
delta += time - then;
then = time;
if (delta >= counter) {
delta = 0;
ctx.setTransform(1, 0, 0, 1, 0, 0); // set default transform
ctx.clearRect(0, 0, canvas.width, canvas.height);
player.update();
// set the view to the camera
ctx.setTransform(1, 0, 0, 1, -player.camX, -player.camY);
render();
}
requestAnimationFrame(main);
}
var player = {
x: 0,
y: 115,
w: 20,
h: 20,
velX: 0,
speed: 3,
color: "maroon",
camX: 0,
camY: 0,
camOffsetX: 250,
camOffsetY: 125,
update() {
this.velX *= friction
this.x += 2 * this.velX;
if (keyDown["37"]) {
if (this.velX > -this.speed) {
this.velX--
}
}
if (keyDown["39"]) {
if (this.velX < this.speed) {
this.velX++
}
}
this.updateCamera();
},
render() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
},
updateCamera() {
this.camX = this.x + this.w / 2 - this.camOffsetX;
this.camY = this.y + this.h / 2 - this.camOffsetY;
}
};
var render = function() {
player.render();
//arrow pointing to the problem
ctx.fillText("<---", player.camX + 10, player.y);
ctx.fillText("Player X pos : " + player.camX.toFixed(3), player.x, 100);
//camera bounderies
ctx.strokeRect(player.x + player.w / 2 - player.camOffsetX, player.y + player.h / 2 - player.camOffsetY, 2 * player.camOffsetX, 2 * player.camOffsetY);
//sets markers so you can tell your're scrolling.
ctx.fillText("250 pixels", 250, 10);
ctx.fillText("500 pixels", 500, 10);
ctx.fillText("750 pixels", 750, 10);
ctx.fillText("1000 pixels", 1000, 10);
ctx.fillText("1250 pixels", 1250, 10);
ctx.fillText("1500 pixels", 1500, 10);
ctx.fillText("1750 pixels", 1750, 10);
}
#wrapper {
width: 500px;
height: 250px;
overflow: hidden;
border: 1px solid cyan;
}
<!-- div is so the canvas can scroll. -->
<div id="wrapper">
<canvas id="canvas" width="500" height="250"></canvas>
</div>