Search code examples
javascriptcanvasmovesmoothing

How I can do a smooth movement of player in canvas game?


How I can do smoothly movement of player in canvas 2d game? This is code of my first canvas-game application.

//Canvas
const canvas = document.getElementById("canvas");
canvas.style.display = "block";
canvas.height = innerHeight;
canvas.width = innerWidth;
canvas.style.backgroundColor = "grey";
//Context
const ctx = canvas.getContext("2d");
//Player
const player = {x: 10, y: 10, w: 20, h: 20};
//Start-position
ctx.fillRect(player.x, player.y, player.w, player.h);
//No-smooth-movement
window.onkeydown = move = (e) => {
    let key = e.keyCode;
    if     (key === 68 && player.x <= canvas.width-25) {player.x += 10;} //right
    else if(key === 65 && player.x >= 10) {player.x -= 10;} //left
    else if(key === 83 && player.y <= canvas.height-25) {player.y += 10;} //down
    else if(key === 87 && player.y >= 10) {player.y -= 10;} //up

    ctx.clearRect(0,0, canvas.width, canvas.height);
    ctx.fillRect(player.x, player.y, player.w, player.h);
}

Solution

  • To make a smoother animation you need a higher frame rate. In the code given you just use the onkeydown event which is not à certain number of frame per second. You can use setInterval in complement or window.requestAnimationFrame :

    //Canvas
    mycan.style.display = "block";
    mycan.height = innerHeight;
    mycan.width = innerWidth;
    mycan.style.backgroundColor = "grey";
    //Context
    const ctx = mycan.getContext("2d");
    //Player
    const player = {x: 10, y: 10, w: 20, h: 20};
    //Start-position
    ctx.fillRect(player.x, player.y, player.w, player.h);
    //No-smooth-movement
    window.onkeydown = move = (e) => {
        let key = e.keyCode;
        if     (key === 68 && player.x <= mycan.width-25) {player.x += 10;} //right
        else if(key === 65 && player.x >= 10) {player.x -= 10;} //left
        else if(key === 83 && player.y <= mycan.height-25) {player.y += 10;} //down
        else if(key === 87 && player.y >= 10) {player.y -= 10;} //up
    }
    
    const draw = ()=>{
      ctx.clearRect(0,0, mycan.width, mycan.height);
      ctx.fillRect(player.x, player.y, player.w, player.h);
    };
    
    setInterval(()=>{
      draw();
    },1000/60);
    <canvas id="mycan"></canvas>

    you can also reduce the pace (here you use 10 to move the player 10 pixels in a direction)

    and customize your event :

    //Canvas
    mycan.style.display = "block";
    mycan.height = innerHeight;
    mycan.width = innerWidth;
    mycan.style.backgroundColor = "grey";
    //Context
    const ctx = mycan.getContext("2d");
    //Player
    const player = {x: 10, y: 10, w: 20, h: 20};
    const keystack = [];
    //Start-position
    ctx.fillRect(player.x, player.y, player.w, player.h);
    //No-smooth-movement
    window.onkeydown = move = (e) => {
        keystack.push(e.keyCode);
    }
    
    const draw = ()=>{
      let key = keystack.pop();
      if     (key === 68 && player.x <= mycan.width-25) {player.x += 10;} //right
        else if(key === 65 && player.x >= 10) {player.x -= 10;} //left
        else if(key === 83 && player.y <= mycan.height-25) {player.y += 10;} //down
        else if(key === 87 && player.y >= 10) {player.y -= 10;} //up
      ctx.clearRect(0,0, mycan.width, mycan.height);
      ctx.fillRect(player.x, player.y, player.w, player.h);
    };
    
    setInterval(()=>{
      draw();
    },1000/60);
    <canvas id=mycan></canvas>

    and the last and most important thing is adding steps to that animation. instead of moving +10 immediately move +5 then again +5 but this time in the drawing and not in the event :

    //Canvas
    mycan.style.display = "block";
    mycan.height = innerHeight;
    mycan.width = innerWidth;
    mycan.style.backgroundColor = "grey";
    //Context
    const ctx = mycan.getContext("2d");
    //Player
    const player = {x: 10, y: 10, w: 20, h: 20};
    const keystack = [];
    let isMoving = 0;
    let prevKey = {};
    //Start-position
    ctx.fillRect(player.x, player.y, player.w, player.h);
    //No-smooth-movement
    window.onkeydown = move = (e) => {
        keystack.push(e.keyCode);
    }
    
    window.onkeyup = move = (e) => {
        keystack.pop();
    }
    
    const draw = ()=>{
      let key = keystack[keystack.length-1];
      //if(!(isMoving=isMoving%2)) key = keystack.pop(); 
      //else key = prevKey;
      
      if     (key === 68 && player.x <= mycan.width-25) {player.x += 5;} //right
        else if(key === 65 && player.x >= 10) {player.x -= 5;} //left
        else if(key === 83 && player.y <= mycan.height-25) {player.y += 5;} //down
        else if(key === 87 && player.y >= 10) {player.y -= 5;} //up
      ctx.clearRect(0,0, mycan.width, mycan.height);
      ctx.fillRect(player.x, player.y, player.w, player.h);
      prevKey = key;
      isMoving++;
    };
    
    setInterval(()=>{
      draw();
    },1000/60);
    <canvas id = mycan > </canvas>

    and to prevent initial stuttering :

    //Canvas
    mycan.style.display = "block";
    mycan.height = innerHeight;
    mycan.width = innerWidth;
    mycan.style.backgroundColor = "grey";
    //Context
    const ctx = mycan.getContext("2d");
    //Player
    const player = {x: 10, y: 10, w: 20, h: 20};
    const keystack = [];
    let isMoving = 0;
    let prevKey = {};
    //Start-position
    ctx.fillRect(player.x, player.y, player.w, player.h);
    //No-smooth-movement
    window.onkeydown = move = (e) => {
        keystack.push(e.keyCode);
    }
    
    window.onkeyup = move = (e) => {
        keystack.pop();
    }
    
    const draw = ()=>{
      let key = keystack[keystack.length-1];
      
      if     (key === 68 && player.x <= mycan.width-25) {player.x += 5;} //right
        else if(key === 65 && player.x >= 10) {player.x -= 5;} //left
        else if(key === 83 && player.y <= mycan.height-25) {player.y += 5;} //down
        else if(key === 87 && player.y >= 10) {player.y -= 5;} //up
      ctx.clearRect(0,0, mycan.width, mycan.height);
      ctx.fillRect(player.x, player.y, player.w, player.h);
      prevKey = key;
      isMoving++;
    };
    
    setInterval(()=>{
      draw();
    },1000/60);
    <canvas id=mycan></canvas>