Математическая ошибка в формуле восстановления физики - PullRequest
4 голосов
/ 14 августа 2011

Я признаю это. Я математический дурак. я должен пытаться это дома? Вероятно, не без серьезных травм. Тем не мение. Любой, кто имеет небольшой опыт в играх и физике, может пересмотреть этот код и понять, почему в прототипе есть встроенная «потеря» скорости.

концепция: актер падает с неба, в какой-то момент пересекает границу (trampoline.y), после которой гравитация (в данном примере значение 1,6) перестает работать и коэффициент восстановления (в данном случае значение - 6) вступает во владение.

Прямо сейчас я пытаюсь создать ситуацию с нулевой суммой, когда у вас будет та же скорость, что и при прыжке на батуте (hero.y> = trampoline.y). дело, как показывают следы.

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

Я отправляю следующий код на ваш обзор:

var newV:Number;
if (hero.y < trampoline.y) { newV = hero.velocityY + gravityAccelerationInPixels }
else { newV = hero.velocityY + TrampolineActor.SPRING_ACCELERATION }

var newY:Number = hero.y + newV;
trace("Coming in: y=" + hero.y + " oldVelocity=" + hero.velocityY + " newVelocity=" + newV + " Change in V: " + (newV - hero.velocityY) + ". Testing for newY=" + newY);

if ((hero.y < trampoline.y && newY < trampoline.y) || (hero.y >= trampoline.y && newY >= trampoline.y)) {
    hero.y = newY;
    hero.velocityY = newV;
} else {
    trace("SPLIT");

    var percent:Number = (trampoline.y - hero.y) / newV; trace("Percent: " + percent);
    var newVV:Number;
    if (hero.y < trampoline.y) {
        // going down!
        newVV = hero.velocityY + percent * gravityAccelerationInPixels; trace("New velocity before split: " + hero.velocityY + " Change in V: " + (newVV - hero.velocityY));
        newVV += (1 - percent) * TrampolineActor.SPRING_ACCELERATION; trace("Percent after split: " + (1 - percent) + " Change in V: " + (newVV - hero.velocityY));
    } else {
        // movin on up!
        newVV = hero.velocityY + percent * TrampolineActor.SPRING_ACCELERATION; trace("New velocity before split: " + hero.velocityY + " Change in V: " + (newVV - hero.velocityY));
        newVV += (1 - percent) * gravityAccelerationInPixels; trace("Percent after split: " + (1 - percent) + " Change in V: " + (newVV - hero.velocityY));
    }
    trace("New velocity: " + newVV + " Change in V: " + (newVV - hero.velocityY));

    hero.velocityY = newVV;

    hero.y += hero.velocityY;
}

Дополнительная информация: Начало экрана вверху слева, поэтому y-- = выше Мой тик в настоящее время установлен на 33 мс gravityAccelerationInPixels в настоящее время 1,62 (разумно произвольно) SPRING_ACCELERATION произвольно равен 6, но я надеюсь настроить это число, чтобы контролировать, как далеко ниже батута персонаж может пройти во время цикла замедления / поглощения.

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

1 Ответ

14 голосов
/ 15 августа 2011

Вот несколько методов отладки для этого типа симуляции:

  • Во-первых: убедитесь, что вы знаете, в каких единицах измерения находятся все ваши переменные и константы. Например, если hero.y - это расстояние в пикселей , то, поскольку оно регулируется с помощью hero.y + newV, должно быть, что newV также в пикселей ; за исключением того, что предполагается, что это скорость (расстояние за время), поэтому на самом деле она должна быть pixels per frame (где временной шаг неявен, потому что вы делаете это один раз за кадр).

    Для следующих шагов отладки было бы лучше убедиться, что все ваши значения выражены в «в секунду», а не «в кадре»; Для этого введите значение, равное вашему временному шагу в секундах на кадр. Тогда у вас есть hero.y + newV * timestep и аналогично newV = hero.velocityY + gravityAccelerationInPixelsPerSecondSquared * timestep.

    Это нормально для вычисления gravityAccelerationInPixelsPerSecondSquared * timestep как константы само по себе, но убедитесь, что все ваши жестко запрограммированные значения не зависят от вашего временного шага, чтобы вы могли легко:

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

  • Попробуйте вычислить общую энергию вашей системы. То есть сумма

    • кинетическая энергия любых движущихся тел, 1/2 · м · v ² ( m = масса, v = скорость)
    • потенциальная энергия любых падающих тел, м · г · ч ( г = гравитационная постоянная ускорения, ч = высота над произвольной контрольной точкой)
    • потенциальная энергия любых пружин, 1/2 · k · d ² ( k = постоянная пружины, надеюсь равная вашему «коэффициенту восстановления» (но положительному), d = расстояние, смещенное от расслабленного состояния)

    Если вы позволите ч = hero.y - trampoline.y, то для вашей конкретной системы у вас будет

    E = 1/2 · м · В ² + м · г · макс ( ч , 0) + 1 / 2 · k · (мин ( ч , 0)) ²

    (Из моего прочтения вашего кода гравитация не применяется, пока персонаж находится на батуте; если это так, измените max ( h , 0) на h . )

    Убедитесь, что вы делаете это в постоянных размерах (вы не можете добавить пикселей в секунду до пикселей ) и единицах (вы не можете добавить пикселей в секунду * От 1088 * до пикселей на кадр или дюймов в секунду ). Не забывайте, что квадраты тоже квадратики юнитов.

    Для вашей заявленной цели - персонажа, отскакивающего с той же скоростью, - у вас есть система без трения , и поэтому энергия системы должна быть одинаковой до, во время и после отскока. Если энергия меняется, то обратите внимание , когда она меняется. Например:

    • Если он меняется, когда персонаж падает, то ваша симуляция этого случая, постоянного ускорения, неверна. Распространенная ошибка (которая выглядит так, как будто вы допустили ее в коде) - забыть о третьем члене формулы для позиции при постоянном ускорении : newPos = pos + velocity * timestep + 1/2 * acceleration * timestep^2, где ускорение это гравитация. (Также важно, чтобы вы обновили позицию до , чтобы обновить скорость, чтобы вы не использовали «завтрашнюю» скорость со «вчерашней» позицией в расчете положения.) Попробуйте имитировать отскок на твердой поверхности. вместо батута для упрощения ситуации.

    • Если он меняется точно, когда персонаж касается батута или покидает его, ваши вычисления percent не достигают своей цели.

...