javascriptjquerycanvasdrawimagesprite-sheet

How can I adjust a sprite sheet frame size within a canvas?


I have a canvas element and i'm drawing on this little duck sprite sheet to fly across from the right hand side of the screen to the left. The code i have right now achieves that. But i can't for the life of me figure out how to make the frame the duck is in, bigger, without messing with the animations of it.

Right now it appears that the duck is confined into a little sliver of a box like this | | so you only see bits and pieces of the animation, not nearly enough to see it consistently flying across the screen. Every time i try to adjust the width or frame, its just making the duck much larger.

I know the snippet doesn't work cuz it wont load in the assets but you can see the JS side of it so hopefully that helps!

Thank you guys

ducksprite sheet: duck sprite sheet

$(document).ready(function () {
  // Variables for IDs - getContext to draw and clear canvas
  const canvas = $("#game-canvas")[0];
  const context = canvas.getContext("2d");
  const startButton = $("#start-button");

  const backgroundImage = new Image();
  backgroundImage.src = "https://opengameart.org/sites/default/files/duck_spritesheet.png";

  // Onload function to pull the background image of the game
  $(backgroundImage).on("load", function () {
    context.drawImage(backgroundImage, 0, 0);
  });

  // On click function to change the crosshair and pull in the duck image
  startButton.on("click", function () {
    const spriteSheet = new Image();
    spriteSheet.src = "https://opengameart.org/sites/default/files/duck_spritesheet.png";

    $("#game-canvas").on("mouseenter", function () {
      $(this).addClass("canvas-cursor");
    });

    $("#game-canvas").on("mouseleave", function () {
      $(this).removeClass("canvas-cursor");
    });

    $(spriteSheet).on("load", function () {
      const totalFrames = 4; // Total number of frames in the sprite sheet
      const scale = 3; // Scale to TRY and scale it, didn't work...
      const duckSpeed = 10; // ducks movement speed across the canvas

      //multiplying any of these didn't work for me
      const duckWidth = spriteSheet.width / totalFrames;
      const duckHeight = spriteSheet.height / totalFrames;
      const frameWidth = duckWidth;
      const frameHeight = duckHeight; 

      const animationSpeed = 200; // Milliseconds per frame

      let currentFrame = 0; // Current frame of the animation
      let duckX = canvas.width; // Starting position of the duck on the right side of the canvas
      let duckY = canvas.height / 2 - (frameHeight * scale) / 2; // Center the duck vertically

      function animateDuck() {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.drawImage(backgroundImage, 0, 0);

        const frameX = (currentFrame % totalFrames) * frameWidth;
        const frameY = Math.floor(currentFrame / totalFrames) * frameHeight;

        // Draw the current frame of the duck image 
        context.drawImage(
          spriteSheet,
          frameX,
          frameY,
          frameWidth,
          frameHeight,
          duckX,
          duckY,
          frameWidth * scale,
          frameHeight * scale
        );

        // current frame
        currentFrame = (currentFrame + 1) % (totalFrames * totalFrames);

        // duck's position
        duckX -= duckSpeed;

        // if the duck has reached the left side of the canvas
        if (duckX + frameWidth * scale * 2 < 0) {
          duckX = canvas.width;
        }

        setTimeout(animateDuck, animationSpeed);
      }

      animateDuck();
    });
  });
});
body {
  background-color: #eee;
  text-align: center;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
}

h1 {
  color: #333;
}

#game-container {
  position: relative;
}

#game-canvas {
  display: block;
  margin: 0 auto;
  border: 1px solid black;
  background-color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<body>
    <div id="game-container">
        <canvas id="game-canvas" width="400" height="100"></canvas>
    </div>
    <button id="start-button">Start Game</button>
</body>


Solution

  • Look at your sprite again:

    and then your code:

          const duckWidth = spriteSheet.width / totalFrames;
          const duckHeight = spriteSheet.height / totalFrames;
          const frameWidth = duckWidth;
          const frameHeight = duckHeight; 
        ...
        const frameX = (currentFrame % totalFrames) * frameWidth;
    
    

    That should be enough clue to get you moving...
    Do the math by hand, that helps, Do you get the expected values for the frames?

    Below is my correction to your code, challenge yourself try to fix yours without looking at my snippet

      const canvas = $("#game-canvas")[0];
      const context = canvas.getContext("2d");
    
      const spriteSheet = new Image();
      spriteSheet.src = "https://opengameart.org/sites/default/files/duck_spritesheet.png";
      $(spriteSheet).on("load", function() {
        const totalFrames = 4; // Total number of frames in the sprite sheet
        const scale = 3; // Scale to TRY and scale it, didn't work...
        const duckSpeed = 2; // ducks movement speed across the canvas
    
        const duckWidth = spriteSheet.width;
        const duckHeight = spriteSheet.height / totalFrames;
        const frameWidth = duckWidth;
        const frameHeight = duckHeight;
    
        let currentFrame = 0; // Current frame of the animation
        let duckX = canvas.width;
        let duckY = canvas.height / 2 - (frameHeight * scale) / 2;
    
        function animateDuck() {
          context.clearRect(0, 0, canvas.width, canvas.height);
    
          const frameX = 0
          const frameY = Math.floor(currentFrame / totalFrames) * frameHeight;
    
          context.drawImage(spriteSheet, frameX, frameY, frameWidth, frameHeight, duckX, duckY, frameWidth * scale, frameHeight * scale );
          currentFrame = (currentFrame + 1) % (totalFrames * totalFrames);
          duckX -= duckSpeed;
          if (duckX + frameWidth * scale * 2 < 0) {
            duckX = canvas.width;
          }
    
          setTimeout(animateDuck, 40);
        }
        animateDuck();
      });
    body {
      background-color: #eee;
      text-align: center;
      font-family: Verdana, Geneva, Tahoma, sans-serif;
    }
    
    h1 {
      color: #333;
    }
    
    #game-container {
      position: relative;
    }
    
    #game-canvas {
      display: block;
      margin: 0 auto;
      border: 1px solid black;
      background-color: #fff;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    
    <body>
      <div id="game-container">
        <canvas id="game-canvas" width="400" height="100"></canvas>
      </div>
    </body>


    In a nutshell the image of your sprite is not a square but a rectangle, the duck width does not need to be divided by the total frames and the framex is also not a value that changes for your sprite this is always 0.

    If you want to challenge yourself try other sprites like this one:
    https://opengameart.org/content/explosion-sheet

    2