Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ajax/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 基于质量和反弹系数计算球对球碰撞的速度和方向_Javascript_Html_Canvas_Physics - Fatal编程技术网

Javascript 基于质量和反弹系数计算球对球碰撞的速度和方向

Javascript 基于质量和反弹系数计算球对球碰撞的速度和方向,javascript,html,canvas,physics,Javascript,Html,Canvas,Physics,我使用了以下基于 但很明显,这个公式并不适用于一维碰撞 所以我试着用下面的公式 但问题是我不知道偏转角度是什么,也不知道如何计算。另外,在这个公式中如何考虑反弹系数 编辑:我可能不清楚。上述代码确实有效,但可能不是预期的行为,因为原始公式是为1D碰撞设计的。因此,我试图解决的问题是: 什么是2D等价物 如何考虑反弹系数 如何计算碰撞后两个球的方向(用vx和vy表示) 这是一个非弹性碰撞方程的演示,为您定制: function BallObject(elasticity) { this.v

我使用了以下基于

但很明显,这个公式并不适用于一维碰撞

所以我试着用下面的公式

但问题是我不知道偏转角度是什么,也不知道如何计算。另外,在这个公式中如何考虑反弹系数

编辑:我可能不清楚。上述代码确实有效,但可能不是预期的行为,因为原始公式是为1D碰撞设计的。因此,我试图解决的问题是:

  • 什么是2D等价物
  • 如何考虑反弹系数
  • 如何计算碰撞后两个球的方向(用vx和vy表示)

    • 这是一个非弹性碰撞方程的演示,为您定制:

      function BallObject(elasticity) {
        this.v = { x: 1, y: 20 }; // velocity: m/s^2
        this.m = 10; // mass: kg
        this.p = { x: 40, y: 0}; // position
        this.r = 15; // radius of obj
        this.cr = elasticity; // elasticity
      }
      
      function draw(obj) {
        ctx.beginPath();
        ctx.arc(obj.p.x, obj.p.y, obj.r, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      }
      
      function collide(obj) {
        obj.v.y = (obj.cr * floor.m * -obj.v.y + obj.m * obj.v.y) / (obj.m + floor.m);
      }
      
      function update(obj, dt) {
      
        // over-simplified collision detection
        // only consider the floor for simplicity
        if ((obj.p.y + obj.r) > c.height) { 
           obj.p.y = c.height - obj.r;
           collide(obj);
        }
      
        obj.v.y += g * dt;
        obj.p.x += obj.v.x * dt * ppm;
        obj.p.y += obj.v.y * dt * ppm;
      }
      
      var d = document,
          c = d.createElement('canvas'),
          b = d.createElement('button'),
          els = d.createElement('input'),
          clr = d.createElement('input'),
          clrl = d.createElement('label'),
          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
          objs = [],
          floor = {
            v: { x: 0, y: 0 }, // floor is immobile
            m: 5.9722 * Math.pow(10, 24) // mass of earth (probably could be smaller)
          },
          t = new Date().getTime();
      
      b.innerHTML = 'add ball with elasticity: <span>0.70</span>';
      b.onclick = function() { objs.push(new BallObject(els.value / 100)); };
      
      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 = 'checkbox';
      clr.checked = true;
      
      clrl.appendChild(clr);
      clrl.appendChild(d.createTextNode('clear each frame'));
      
      c.style.border = 'solid 1px #3369ff';
      c.style.borderRadius = '10px';
      c.style.display = 'block';
      c.width = 400;
      c.height = 400;
      
      ctx.fillStyle = 'rgb(100,200,255)';
      ctx.strokeStyle = 'rgb(33,69,233)';
      
      d.body.appendChild(c);
      d.body.appendChild(els);
      d.body.appendChild(b);
      d.body.appendChild(clrl);
      
      setInterval(function() {
      
        var nt = new Date().getTime(),
            dt = (nt - t) / 1000;
      
        if (clr.checked) {
          ctx.clearRect(0, 0, c.width, c.height);
        }
      
        for (var i = 0; i < objs.length; i++) {
          update(objs[i], dt);
          draw(objs[i]);
        }
      
        t = nt;
      
      }, 1000 / fps);
      
      函数对象(弹性){
      this.v={x:1,y:20};//速度:m/s^2
      此值为.m=10;//质量:kg
      this.p={x:40,y:0};//位置
      this.r=15;//物体的半径
      this.cr=弹性;//弹性
      }
      功能图(obj){
      ctx.beginPath();
      ctx.arc(obj.p.x,obj.p.y,obj.r,0,2*Math.PI);
      ctx.closePath();
      ctx.stroke();
      ctx.fill();
      }
      函数碰撞(obj){
      obj.v.y=(obj.cr*floor.m*-obj.v.y+obj.m*obj.v.y)/(obj.m+floor.m);
      }
      功能更新(obj、dt){
      //过简化碰撞检测
      为简单起见考虑地板
      如果((obj.p.y+obj.r)>c.height){
      obj.p.y=c.高度-obj.r;
      碰撞(obj);
      }
      目标v.y+=g*dt;
      obj.p.x+=obj.v.x*dt*ppm;
      obj.p.y+=obj.v.y*dt*ppm;
      }
      var d=文件,
      c=d.createElement('canvas'),
      b=d.createElement(“按钮”),
      els=d.createElement(“输入”),
      clr=d.createElement('input'),
      clrl=d.createElement('label'),
      ctx=c.getContext('2d'),
      fps=30,//每秒目标帧数
      ppm=20,//每米像素
      g=9.8,//m/s^2-重力引起的加速度
      objs=[],
      楼层={
      v:{x:0,y:0},//地板不动
      m:5.9722*Math.pow(10,24)//地球的质量(可能更小)
      },
      t=新日期().getTime();
      b、 innerHTML='添加弹性球:0.70';
      b、 onclick=function(){objs.push(newballobObject(els.value/100));};
      els.type=‘范围’;
      els.min=0;
      els.max=100;
      els.step=1;
      els.value=70;
      els.style.display='block';
      els.onchange=函数(){
      b、 getElementsByTagName('span')[0].innerHTML=(this.value/100).toFixed(2);
      };
      clr.type='复选框';
      clr.checked=true;
      clrl.appendChild(clr);
      clrl.appendChild(d.createTextNode('clear each frame'));
      c、 style.border='solid 1px#3369ff';
      c、 style.borderRadius='10px';
      c、 style.display='block';
      c、 宽度=400;
      c、 高度=400;
      ctx.fillStyle='rgb(100200255)';
      ctx.strokeStyle='rgb(33,69233)';
      d、 附肢儿童(c);
      d、 附肢儿童(els);
      d、 儿童尸体(b);
      d、 附体子体(clrl);
      setInterval(函数(){
      var nt=new Date().getTime(),
      dt=(nt-t)/1000;
      如果(清除选中){
      ctx.clearRect(0,0,c.宽度,c.高度);
      }
      对于(var i=0;i
      要亲眼看到它的实际应用,请点击此处:

      这利用了这个等式:

      因为你的“地板”不动,你只需要考虑对球Y速度的影响。请注意,这里有很多捷径和疏忽,所以这是一个非常原始的物理引擎,主要是为了说明这个方程


      希望这能有所帮助-ck

      我强烈建议您熟悉。它使碰撞更容易理解。(如果没有这种理解,你只是在操纵神秘的方程式,你永远不会知道为什么事情会出错。)

      无论如何,要确定角度,可以使用碰撞参数,即一个球与另一个球的“偏离中心”距离。两个球以相反的方向(在动量系的中心)相互接近,它们的中心与这些速度垂直的距离是碰撞参数
      h
      。那么偏转角为2 acos(h/(r1+r2))


      一旦你能很好地工作,你就可以担心非弹性碰撞和恢复系数了。

      我应该说:我创造了一个新的答案,因为我觉得旧的答案因为简单而有价值

      正如这里承诺的那样,这是一个更加复杂的物理引擎,但我仍然觉得它足够简单,可以遵循(希望如此!或者我只是浪费了我的时间…哈哈),(url:)

      函数向量(x,y){ 这个.x=x; 这个。y=y; } Vector.prototype.dot=函数(v){ 返回此.x*v.x+此.y*v.y; }; Vector.prototype.length=函数(){ 返回Math.sqrt(this.x*this.x+this.y*this.y); }; Vector.prototype.normalize=函数(){ var s=1/this.length(); 这个.x*=s; 这个.y*=s; 归还这个; }; Vector.prototype.multiply=函数{ 返回新向量(this.x*s,this.y*s); }; Vector.prototype.tx=函数(v){ 这个.x+=v.x; 这个.y+=v.y; 归还这个; }; 功能对象(弹性、vx、vy){ this.v=新向量(vx | | 0,vy | | 0);//速度:m/s^2 此值为.m=10;//质量:kg this.r=15;//物体的半径 this.p=新向量(0,0);//位置 this.cr=弹性;//弹性 } balobject.prototype.draw=函数(ctx){ ctx.beginPath(); ctx.arc(this.p.x,this.p.y,this.r,0,2*Math.PI); ctx.closePath(); ctx.fill(); ctx.stroke(); }; balobject.prototype.update=函数(g,dt,ppm){ 这个.v.y+=g*dt; 这个.p.x+=这个.v.x*dt*ppm; 这个.p.y+=这个.v.y*dt*ppm; }; balobject.prototype.collide=函数(obj){ 变量dt、mT、v1、v2、cr、sm、, dn
      function BallObject(elasticity) {
        this.v = { x: 1, y: 20 }; // velocity: m/s^2
        this.m = 10; // mass: kg
        this.p = { x: 40, y: 0}; // position
        this.r = 15; // radius of obj
        this.cr = elasticity; // elasticity
      }
      
      function draw(obj) {
        ctx.beginPath();
        ctx.arc(obj.p.x, obj.p.y, obj.r, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      }
      
      function collide(obj) {
        obj.v.y = (obj.cr * floor.m * -obj.v.y + obj.m * obj.v.y) / (obj.m + floor.m);
      }
      
      function update(obj, dt) {
      
        // over-simplified collision detection
        // only consider the floor for simplicity
        if ((obj.p.y + obj.r) > c.height) { 
           obj.p.y = c.height - obj.r;
           collide(obj);
        }
      
        obj.v.y += g * dt;
        obj.p.x += obj.v.x * dt * ppm;
        obj.p.y += obj.v.y * dt * ppm;
      }
      
      var d = document,
          c = d.createElement('canvas'),
          b = d.createElement('button'),
          els = d.createElement('input'),
          clr = d.createElement('input'),
          clrl = d.createElement('label'),
          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
          objs = [],
          floor = {
            v: { x: 0, y: 0 }, // floor is immobile
            m: 5.9722 * Math.pow(10, 24) // mass of earth (probably could be smaller)
          },
          t = new Date().getTime();
      
      b.innerHTML = 'add ball with elasticity: <span>0.70</span>';
      b.onclick = function() { objs.push(new BallObject(els.value / 100)); };
      
      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 = 'checkbox';
      clr.checked = true;
      
      clrl.appendChild(clr);
      clrl.appendChild(d.createTextNode('clear each frame'));
      
      c.style.border = 'solid 1px #3369ff';
      c.style.borderRadius = '10px';
      c.style.display = 'block';
      c.width = 400;
      c.height = 400;
      
      ctx.fillStyle = 'rgb(100,200,255)';
      ctx.strokeStyle = 'rgb(33,69,233)';
      
      d.body.appendChild(c);
      d.body.appendChild(els);
      d.body.appendChild(b);
      d.body.appendChild(clrl);
      
      setInterval(function() {
      
        var nt = new Date().getTime(),
            dt = (nt - t) / 1000;
      
        if (clr.checked) {
          ctx.clearRect(0, 0, c.width, c.height);
        }
      
        for (var i = 0; i < objs.length; i++) {
          update(objs[i], dt);
          draw(objs[i]);
        }
      
        t = nt;
      
      }, 1000 / fps);
      
      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);