Прыгающий мяч не соответствует правилу сохранения энергии - PullRequest
3 голосов
/ 11 мая 2009

В настоящее время я занимаюсь написанием небольшого физического движка для моего курса по программированию в Win32 API и c ++. Я закончил рендерер GDI Backbuffer и весь графический интерфейс (еще несколько вещей для настройки), но я очень близок к завершению. Единственные большие препятствия, которые длятся в последний раз, - это столкновение мяча с мячом (но я могу это исправить самостоятельно), но самая большая проблема из них - это отскок мячей. Что происходит, когда я бросаю мяч, и он действительно падает, но как только он отскакивает, он будет подпрыгивать выше, чем точка, в которой я его выпустил ??? Самое смешное, что это происходит только если ниже определенной высоты. Эта часть является физическим кодом: (Если вам нужно больше кода или объяснения, пожалуйста, спросите, но я был бы очень признателен, если бы вы, ребята, могли взглянуть на мой код.)

#void RunPhysics(OPTIONS &o, vector<BALL*> &b)
{ 
    UINT simspeed = o.iSimSpeed;
    DOUBLE  DT; //Delta T
    BOOL bounce; //for playing sound

    DT= 1/o.REFRESH;

    for(UINT i=0; i<b.size(); i++)
    {
        for(UINT k=0; k<simspeed; k++)
        {           
            bounce=false;

            //handle the X bounce
            if( b.at(i)->rBall.left <= 0 && b.at(i)->dVelocityX < 0 ) //ball bounces against the left wall
            {
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            else if( b.at(i)->rBall.right >= SCREEN_WIDTH && b.at(i)->dVelocityX > 0) //ball bounces against the right wall
            {           
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            //handle the Y bounce
            if( b.at(i)->rBall.bottom >= SCREEN_HEIGHT && b.at(i)->dVelocityY > 0 ) //ball bounces against the left wall
            {
                //damping of the ball
                if(b.at(i)->dVelocityY < 2+o.dGravity/o.REFRESH)
                {
                    b.at(i)->dVelocityY = 0;
                }

                //decrease the Velocity of the ball according to the bouncecof
                b.at(i)->dVelocityY = b.at(i)->dVelocityY * -1*b.at(i)->dBounceCof;
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * b.at(i)->dBounceCof;

                bounce=true;
            }


            //gravity
            b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
            b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 
            //METER IS DEFINED GLOBALLY AS 100 which is the amount of pixels in a meter

            b.at(i)->pOrigin.x += b.at(i)->dVelocityX/o.REFRESH*METER; 

            b.at(i)->UpdateRect();
        }
    }
    return;
}

Ответы [ 9 ]

4 голосов
/ 11 мая 2009

Вы используете метод интеграции Эйлера. Возможно, ваш шаг по времени (DT) слишком велик. Также кажется, что в строке, которая обновляет координату Y, есть ошибка:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 

Вы уже добавили гравитацию к скорости, поэтому вам не нужно добавлять ее в позицию, и вы не умножаете скорость на DT. Должно быть так:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY * DT; 

Кроме того, кажется, что существует некоторая путаница относительно единиц (способ использования METER).

1 голос
/ 13 мая 2009

ОТВЕТ !! ОТВЕТ !! ОТВЕТ !! но я забыл свой другой аккаунт, поэтому не могу пометить его: - (

Спасибо за все замечательные ответы, это мне очень помогло! Ответы, которые вы дали, были действительно правильными, пара моих формул была неправильной, и некоторая оптимизация кода могла быть выполнена, но ни один из них не был действительно решением проблемы. Так что я просто сел с листом бумаги и начал вычислять каждое значение, полученное от моей программы вручную, заняло у меня два часа: O Но я действительно нашел решение своей проблемы: Проблема в том, что когда я обновляю свою скорость (с исправленным кодом), я получаю десятичное значение, никаких проблем нет. Позже я увеличиваю позицию в Y, добавляя скорость, умноженную на Delta T, что является очень малым значением. Результатом является очень небольшое значение, которое необходимо добавить. Теперь проблема в том, что если вы рисуете Elipse () в Win32, точка будет длинной, поэтому все десятичные значения будут потеряны. Это означает, что только после очень долгого периода, когда скорость значений начинает выходить из десятичных значений, что-то происходит, и что наряду с этим, чем выше вы бросаете мяч, тем лучше результаты (один из моих симптомов). эта проблема была действительно простой, добавив дополнительное значение DOUBLE для моего класса Ball, которое содержало истинную позицию (включая десятичные дроби) моего шара. Во время RenderFrame () вы просто берете значение пола или потолка double, чтобы нарисовать эллипс, но для всех расчетов вы используете значение Double. Еще раз большое спасибо за все ваши ответы, STACKOVERFLOW PEOPLE ROCK !!!

1 голос
/ 11 мая 2009

Когда вы вызываете RunPhysics? В цикле таймера? Этот код является лишь приблизительным и не точный расчет. За короткий интервал delta t шар уже поменял свою позицию и скорость, что не учитывается в вашем алгоритме и приводит к небольшим ошибкам. Вам нужно будет рассчитать время, пока мяч не упадет на землю, и предсказать изменения.

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

b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

Кстати: сохраните b.at (i) во временной переменной, чтобы вам не приходилось пересчитывать его в каждой строке.

Ball* CurrentBall = b.at(i);
1 голос
/ 11 мая 2009

Хорошо, несколько вещей здесь.

У вас есть разные пути кода для отскока от левой стены и от правой стены, но код тот же. Объедините эти пути кода, так как код один и тот же.

Что касается вашей основной проблемы: я подозреваю, что ваша проблема связана с тем, что вы применяете гравитацию после того, как применяете какие-либо демпфирующие силы / силы отскока.

0 голосов
/ 12 мая 2009

При моделировании твердого тела вам нужно запустить интегрирование до момента столкновения, затем отрегулировать скорости, чтобы избежать проникновения при столкновении, и затем возобновить интегрирование. Это своего рода мгновенный удар, чтобы скрыть тот факт, что твердые тела являются приближением. (Настоящий шар деформируется во время столкновения. Его сложно моделировать, и он не нужен для большинства целей.)

Вы объединяете эти два шага (объединение сил и разрешение столкновений). Для простого моделирования, как вы показали, вероятно, достаточно пропустить бит гравитации на любой итерации, где вы обрабатывали вертикальный отскок.

В более продвинутой симуляции вы должны разделить любой интервал (dt), который содержит столкновение, на фактический случай столкновения. Интегрируйте до столкновения, затем разрешите столкновение (регулируя скорость), а затем интегрируйте до конца интервала. Но это выглядит излишним для вашей ситуации.

0 голосов
/ 11 мая 2009

Вам необходимо пересчитать свою позицию на отскоке, чтобы убедиться, что вы отскочили от правильного места на стене.

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

W.r.t. шаг по времени, вы можете проверить мой ответ здесь .

0 голосов
/ 11 мая 2009

Спасибо за быстрые ответы !!! Извините, я должен был быть более ясным, RunPhysics запускается после PeekMessage. Я также добавил ограничитель кадров, который гарантирует, что в секунду не выполняется больше вычислений, чем частота обновления монитора. Поэтому моя длительность составляет 1 секунду, деленная на частоту обновления. Может быть, мой DT на самом деле слишком мал для вычисления, хотя это двойное значение ??? Моя сумма реституции регулируется, но начинается с 0,9

0 голосов
/ 11 мая 2009

Я не думаю, что ваше уравнение для позиции правильное:

 b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;

Это v=v0+gt - это выглядит нормально, хотя я бы написал dGravity*DT вместо dGravity/REFRESH_FREQ.

 b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 

Но, похоже, это не так: это эквивалентно p = p0+v + 1/2gt^2.

  • Вы должны умножить скорость * время, чтобы получить правильные единицы
  • Вы масштабируете член гравитации на пиксели / метр, но не на член скорости. Так что это должно быть умножено на METER также
  • Вы уже учли эффект гравитации при обновлении скорости, поэтому вам не нужно снова добавлять член гравитации.
0 голосов
/ 11 мая 2009

Если ваш dBounceCof> 1, тогда ваш мяч отскочит выше. У нас нет всех значений, чтобы ответить на ваш вопрос.

...