Что на самом деле движется в бесконечном бегуне?

107

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

Лендрит Ибрахими
источник
1
Мы удалили много не по теме комментариев к этому вопросу и его ответам. Мы также перенесли несколько разумных обсуждений в чат, в некоторых случаях неоднократно. Несмотря на это, несколько пользователей почувствовали необходимость игнорировать тот факт, что комментарии не предназначены для расширенных обсуждений и продолжаются. Таким образом, этот вопрос был временно заблокирован.
Джош

Ответы:

146

Я немного не согласен с ответом Филиппа ; или хотя бы с тем, как он это представил. Создается впечатление, что лучше было бы передвигать мир вокруг игрока; когда все наоборот. Так вот мой собственный ответ ...


Оба варианта могут работать, но, как правило, плохая идея «инвертировать физику», перемещая мир вокруг игрока, а не игрока по всему миру.


Потеря производительности / растрата:

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

Это (очевидно) значительно дороже, чем перемещать только то, что на самом деле движется (игрок, и, возможно, еще несколько вещей).


Ремонтопригодность и расширяемость:

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

Есть также много других проблем с этим подходом. Например, это нарушает многие предположения о том, как все должно работать в двигателе. Вы не сможете использовать динамические RigidBodyдля чего-либо, кроме игрока, например; поскольку объект с прикрепленным, RigidBodyне установленным кинематическим, будет вести себя неожиданно при настройке позиции / поворота / масштаба (что вы будете делать каждый кадр, для каждого объекта в сцене, кроме игрока 😨)


Таким образом, ответ - двигать игрока только тогда!

Ну ... да и нет . Как упомянуто в ответе Филиппа, в играх с бесконечным бегущим (или в любой игре с большой бесшовной исследуемой областью) слишком большое расстояние от источника может в конечном итоге привести к появлению заметных FPPE ( ошибок точности с плавающей запятой ), и еще дальше, в конце концов, Переполнение числового типа, что может привести к сбою в игре или, по сути, к образованию дыма в игровом мире ... На стероидах! 😵 (потому что к этому моменту FPPE сделает игру уже на «нормальном» трещине)


Фактическое решение:

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

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

Для этого у вас есть два основных варианта:

  1. Переместите игрока в корень сцены и переместите кусок мира в его новую позицию относительно игрока.
  2. Думайте о мире как о сетке; переместите часть сетки, в которой находится игрок, в корень, и переместите игрока в его новое положение относительно этой части сетки.

Вот пример этого процесса в действии


Но как далеко это слишком далеко?

Если вы посмотрите на исходный код Unity, они используют 1e-5( 0.00001) в качестве основы для рассмотрения двух значений с плавающей запятой «равными» внутри Vector2и Vector3(типы данных, отвечающие за положение объектов, [euler-] повороты и масштабирование). Поскольку потеря точности с плавающей точкой происходит в обоих направлениях от нуля, можно с уверенностью предположить, что все, что находится под единицами 1e+5( 100000) от корня / источника сцены, безопасно для работы.

Но! Поскольку...

  1. Более уместно сделать систему для автоматической обработки этих процессов повторного укоренения.
  2. Какой бы ни была ваша игра, нет необходимости в непрерывной «части» мира шириной 100 000 единиц (метров [?]).

... тогда, вероятно, будет хорошей идеей повторно получить root намного раньше / чаще, чем отметка в 100000 единиц. Например, приведенный мной пример видео, кажется, делает это каждые 1000 единиц или около того.

XenoRo
источник
2
Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат . Пожалуйста, используйте этот чат вместо того, чтобы оставлять больше комментариев здесь.
Александр Vaillancourt
> Это (очевидно) значительно дороже, чем перемещать только то, что на самом деле движется <Это? При рендеринге вам в любом случае придется выполнять умножение матриц на матрицу камеры на всех объектах, так как установка камеры с идентификатором будет стоить дешевле .
Мацеманн
Тот момент, когда разработка игр становится развитием единства ...
ЖЖ ᛃ
@ Мацеманн - Если бы вы пошли в чат, вы бы заметили, что это не новость. Как уже объяснялось, рендер НЕ-РАВНЫ сцены; это разные и почти полностью независимые предметы и конвейеры. Инверсия системы координат в том, как объекты расположены на сцене, будет означать, что у вас будет более тяжелый процесс на обоих концах , а не только при рендеринге. --- Кроме того, даже если бы производительность была одинаковой, как вы утверждаете, все другие проблемы с инверсией все равно остались бы нерешенными, что делало бы этот подход еще хуже, чем перекорневой.
XenoRo
@LJ specifically Был задан вопрос о единстве (см. Теги OP до правок Д. Эверхарда [IMHO defacing]), и поэтому ответ был адаптирован, чтобы отразить это . --- Но вот лучшая часть: будь то Unity, Unreal, CryEngine, Source и т. Д ... Лучшие и / или самые популярные движки работают таким образом (и делают это по причине), поэтому ответ не только в общем, совершенно справедливо , но высказанные точки все еще находятся на вершине точности . Вообще говоря, если вы инвертируете систему отсчета физики, вы можете столкнуться с проблемами, особенно с динамическими объектами.
XenoRo
89

Оба варианта работают.

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

Так что делайте то, что кажется вам концептуально более интуитивным.

Philipp
источник
Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
Джош
1
Я не думаю, что это что-то решает. Вместо того, чтобы удерживать положение игрока (и камеры), вы сохраняете положение прокрутки в мире - чем это отличается?
Agent_L
1
@Agent_L Обычно миры создаются по частям, и каждая часть имеет свою позицию X / Y. Когда фигура достаточно сильно отстает от игрока (например, за кадром), она удаляется, и они генерируются заранее только на определенное количество, поэтому они всегда находятся в относительно небольшом диапазоне - одна игра, которую я сделал, никогда не имела координаты, большей чем ~ 1000 или меньше 0, несмотря на то, что был бесконечным, случайно сгенерированным бегуном, из-за этого - я отслеживал положение игрока в каждом чанке и индекс каждого чанка в последовательности.
Ник Хартли,
Я не покупаю концепцию «переполнения переменной». Если игрок не двигается, то «фон» будет отрицательно смещен на ту же величину, что и игрок, если бы он двигался. Оба случая столкнулись бы с одной и той же проблемой (в противоположных направлениях), и оба потребовали бы специальной обработки в пределах переполнения.
транжира
2
при 1000 пикселей в секунду для перехода через 64-битное целое число потребуется более 586 миллионов лет. Это действительно проблема, только если вы используете действительно маленькие типы, такие как 16-битные целые числа.
Линдон Уайт
38

Основываясь на ответе XenoRo , вместо метода повторного укоренения, который они описывают, можно сделать следующее:

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

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

Вот наглядный пример того, о чем я говорю:

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

Вот пример того, что происходит с упаковкой в ​​конце.

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

С этим методом вы никогда не столкнетесь с ошибками точности, но вам может потребоваться создать очень большой буфер, если вы собираетесь делать это в 3D с очень большим расстоянием обзора (так как вам все равно нужно будет видеть впереди себя ). Если это беспорядочная птица, ваш размер фрагмента будет, вероятно, только настолько большим, насколько это необходимо для размещения одной сцены на одном экране, и ваш буфер может быть очень маленьким.

Обратите внимание , что вы будете начать , чтобы получить результаты , повторяя с ЛЮБЫМ PRNG, и максимальное количество не повторяющихся последовательностей PRNG поколения , как правило , меньше , чем длина мощна (2, число бит , используемой внутри), с merzenne TwisteR это не большая часть проблемы, так как он использует 2.5k внутреннего состояния, но если вы используете крошечный вариант, у вас есть 2 ^ 127-1 макс. итераций перед повторением (или хуже), однако это все еще астрономически большое число . Вы можете исправить проблемы повторяющихся периодов, даже если ваш PRNG имеет короткий период с помощью хороших лавинных функций смешивания для затравки (поскольку вы добавляете больше состояний неявно) несколько раз.

опа
источник
Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
Джош
15

Как уже было сказано и принято, это действительно зависит от объема и стиля вашей игры, но поскольку об этом не упоминалось: FlappyBird перемещает препятствие по экрану, а не по игроку по всему миру.

Спавнер создает объекты вне экрана с фиксированной скоростью в Vector2.leftнаправлении.

Stephan
источник
3
Это работает для FlappyBird, потому что это очень простая игра. Как уже упоминалось в моем ответе, такая же техника, хотя все еще возможна , будет очень проблематичной на чем-нибудь более сложном (с большим количеством мировых объектов / деталей), таких как Subway Surfer.
XenoRo
15
Я согласен, и ваш ответ очень тщательный. Я просто не видел, чтобы кто-нибудь упоминал, какой метод на самом деле использует птица-хлопушка, поэтому я решил добавить его.
Стефан