javascript - 为什么圆圈在碰撞时振动(Canvas)

标签 javascript canvas

我一直在创建 agar.io 的克隆,但我不明白为什么圆圈在相互接触时会开始振动。下面是我的代码:

var
  canvas,
  ctx,
  width = innerWidth,
  height = innerHeight,
  mouseX = 0,
  mouseY = 0;

var

  camera = {
    x: 0,
    y: 0,

    update: function(obj) {
      this.x = obj.x - width / 2;
      this.y = obj.y - height / 2;
    }
  },

  player = {
    defaultMass: 54,
    x: 0,
    y: 0,
    blobs: [],

    update: function() {
      for (var i = 0; i < this.blobs.length; i++) {
        var x = mouseX + camera.x - this.blobs[i].x;
        var y = mouseY + camera.y - this.blobs[i].y;
        var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        var speed = 54 / this.blobs[i].mass;

        this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
        this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));

        this.blobs[i].x += this.blobs[i].velX;
        this.blobs[i].y += this.blobs[i].velY;

        for (var j = 0; j < this.blobs.length; j++) {
          if (j != i && this.blobs[i] !== undefined) {
            var blob1 = this.blobs[i];
            var blob2 = this.blobs[j];
            var dist = Math.sqrt(Math.pow(blob2.x - blob1.x, 2) + Math.pow(blob2.y - blob1.y, 2));

            if (dist < blob1.mass + blob2.mass) {
              if (this.blobs[i].x < this.blobs[j].x) {
                this.blobs[i].x--;
              } else if (this.blobs[i].x > this.blobs[j].x) {
                this.blobs[i].x++;
              }
              if (this.blobs[i].y < this.blobs[j].y) {
                this.blobs[i].y--;
              } else if ((this.blobs[i].y > this.blobs[j].y)) {
                this.blobs[i].y++;
              }
            }
          }
        }
      }

      this.x += (mouseX - width / 2) / (width / 2) * 1;
      this.y += (mouseY - height / 2) / (height / 2) * 1
    },

    split: function(cell) {
      cell.mass /= 2;

      this.blobs.push({
        x: cell.x,
        y: cell.y,
        mass: cell.mass
      });
    },

    draw: function() {
      for (var i = 0; i < this.blobs.length; i++) {
        ctx.fillStyle = "red";

        ctx.beginPath();
        ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
      }
    }
  };

function handleMouseMove(e) {
  mouseX = e.clientX;
  mouseY = e.clientY;
}

function setup() {
  canvas = document.getElementById("game");
  ctx = canvas.getContext("2d");
  canvas.width = width;
  canvas.height = height;

  addEventListener("mousemove", handleMouseMove);

  player.blobs.push({
    x: 0,
    y: 0,
    mass: player.defaultMass
  });
  player.blobs.push({
    x: 100,
    y: 100,
    mass: player.defaultMass / 2
  });
  player.blobs.push({
    x: 100,
    y: 100,
    mass: player.defaultMass * 2
  });

  var loop = function() {
    update();
    draw();
    requestAnimationFrame(loop);
  }
  requestAnimationFrame(loop);
}

function update() {
  camera.update(player.blobs[0]);
  player.update();
}

function draw() {
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, width, height);

  player.draw();
}

setup();
body {
  margin: 0;
  padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>

最佳答案

分隔圆圈

您的分隔代码不正确。使用它们之间的向量来获得新的位置。

它们之间的向量

要判断两个圆是否相交,请求向量从一个到下一个的长度

两个圆圈。

var cir1 = {x : 100, y : 100, r : 120}; // r is the radius
var cir2 = {x : 250, y : 280, r : 150}; // r is the radius

cir2cir1的向量

var vx = cir2.x - cir1.x;
var vy = cir2.y - cir1.y;

向量的长度

var len = Math.sqrt(x * x + y * y);
// or use the ES6 Math.hypot function
/* var len = Math.hypot(x,y); */

如果半径之和大于它们之间向量的长度,则圆重叠

if(cir1.r + cir2.r > len){ // circles overlap

归一化向量

如果它们重叠,您需要将一个从另一个移开。有很多方法可以做到这一点,最简单的方法是沿着它们之间的线移动一个圆圈。

首先将向量从 cir1 归一化为 cir2 除以其(向量)长度。

    vx \= len;
    vy \= len;

请注意,长度可以为零。如果发生这种情况,您将在进一步的计算中得到 NaN。如果你怀疑你可能会在与另一个相同的位置得到一个圆圈,那么处理零的最简单方法就是稍微移动一个圆圈。

    // replace the two lines above with
    if(len === 0){ // circles are on top of each other
        vx = 1;  // move the circle (abstracted into the vector)
    }else{
        vx \= len;  // normalise the vector
        vy \= len;
    }

移动圆圈以触摸

现在您有了 1 个单位长的归一化向量,您可以通过将两个标量 vxvy 与所需长度相乘来使其成为您需要的任何长度,其中这种情况是两个圆半径之和。

    var mx = vx * (cir1.r + cir2.r);  // move distance
    var my = vy * (cir1.r + cir2.r);

只能使用以下方法之一

您现在可以将其中一个圆圈放置在正确的距离处,以便它们刚好接触

   // move cir1
   cir1.x = cir2.x - mx;
   cir1.y = cir2.y - my;

或者移动第二个圆圈

   cir2.x = cir1.x + mx;
   cir2.y = cir1.y + my;

或者移动两个圆但是你必须先找到两者之间的比例中心

   var pLen = cir1.r / (cir1.r + cir2.r); // find the ratio of the radii
   var cx = cir1.x + pLen * vx * len;  // find the proportional center between
   var cy = cir1.y + pLen * vy * len;  // the two circles     

然后将两个圆从该点移开它们的半径

   cir1.x = cx - vx * cir1.r;     // move circle 1 away from the shared center
   cir1.y = cy - vy * cir1.r;     
   cir2.x = cx + vx * cir2.r;     // move circle 2 away from the shared center
   cir2.y = cy + vy * cir2.r;     

演示

OP 的片段副本和 mod 解决问题的方法是将第一个圆 blob1 移离第二个 blob2 并假设它们永远不会在同一位置(不除以零)

var
  canvas,
  ctx,
  width = innerWidth,
  height = innerHeight,
  mouseX = 0,
  mouseY = 0;

var

  camera = {
    x: 0,
    y: 0,

    update: function(obj) {
      this.x = obj.x - width / 2;
      this.y = obj.y - height / 2;
    }
  },

  player = {
    defaultMass: 54,
    x: 0,
    y: 0,
    blobs: [],

    update: function() {
      for (var i = 0; i < this.blobs.length; i++) {
        var x = mouseX + camera.x - this.blobs[i].x;
        var y = mouseY + camera.y - this.blobs[i].y;
        var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        var speed = 54 / this.blobs[i].mass;

        this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
        this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));

        this.blobs[i].x += this.blobs[i].velX;
        this.blobs[i].y += this.blobs[i].velY;

        for (var j = 0; j < this.blobs.length; j++) {
          if (j != i && this.blobs[i] !== undefined) {
            var blob1 = this.blobs[i];
            var blob2 = this.blobs[j];
            var x = blob2.x - blob1.x; // get the vector from blob1 to blob2
            var y = blob2.y - blob1.y; //
            var dist = Math.sqrt(x * x + y * y); // get the distance between the two blobs

            if (dist < blob1.mass + blob2.mass) {  // if the distance is less than the 2 radius
             // if there is overlap move blob one along the line between the two the distance of the two radius
              x /= dist; // normalize the vector. This makes the vector 1 unit long
              y /= dist;
              // multiplying the normalised vector by the correct distance between the two 
              // and subtracting that distance from the blob 2 give the new pos of 
              // blob 1
              blob1.x = blob2.x - x * (blob1.mass + blob2.mass);
              blob1.y = blob2.y - y * (blob1.mass + blob2.mass);
            }
          }
        }
      }

      this.x += (mouseX - width / 2) / (width / 2) * 1;
      this.y += (mouseY - height / 2) / (height / 2) * 1
    },

    split: function(cell) {
      cell.mass /= 2;

      this.blobs.push({
        x: cell.x,
        y: cell.y,
        mass: cell.mass
      });
    },

    draw: function() {
      for (var i = 0; i < this.blobs.length; i++) {
        ctx.fillStyle = "red";

        ctx.beginPath();
        ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
        ctx.fill();
        ctx.closePath();
      }
    }
  };

function handleMouseMove(e) {
  mouseX = e.clientX;
  mouseY = e.clientY;
}

function setup() {
  canvas = document.getElementById("game");
  ctx = canvas.getContext("2d");
  canvas.width = width;
  canvas.height = height;

  addEventListener("mousemove", handleMouseMove);

  player.blobs.push({
    x: 0,
    y: 0,
    mass: player.defaultMass
  });
  player.blobs.push({
    x: 100,
    y: 100,
    mass: player.defaultMass / 2
  });
  player.blobs.push({
    x: 100,
    y: 100,
    mass: player.defaultMass * 2
  });

  var loop = function() {
    update();
    draw();
    requestAnimationFrame(loop);
  }
  requestAnimationFrame(loop);
}

function update() {
  camera.update(player.blobs[0]);
  player.update();
}

function draw() {
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, width, height);

  player.draw();
}

setup();
body {
  margin: 0;
  padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>

关于javascript - 为什么圆圈在碰撞时振动(Canvas),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43941749/

相关文章:

javascript - 如何处理智能手机浏览器上的触摸 - javascript

javascript - 在 HTML5 Canvas 上绘画时如何检查键盘状态?

Javascript 日期对象时区问题 - 不同的时区

java - 如何提高 Javafx Canvas 上线条的绘制速度

javascript - 无法在 Canvas 中引用窗口宽度/高度

javascript - (ui-)router 中的 Angular-app、身份验证和解析器顺序

javascript - 如何在鼠标移动事件后围绕其中心旋转 Canvas 对象?

javascript - iMacro 脚本和 wird 第 3 个选项卡

javascript - Tablesorter 不会在初始页面加载时进行排序

javascript - 验证密码包含字母