2012-02-24 1 views
6

Я использовал следующий код, основанный на 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); 

но это явно не так, как формула предназначена для одномерных столкновения.

Поэтому я попытался использовать приведенную ниже формулу от this section.

Но проблема в том, что я не знаю, что угол отклонения и как вычислить его. Также, как принять во внимание коэффициент подпрыгивания в этой формуле?

Редактировать: Возможно, я не был чист. Вышеприведенный код делает, хотя это может и не быть ожидаемым поведением, поскольку исходная формула предназначена для 1D-столкновений. Проблемы, я стараюсь, следовательно, являются:

  • Что такое 2D эквивалент?
  • Как принять во внимание коэффициент отскока ?
  • Как рассчитать направление (которое выражается с v х и против у) двух шаров после столкновения?
+0

Вы, кажется, используете эластичные формулы столкновения, когда и если вы собираетесь беспокоиться о «прыгающих коэффициентах», вы хотите посмотреть на неупругие столкновения. http://en.wikipedia.org/wiki/Inelastic_collision как уравнения вашего используя эту концепцию, поскольку они «прекрасно поддерживают всю энергию системы» – ckozl

+0

Оказывается, вы правы. Но теперь, как применить данную формулу к двумерному столкновению? – seriousdev

+0

в ответ на редактирование: нет такой вещи, как «двумерные эквивалентные» столкновения линейны по своей природе – ckozl

ответ

3

здесь демо-неупругого уравнения столкновений в действии, выполненное на заказ для вас:

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); 

, чтобы увидеть его в действии самостоятельно, просто зайдите сюда: http://jsbin.com/iwuxol/edit#javascript,live

Это использует это уравнение : enter image description here

и так как ваш «пол» не двигается, вам нужно только учитывать влияние на скорость мяча. учти есть довольно много ярлыков и просчеты здесь, так что это очень примитивный физический движок, и в основном предназначены для иллюстрации этого одно уравнение ...

надеюсь, что это помогает -ck

+0

Спасибо за всю работу! Но я думаю, вы пропустили суть: как насчет шаров к мячам? – seriousdev

+0

@seriousdev, честно говоря, я думаю, что у вас много чтения перед вами. но он ломается так, что объекты только когда-либо взаимодействуют друг с другом по скоростям на одном преобразованном множестве оси, случай, который я проиллюстрировал выше, является самым простым. один объект должен быть [почти] не затронут столкновением, а другой действует непосредственно на оси y. И, пожалуйста, не говорите мне, что я «пропустил дело», вы просто не задаете правильных вопросов. Вы должны думать о угловых скоростях в терминах своих векторных компонентов, и вы должны векторизовать свои скорости, поэтому [..cont ..] – ckozl

+0

.. столкновение происходит против одной оси, что означает вращение системы псевдо координат, скорость, влияющая на результат удара, лежит на одной оси. если я получаю время, возможно, я мог бы дать вам более сложную, но все же легко следовать демо ...в то же время, подумайте об этом так: формула не «предназначена для одномерных столкновений» - это просто, что столкновение происходит только в одном измерении, игнорируя трение, воздействие не влияет на какое-либо другое измерение независимо от угла от падения ... – ckozl

1

Я настоятельно рекомендую вам ознакомиться самостоятельно с center of momentum frame. Он вызывает столкновения много проще понять. (И без этого понимания вы просто манипулируете загадочными уравнениями, и вы никогда не узнаете, почему все пошло не так.)

В любом случае, чтобы определить угол, вы можете использовать параметр удара, в основном, как далеко «от центра» мяч попадает в другую. Два шара приближаются друг к другу в противоположных направлениях (в раме центра-импульса), а расстояние между их центрами , перпендикулярным этим скоростям, является параметром удара h. Тогда угол отклонения составляет 2 acos (h/(r + r)).

Как только вы получите эту работу отлично, вы можете беспокоиться об неупругих столкновениях и коэффициенте реституции.

11

я должен начать, говоря: я создал новый ответ, потому что я чувствую, что старый один имеет значение для его простоты

, как было обещано здесь является гораздо более сложной физический движок, но я все еще чувствую, что это достаточно просто следовать (! надеюсь, или я просто впустую мое время ... лол) (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); 

реальное «мясо» и происхождение этого обсуждения является obj.collide(obj) метод.

Если мы погружаемся (я прокомментировал это на этот раз, поскольку он намного сложнее, чем «последний»), вы увидите, что это уравнение: equation for inelastic collision, по-прежнему остается единственным, используемым в этой строке: this.v.tx(dn.multiply((cr * obj.m * (v2 - v1) + this.m * v1 + obj.m * v2)/sm)); сейчас Я уверен, что вы все еще говорите: «zomg wtf! Это одно и то же одномерное уравнение!», но когда вы останавливаетесь и думаете об этом, «столкновение» происходит только в одном измерении. Именно поэтому мы используем векторные уравнения для извлечения применимых компонентов и применяем коллизии только к тем конкретным частям, оставляя другие нетронутыми, чтобы идти своим весельем (игнорируя трение и упрощая столкновение, чтобы не учитывать динамические силы преобразования энергии, как описано в комментарии для CR). Эта концепция, очевидно, усложняется по мере роста сложности объекта и увеличения количества точек данных сцены, чтобы учитывать такие вещи, как деформация, вращательная инерция, неравномерное распределение массы и точки трения ... но это пока что выходит за рамки этого. Стоит упомянуть.

В принципе, понятия, которые вам действительно нужны, чтобы «понять», чтобы это было интуитивно понятным для вас, - это основы уравнений вектора (все они расположены в прототипе Vector), как они взаимодействуют с каждым (что на самом деле означает нормализовать или взять точечно-скалярный продукт, например, читать/разговаривать с кем-то знающим) и базовое понимание того, как столкновения воздействуют на свойства объекта (масса, скорость и т. д.) снова, читать/разговаривать с кем-то знающий)

Надеюсь, это помогает, удачи! -ck

Смежные вопросы