0

I made a little game where there are 3 objects

Player: Can move using WASD

Bullet: Moved by an event listener of click

Enemy: Follows the player like a zombie.

How do I make it so the angle of the zombie is the direction he is moving, Rather than always facing the player object?

Fiddle

    tick: function() {
     enemy.angle = Math.atan2(player.y - enemy.y, player.x - enemy.x);// * (180 / Math.PI);
    },

above is the code I use to make the enemy face the player

Brad
  • 109
  • 6
  • 1
    The zombie is always moving towards the player, so the "direction he is moving" is the same as "always facing the player object". – fqhv Feb 01 '18 at 21:40
  • ummm i cannot see any enemy coming – Muhammad Omer Aslam Feb 01 '18 at 21:40
  • 1
    he is on top of you – fqhv Feb 01 '18 at 21:40
  • I am trying to make the green triangle face towards the angle he is moving rather then always facing the player. – Brad Feb 01 '18 at 21:47
  • @Brad but he is always moving towards the player. For it to be evident, he needs another behavior like, strafe or retreat, or find cover (note zombies should not be able to do these things ;p). – Aluan Haddad Feb 01 '18 at 21:51
  • Yeah but I think it looks weird that the zombie is always facing you I just want to still move towards you but look in the direction its walking – Brad Feb 01 '18 at 21:54

1 Answers1

0

I understand what you're trying to ask, but really, right now the zombie is always going directly to player, so it would actually be weird if it looked some other way. What you need to do is to actually make zombie slowly turn and walk on it's own, rather than just reducing euclidean distance between it and the player.

First stem, one that I'll do for you is calculating the rotation. Instead of just getting to the correct angle instantly, we have to slowly turn every tick:

    tick: function() {
        // 1 degree per tick
        const rotationSpeed = (1/180)*Math.PI;
        // the angle we want - facing the player
        const desiredAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x)
        // transition angle will be explained below 
        enemy.angle = transitionAngle(enemy.angle, desiredAngle,rotationSpeed );
    },

This is not as easy as it sounds, because you need to calculate whether it's faster to turn left or right to face the player again. Fortunately this is easy to google once you know what you need. I based my function on this answer:

function transitionAngle(fromAngle, toAngle, speed) {
    // normalize the angles to 0-360 range
    const rad360 = 2*Math.PI;
    fromAngle = fromAngle % rad360;
    toAngle = toAngle % rad360;



    if (fromAngle < toAngle) {
        if (Math.abs(fromAngle - toAngle) < Math.PI)
            fromAngle += speed;
        else fromAngle -= speed;
    }

    else {
        if (Math.abs(fromAngle - toAngle) < Math.PI)
            fromAngle -= speed;
        else fromAngle += speed;
    }
    return fromAngle;
}

With that, the zombie slows slowly, and it already looks a little better.

      var canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
      var bounds = canvas.getBoundingClientRect();
      var mouseX = 0.0;
      var mouseY = 0.0;
      var pressingDown = false;
      var pressingUp = false;
      var pressingLeft = false;
      var pressingRight = false;

      var player = {
        x: 210,
        y: 250,
        radius: 17.5,
        angle: 0.0,
        tick: function() {
          this.angle = Math.atan2(mouseY - this.y, mouseX - this.x);
        },

        draw: function() {
          context.fillStyle = "darkred";
          context.strokeStyle = "black";
          context.translate(this.x, this.y);
          context.rotate(this.angle);
          context.beginPath();
          context.moveTo(this.radius, 0.0);
          context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
          context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
          context.lineTo(this.radius, 0.0);
          context.fill();
          context.stroke();
          context.rotate(-this.angle);
          context.translate(-this.x, -this.y);
        },

        updatePlayerPosition: function() {
          if (pressingRight)
            player.x += 1;
          if (pressingLeft)
            player.x -= 1;
          if (pressingDown)
            player.y += 1;
          if (pressingUp)
            player.y -= 1;
        }

      }

      var bullet = {
        x: player.x,
        y: player.y,
        dx: 0.0,
        dy: 0.0,
        radius: 5.0,

        tick: function() {
          this.x += this.dx;
          this.y += this.dy;

          if (this.x + this.radius < 0.0 || this.x - this.radius > canvas.width || this.y + this.radius < 0.0 || this.y - this.radius > canvas.height) {
            this.dx = 0.0;
            this.dy = 0.0;
          }
        },

        render: function() {
          context.fillStyle = "darkcyan";
          context.strokeStyle = "white";
          context.beginPath();
          context.arc(this.x, this.y, this.radius, 0.0, 2.0 * Math.PI, false);
          context.fill();
          context.stroke();
        }
      };
 
   var enemy = {       
      x: 200,
        y: 300,
        radius: 17.5,
        angle: 0.0,
        tick: function() {
            // 1 degree per tick
            const rotationSpeed = (1/180)*Math.PI;
            const desiredAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x)
            enemy.angle = transitionAngle(enemy.angle, desiredAngle,rotationSpeed );
        },

        draw: function() {
          context.fillStyle = "Green";
          context.strokeStyle = "darkgreen";
          context.translate(this.x, this.y);
          context.rotate(this.angle);
          context.beginPath();
          context.moveTo(this.radius, 0.0);
          context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
          context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
          context.lineTo(this.radius, 0.0);
          context.fill();
          context.stroke();
          context.rotate(-this.angle);
          context.translate(-this.x, -this.y);
        },
        drawEnemy: function(something){
          var diffX = player.x - something.x;
          var diffY = player.y - something.y

        if (diffX > 0)
          something.x += .3
        else
          something.x -= .3;

        if (diffY > 0)
          something.y += .3
        else
          something.y -= .3;
      }
        
        }





      function Refresh() {
        context.clearRect(0, 0, canvas.width, canvas.height);
        bullet.render();
        bullet.tick();
        player.draw();
        player.tick();
        player.updatePlayerPosition();
        enemy.draw();
        enemy.tick();
        enemy.drawEnemy(enemy);

      }
      

      setInterval(Refresh, 0)

      window.onmousemove = function(e) {
        mouseX = e.clientX - bounds.left;
        mouseY = e.clientY - bounds.top;
      }

      document.onkeydown = function(event) {
        if (event.keyCode === 83) //s
          pressingDown = true;
        else if (event.keyCode === 87) //w
          pressingUp = true;
        else if (event.keyCode === 65) //a
          pressingLeft = true;
        else if (event.keyCode === 68) //d
          pressingRight = true;
      }

      document.onkeyup = function(event) {
        if (event.keyCode === 83) //s
          pressingDown = false;
        else if (event.keyCode === 87) //w
          pressingUp = false;
        else if (event.keyCode === 65) //a
          pressingLeft = false;
        else if (event.keyCode === 68) //d
          pressingRight = false;
      }

      window.onmousedown = function(e) {
        // The mouse pos - the player pos gives a vector
        // that points from the player toward the mouse
        var x = mouseX - player.x;
        var y = mouseY - player.y;

        // Using pythagoras' theorm to find the distance (the length of the vector)
        var l = Math.sqrt(x * x + y * y);

        // Dividing by the distance gives a normalized vector whose length is 1
        x = x / l;
        y = y / l;

        // Reset bullet position
        bullet.x = player.x;
        bullet.y = player.y;

        // Get the bullet to travel towards the mouse pos with a new speed of 10.0 (you can change this)
        bullet.dx = x * 10.0;
        bullet.dy = y * 10.0;
      }

function transitionAngle(fromAngle, toAngle, speed) {
    // normalize the angles to 0-360 range
    const rad360 = 2*Math.PI;
    fromAngle = fromAngle % rad360;
    toAngle = toAngle % rad360;
    
    
    
    if (fromAngle < toAngle) {
        if (Math.abs(fromAngle - toAngle) < Math.PI)
            fromAngle += speed;
        else fromAngle -= speed;
    }

    else {
        if (Math.abs(fromAngle - toAngle) < Math.PI)
            fromAngle -= speed;
        else fromAngle += speed;
    }
    return fromAngle;
}
<canvas id="canvas" style="border:2px solid darkred" width="700" height="500"></canvas>

The second part of the job - and I'm leaving that to you - is to make zombie actually walk in the direction it's facing. To do that, assign it a walking speed and then use sin and cos of the zombie's angle multiplied with the speed to get X and Y offsets. Again, google is your friend, just type "move by speed and angle javascript".

Giulio Bambini
  • 4,695
  • 4
  • 21
  • 36
  • There seems to be a weird bug I didn't notice in OP's version: If you move up or down along the left side of the green arrow, it turns at a rate approximately twice what I'd expect it to, then corrects its facing angle at the next user input. – Patrick Roberts Feb 01 '18 at 22:53
  • http://jsfiddle.net/nuc23154/20/ I put it into my code however I must be doing it wrong can you clarify this is all I have to do? – Brad Feb 02 '18 at 02:16
  • @Brad You forgot the third "speed" argument in the function I've written for you. Why didn't you just copy/paste the code I've provided for the `tick` method? – Giulio Bambini Feb 02 '18 at 10:46