Прыгающий мяч, не соответствующий правилу сохранения энергии

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

#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;
}

person Community    schedule 11.05.2009    source источник
comment
DT установлен на 1 / o.REFRESH и позже используется в o.REFRESH * DT, поэтому я предполагаю, что в этом есть какая-то логическая ошибка, или вы можете просто удалить ее.   -  person schnaader    schedule 11.05.2009


Ответы (9)


Вы используете метод интегрирования Эйлера. Возможно, ваш временной шаг (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; 

Кроме того, похоже, существует некоторая путаница в отношении единиц измерения (способ использования СЧЕТЧИКА).

person TrayMan    schedule 11.05.2009
comment
Никогда не используйте метод Эйлера с отскоком столкновения. При обнаружении столкновения немедленно пересчитайте скорость с сохранением количества движения и новой позиции. - person Joshua; 11.05.2009
comment
Если я использую ваш метод, мяч вообще не движется в направлении y, в любом случае спасибо - person ; 11.05.2009
comment
@Joshua: Я лично использовал (с большим успехом) сегментацию с переменным временем. Пока вы не сталкиваетесь с вычислительными пределами, это работает как шарм. - person Paul Sonier; 12.05.2009
comment
@Rik: Вероятно, мяч движется слишком медленно, чтобы его можно было увидеть, потому что координата Y никогда не умножается на МЕТР. Вы должны вычислить координаты в метрах, а затем преобразовать их отдельно в пиксели. Это делает код более понятным. - person TrayMan; 12.05.2009

Хорошо, здесь есть кое-что.

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

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

person Paul Sonier    schedule 11.05.2009

Когда вы звоните в RunPhysics? В цикле таймера? Этот код является приблизительным, а не точным. За короткий интервал дельты 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);
person Dario    schedule 11.05.2009

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

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

person Rik Nauta    schedule 12.05.2009

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

person MickaelFM    schedule 11.05.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.

  • Вы должны умножить скорость на время, чтобы получить правильные единицы
  • Вы масштабируете параметр гравитации на пиксели / метр, но не параметр скорости. Так что это следует также умножить на МЕТР.
  • Вы уже учли эффект гравитации при обновлении скорости, поэтому вам не нужно снова добавлять член гравитации.
person AShelly    schedule 11.05.2009

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

person Community    schedule 11.05.2009
comment
Значение dt должно работать, если оно находится между 50 мс и 2 мкс. Попробуйте уменьшить его, чтобы сделать вашу симуляцию более точной, но более тяжелой по циклам процессора. Если вы используете Эйлера, как вы, возможно, вы захотите использовать dt 2-10 мс. - person Macke; 11.05.2009

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

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

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

person Macke    schedule 11.05.2009
comment
У меня таймер именно такой: P в зависимости от обновления монитора меняется временной интервал. И я не возражаю, чтобы он отскакивал от стены, поскольку это был такой короткий промежуток времени, и это не должно влиять на отскок задницы, точка, в которой он отскакивает (за стену), должна быть компенсирована снижением скорости снова. - person ; 11.05.2009
comment
У вас есть верхняя граница вашего dt? Как максимум 0,05 с? Вы пробовали запускать цикл вычислений несколько раз за кадр? (Как 10 или 50) - person Macke; 11.05.2009

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

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

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

person Adrian McCarthy    schedule 11.05.2009
comment
Спасибо, причина, по которой я хочу иметь гравитацию, заключается в том, что моя цель - сохранить все настраиваемым, я не возражаю, если это приближение, пока эффект проявляется. Не могли бы вы показать какой-нибудь пример того, как вы рассчитываете траекторию? - person ; 12.05.2009