Экстраполяция разрывов обнаружения столкновений

10

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

Вот как все работало до экстраполяции:

введите описание изображения здесь

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

После того, как я применяю свою экстраполяцию

введите описание изображения здесь

Как исправить это поведение?

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

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

Я нашел пару подобных вопросов здесь, но ответы не помогли мне.

Это мой экстраполяционный код:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

Применение экстраполяции

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

редактировать

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

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

Я попытался иметь флаги для левого / правого и только экстраполяции, если любой из них включен. Я также попытался скопировать последнюю и текущую позиции, чтобы увидеть, есть ли разница. Однако, что касается столкновения, это не помогает.

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

BungleBonce
источник
1
Это займет некоторое усвоение, чтобы полностью понять («интерполяция» имеет, по-видимому, десяток разных значений, и на первый взгляд не совсем понятно, что вы подразумеваете под этим здесь), но мой первый инстинкт - «вы не должны делать что-либо, чтобы повлиять на вашу положение объекта в вашей процедуре рендеринга ». Ваш рендерер должен нарисовать ваш объект в заданной позиции объекта, и манипулирование этим объектом приводит к неприятностям, так как он по своей сути связывает рендерер с физикой игры. В идеальном мире ваш рендерер должен иметь возможность использовать константные указатели для игровых объектов.
Стивен Стадницкий,
Привет @StevenStadnicki, большое спасибо за твой комментарий, есть множество примеров, показывающих, как значение интерполяции передается в средство визуализации, пожалуйста, смотрите это: mysecretroom.com/www/programming-and-software/…, откуда я адаптировал свой код. Мое ограниченное понимание состоит в том, что это интерполирует позицию между последним и следующим обновлениями на основе количества времени, прошедшего с момента последнего обновления - я согласен, что это немного кошмар! Я был бы признателен, если бы вы могли предложить и альтернативу, чтобы мне было легче работать - ура :-)
BungleBonce
6
Что ж, я скажу: «только потому, что вы можете найти код для чего-то, это не делает его лучшей практикой». :-) В этом случае, однако, я подозреваю, что страница, на которую вы ссылались, использует значение интерполяции, чтобы выяснить, где отображать свои объекты, но на самом деле она не обновляет позиции объектов с ними; Вы можете сделать это тоже, просто имея специфическую для рисования позицию, которая рассчитывается каждый кадр, но сохраняя эту позицию отдельно от фактической «физической» позиции объекта.
Стивен Стадницкий,
Привет @StevenStadnicki, как изложено в моем вопросе (абзац, начинающийся с «Я также пытался сделать копию»), я фактически уже пытался использовать позицию «только для рисования» :-) Можете ли вы предложить метод интерполяции, где я не нужно вносить корректировки в положение спрайта в процедуре рендеринга? Спасибо!
BungleBonce
Глядя на ваш код, вы видите, что вы делаете экстраполяцию вместо интерполяции.
Durza007

Ответы:

1

Я не могу оставить комментарий, поэтому я опубликую это как ответ.

Если я правильно понимаю проблему, она выглядит примерно так:

  1. сначала у вас есть столкновение
  2. затем положение объекта корректируется (предположительно, с помощью процедуры обнаружения столкновений)
  3. обновленная позиция объекта отправляется в функцию рендеринга
  4. функция рендеринга затем обновляет местоположение объекта, используя экстраполяцию
  5. положение экстраполированного объекта теперь нарушает процедуру обнаружения столкновений

Я могу придумать 3 возможных решения. Я перечислю их в том порядке, который наиболее желателен, по крайней мере, ИМХО.

  1. Переместите экстраполяцию из функции рендеринга. Экстраполируйте положение объекта и затем проверьте на столкновение.
  2. или если вы хотите сохранить экстраполяцию в функции рендеринга, установите флаг, чтобы обозначить, что произошло столкновение. Пусть обнаружение столкновений исправит положение объекта, как вы уже это делаете, но прежде чем вычислять значение экстраполяции, сначала проверьте флаг столкновения. Поскольку объект уже находится там, где он должен быть, нет необходимости перемещать его.
  3. Последняя возможность, которая для меня является скорее обходным путем, чем исправлением, состоит в том, чтобы сверхкомпенсировать обнаружение столкновений. После столкновения отодвиньте объект от стены, чтобы после экстраполяции объект снова оказался у стены.

Пример кода для # 2.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

Я думаю, что № 2, вероятно, будет самым быстрым и легким для запуска, хотя № 1 представляется более логически точным решением. В зависимости от того, как вы обрабатываете свое дельта-время, решение № 1 может быть практически одинаково разбито большой дельтой, и в этом случае вам, возможно, придется использовать и № 1, и № 2 вместе.

РЕДАКТИРОВАТЬ: я неправильно понял ваш код ранее. Циклы предназначены для максимально быстрого рендеринга и обновления с заданным интервалом. Вот почему вы должны интерполировать позицию спрайта, чтобы обработать случай, когда вы рисуете больше, чем обновление. Однако, если цикл отстает, то вы будете опрашивать об обновлении до тех пор, пока вы не будете захвачены или не пропустите максимальное количество кадров.

При этом единственная проблема заключается в том, что объект движется после столкновения. В случае столкновения объект должен перестать двигаться в этом направлении. Таким образом, если происходит столкновение, установите его скорость равной 0. Это должно остановить функцию рендеринга от дальнейшего перемещения объекта.

Aholio
источник
Привет @Aholio Я попробовал вариант 2, и он работает, но вызывает несколько глюков, может быть, я сделал это неправильно, я еще раз вернусь. Однако я очень заинтересован в варианте 1, хотя я не могу найти информацию о том, как это сделать. Как я могу экстраполировать слова в моей логике? Кстати, моя дельта является фиксированной, поэтому она равна 1/60 или 0,01667 (точнее, это цифра, которую я использую для интеграции, хотя количество времени, которое занимает каждая итерация моей игры, очевидно, может варьироваться в зависимости от происходящего, но никогда не должно превышать 0,01667. ) так что любая помощь по этому вопросу будет большое спасибо.
BungleBonce
Возможно, я не совсем понимаю, что вы делаете. Что-то, что кажется мне немного странным, это то, что вы не только выполняете движения позиции в своей функции рисования; Вы также делаете расчеты времени. Это сделано для каждого объекта? Правильный порядок должен быть: расчет времени производится в коде игрового цикла. Игровой цикл передает дельту в функцию обновления (dt). Обновление (DT) обновит все игровые объекты. Он должен обрабатывать любое движение, экстраполяцию и обнаружение столкновений. После того как update () возвращается в игровой цикл, он вызывает функцию render (), которая рисует все недавно обновленные игровые объекты.
Ахолио
Хммммм, в настоящее время моя функция обновления делает все. Делает ли движение (под движением я имею в виду вычисляет новую позицию спрайтов - это вычисляется из моего времени дельты). Единственное, что я делаю в своей функции рендеринга (кроме собственно рисования), это экстраполяция «новой» позиции, но, конечно, это использует время, поскольку оно должно учитывать время от последнего обновления логики до вызова рендеринга. В любом случае, это мое понимание :-) Как бы вы это сделали? Я не понимаю, как использовать экстраполяцию только в обновлении логики. Спасибо!
BungleBonce
Обновил мой ответ. Надеюсь, что это помогает
Ахолио
Спасибо, см. В моем исходном вопросе «когда он останавливается, он слегка колеблется влево / вправо» - так что даже если я остановлю свой спрайт, экстраполяция все еще заставляет спрайт «двигаться» (колебание) - это происходит потому, что я не использую старые и текущие позиции спрайта для проработки моей экстраполяции, поэтому, даже если спрайт статичен, он все равно имеет этот эффект колебания, основанный на значении «экстраполяции», которое вырабатывается при каждой итерации кадра. Я даже пытался сказать «если старые и текущие позиции одинаковы, то не экстраполировать», но это все равно не работает, но я вернусь и еще раз посмотрю!
BungleBonce
0

Похоже, вам нужно полностью отделить рендеринг и обновление физики. Обычно базовое моделирование будет выполняться с дискретными временными шагами, и частота никогда не изменится. Например, вы можете имитировать движение вашего шарика каждую 1/60 секунды, и все.

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

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

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

Вот важный код псевдо из статьи:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

RenderGameФункция представляет наибольший интерес. Идея состоит в том, чтобы использовать интерполяцию между позициями дискретного моделирования. Код рендеринга может создавать собственные копии данных симуляции только для чтения и использовать временное интерполированное значение для рендеринга. Это даст вам очень плавное движение без каких-либо глупых проблем, таких как то, что вы, похоже, испытываете!

RandyGaul
источник