1

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

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>
Pete21
  • 102
  • 11

2 Answers2

2

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>
Pete21
  • 102
  • 11
  • Nice for answering on your own issue! You might add some details as well on what exactly helped. Besides that, I noticed a beginner mistake, and that's when using diagonal movement (two arrows simultaneously) - the player moves faster since moves i.e: both X+1 and Y+1 instead of `1 / Math.sqrt(2)` (or `Math.sqrt(2) / 2`) which is approx `~0.707` (instead of `1`). – Roko C. Buljan Jul 01 '23 at 23:48
  • Also, don't forget to use `Event.preventDefault()` when using arrow keys, otherwise the default browser behavior might be to scroll the page disrupting the gameplay in the case your game is part of a wider, overflowing page / application. Regarding the 0.707 see: https://stackoverflow.com/a/7320782/383904 – Roko C. Buljan Jul 01 '23 at 23:56
  • @RokoC.Buljan Yeah, I noticed that in most my games, the player moves faster when they move diagonally. Also, I started this project awhile back, so it was already finished. – Pete21 Jul 01 '23 at 23:59
  • 1
    Than, my comments are more for the future reader of this answer. :) – Roko C. Buljan Jul 02 '23 at 00:07
0

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.

ProfDFrancis
  • 8,816
  • 1
  • 17
  • 26