User K
User K

Reputation: 380

Canvas drawImage is inexplicably offset by 1 Pixel

I'm working on a canvas game that's using a sprite sheet for the character.
The dimensions of character is 64px wide and 128px high with 10 frames per animation.
So the total width of a single animation is 640px wide and 128px high.
However when I use the following code, The animation is offset by 1px, Sometimes flashing when I hold down a movement key.

player.width = 64;  
player.height = 128;  
player.x = canvas.width / 2;
player.y = canvas.height / 2;


ctx.drawImage(
    LoadedImages.player_sprite,
    64, // This is offset by 1px when moving. 63px fixes it.
    128,
    player.width,
    player.height,
    player.x,
    player.y,
    player.width,
    player.height
);  

Here's a picture of what happens:
Damn
Changing the width to 63 seems to fix the problem, But it doesn't explain why it's doing it in the first place.

The full code is available on Codepen
http://codepen.io/Codewoofy/pen/QNGLNj
Sprite Sheet:
oh

Full Code:

var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;

// jQuery Objects
var $canvas = $("#canvas");
var $debug = $("#debug");

var KEY = {
   W: 1,
   UP: 38,
   LEFT: 39,
   RIGHT: 37,
   SPACE: 32,
   SHIFT: 16
};

var COLOR = {
   DARKRED: "#9C1E33",
   WHITE: "#FFFFFF"
}

var MESSAGE = {
   UNDEFINED: "",
   PRELOAD_ATTEMPT: "Attempting to preload: ",
   ERROR_IMAGE_PRELOAD: "Unable to preload images.",
   SUCCESS_IMAGE_PRELOAD: "Images successfully preloaded."
}

// Images
var Images = {
   player_sprite: "http://h.dropcanvas.com/30npe/Talia-Sheet-Fix.png",
   main_background: "http://h.dropcanvas.com/9kgs1/background_main.png"
}
var LoadedImages = {};

// Dictionaries.
var game = {};
game.enviroment = {};
game.canvas = $canvas[0];
game.canvas.height = 500;
game.canvas.width = 700;
var ctx = game.canvas.getContext('2d');
var player = {};

// Debug
game.debug = function(msg) {
   if (msg == "") msg = MESSAGE.UNDEFINED;
   $debug.prepend(msg + "<br />");
}

// Preloader.
game.loadImages = function() {
   LoadedImages = {};
   Object.keys(Images).forEach(function(path) {
      game.debug(MESSAGE.PRELOAD_ATTEMPT + path);
      var img = new Image;
      img.onload = function() {
         LoadedImages[path] = img;
         if (Object.keys(LoadedImages).length == Object.keys(Images).length) {
            game.onImagesLoaded();
         }
      }
      img.onerror = function() {
         game.onFailedPreload();
      }
      img.src = Images[path];
   });
}

game.onFailedPreload = function() {
   game.debug(MESSAGE.ERROR_IMAGE_PRELOAD);
}

game.onImagesLoaded = function() {
   game.debug(MESSAGE.SUCCESS_IMAGE_PRELOAD);
   game.game_update();
}

game.onLoad = function() {
   // Game settings
   game.keys = [];
   game.running = false;
   game.lastUpdate = 0;
   // Enviroment
   game.enviroment.gravity = 0.5;
   game.enviroment.friction = 0.9;
   // Player settings
   player.name = "Talia";
   player.color = COLOR.DARKRED;
   player.direction = 'L';
   player.width = 64;
   player.height = 128;
   player.speed = 4;
   player.walkspeed = 4;
   player.sprintspeed = 10;
   player.jumping = false;
   player.animation_frame = 0;
   player.velX = 0;
   player.velY = 0;
   player.x = 0;
   player.y = 0;
   // Player Stats
   player.health = 100;
   player.mana = 100;
   player.maxhealth = 100;
   player.maxmana = 100;

   game.loadImages();
}

/* Update the game every frame */
game.game_update = function() {
   // Sprint
   if (game.keys[KEY.SHIFT]) {
      console.log(LoadedImages);
      player.speed = player.sprintspeed;
   } else {
      player.speed = player.walkspeed;
   }
   // Jump
   if (game.keys[KEY.UP] || game.keys[KEY.SPACE]) {
      if (!player.jumping) {
         player.jumping = true;
         player.velY = -player.walkspeed * 2;
      }
   }
   // Left
   if (game.keys[KEY.LEFT]) {
      player.direction = "L";
      if (player.velX < player.speed) {
         player.velX++;
      }
   }
   // Right
   if (game.keys[KEY.RIGHT]) {
      player.direction = "R";
      if (player.velX > -player.speed) {
         player.velX--;
      }
   }
   // Gravity and Friction
   player.velX *= game.enviroment.friction;
   player.velY += game.enviroment.gravity;
   player.x += player.velX;
   player.y += player.velY;

   // Collisions
   // LEFT RIGHT
   if (player.x >= game.canvas.width - player.width) { // Check Right Collision
      player.x = game.canvas.width - player.width;
   } else if (player.x <= 0) { // Check Left Collision
      player.x = 0;
   }
   // UP DOWN
   if (player.y >= game.canvas.height - player.height) {
      player.y = game.canvas.height - player.height;
      player.jumping = false;
   }

   // Draw Objects
   game.draw_background();
   game.draw_player();

   // Request next animation frame
   requestAnimationFrame(game.game_update);
}

game.draw_player = function() {
   ctx.beginPath();
   ctx.drawImage(LoadedImages.player_sprite, 64, 128, player.width, player.height, player.x, player.y, player.width, player.height);
   /*
   if (player.direction == "R") {
      ctx.drawImage(LoadedImages.player_sprite, 65, 128, player.width, player.height, player.x, player.y, player.width, player.height);
   } else if (player.direction == "L") {
      ctx.drawImage(LoadedImages.player_sprite, 63, 0, player.width, player.height, player.x, player.y, player.width, player.height);
   }
   */

   ctx.closePath();
}

game.draw_background = function() {
   ctx.beginPath();
   ctx.clearRect(0, 0, game.canvas.width, game.canvas.height);
   ctx.closePath();
}

game.draw_UI = function() {
   ctx.beginPath();

   ctx.closePath();
}

/* Listeners */
document.body.addEventListener("keydown", function(e) {
   game.keys[e.keyCode] = true;
});

document.body.addEventListener("keyup", function(e) {
   game.keys[e.keyCode] = false;
});

/* Load Game */
window.addEventListener("load", function() {
   game.onLoad();
});
body,
html {
   position: relative;
}

canvas {
   border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="700" height="500"></canvas>Use Arrow keys to move.
<p id="debug"><p>

Upvotes: 3

Views: 1066

Answers (1)

1cgonza
1cgonza

Reputation: 1588

The issue seems to be that your player.x is a float. That way is hard to account for pixel perfect paints.

Round player.x on the drawImage() or when you update the value with velocity.

Using a bitwise operator, you could simply do:

player.x += player.velX | 0;

Upvotes: 4

Related Questions