Search code examples
javascripthtmlmathcanvasatan2

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


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

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>


Solution

  • 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>