Search code examples
javascriptcollision-detection

How to prevent 2 players to cross each other in html5 game


i would like add collisions between my 2 players (in server side) My two players

Video to show what i want (it's from a game named taming.io) : https://www.youtube.com/watch?v=tGb6PwjHao8

Here is my actual code (every time a player connect, it create a new Player class) :

class Player {
    static list = new Set()
    constructor(id) {
        Player.list[id] = this
        this.id = id;
        this.x = 250;
        this.y = 250;
        this.pressingUp = false;
        this.pressingLeft = false;
        this.pressingDown = false;
        this.pressingRight = false;
        this.moveSpeed = 5;
        this.moveAngle = 45;
        this.radians = this.moveAngle * Math.PI / 180;
        this.vx = Math.cos(this.radians) * this.moveSpeed;
        this.vy = Math.sin(this.radians) * this.moveSpeed;
    }


    //Apply new positions
    update() {

        //Players collisions
        for (const i in Player.list) {

            const player = Player.list[i];

            const calcDistance = Math.hypot(player.x - this.x, player.y - this.y)

            if (calcDistance <= 200 && player != this) {
                //What should i put ?
            }
        }

        //Player moves
        if (this.pressingUp === true) {
            this.y -= this.vy;
        }
        else if (this.pressingDown === true) {
            this.y += this.vy;
        }
        if (this.pressingRight === true) {
            this.x += this.vx;
        }
        else if (this.pressingLeft === true) {
            this.x -= this.vx;
        }   
    }
}

So i'm calculating distance between players with calcDistance const, if calcDistance = 200 (players radius hitbox = 100, so player1 radius hitbox + player2 radius hitbox = 200), it should do something, but i dont know what

Can you please help me ?


Solution

  • Get the distance between the center of the circles using Math.hypot() like you have. See if that distance is less than the radii of the two circles. Once you have that you can set the x,y and optionally the vx,vy of the player.

    The x will be set to obj2.x plus the two radii combined multiplied by the distance of each x and y value divided by the total distance. More triangle math...

    Here's an example

    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    canvas.width = 500;
    canvas.height = 500;
    
    class Circle {
      constructor(x, y, c) {
        this.x = x;
        this.y = y;
        this.color = c;
        this.r = 30;
        this.vx = 0;
        this.vy = 0;
        this.speed = 0.5;
      }
      draw() {
        ctx.beginPath();
        ctx.fillStyle = this.color;
        ctx.arc(this.x, this.y, this.r, 0, Math.PI*2);
        ctx.fill();
    
      }
      update() {
        if (controller.left) {
          this.vx -= this.speed;
        }
        if (controller.up) {
          this.vy -= this.speed;
        }
        if (controller.right) {
          this.vx +=  this.speed;
        }
        if (controller.down) {
          this.vy += this.speed
        }
        
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= 0.9;
        this.vy *= 0.9;
      }
    }
    
    class Controller {
      constructor() {
        this.up = false;
        this.right = false;
        this.down = false;
        this.left = false;
    
        let keyEvent = (e) => {
          if (e.code == "ArrowUp") {
            this.up = e.type == "keydown";
          }
          if (e.code == "ArrowRight") {
            this.right = e.type == "keydown";
          }
          if (e.code == "ArrowLeft") {
            this.left = e.type == "keydown";
          }
          if (e.code == "ArrowDown") {
            this.down = e.type == "keydown";
          }
        };
        addEventListener("keydown", keyEvent);
        addEventListener("keyup", keyEvent);
      }
    }
    
    let player = new Circle(25, 25, 'blue');
    let obstacle = new Circle(canvas.width/2, 150, 'red');
    let controller = new Controller();
    
    function collisionDetection(obj1, obj2) {
      if (Math.hypot(obj1.x - obj2.x, obj1.y - obj2.y) <= obj1.r + obj2.r) {
          collisionResponse(obj1, obj2)
      }
      
    }
    
    function collisionResponse(obj1, obj2) {
      let dx = obj1.x - obj2.x;
      let dy = obj1.y - obj2.y;
      let dist = (Math.hypot(obj1.x - obj2.x, obj1.y - obj2.y)) || 1;
      let radii = obj1.r + obj2.r;
      let x = dx / dist;
      let y = dy / dist;
      
      obj1.x = obj2.x + radii * x;
      obj1.y = obj2.y + radii * y;
      obj1.vx = 0; //optional
      obj1.vy = 0; //optional
    }
    
    function animate() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      collisionDetection(player, obstacle)
      player.update();
      player.draw();  
      obstacle.draw();
      requestAnimationFrame(animate);
    }
    animate();
    <canvas id="canvas"></canvas>

    Setting the vx and vy are optional and will prevent any overlap from occurring. You can also choose to not set them to 0 and you will have some overlap but the circle will seem to slide off of eachother smoother.