Сделать так, чтобы круг не отталкивался от другого круга, когда он перекрывается?

Я написал код, в котором за мышью следуют два круга. Мне удалось сделать обнаружение столкновений, на данный момент оно работает нормально, но я не могу заставить круги оставаться вместе, не отталкиваясь.

Я пытался использовать логическое значение, для которого установлено значение true, всякий раз, когда они перекрываются, а затем я проверял, истинно ли это логическое значение, и снова умножал ускорение на -1, но это просто не работает, поскольку они просто «сливаются».

Я также попытался добавить радиус другого круга к его положению, но он просто делает «телепорт». Это не школьная домашка, это личный проект :) )

РЕДАКТИРОВАТЬ: ожидаемое поведение состоит в том, чтобы не отталкиваться от другого круга, а оставаться вместе, перемещаться по кругу и продвигаться к позиции мыши, не будучи отталкиваемым. Так же, как и в игре agar.io, когда вы разделяете клетки, когда они двигаются и сталкиваются, они не отталкиваются, а плавно перемещаются друг вокруг друга.

// Setting all up
const canvas = document.getElementById("cv");
const ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;

// Math Variables and utilities
const PI = Math.PI;
const TWO_PI = PI * 2;
const HALF_PI = PI / 2;

const random = (n1, n2 = 0) => {
  return Math.random() * (n2 - n1) + n1;
};

const distance = (n1, n2, n3, n4) => {
  let dX = n1 - n3;
  let dY = n2 - n4;
  return Math.sqrt(dX * dX + dY * dY);
};

let circles = []; // Array that stores the two circles
let mouse = {
  x: 0,
  y: 0
}; // mouse object

// Creating the circle class

function Circle(px, py, r, ctx) {
  this.x = px; // X Position
  this.y = py; // Y Position
  this.r = r; // Radius
  this.ctx = ctx; // Canvas context
  this.acc = 0.005; // HardCoded acceleration value

  // Draw circle function
  this.show = function() {
    this.ctx.beginPath();
    this.ctx.fillStyle = "#fff";
    this.ctx.arc(this.x, this.y, this.r, 0, TWO_PI, false);
    this.ctx.fill();
  };

  this.update = function(x, y) {
    // Distance between the mouse's X and Y coords and circle's         // X and Y coords
    let dist = {
      x: x - this.x,
      y: y - this.y
    };

    // Distance formula stated above
    let d = distance(x, y, this.x, this.y);
    if (d > 1) {
      this.x += dist.x * this.acc;
      this.y += dist.y * this.acc;
    }
  };

  // Circle collision
  this.collides = function(other) {
    let d1 = distance(this.x, this.y, other.x, other.y);
    if (d1 <= other.r + this.r) {
      //this.acc *= -1;
      // Do stuff to make the circle that collides to round the other
      // Without getting inside it
    }
  }
}

// Generating the circles
const genCircles = () => {
  // Just generating two circles
  for (let i = 0; i < 2; i++) {
    circles.push(new Circle(random(canvas.width / 2), random(canvas.height / 2), 50, ctx));
  }
};
genCircles();

// Displaying and updating the circles
const showCircles = () => {
  for (let i = 0; i < circles.length; i++) {
    // Mouse Event to update mouse's coords
    canvas.addEventListener("mousemove", (e) => {
      mouse.x = e.x;
      mouse.y = e.y;
    }, true);
    // Iterating over the circles to check for collision
    for (let j = 0; j < circles.length; j++) {
      if (i !== j) {
        circles[i].collides(circles[j])
      }
    }
    // Doing the movement and the display functions
    circles[i].update(mouse.x, mouse.y);
    circles[i].show();
  }
};

// Loop to make it run
const update = () => {
  requestAnimationFrame(update);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "#000";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  showCircles();
};

update();
html body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  display: block;
}
<canvas id="cv"></canvas>


person rochismo    schedule 12.01.2018    source источник
comment
Нажмите изменить. Затем нажмите кнопку [‹›] на панели инструментов редактора.   -  person mplungjan    schedule 12.01.2018
comment
Теперь у меня работает, я удалил весь ненужный HTML и предысторию :)   -  person mplungjan    schedule 12.01.2018
comment
Я не очень понимаю, что такое желаемый результат. Если вы не хотите, чтобы они отскакивали друг от друга, то (1) для чего нужно обнаружение столкновений и (2) как вы представляете, что они не сталкиваются и при этом не сливаются? Я думаю, что первая проблема, которую необходимо решить, — концептуальная, а не техническая.   -  person JLRishe    schedule 12.01.2018
comment
@JLRishe Сейчас я добавлю ожидаемое поведение, спасибо, что заметили. Но ожидаемое поведение состоит в том, чтобы не отталкиваться от другого круга, а оставаться вместе, двигаться по кругу и продвигаться к позиции мыши, не будучи отталкиваемым. Если эту концепцию легко понять.   -  person rochismo    schedule 12.01.2018
comment
Я предполагаю, что this.acc *= -1; был добавлен, чтобы просто показать, что обнаружение столкновений работает (закомментируйте эту строку, чтобы лучше понять, что происходит); но что действительно нужно ОП, так это то, чтобы круги меняли траекторию. Их нужно по-прежнему притягивать к мышке, но при этом они не должны сталкиваться. Желаемым результатом, насколько я понимаю, было бы то, что круги по-прежнему будут двигаться к мыши и в то же время избегать друг друга. Я думаю, это потребует серьезной математики. По крайней мере помогает то, что они круги.   -  person Joseph Marikle    schedule 12.01.2018
comment
Прибил это @JosephMarikle. Я думал сделать это с помощью синуса и косинуса, но я действительно не знаю, что с этим изменить.   -  person rochismo    schedule 12.01.2018


Ответы (1)


Это очень далеко от совершенства, но вот что я придумал: вы можете обновить «центр» x и y каждого круга на основе столкнувшихся кругов. для каждой столкнувшейся окружности x и y становятся средней точкой между двумя центральными значениями этих окружностей. Эти значения не установлены на this.x или this.y, иначе они будут прыгать странно. Вместо этого эти новые «относительные» x и y рассчитываются и используются только при определении пройденного расстояния для каждого розыгрыша. Нижний фрагмент работает не совсем правильно, потому что в конце он не заканчивается на нужном x,y, но дает вам представление о том, что он должен делать. Любой другой может свободно опираться на то, что у меня есть здесь, в своем собственном ответе или редактировать мой.

// Setting all up
const canvas = document.getElementById("cv");
const ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;

// Math Variables and utilities
const PI = Math.PI;
const TWO_PI = PI * 2;
const HALF_PI = PI / 2;

const random = (n1, n2 = 0) => {
  return Math.random() * (n2 - n1) + n1;
};

const distance = (n1, n2, n3, n4) => {
  let dX = n1 - n3;
  let dY = n2 - n4;
  return Math.sqrt(dX * dX + dY * dY);
};

let circles = []; // Array that stores the two circles
let mouse = {
  x: 0,
  y: 0
}; // mouse object

// Creating the circle class

function Circle(px, py, r, ctx) {
  this.x = px; // X Position
  this.y = py; // Y Position
  this.r = r; // Radius
  this.ctx = ctx; // Canvas context
  this.acc = 0.005; // HardCoded acceleration value

  // Draw circle function
  this.show = function() {
    this.ctx.beginPath();
    this.ctx.fillStyle = "#fff";
    this.ctx.arc(this.x, this.y, this.r, 0, TWO_PI, false);
    this.ctx.fill();
  };

  this.update = function(x, y) {
    // Distance between the mouse's X and Y coords and circle's         // X and Y coords
    
    var reletave = {x: this.x, y: this.y};
    
    circles.forEach((cir) => {
      if(cir === this) return;
      if(this.collides(cir))  {
        var floor = {x: Math.floor(cir.x, reletave.x), y: Math.floor(cir.y, reletave.y)};
        var dist = {x: Math.abs(cir.x - reletave.x), y: Math.abs(cir.y - reletave.y)};
        reletave.x = floor.x + dist.x;
        reletave.y = floor.y + dist.y;
      }
    })
    
    let dist = {
      x: x - reletave.x,
      y: y - reletave.y
    };

    // Distance formula stated above
    let d = distance(x, y, reletave.x, reletave.y);
    if (d > 0) {
      this.x += dist.x * this.acc;
      this.y += dist.y * this.acc;
    }
  };

  // Circle collision
  this.collides = function(other) {
    let d1 = distance(this.x, this.y, other.x, other.y);
    return d1 <= other.r + this.r;
  }
}

// Generating the circles
const genCircles = () => {
  // Just generating two circles
  for (let i = 0; i < 2; i++) {
    var collides = true;
    while(collides){
      var circleAdd = new Circle(random(canvas.width / 2), random(canvas.height / 2), 50, ctx);
      collides = false;
      circles.forEach((cir) => {
        collides = circleAdd.collides(cir) ? true : collides;
      });
      if(collides) delete circleAdd;
    }
    circles.push(circleAdd);
  }
};
genCircles();

// Displaying and updating the circles
const showCircles = () => {
  for (let i = 0; i < circles.length; i++) {
    // Mouse Event to update mouse's coords
    canvas.addEventListener("mousemove", (e) => {
      mouse.x = e.x;
      mouse.y = e.y;
    }, true);
    // Iterating over the circles to check for collision
    for (let j = 0; j < circles.length; j++) {
      if (i !== j) {
        if(circles[i].collides(circles[j])) {
          //circles[i].acc = circles[j].acc = 0;
        }
      }
    }
    // Doing the movement and the display functions
    circles[i].update(mouse.x, mouse.y);
    circles[i].show();
  }
};

// Loop to make it run
const update = () => {
  requestAnimationFrame(update);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "#000";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  showCircles();
};

update();
html body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  display: block;
}
<canvas id="cv"></canvas>

person Joseph Marikle    schedule 12.01.2018
comment
Это как раз то, о чем я просил, я добавлю настройки, чтобы адаптировать его к ES6, так как это дало мне некоторые ошибки в моем другом коде, большое спасибо. - person rochismo; 12.01.2018