Що насправді рухається у нескінченному бігуні?


107

Наприклад, знаменита гра Flappy Bird, або все, що справді подібне, - гравець (у цьому випадку птах або камера, що б вам не було зручніше) рухається вперед або весь світ рухається назад (птах змінює лише положення Y і має постійне положення X)?


1
З цього питання та його відповідей ми видалили багато коментарів, що не входять до теми. Ми також перенесли кілька обґрунтованих дискусій для спілкування, в деяких випадках неодноразово. Незважаючи на це, декілька користувачів відчули необхідність ігнорувати той факт, що коментарі не для тривалих обговорень та продовження. Як таке, це питання було тимчасово заблоковано.
Джош

Відповіді:


146

Я трохи не погоджуюся з відповіддю Філіпа ; або хоча б з тим, як він це представив. Створюється враження, що переміщення світу навколо гравця може бути кращою ідеєю; коли це зовсім навпаки. Тож ось моя власна відповідь ...


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


Втрати / відходи від продуктивності:

У світі зазвичай буде багато об’єктів; багато, якщо не більшість, статичні або сплячі. У гравця буде один або порівняно небагато предметів. Переміщати весь світ навколо гравця, означає пересувати все на сцені, крім гравця. Статичні об'єкти, сплячі динамічні об'єкти, активні динамічні об'єкти, джерела світла, джерела звуку тощо; все треба перемістити.

Це (очевидно) значно дорожче, ніж переміщати лише те, що насправді рухається (гравець, а може, ще кілька речей).


Технічне обслуговування та розширення:

Переміщення світу навколо гравця робить світ (і все в ньому) точкою, коли речі найбільш активно відбуваються. Будь-яка помилка чи зміна в системі означає, що потенційно все змінюється. Це не гарний спосіб робити речі; ви хочете, щоб помилки / зміни були максимально ізольованими, щоб не виникати несподіваних форм поведінки, де ви не внесли зміни.

Існує також багато інших проблем з таким підходом. Наприклад, це порушує багато припущень щодо того, як все має працювати в двигуні. RigidBodyНаприклад, ви не зможете використовувати динаміку ні для чого, крім гравця; як об'єкт із доданим RigidBodyне встановленим кінематичним поводом буде несподівано під час встановлення позиції / обертання / масштабу (що ви робите для кожного кадру, для кожного об'єкта в сцені, крім гравця 😨)


Тож відповідь - лише рухати гравця тоді!

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


Фактичне рішення:

Не роби і того, і іншого! Ви повинні тримати світ статичним і рухати гравця навколо нього. Але «перезавантажте» і гравця, і світ, коли гравець починає надто далеко від кореня (позиції [0, 0, 0]) сцени.

Якщо ви збережете відносну позицію речей (гравця до навколишнього світу) і виконайте цей процес в одному оновленні кадру, (фактичний) гравець навіть не помітить!

Для цього у вас є два основні варіанти:

  1. Перемістіть програвача до кореня сцени та перемістіть світовий шматок на нову позицію щодо гравця.
  2. Думайте про світ як про сітку; перемістіть частину сітки, в якій гравець знаходиться, до кореня, і перемістіть гравця на нове місце відносно тієї частини сітки.

Ось приклад цього процесу в дії


Але, як далеко далеко?

Якщо ви подивитесь на вихідний код Unity, вони використовують 1e-5( 0.00001) як основу для розгляду двох значень з плаваючою комою "рівних", усередині Vector2та Vector3(типів даних, що відповідають за позиції об'єктів, [ейлер-] обертання та масштаби). Оскільки втрата точності з плаваючою комою відбувається обома способами від нуля, можна припустити, що все, що знаходиться під 1e+5( 100000) одиницями, розташованими від кореня / джерела сцени, безпечно працювати.

Але! Оскільки ...

  1. Більш доцільно скласти систему для автоматичного управління цими процесами повторного вкорінення.
  2. Якою б не була ваша гра, не потрібно, щоб суцільний "розділ" світу був шириною 100000 одиниць (метрів [?]).

... тоді, мабуть, є хорошою ідеєю відновити корінь набагато раніше / частіше, ніж 100000 одиниць. Приклад відео, яке я надав, здається, робить це кожні 1000 одиниць або близько того, наприклад.


2
Коментарі не для розширеного обговорення; ця розмова переміщена до чату . Будь ласка, використовуйте цей чат, а не публікуйте тут більше коментарів.
Олександр Вайланкорт

> Це (очевидно) значно дорожче, ніж переміщення лише того, що насправді рухається <Це? Під час візуалізації вам все одно доведеться робити множення матриць з матрицею камери на всіх об'єктах, тому що камера, зафіксована на ідентичності, буде менш дорогою.
Мацеманн

Той момент, коли розвиток ігор стає розвитком єдності ...
LJ ᛃ

@Matsemann - Якби ти пішов у чат, ти помітив би, що це не новий момент. Як уже було пояснено, візуалізуйте сцену NOT-EQUALS; вони різні і майже повністю незалежні теми та трубопроводи. Здійснення інверсії системи відліку в тому, як об'єкти розміщені на сцені, означатиме, що у вас буде більш важкий процес на обох кінцях , а не просто у візуалізації. --- Крім того, навіть якщо продуктивність торгується однаковою мірою, як ви стверджуєте, всі інші проблеми з інверсією все ще залишаться невирішеними, що робить такий підхід ще гіршим, ніж той, що повторно вкорінюється.
XenoRo

@LJ ᛃ Питання було поставлено саме про єдність (див. Теги ОП до редагувань Д. Еверхарда [IMHO defaiting]), і тому відповідь була розроблена так, щоб це відобразило . --- Але ось найкраща частина: будь то Unity, Unreal, CryEngine, Source та ін. ... Найкращі та / або найпопулярніші двигуни працюють саме так (і вони роблять це з причини), тому відповідь не лише цілком справедливі в цілому, але бали, які були зроблені, все ще знаходяться на вершині точності . Взагалі кажучи, якщо перевернути орієнтир фізики, ви можете розраховувати на проблеми, особливо на динамічних об'єктах.
XenoRo

89

Обидва варіанти працюють.

Але якщо ви хочете, щоб нескінченний бігун був справді нескінченним, вам доведеться тримати гравця нерухомим та рухатись по світу. Інакше ви врешті-решт потрапите на межі змінних, які використовуєте для зберігання X-позиції. Ціле число з часом переповниться, і змінна з плаваючою точкою стане все менш точною, що зробить геймплей глюкізним через деякий час. Але ви можете уникнути цієї проблеми, використовуючи досить великий тип, щоб ніхто не стикався з цими проблемами протягом періоду часу, який можна було б відтворити за один сеанс (коли гравець рухається 1000 пікселів в секунду, 32-бітове ціле число переповниться через 49 днів).

Тож робіть все, що вам здається концептуально більш інтуїтивним.


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Джош

1
Я не думаю, що це вирішує щось. Замість того, щоб зберігати позицію гравця (та камери), ви зберігаєте позицію прокрутки у світі - чим це відрізняється?
Agent_L

1
@Agent_L Як правило, світи породжуються поодиноко, і кожен шматок має своє X / Y положення. Коли шматок достатньо відстає від гравця (наприклад, поза екраном), він видаляється, і вони генерують лише певну суму заздалегідь, тому вони завжди знаходяться в порівняно невеликому діапазоні - одна гра, яку я зробив, ніколи не мала координати більше ніж ~ 1000 або менше 0, не дивлячись на те, що він був нескінченним, випадково генерованим бігуном, через це - я відслідковував позицію гравця в кожному фрагменті та індекс кожного фрагмента в послідовності.
Нік Хартлі

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

2
зі швидкістю 1000 пікселів в секунду, це займе 586 мільйонів років, щоб пройти 64-бітове ціле число. Це справді проблема лише в тому випадку, якщо ви використовуєте дійсно малі типи, такі як 16 бітні цілі числа.
Ліндон Уайт

38

Створюючи відповідь XenoRo , замість описаного ними методу повторного вкорінення можна зробити наступне:

Створіть круговий буфер частин вашої генерованої нескінченної карти, який ваш персонаж переміщається з положенням, оновленим арифметикою за модулем (так що ви просто біжите навколо кругового буфера). Почніть замінювати частини буфера, як тільки ваш персонаж покидає шматок. Рівняння оновлення гравців буде приблизно таким:

player.position = (player.position + player.velocity) % worldBuffer.width;

ось піктографічний приклад того, про що я говорю:

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

Ось приклад того, що відбувається при обгортанні в кінці.

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

За допомогою цього методу ви ніколи не зіткнетеся з помилками точності, але вам може знадобитися зробити дуже великий буфер, якщо ви збираєтесь робити це в 3d з дуже далеким оглядом (так як ви все ще повинні мати можливість бачити попереду себе ). Якщо у його крихкого птаха розмір вашої шматки, ймовірно, буде лише настільки великим, як потрібно для проведення однієї сцени для одного екрану, і ваш буфер може бути дуже крихітним.

Зауважте, що ви почнете отримувати повторювані результати з будь-якого PRng, а максимальна кількість повторюваних послідовностей покоління PRNG, як правило, менша за довжину пороху (2, кількість бітів, які використовуються всередині), при мерзенному твістері це не так більша частина проблеми, оскільки вона використовує 2,5 тис. внутрішнього стану, але якщо ви використовуєте крихітний варіант, у вас є 2 ^ 127-1 макс. ітерації перед повторним (або гірше), проте це все-таки астрономічно велика кількість . Ви можете виправити неповторні проблеми періоду, навіть якщо ваш PRNG має короткий період за допомогою хороших функцій перемішування лавини для насіння (оскільки ви додаєте більше стану непрямо) кілька разів.


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Джош

15

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

Нереститель створює екземпляри об'єктів із екрану з фіксованою швидкістю у Vector2.leftнапрямку.


3
Це працює для FlappyBird, оскільки це дуже проста гра. Як було сказано у моїй відповіді, той самий прийом, хоча це все ще можливо , був би дуже проблематичним для будь-якого складнішого (з більшою кількістю світових об'єктів / деталей), як Subway Surfer.
XenoRo

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