Я пытаюсь разрешить круговые столкновения. Проблема в том, что шары никогда не оседают на дно - они продолжают перемешиваться. Что я могу сделать, чтобы шарики оседали на дно в конце цикла (без покачивания)?
CodePen: https://codepen.io/KriegersVan/pen/gOpXzwE?editors=0010
var _bouncingBalls = (function() {
document.getElementById("canvas").width = window.innerWidth;
document.getElementById("canvas").height = window.innerHeight;
function _randomNumBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function _negativeOrPositiveOne() {
return Math.random() < 0.50 ? -1 : 1;
}
function _random10Chars() {
return Math.random().toString(36).substr(2, 10);
}
var _balls = {
numOf: 10,
colors: ["#6a00ff", "#9500ff", "#d000ff", "#ff00ff", "#00f2ff", "#f59b42", "#e9f542"],
particles: [],
ctx: document.getElementById("canvas").getContext("2d"),
lastFrameTime: 0,
startTime: 0,
inProgress: false,
init: function(x, y) {
for (var i = 0; i < this.numOf; i++) {
this.particles.push({
x: x+(i*(window.innerWidth * 0.02)), //So they don't overlap at the beginning
y: y,
vy: Math.random() * _negativeOrPositiveOne(),
vx: 2 * Math.random() * _negativeOrPositiveOne(),
color: this.colors[_randomNumBetween(0, this.colors.length - 1)],
radius: window.innerWidth * 0.008,
gravity: 0.70,
damping: 0.50,
id: _random10Chars() + _random10Chars()
});
}
this.startTime = window.performance.now();
if (!this.inProgress) {
this.inProgress = true;
window.requestAnimationFrame(this.render);
}
},
isBallCollision: function(particle) {
//https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
for (var j = 0; j < _balls.particles.length; j++) {
var comparisonParticle = _balls.particles[j];
var dx = particle.x - comparisonParticle.x;
var dy = particle.y - comparisonParticle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (particle.id === comparisonParticle.id) {
continue;
}
var minDist = comparisonParticle.radius + particle.radius;
if (distance < minDist) {
//https://www.youtube.com/watch?v=nlwtgvZCz0k
var distance_x = dx;
var distance_y = dy;
var radii_sum = particle.radius+comparisonParticle.radius;
var length = Math.sqrt(distance_x * distance_x + distance_y * distance_y) || 1;
var unit_x = distance_x/length;
var unit_y = distance_y/length;
particle.x = comparisonParticle.x+(radii_sum+1)*unit_x;
particle.y = comparisonParticle.y+(radii_sum+1)*unit_y;
return comparisonParticle;
}
}
return null;
},
isBottomHit: function(obj, comparisonObj){
return obj.y+obj.radius > comparisonObj.y-comparisonObj.radius &&
obj.y+obj.radius < comparisonObj.y+comparisonObj.radius;
},
isTopHit: function(obj, comparisonObj){
return obj.y-obj.radius < comparisonObj.y+comparisonObj.radius &&
obj.y-obj.radius > comparisonObj.y-comparisonObj.radius;
},
isLeftHit: function(obj, comparisonObj){
return obj.x-obj.radius < comparisonObj.x+comparisonObj.radius &&
obj.x-obj.radius > comparisonObj.x-comparisonObj.radius;
},
isRightHit: function(obj, comparisonObj){
return obj.x+obj.radius > comparisonObj.x-comparisonObj.radius &&
obj.x+obj.radius < comparisonObj.x+comparisonObj.radius;
},
bottomReached: function(obj) {
return (obj.y + obj.radius) >= window.innerHeight;
},
topReached: function(obj) {
return (obj.y - obj.radius) <= 0;
},
leftSideReached: function(obj) {
return (obj.x - obj.radius) <= 0;
},
rightSideReached: function(obj) {
return (obj.x + obj.radius) >= window.innerWidth;
},
draw: function(deltaNum, timestamp) {
var r = _balls;
var ctx = r.ctx;
var elapsed = timestamp - r.startTime;
for (var i = 0; i < r.particles.length; i++) {
var obj = r.particles[i];
ctx.beginPath();
ctx.fillStyle = obj.color;
ctx.moveTo(
obj.x,
obj.y
);
ctx.arc(
obj.x,
obj.y,
obj.radius,
0,
2 * Math.PI,
false
);
ctx.fill();
ctx.closePath();
obj.y += (obj.vy * deltaNum);
obj.vy += (obj.gravity * deltaNum);
obj.x += (obj.vx * deltaNum);
var otherBall = r.isBallCollision(obj);
if (otherBall) {
if (r.isBottomHit(obj, otherBall)){
//Bounce up
obj.vy = -Math.abs(obj.vy*obj.damping);
} else if (r.isTopHit(obj, otherBall)){
//bounce down
obj.vy = Math.abs(obj.vy*obj.damping);
}
if (r.isLeftHit(obj, otherBall)){
//Bounce right
obj.vx = Math.abs(obj.vx*obj.damping);
} else if (r.isRightHit(obj, otherBall)){
//Bounce left
obj.vx = -Math.abs(obj.vx*obj.damping);
}
}
if (r.bottomReached(obj)) {
obj.y = window.innerHeight - obj.radius;
if (obj.vy > 5){
//Bounce
obj.vy = -(obj.vy * 0.70);
} else {
//Stop bouncing
obj.vy = 0;
if (otherBall) {
obj.vx = -(obj.vx * obj.damping);
}
obj.vx += obj.vx > 0 ? -0.008 : 0.008;
if (obj.vx < 0.010 && obj.vx > -0.010) {
obj.vx = 0;
}
}
} else if (r.topReached(obj)) {
obj.vy = Math.abs(obj.vy);
}
if (r.rightSideReached(obj)) {
obj.vx = -obj.vx;
} else if (r.leftSideReached(obj)) {
obj.vx = Math.abs(obj.vx);
}
}
},
render: function(timestamp) {
var a = _balls;
var delta = timestamp - (a.lastFrameTime || timestamp);
a.lastFrameTime = timestamp;
if (a.particles.length) {
a.ctx.clearRect(
0,
0,
window.innerWidth,
window.innerHeight
);
a.draw(delta / 16.6, timestamp);
}
window.requestAnimationFrame(a.render);
}
};
var _mouseControl = (function() {
var _el = document.getElementById("canvas");
_el.addEventListener("click", function(e) {
_balls.init.call(_balls, e.clientX, e.clientY);
});
}());
}());