Ronald McDonald Ronald McDonald - 19 days ago 5
HTML Question

HTML Canvas Rotating a character's gun to face mouse

I'm very new to Javascript and I've started a simple game. I want the character's gun to rotate to follow the mouse. So far, movement and everything else works fine, except that when I added the rotation functionality the character seems to rotate in a huge circle around the screen. Here's the jsfiddle: https://jsfiddle.net/jvwr8bug/#

function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
var mouseX = evt.clientX - rect.top;
var mouseY = evt.clientY - rect.left;
return {
x: mouseX,
y: mouseY
};
}

window.addEventListener('load', function() {
canvas.addEventListener('mousemove', function(evt) {
var m = getMousePos(canvas, evt);
mouse.x = m.x;
mouse.y = m.y;
}, false);
}, false);


The error seems to be somewhere there but obviously it could be something else

**Edit: Thanks to Blindman67 for the fix.

Answer

You were rotating the current transform by rotation each frame. ctx.rotate(a) rotates the current transform so each time it is called you increase the rotation amount by a. Think of it as a relative rotation rather than setting the absolute rotation.

To fix your code replace the canon rendering with

  //cannon
  //ctx.rotate(rotation);  // << you had

  // setTransform overwrites the current transform with a new one
  // The arguments represent the vectors for the X and Y axis
  // And are simply the direction and length of one pixel for each axis
  // And a coordinate for the origin.
  // All values are in screen/canvas pixels coordinates
  // setTransform(xAxisX, xAxisY, yAxisX, yAxisY, originX, originY)
  ctx.setTransform(1,0,0,1,x,y); // set center of rotation (origin) to center of gun
  ctx.rotate(rotation); // rotate about that point.
  ctx.fillStyle = "#989898";
  ctx.fillRect(15, - 12.5, 25, 25);  // draw relative to origin
  ctx.lineWidth = 2;
  ctx.strokeStyle = "#4f4f4f";
  ctx.strokeRect( 15,- 12.5, 25, 25); // draw relative to origin
  //body
  ctx.fillStyle = "#5079c4";
  ctx.beginPath();
  ctx.arc(0, 0, size, 0, Math.PI * 2); // draw relative to origin
  ctx.fill();
  ctx.stroke();

  // can't leave the transformed state as is because that will effect anything else
 // that will be rendered. So reset to the default.
  ctx.setTransform(1,0,0,1,0,0);   // restore the origin to the default 

And a few more problems to get it working

Just above rendering the canon get the direction to the mouse

// you had coordinates mixed up
// rotation = Math.atan2(mouse.x - y, mouse.y - x); // you had (similar)

rotation = Math.atan2(mouse.y - y, mouse.x - x);

And your mouse event listener is mixing up coordinates and not running very efficiently

Replace all your mouse code with. You don't need onload as the canvas already exists.

canvas.addEventListener('mousemove', function(evt) {
    var rect = this.getBoundingClientRect();
    mouse.x = evt.clientX - rect.left; // you had evt.clientX - rect.top
    mouse.y = evt.clientY - rect.top;  // you had evt.clientY - rect.left
}, false);