基于质量和弹跳系数计算球与球碰撞的速度和方向 [英] Calculate velocity and direction of a ball to ball collision based on mass and bouncing coefficient

查看:410
本文介绍了基于质量和弹跳系数计算球与球碰撞的速度和方向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用了以下基于)

 函数Vector(x,y){
this.x = x;
this.y = y;
}

Vector.prototype.dot = function(v){
return this.x * v.x + this.y * v.y;
};

Vector.prototype.length = function(){
return Math.sqrt(this.x * this.x + this.y * this.y);
};

Vector.prototype.normalize = function(){
var s = 1 / this.length();
this.x * = s;
this.y * = s;
return this;
};

Vector.prototype.multiply = function(s){
return new Vector(this.x * s,this.y * s);
};

Vector.prototype.tx = function(v){
this.x + = v.x;
this.y + = v.y;
return this;
};

function BallObject(elasticity,vx,vy){
this.v = new Vector(vx || 0,vy || 0); // velocity:m / s ^ 2
this.m = 10; // mass:kg
this.r = 15; // radius of obj
this.p = new Vector(0,0); // position
this.cr = elasticity; // elastic
}

BallObject.prototype.draw = function(ctx){
ctx.beginPath();
ctx.arc(this.p.x,this.p.y,this.r,0,2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.stroke();
};

BallObject.prototype.update = function(g,dt,ppm){

this.v.y + = g * dt;
this.p.x + = this.v.x * dt * ppm;
this.p.y + = this.v.y * dt * ppm;

};

BallObject.prototype.collide = function(obj){

var dt,mT,v1,v2,cr,sm,
dn = new Vector .px - obj.px,this.py - obj.py),
sr = this.r + obj.r,//半径总和
dx = dn.length(); //预归一化幅度

if(dx> sr){
return; //没有碰撞
}

//合计质量,归一化碰撞向量并得到它的切线
sm = this.m + obj.m;
dn.normalize();
dt = new Vector(dn.y,-dn.x);

//通过不变形球避免双重碰撞(更大的质量==小于tx)
//这是容易出现舍入误差,摇摆行为和反重力b $ b //对象的暂停进入一个奇怪的状态
mT = dn.multiply(this.r + obj.r - dx);
this.p.tx(mT.multiply(obj.m / sm));
obj.p.tx(mT.multiply(-this.m / sm));

//这种交互很奇怪,因为CR描述的不仅仅是
//球的反弹属性,它描述了在碰撞中观察到的守恒水平
// (例如cr> 1或cr <0)的情况下,真需要描述,刚度,
//弹性,变形或粘附损失的能量水平,以及疯狂的
//值对于奇怪的边缘情况显然不是
//处理这里(参见:http://en.wikipedia.org/wiki/Coefficient_of_restitution)
//现在假设具有最小弹性的球描述了
//碰撞作为一个整体:
cr = Math.min(this.cr,obj.cr);

//缓存相关速度的适用分量的大小
v1 = dn.multiply(this.v.dot(dn))length();
v2 = dn.multiply(obj.v.dot(dn))。length();

//保持相关速度的unapplicatble分量
//然后应用非弹性碰撞的公式
this.v = dt.multiply(this.v.dot(dt ));
this.v.tx(dn.multiply((cr * obj.m *(v2 - v1)+ this.m * v1 + obj.m * v2)/ sm));

//为每个对象执行一次,因为我们假设碰撞将被称为
//每个框架只有一次,并且它也更有效地用于计算缓存
/ / purposes
obj.v = dt.multiply(obj.v.dot(dt));
obj.v.tx(dn.multiply((cr * this.m *(v1 - v2)+ obj.m * v2 + this.m * v1)/ sm));
};

function FloorObject(floor){
var py;

this.v = new Vector(0,0);
this.m = 5.9722 * Math.pow(10,24);
this.r = 10000000;
this.p = new Vector(0,py = this.r + floor);
this.update = function(){
this.v.x = 0;
this.v.y = 0;
this.p.x = 0;
this.p.y = py;
};
//自定义以减少不必要的填充:
this.draw = function(ctx){
var c = ctx.canvas,s = ctx.scale;
ctx.fillRect(c.width / -2 / s,floor,ctx.canvas.width / s,(ctx.canvas.height / s) - floor);
};
}

FloorObject.prototype = new BallObject(1);

function createCanvasWithControls(objs){
var addBall = function(){objs.unshift(new BallObject(els.value / 100,(Math.random()* 10) -20)); },
d = document,
c = d.createElement('canvas'),
b = d.createElement('button'),
els = d.createElement ),
clr = d.createElement('input'),
cnt = d.createElement('input'),
clrl = d.createElement('label'),
cntl = d.createElement('label');

b.innerHTML ='添加弹性球:< span> 0.70< / span>';
b.onclick = addBall;

els.type ='range';
els.min = 0;
els.max = 100;
els.step = 1;
els.value = 70;
els.style.display ='block';
els.onchange = function(){
b.getElementsByTagName('span')[0] .innerHTML =(this.value / 100).toFixed(2)
};

clr.type = cnt.type ='checkbox';
clr.checked = cnt.checked = true;
clrl.style.display = cntl.style.display ='block';

clrl.appendChild(clr);
clrl.appendChild(d.createTextNode('clear each frame'));

cntl.appendChild(cnt);
cntl.appendChild(d.createTextNode('continuous shower!'));

c.style.border ='solid 1px#3369ff';
c.style.display ='block';
c.width = 700;
c.height = 550;
c.shouldClear = function(){return clr.checked; };

d.body.appendChild(c);
d.body.appendChild(els);
d.body.appendChild(b);
d.body.appendChild(clrl);
d.body.appendChild(cntl);

setInterval(function(){
if(cnt.checked){
addBall();
}
},333)

return c;
}

//开始:
var objs = [],
c = createCanvasWithControls(objs),
ctx = c.getContext ),
fps = 30,//目标帧每秒
ppm = 20,//每米像素数
g = 9.8,// m / s ^ 2 - 重力加速度
t = new Date()。getTime();

//添加楼层:
objs.push(new FloorObject(c.height - 10));

// as expando so its accessible in draw [this overides .scale(x,y)]
ctx.scale = 0.5;
ctx.fillStyle ='rgb(100,200,255)';
ctx.strokeStyle ='rgb(33,69,233)';
ctx.transform(ctx.scale,0,0,ctx.scale,c.width / 2,c.height / 2);

setInterval(function(){

var i,j,
nw = c.width / ctx.scale,
nh = c.height / ctx.scale,
nt = new Date()。getTime(),
dt =(nt - t)/ 1000;

if(c.shouldClear对于(i = 0; i ctx.clearRect(nw / -2,nh / -2,nw,nh);
}

i ++){

//如果球>视口宽度远离中心移除它
while(objs [i] .px< -nw || objs [i] .px> ; nw){
objs.splice(i,1);
}

objs [i] .update(g,dt,ppm,objs,i);

for(j = i + 1; j< objs.length; j ++){
objs [j] .collide(objs [i]);
}

objs [i] .draw(ctx);
}

t = nt;

},1000 /

真正的肉,这个讨论的起源是 obj。碰撞(obj)方法。



如果我们潜水(我这次评论,因为它比最后复杂得多) ,你会看到这个方程:,仍然是这一行中使用的唯一一个: this.v.tx(dn.multiply((cr * obj.m *(v2 - v1)+ this.m * v1 + obj.m * v2)/ sm)); c $ c>现在我相信你还在说:zomg wtf!这是同一个单维度方程!但是当你停下来思考它时,碰撞只发生在单维度。这就是为什么我们使用矢量方程来提取适用的分量,并将碰撞仅应用于那些特定的部分,而其他部分保持不变的状态(忽略摩擦和简化碰撞,不考虑动态能量转换力,如CR的意见)。这个概念显然变得更复杂,因为对象复杂性的增长和场景数据点的数量增加,以解决诸如畸形,旋转惯性,不均匀的质量分布和摩擦点等事情,但这是远远超出了它的范围,几乎不值得一提的是。



基本上,你真正需要掌握这个概念对你感到直观的概念是矢量方程的基础(所有这些都位于矢量原型),它们如何与每个(它实际上意味着规范化,或采取点/标量产品,例如阅读/谈话知识的人)和基本的理解如何碰撞作用于对象的属性(质量,速度,



我希望这有助于你,祝你好运! -ck


I used the following code based on this

ballA.vx = (u1x * (m1 - m2) + 2 * m2 * u2x) / (m1 + m2);
ballA.vy = (u1y * (m1 - m2) + 2 * m2 * u2y) / (m1 + m2);

ballB.vx = (u2x * (m2 - m1) + 2 * m1 * u1x) / (m1 + m2);
ballB.vy = (u2y * (m2 - m1) + 2 * m1 * u1y) / (m1 + m2);

but it obviously doesn't well as the formula is designed for one-dimensional collisions.

So I tried to use the below formula from this section.

But the problem is that I don't know what the angle of deflection is and how to calculate it. Also, how to take into account the bouncing coefficient in this formula?

Edit: I may have not been clear. The above code does work, although it may not be the expected behavior, as the original formula is designed for 1D collisions. The issues I'm trying therefore are:

  • What is the 2D equivalent?
  • How to take the bouncing coefficient into account?
  • How to calculate the direction (which is expressed with vx and vy) of the two balls following the collision?

解决方案

I should start by saying: I created a new answer because I feel the old one has value for its simplicity

as promised here is a much more complex physics engine, yet I still feel it's simple enough to follow (hopefully! or I just wasted my time... lol), (url: http://jsbin.com/otipiv/edit#javascript,live)

function Vector(x, y) {
  this.x = x;
  this.y = y;
}

Vector.prototype.dot = function (v) {
  return this.x * v.x + this.y * v.y;
};

Vector.prototype.length = function() {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

Vector.prototype.normalize = function() {
  var s = 1 / this.length();
  this.x *= s;
  this.y *= s;
  return this;
};

Vector.prototype.multiply = function(s) {
  return new Vector(this.x * s, this.y * s);
};

Vector.prototype.tx = function(v) {
  this.x += v.x;
  this.y += v.y;
  return this;
};

function BallObject(elasticity, vx, vy) {
  this.v = new Vector(vx || 0, vy || 0); // velocity: m/s^2
  this.m = 10; // mass: kg
  this.r = 15; // radius of obj
  this.p = new Vector(0, 0); // position  
  this.cr = elasticity; // elasticity
}

BallObject.prototype.draw = function(ctx) {
  ctx.beginPath();
  ctx.arc(this.p.x, this.p.y, this.r, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
};

BallObject.prototype.update = function(g, dt, ppm) {

  this.v.y += g * dt;
  this.p.x += this.v.x * dt * ppm;
  this.p.y += this.v.y * dt * ppm;

};

BallObject.prototype.collide = function(obj) {

  var dt, mT, v1, v2, cr, sm,
      dn = new Vector(this.p.x - obj.p.x, this.p.y - obj.p.y),
      sr = this.r + obj.r, // sum of radii
      dx = dn.length(); // pre-normalized magnitude

  if (dx > sr) {
    return; // no collision
  }

  // sum the masses, normalize the collision vector and get its tangential
  sm = this.m + obj.m;
  dn.normalize();
  dt = new Vector(dn.y, -dn.x);

  // avoid double collisions by "un-deforming" balls (larger mass == less tx)
  // this is susceptible to rounding errors, "jiggle" behavior and anti-gravity
  // suspension of the object get into a strange state
  mT = dn.multiply(this.r + obj.r - dx);
  this.p.tx(mT.multiply(obj.m / sm));
  obj.p.tx(mT.multiply(-this.m / sm));

  // this interaction is strange, as the CR describes more than just
  // the ball's bounce properties, it describes the level of conservation
  // observed in a collision and to be "true" needs to describe, rigidity, 
  // elasticity, level of energy lost to deformation or adhesion, and crazy
  // values (such as cr > 1 or cr < 0) for stange edge cases obviously not
  // handled here (see: http://en.wikipedia.org/wiki/Coefficient_of_restitution)
  // for now assume the ball with the least amount of elasticity describes the
  // collision as a whole:
  cr = Math.min(this.cr, obj.cr);

  // cache the magnitude of the applicable component of the relevant velocity
  v1 = dn.multiply(this.v.dot(dn)).length();
  v2 = dn.multiply(obj.v.dot(dn)).length(); 

  // maintain the unapplicatble component of the relevant velocity
  // then apply the formula for inelastic collisions
  this.v = dt.multiply(this.v.dot(dt));
  this.v.tx(dn.multiply((cr * obj.m * (v2 - v1) + this.m * v1 + obj.m * v2) / sm));

  // do this once for each object, since we are assuming collide will be called 
  // only once per "frame" and its also more effiecient for calculation cacheing 
  // purposes
  obj.v = dt.multiply(obj.v.dot(dt));
  obj.v.tx(dn.multiply((cr * this.m * (v1 - v2) + obj.m * v2 + this.m * v1) / sm));
};

function FloorObject(floor) {
  var py;

  this.v = new Vector(0, 0);
  this.m = 5.9722 * Math.pow(10, 24);
  this.r = 10000000;
  this.p = new Vector(0, py = this.r + floor);
  this.update = function() {
      this.v.x = 0;
      this.v.y = 0;
      this.p.x = 0;
      this.p.y = py;
  };
  // custom to minimize unnecessary filling:
  this.draw = function(ctx) {
    var c = ctx.canvas, s = ctx.scale;
    ctx.fillRect(c.width / -2 / s, floor, ctx.canvas.width / s, (ctx.canvas.height / s) - floor);
  };
}

FloorObject.prototype = new BallObject(1);

function createCanvasWithControls(objs) {
  var addBall = function() { objs.unshift(new BallObject(els.value / 100, (Math.random() * 10) - 5, -20)); },
      d = document,
      c = d.createElement('canvas'),
      b = d.createElement('button'),
      els = d.createElement('input'),
      clr = d.createElement('input'),
      cnt = d.createElement('input'),
      clrl = d.createElement('label'),
      cntl = d.createElement('label');

  b.innerHTML = 'add ball with elasticity: <span>0.70</span>';
  b.onclick = addBall;

  els.type = 'range';
  els.min = 0;
  els.max = 100;
  els.step = 1;
  els.value = 70;
  els.style.display = 'block';
  els.onchange = function() { 
    b.getElementsByTagName('span')[0].innerHTML = (this.value / 100).toFixed(2);
  };

  clr.type = cnt.type = 'checkbox';
  clr.checked = cnt.checked = true;
  clrl.style.display = cntl.style.display = 'block';

  clrl.appendChild(clr);
  clrl.appendChild(d.createTextNode('clear each frame'));

  cntl.appendChild(cnt);
  cntl.appendChild(d.createTextNode('continuous shower!'));

  c.style.border = 'solid 1px #3369ff';
  c.style.display = 'block';
  c.width = 700;
  c.height = 550;
  c.shouldClear = function() { return clr.checked; };

  d.body.appendChild(c);
  d.body.appendChild(els);
  d.body.appendChild(b);
  d.body.appendChild(clrl);
  d.body.appendChild(cntl);

  setInterval(function() {
    if (cnt.checked) {
       addBall();
    }
  }, 333);

  return c;
}

// start:
var objs = [],
    c = createCanvasWithControls(objs),
    ctx = c.getContext('2d'),
    fps = 30, // target frames per second
    ppm = 20, // pixels per meter
    g = 9.8, // m/s^2 - acceleration due to gravity
    t = new Date().getTime();

// add the floor:
objs.push(new FloorObject(c.height - 10));

// as expando so its accessible in draw [this overides .scale(x,y)]
ctx.scale = 0.5; 
ctx.fillStyle = 'rgb(100,200,255)';
ctx.strokeStyle = 'rgb(33,69,233)';
ctx.transform(ctx.scale, 0, 0, ctx.scale, c.width / 2, c.height / 2);

setInterval(function() {

  var i, j,
      nw = c.width / ctx.scale,
      nh = c.height / ctx.scale,
      nt = new Date().getTime(),
      dt = (nt - t) / 1000;

  if (c.shouldClear()) {
    ctx.clearRect(nw / -2, nh / -2, nw, nh);
  }

  for (i = 0; i < objs.length; i++) {

    // if a ball > viewport width away from center remove it
    while (objs[i].p.x < -nw || objs[i].p.x > nw) { 
      objs.splice(i, 1);
    }

    objs[i].update(g, dt, ppm, objs, i);

    for (j = i + 1; j < objs.length; j++) {
      objs[j].collide(objs[i]);
    }

    objs[i].draw(ctx);
  }

  t = nt;

}, 1000 / fps);

the real "meat" and the origin for this discussion is the obj.collide(obj) method.

if we dive in (I commented it this time as it is much more complex than the "last"), you'll see that this equation: , is still the only one being used in this line: this.v.tx(dn.multiply((cr * obj.m * (v2 - v1) + this.m * v1 + obj.m * v2) / sm)); now I'm sure you're still saying: "zomg wtf! that's the same single dimension equation!" but when you stop and think about it a "collision" only ever happens in a single dimension. Which is why we use vector equations to extract the applicable components and apply the collisions only to those specific parts leaving the others untouched to go on their merry way (ignoring friction and simplifying the collision to not account for dynamic energy transforming forces as described in the comments for CR). This concept obviously gets more complicated as the object complexity grows and number of scene data points increases to account for things like deformity, rotational inertia, uneven mass distribution and points of friction... but that's so far beyond the scope of this it's almost not worth mentioning..

Basically, the concepts you really need to "grasp" for this to feel intuitive to you are the basics of Vector equations (all located in the Vector prototype), how they interact with each (what it actually means to normalize, or take a dot/scalar product, eg. reading/talking to someone knowledgeable) and a basic understanding of how collisions act on properties of an object (mass, speed, etc... again, read/talk to someone knowledgeable)

I hope this helps, good luck! -ck

这篇关于基于质量和弹跳系数计算球与球碰撞的速度和方向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆