2017-10-28 21 views
0

저는 HTML5 캔버스 플랫폼 게임을 만들고 있습니다. 플레이어의 위치 (카메라)는 플레이어의 위치가 업데이트 된 후 모든 프레임마다 업데이트됩니다. 이로 인해 카메라가 약간 흔들 렸습니다 (카메라 윤곽의 가장 왼쪽 부분을 볼 경우). 카메라 위치가 그렇게 고르지 못하게 할 수 있습니다.HTML5 Canvas scroll lag

질문 : 플레이어의 위치가 업데이트되도록 카메라를 수정하려면 어떻게해야합니까? (원활한 이동, 랙이 고르지하지 않음)

//setting everything up. 
 
var canvas = document.getElementById("canvas"), 
 
\t ctx = canvas.getContext("2d"), 
 
\t wrapper = document.getElementById("wrapper"), 
 
\t requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame, 
 
\t then = Date.now(), 
 
\t now, 
 
\t framesPerSecond = 30, 
 
\t counter = 1000/framesPerSecond, 
 
\t delta = 0, 
 
\t //for smooth movement. 
 
\t friction = 0.9, 
 
\t //to track key presses. 
 
\t keyDown = {}; 
 

 
var main = function(){ 
 
\t now = Date.now(); 
 
\t delta += now - then; 
 
\t then = now; 
 

 
\t if(delta >= counter){ 
 
\t \t delta = 0; 
 
\t \t ctx.clearRect(0, 0, canvas.width, canvas.height); 
 
\t \t tick(); 
 
\t \t render(); 
 
\t } 
 

 
\t requestAnimationFrame(main); 
 
} 
 

 
var player = { 
 
\t x:0, 
 
\t y:115, 
 
\t w:20, 
 
\t h:20, 
 
\t velX:0, 
 
\t speed:3, 
 
\t color:"maroon", 
 
\t camX:0, 
 
\t camY:0, 
 
\t camOffsetX:250, 
 
\t camOffsetY:125, 
 

 
\t tick:function(){ 
 
\t \t this.velX *= friction 
 
\t \t this.x += 2*this.velX; 
 

 
\t \t //left arrow key. 
 
\t \t if(37 in keyDown){ 
 
\t \t \t if(this.velX > -this.speed){ 
 
\t \t \t \t this.velX--; 
 
\t \t \t } 
 
\t \t } 
 

 
\t \t //right arrow key. 
 
\t \t if(39 in keyDown){ 
 
\t \t \t if(this.velX < this.speed){ 
 
\t \t \t \t this.velX++; 
 
\t \t \t } 
 
\t \t } 
 

 
\t \t //update new camera position after the player's position got updated. 
 
\t \t this.updateCamera(); 
 
\t }, 
 

 
\t render:function(){ 
 
\t \t ctx.fillStyle = this.color; 
 
\t \t ctx.fillRect(this.x, this.y, this.w, this.h); 
 
\t }, 
 

 
\t updateCamera:function(){ 
 
\t \t //sets the center of the camera view to the center of the player 
 
\t \t this.camX = this.x + this.w/2 - this.camOffsetX; 
 
\t \t this.camY = this.y + this.h/2 - this.camOffsetY; 
 
\t \t //scrolls canvas with the camera 
 
\t \t wrapper.scrollLeft = this.camX; 
 
\t } 
 
}; 
 

 
var tick = function(){ 
 
\t player.tick(); 
 
} 
 

 
var render = function(){ 
 
\t player.render(); 
 
    
 
    //arrow pointing to the problem 
 
    ctx.fillText("<---", player.camX + 10, player.y); 
 

 
\t //camera bounderies 
 
\t ctx.strokeRect(player.x + player.w/2 - player.camOffsetX, player.y + player.h/2 - player.camOffsetY, 2*player.camOffsetX, 2*player.camOffsetY); 
 
\t \t 
 
\t //sets markers so you can tell your're scrolling. 
 
\t ctx.fillText("250 pixels", 250, 10); 
 
\t ctx.fillText("500 pixels", 500, 10); 
 
\t ctx.fillText("750 pixels", 750, 10); 
 
\t ctx.fillText("1000 pixels", 1000, 10); 
 
\t ctx.fillText("1250 pixels", 1250, 10); 
 
\t ctx.fillText("1500 pixels", 1500, 10); 
 
\t ctx.fillText("1750 pixels", 1750, 10); 
 
} 
 

 
//adds or removes keys from keyDown on keydown or keyup 
 
document.addEventListener("keydown", function(e){ 
 
\t keyDown[e.keyCode] = true; 
 
}); 
 

 
document.addEventListener("keyup", function(e){ 
 
\t 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;"> 
 
\t <canvas id="canvas" width="2000" height="250"></canvas> 
 
</div>

답변

0

문제는 캔버스를 사용하는 방법입니다. 화면에 표시 할 수있는 것보다 큰 캔버스를 만들면 안됩니다. 그렇게하면 렌더링 코드가 지나치게 복잡해집니다.

깜박임은 캔버스 픽셀을 픽셀의 일부분에 맞추려고하기 때문에 발생합니다. 카메라 위치를 x = 100.5 픽셀로 설정하면 디스플레이가 물리적으로 디스플레이 픽셀을 1/2로 이동할 수 없으므로 디스플레이 픽셀에 맞게 캔버스 픽셀을 보간하는 것이 가장 좋습니다. 결과가 깜박입니다.

하지만 캔버스를 움직여서는 안되며 캔버스 변환을 옮겨야합니다. 이렇게하면 픽셀의 일부분을 스크롤 할 때도 경계 사각형이 항상 디스플레이 픽셀과 정렬됩니다.

귀하의 코드를 일부 변경했습니다. main 함수 내에서 캔버스를 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>

+0

신난다! 즉, 코드가 작동합니다. 나는 왜 그것을'-player.camX'로 바꾸고'-player.camY'를 사용하면 효과가 있는지 설명 할 수 있을지 궁금합니다. 그것을 설명하기 위해 답을 편집 할 수 있습니까? –

+0

@ Jordan 왜냐하면 당신이 캔버스를 움직일 때 픽셀의 분수를 움직이기 때문에 이것은 디스플레이 픽셀 정렬과 일치하지 않기 때문입니다. 변환을 이동할 때 원점을 왼쪽 상단 캔버스 픽셀에 정렬하여 이동합니다. 이렇게하면보기가 항상 표시 픽셀과 정렬됩니다. – Blindman67