Reputation: 63
I've written a code where there are two circles that follow the mouse. I managed to make collision detection, it works just fine for the moment but I cannot make the circles stay together without being repelled.
I have tried by using a boolean that is set to true whenever they overlap and then I check if that boolean is true and I multiply the acceleration by -1 again, but it just doesn't work since they will just "merge".
I have also tried adding the other circle's radius to its position but it just makes a "teleport". This is not school homework, it's a personal project :) )
EDIT: The expected behavior is to not be repelled by the other circle but stay together and move around the circle and advance to the mouse position without being repelled. Just like on the game agar.io, when you split the cells when they are moving and are colliding they do not repel but they move around each other smoothly.
// 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>
Upvotes: 3
Views: 152
Reputation: 78520
It's very far from perfect, but here's what I've come up with: You can update the "center" x
and y
of each circle based on collided circles. for every collided circe, the x
and y
become a middle point between the two center values of those circles. These values are not set to this.x
or this.y
or they would jump oddly. Instead, this new "relative" x
and y
are only calculated and used when determining the distance traveled for every draw. The bottom snippet does not work exactly correctly because it doesn't end up at the right x,y
in the end, but gives you an idea of what it's supposed to do. Anyone else may feel free to build on what I have here in their own answer or an edit to mine.
// 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>
Upvotes: 1