Екстраполяція порушує виявлення зіткнень


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


        }

Редагувати

Як я вже згадував вище, я намагався зробити копію координат спрайта, щоб намалювати .... це має свої проблеми.

По-перше, незалежно від копіювання, коли спрайт рухається, він надто гладкий, коли він зупиняється, він коливається трохи вліво / вправо - оскільки це все ще екстраполює своє положення залежно від часу. Це нормальна поведінка і чи можемо ми її "вимкнути", коли спрайт зупиниться?

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

Якщо користувач натискає скажіть, права кнопка і спрайт рухається праворуч, коли він вдаряється про стіну, якщо користувач продовжує утримувати праву кнопку вниз, спрайт буде продовжувати анімувати праворуч, зупиняючись стіною ( тому насправді не рухається), однак тому, що правий прапор все ще встановлений, а також тому, що рутина зіткнення постійно переміщує спрайт зі стіни, все одно в коді (а не гравцеві) все одно здається, що спрайт все ще рухається, і тому екстраполяція триває. Отож, що бачив би гравець, - це спрайт "статичний" (так, це анімація, але насправді він не рухається по екрану), і раз у раз він сильно трясеться, коли екстраполяція намагається це зробити ... .. Сподіваюся, що це допоможе


1
Це потребує певного перебору, щоб повністю зрозуміти («інтерполяція», мабуть, має десяток різних значень, і на перший погляд не зовсім зрозуміло, що ви тут маєте на увазі), але мій перший інстинкт - «ви не повинні нічого робити, щоб вплинути на ваше позиція об'єкта у вашій програмі візуалізації '. Ваш рендер повинен намалювати ваш об'єкт у заданому положенні, а для маніпулювання об'єктом є рецепт неприємностей, оскільки він властиво з'єднує рендера з фізикою гри. В ідеальному світі ваш рендер повинен мати можливість приймати покажчики const на ігрові об’єкти.
Стівен Стадницький

Привіт @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 разом.

EDIT: Раніше я неправильно зрозумів ваш код. Цикли призначені для відображення якнайшвидшого та оновлення через встановлений інтервал. Ось чому ви б інтерполювали позицію спрайта, щоб обробити той випадок, коли ви малюєте більше, ніж оновлення. Однак якщо цикл відстає, то ви опитуєтесь на оновлення, поки ви не наздогнали або не пропустили максимальну кількість малюнків кадрів.

Однак, єдиною проблемою є те, що об'єкт рухається після зіткнення. У разі зіткнення об’єкт повинен припинити рух у цьому напрямку. Отже, якщо сталося зіткнення, то встановіть його швидкість на 0. Це повинно зупинити подальше переміщення об'єкта за допомогою функції візуалізації.


Привіт @Aholio Я спробував варіант 2, і він спрацьовує, але викликає кілька збоїв, можливо, я зробив це неправильно, я повторю це. Однак мене дуже цікавить варіант 1, хоча я не можу знайти інформацію про те, як це зробити. Як я можу екстраполювати слово в моїй логічній процедурі? До речі, моя дельта виправлена, так що це 1/60 або 0,01667 (швидше це цифра, яку я використовую для інтеграції, хоча кількість часу, яке кожна ітерація моєї гри займає, очевидно, може змінюватись залежно від того, що відбувається, але ніколи насправді не повинно зайняти більше 0,01667 ) тому будь-яка допомога з цього приводу буде великою подякою.
BungleBonce

Можливо, я не повністю розумію, що ти робиш. Щось здається мені дивним, це те, що ви не тільки займаєтеся рухом позиції у своїй функції малювання; Ви також робите підрахунки часу. Це робиться для кожного об’єкта? Правильний порядок повинен бути: обчислення часу, зроблене в коді ігрового циклу. Ігровий цикл передає дельту в функцію оновлення (dt). Update (dt) оновить усі ігрові об'єкти. Він повинен обробляти будь-який рух, екстраполяцію та виявлення зіткнень. Після того як update () повертається до циклу гри, він викликає функцію render (), яка малює всі щойно оновлені ігрові об'єкти.
Ахоліо

Хммммм, в даний час моя функція оновлення робить все. Чи рухає (маючи на увазі, я обчислює нове положення спрайтів - це обчислюється з мого часу дельти). Єдине, що я роблю в своїй функції візуалізації (крім власне малювання) - це екстраполяція "нової" позиції, але, звичайно, для цього потрібен час, оскільки він повинен враховувати час від останнього оновлення логіки до виклику візуалізації. Це все-таки моє розуміння :-) Як би ти це зробив? Я не розумію, як використовувати екстраполяцію в межах лише логічного оновлення. Дякую!
BungleBonce

Оновлено мою відповідь. Сподіваюсь, що це допомагає
Ахоліо

Дякую, дивіться у моєму первісному запитанні "коли він зупиняється, він коливається трохи вліво / вправо" - так що навіть якщо я зупиняю спрайт, екстраполяція все ще тримає спрайт "рухатися" (коливаючись) - це відбувається тому, що я не використовую Старі та поточні позиції спрайту розробити мою екстраполяцію, тому навіть якщо спрайт є статичним, він все ще має цей ефект коливання, заснований на значенні "екстраполяції", яке розробляється для кожної ітерації кадру. Я навіть намагався сказати: "Якщо старі та поточні пози однакові, то не екстраполюйте", але це все ще не працює, я повернусь і погляну ще раз!
BungleBonce

0

Схоже, вам потрібно повністю розділити перегляд і оновлення фізики. Зазвичай базове моделювання буде виконуватися з дискретних часових кроків, і частота ніколи не зміниться. Наприклад, ви можете імітувати рух свого м'яча кожні 1/60-ту секунду, і все.

Для того, щоб дозволити змінну частоту кадрів, код візуалізації повинен працювати на змінній частоті, але будь-яке моделювання все ще повинно бути встановленим у визначеному часовому кроці. Це дозволяє графічній пам’яті зчитувати з симуляції як лише для читання, а також дозволяє встановлювати інтерполяцію замість екстраполяції.

Оскільки екстраполяція намагається передбачити, куди йдуть майбутні значення, раптові зміни в русі можуть дати вам величезні помилки екстраполяції. Краще замість цього відобразити свою сцену про кадр, що стоїть за імітацією, і інтерполювати між відомими окремими позиціями.

Якщо ви хочете побачити деякі деталі реалізації, я вже написав короткий розділ про цю тему в статті тут . Будь ласка, дивіться розділ під назвою "Тайм-аут".

Ось важливий код psuedo зі статті:

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Функція представляє найбільший інтерес. Ідея полягає у використанні інтерполяції між дискретними позиціями моделювання. Код візуалізації може робити власні копії даних моделювання лише для читання та використовувати тимчасове інтерпольоване значення для візуалізації. Це дасть вам дуже плавний рух без будь-яких дурних питань, як, наприклад, те, що у вас, здається, є!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.