How to rotate rectangle toward mouse position on translated canvas? Javascript

70 views Asked by At

——————————————————————————————

How do I rotate the gun (the gray rectangle) around the player toward the current mouse position? (preferably using vectors or Math.atan2()) Vanila Js

I have tried but it doesn't stay on the player and doesn't rotate toward the mouse position.

The problem in action: The gray rectangle doesn't rotate around the player or point toward the mouse position.

Here is the code:

"use strict"

/**
 * @type { HTMLCanvasElement }
 */
var scene = document.getElementById("scene");
var ctx = scene.getContext("2d");

scene.width = 1024;
scene.height = 576;

var vWidth = scene.width;
var vHeight = scene.height;

var mouseX = 0;
var mouseY = 0;

var friction = 0.15;

var keysDown = [];

class Player {
    constructor(x, y, width, height, color, borderColor = "#000000") {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
        this.borderColor = borderColor;
        this.velX = 0;
        this.velY = 0;
        this.maxSpeed = 5;
        this.name = null;
        this.angle = 0;
    }
}

var walls = [
];

function createWall(x, y, width, height, color = "#000000", id = "") {
    walls.push({
        x: x,
        y: y,
        width: width,
        height: height,
        color: color,
        id: id
    });
}

function drawGrid(startX, startY, endX, endY, gridCellSize = 50) {
    ctx.beginPath();
    ctx.lineWidth = 1;

    for (var x = startX; x <= endX; x += gridCellSize) {
        ctx.moveTo(x, startY);
        ctx.lineTo(x, endY);
    }

    for (var y = startY; y <= endY; y += gridCellSize) {
        ctx.moveTo(startX, y);
        ctx.lineTo(endX, y);
    }

    ctx.strokeStyle = "#dedede";
    ctx.stroke();
    ctx.closePath();
}

var player = new Player(-25, -25, 50, 50, "#ff0000", "#cc0000");

friction = 1 - friction;

scene.width = vWidth;
scene.height = vHeight;

const updateSpeed = 60;

function main() {

    if (player.x < -1000) {
        player.x = -1000;
        player.velX = 0;
    }

    if (player.y < -1000) {
        player.y = -1000;
        player.velY = 0;
    }

    if (player.x + player.width > 1000) {
        player.x = 1000 - player.width;
        player.velX = 0;
    }

    if (player.y + player.height > 1000) {
        player.y = 1000 - player.height;
        player.velY = 0;
    }

    if (keysDown["w"] || keysDown["ArrowUp"]) {
        if (player.velY > player.maxSpeed * -1) {
            player.velY--;
        }
    }

    if (keysDown["a"] || keysDown["ArrowLeft"]) {
        if (player.velX > player.maxSpeed * -1) {
            player.velX--;
        }
    }

    if (keysDown["s"] || keysDown["ArrowDown"]) {
        if (player.velY < player.maxSpeed) {
            player.velY++;
        }
    }

    if (keysDown["d"] || keysDown["ArrowRight"]) {
        if (player.velX < player.maxSpeed) {
            player.velX++;
        }
    }

    player.velX *= friction;
    player.velY *= friction;

    player.x += player.velX;
    player.y += player.velY;

    ctx.save();
    ctx.translate(-player.x - player.width / 2 + vWidth / 2, -player.y - player.height / 2 + vHeight / 2);
    // ctx.translate(player.velX, player.velY);

    ctx.clearRect(0, 0, vWidth, vHeight);

    ctx.beginPath();
    ctx.strokeStyle = "#000000";
    ctx.fillStyle = "#dedede";
    ctx.rect(-2000, -2000, 4000, 4000);
    ctx.fill();
    // ctx.stroke();
    ctx.closePath();

    ctx.beginPath();
    ctx.strokeStyle = "#000000";
    ctx.fillStyle = "#ffffff";
    ctx.rect(-1000, -1000, 2000, 2000);
    ctx.fill();
    // ctx.stroke();
    ctx.closePath();

    drawGrid(-1000, -1000, 1000, 1000, 25);

    ctx.lineWidth = 3;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    // Make this rectangle rotate around the player pointing toward the mouse position
    ctx.save();
    ctx.beginPath();
    ctx.rotate(player.angle);
    ctx.fillStyle = "#cccccc";
    ctx.strokeStyle = "#808080";
    ctx.roundRect(player.x + player.width / 2, player.y + player.height / 2 - 10, player.height - 5, 20, 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
    ctx.restore();

    ctx.beginPath();
    ctx.strokeStyle = player.borderColor;
    ctx.fillStyle = player.color;
    ctx.roundRect(player.x, player.y, player.width, player.height, 50);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();

    for (var i = 0; i < walls.length; i++) {
        var wall = walls[i];
        ctx.fillStyle = wall.color;
        ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
    }

    ctx.restore();

    requestAnimationFrame(main);
}

window.onload = function () {
    // setInterval(main, 1000 / updateSpeed);
    main();
}

scene.onclick = function () {
    scene.requestFullscreen();
}

document.body.addEventListener("keydown", (e) => {
    keysDown[e.key] = true;
});

document.body.addEventListener("keyup", (e) => {
    keysDown[e.key] = false;
});

document.body.addEventListener("mousemove", (e) => {
    mouseX = e.clientX;
    mouseY = e.clientY;
    player.angle = Math.atan2(mouseY, mouseX);
});
*, *:before, *:after {
    font-family: roboto, Arial, Helvetica, sans-serif, system-ui, 'Courier New', Courier, monospace;
    padding: 0px 0px;
    margin: 0px 0px;
    box-sizing: border-box;
}

#scene {
    height: 100vh;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>2D</title>
</head>
<body>
  Use WASD Or Arrow Keys To Move.
    <canvas id="scene"></canvas>
</body>
</html>

2

There are 2 answers

4
Pete21 On BEST ANSWER

Figured out this one awhile back, but I decided to answer it here in case anyone else has the similar problem.

Here it is:

"use strict"

/**
 * @type { HTMLCanvasElement }
 */
var scene = document.getElementById("scene");
var ctx = scene.getContext("2d");

scene.width = window.innerWidth;
scene.height = window.innerHeight;

var vWidth = window.innerWidth;
var vHeight = window.innerHeight;

var mouseX = 0;
var mouseY = 0;

var friction = 0.15;

var keysDown = [];

class Player {
  constructor(x, y, radius, color, borderColor = "#000000") {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.borderColor = borderColor;
    this.velX = 0;
    this.velY = 0;
    this.maxSpeed = 5;
    this.name = null;
    this.angle = 0;
  }
}

var walls = [];

function createWall(x, y, width, height, color = "#000000", id = "") {
  walls.push({
    x: x,
    y: y,
    width: width,
    height: height,
    color: color,
    id: id
  });
}

function drawGrid(startX, startY, endX, endY, gridCellSize = 50) {
  ctx.beginPath();
  ctx.lineWidth = 1;

  for (var x = startX; x <= endX; x += gridCellSize) {
    ctx.moveTo(x, startY);
    ctx.lineTo(x, endY);
  }

  for (var y = startY; y <= endY; y += gridCellSize) {
    ctx.moveTo(startX, y);
    ctx.lineTo(endX, y);
  }

  ctx.strokeStyle = "#dedede";
  ctx.stroke();
  ctx.closePath();
}

var player = new Player(0, 0, 25, "#ff0000", "#cc0000");

friction = 1 - friction;

scene.width = vWidth;
scene.height = vHeight;

const updateSpeed = 60;

function main() {

  if (keysDown["w"] || keysDown["ArrowUp"]) {
    if (player.velY > player.maxSpeed * -1) {
      player.velY--;
    }
  }

  if (keysDown["a"] || keysDown["ArrowLeft"]) {
    if (player.velX > player.maxSpeed * -1) {
      player.velX--;
    }
  }

  if (keysDown["s"] || keysDown["ArrowDown"]) {
    if (player.velY < player.maxSpeed) {
      player.velY++;
    }
  }

  if (keysDown["d"] || keysDown["ArrowRight"]) {
    if (player.velX < player.maxSpeed) {
      player.velX++;
    }
  }

  player.velX *= friction;
  player.velY *= friction;

  player.x += player.velX;
  player.y += player.velY;

  ctx.save();
  ctx.translate(-player.x + vWidth / 2, -player.y + vHeight / 2);
  // ctx.translate(player.velX, player.velY);

  ctx.clearRect(0, 0, vWidth, vHeight);

  ctx.beginPath();
  ctx.strokeStyle = "#000000";
  ctx.fillStyle = "#dedede";
  ctx.rect(-2000, -2000, 4000, 4000);
  ctx.fill();
  // ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.strokeStyle = "#000000";
  ctx.fillStyle = "#ffffff";
  ctx.rect(-1000, -1000, 2000, 2000);
  ctx.fill();
  // ctx.stroke();
  ctx.closePath();

  drawGrid(-1000, -1000, 1000, 1000, 25);

  ctx.lineWidth = 3;
  ctx.lineCap = "round";
  ctx.lineJoin = "round";

  ctx.save();
  ctx.beginPath();
  ctx.translate(player.x, player.y);
  ctx.rotate(player.angle);
  ctx.fillStyle = "#cccccc";
  ctx.strokeStyle = "#808080";
  ctx.roundRect(0, -10, player.radius * 2, 20, 2);
  ctx.fill();
  ctx.stroke();
  ctx.closePath();
  ctx.restore();

  ctx.beginPath();
  ctx.strokeStyle = player.borderColor;
  ctx.fillStyle = player.color;
  ctx.arc(player.x, player.y, player.radius, 0, 2 * Math.PI);
  ctx.fill();
  ctx.stroke();
  ctx.closePath();

  for (var i = 0; i < walls.length; i++) {
    var wall = walls[i];
    ctx.fillStyle = wall.color;
    ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
  }

  ctx.restore();

  requestAnimationFrame(main);
}

window.onload = function() {
  // setInterval(main, 1000 / updateSpeed);
  main();
}

document.body.addEventListener("keydown", (e) => {
  keysDown[e.key] = true;
});

document.body.addEventListener("keyup", (e) => {
  keysDown[e.key] = false;
});

document.body.addEventListener("mousemove", (e) => {
  mouseX = e.clientX;
  mouseY = e.clientY;
  player.angle = Math.atan2(e.clientY - vHeight / 2, e.clientX - vWidth / 2);
});

window.addEventListener("resize", (e) => {
  vWidth = window.innerWidth;
  vHeight = window.innerHeight;

  scene.width = vWidth;
  scene.height = vHeight;
});
*,
*:before,
*:after {
  font-family: roboto, Arial, Helvetica, sans-serif, system-ui, 'Courier New', Courier, monospace;
  padding: 0px 0px;
  margin: 0px 0px;
  box-sizing: border-box;
}

#scene {
  height: 100vh;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>2D</title>
</head>

<body>
  <canvas id="scene"></canvas>
</body>

</html>

0
ProfDFrancis On

To calculate the angle, you need the mouse position relative to the player

At the moment you are just taking the mouse position relative to the top left of the window. That means it is always downwards and rightwards, which is why the gun always points that way. (Click the link for "Full Page", and you will see it more easily. For example, when you move the mouse around the top-left of the screen, you can direct the gun completely from rightwards to downwards.)

The formula, with atan2, is basically correct, but you need to subtract the player position, so that you are only feeding into atan2 the difference in position between the mouse and the player.

Debugging

Thank you for letting reporting back in the comments, that player.angle = Math.atan2(mouseY - player.y, mouseX - player.x); did not work. What this tells us is that those are not the right variables to subtract.

Try console.logging the values of mouseY-player.y and mouseX-player.x. Make sure they are ~0 when the mouse is at the player's position, and negative when it is to the top-left.

If they are not doing that, then try console.logging mouseY and player.y separately, so you can see where the problem lies.

Origin of problem revealed by debugging

I was hoping that you would final suggestion in the section above. When you log player.x and player.y, you find they are zero. This explains why subtracting them does not have the desired effect.