Здравствуйте и добро пожаловать в Библию движений Doom! В статье разобраны и рассортированы по категориям все причуды и капризы кода движений в Doom, включая замысловатые трюки с описанием их работы.



Метрика


Начнем с основ. Код движка для движений игрока в Doom имеет следующий вид:
  • Сбор исходных данных игрока
  • Применение векторов движущей силы к игроку на основе исходных данных
  • Сжатие скорости игрока, если она слишком велика
  • Проверка, расчеты и перемещения
  • Использование ускорения через трение для следующего движения игрока

Рассмотрим все поподробней.
Благодаря только этому и паре переменных мы сможем определить возможные максимальные скорости персонажа игрока.

По вполне понятным причинам максимальная скорость игрока достигается тогда, когда ускорение, заданное игроком, равно и противоположно замедлению от трения.

За каждый тик игрок получает толчок равный 0.03125 единиц в расчете от величины введенной команды. Поскольку команды (обычно) ограничиваются максимумом в 50. Это дает нам максимальное ускорение трения в 1.5625 единиц/тик^2.

После расчета движения за тик, игрок замедляется с помощью коэффициента трения. Конкретное значение составляет 0.90625 — другими словами, каждое движение игрока замедляется на 90.625% от его текущей скорости.

Для равнозначности ускорения и замедления каждого тика, со скоростью 50, нам нужно найти «х» при (1 — 0.90625)x = 1.5625. Это выводит нас к теоретическому пику прогрессивной скорости движения в 16.666 единиц за тик. (Обратите внимание, что фактическая эмпирическая максимальная скорость, по определенным причинам, составляет чуть меньше данного значения. Но все же она достаточно близка к указанному показателю).

Также обратите внимание, что игрок имеет неизменную максимальную скорость, заданную кодом. Еще до того как проводить фактическое исчисление движения игрока, в начале кода игра проверяет не превышают ли X- и Y-координаты показатель скорости в 30 единиц/движение^2 (или менее чем -30). Если превышают, то игра сокращает скорость до 30 (или до -30 соответственно). Мы не будем зацикливаться на лимите скорости, но следует обратить внимание, что это происходит только перед началом кода движения и не препятствует достижению скорости свыше 30 внутри самого кода движения.



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

Strafe running


Еще одна причуда движка Doom (как в сущности и множества других игровых движков) заключается в том, что X- и Y-координаты хранятся и вычисляются отдельно. Это приводит к одному небольшому, но забавному эффекту. Когда игрок одновременно движется вперед и в сторону, в расчет применяются оба вектора движения и направление общего движения получает большую величину, чем в векторах по отдельности.

В нормальном геймплее Doom стрейф имеет максимальную движущую силу в 50 единиц, а при боковом движении — 40. Таким образом при стрейфе показатель движущей силы за тик достигает 1.25 единицы, а максимальная скорость — 13.333 единиц/движений. Если рассмотреть векторы бокового и прямого движения как стороны прямоугольного треугольника, Вы обнаружите, что максимальная теоретическая скорость при стрейфе равна 21.34 единицам за движение. А это на 28% выше обычной скорости, что уже весьма не плохо.

«SR50» strafe running


Но существует способ двигаться еще быстрее. Согласно некоторым особенностям кода, есть возможность зажать одновременно сразу несколько клавиш таким образом, чтобы получить движущую силу при стрейфе в 50 единиц, вместо 40. Таким образом у Вас появляется возможность разогнаться до максимальной скорости в 23.57 единиц/движение (то есть на 41% выше обычной скорости при нормальном беге и на 10% — при стрейф беге).

Такое разнообразие стрейф бега по довольно загадочным причинам обычно связывают с аббревиатурой «SR50». Профессионалы по скоростному прохождению игр создали различные инструменты, применяющиеся для анализа демо-версий. И с помощью некоторых инструментов можно разобрать каждый тик. Собственно, одна из команд-создателей называлась «SR» — стрейф вправо (strafe right), за которым следовал аргумент команды. Поскольку эта техника позволяла достигать стрейф бега с показателем в 50 вместо 40, инструменты показывали команды «SR50» вместо «SR40». Именно «SR50» и стала широко использоваться как синекдоха для данной техники.

О, и еще, так сказать, для будущих поколений — давайте высчитаем максимальную скорость бега в «реальном мире»:

23.57 единиц/движение * 35 движений/секунду = 825 единиц/секунду
825 единиц/секунду/16 единиц/фут (общий коэффициент пересчета) = 51.5 футов/секунду
51.5 футов/секунду = 35 миль в час (около 56 км/час).

Skip glide




трюк на 0:02

Теперь давайте перейдем к конкретным приемам. Первый из них считается самым сложным для исполнения на практике. Но в то же время, его проще всего объяснить с позиции движка. Я называю этот трюк «skip glide» (прыжок со скольжением) хотя иногда его еще называют «bar glide», что может создать путаницу, поскольку существует другой трюк с таким же названием.

Чтобы понять этот трюк, сначала нужно разобрать основной способ, с помощью которого Doom пытается обрабатывать движения. В каждом тике DoomGuy имеет определенную x- и y-координаты импульса (направление инерции). Впрочем я не буду называть это «импульсом», так как подобный термин предполагает определенные свойства, которых Doom не имеет (Вы можете двигаться за приделы возможностей “импульса”, или наоборот, у Вас может быть «импульс» даже если движения не происходит вовсе). Вместо этого я буду говорить об «импульсе» с точки зрения потенциально новой позиции, в которую игрок хочет перейти и ссылаться на него как на «Состояние Попытки Телепортации» (Teleport Attempt Position) или СПТ. (С этого момента встречая в тексте сокращение «СПТ», Вы можете мысленно заменять его на «импульс». Но я надеюсь, использование другого термина поможет Вам запомнить, что на самом деле это — не «реальный» импульс, а некий причудливый псевдо-аналог, имеющий другие свойства.)

Причина заключается в том, что игрок, в сущности, бездействует во время тика. Вместо этого игра просто удаляет игрока из одной точки на карте и мгновенно помещает в другую. Такой род дискретного движения, вероятно, универсален для игр. Но в то же время возникает резонный вопрос: как определить, что столкнулся с чем-либо, находящимся перед тобой?

Данная особенность помогает понять как именно работает «skip glide». Рассмотрим канонический пример из Doom 2 при прохождении Map 21. Игрок занимает вполне естественную позицию прямо перед диагональным заслоном, который закрывает щель. На следующем тике движок проводит тест, чтобы определить можно ли телепортировать игрока на позицию сразу *за* диагональным заслоном. При тщательном выборе направления можно обозначить позицию, которая не будет противоречить условиям «телепорта» и движок перенесет игрока на желаемую точку без каких-либо проблем.

Squeeze Glide




трюк на 0:25

Также в арсенале имеется другой, еще более распространенный трюк, который связан с приемом «bar glide», потому что он тоже основан на движение между заслонами, расположенными на расстоянии в 32 единицы. Для меня же термин «squeeze glide» (скольжение с «протискиванием») кажется более адекватным для запоминания и менее запутывающим.

Для squeeze glide игрок должен идеально выравниваться с зазором, одинакового с персонажем размера (в 32 единицы). Затем, после ряда безуспешных попыток, длящихся, как кажется, целую вечность, ему наконец-то удается втиснуться в этот зазор!

image

В теории “squeeze glide” должен быть вполне простым и понятным: игрок имеет ширину в 32 единицы (вдоль осей). И значит если Вы находите пространство, четко выровненное по осям и с шириной 32 единицы, то должны быть в состоянии пройти через него. Но с движком Doom редко бывает все так просто.

Основная проблема состоит в том, что движение в движке Doom квантовано и сверх того, оно квантовано не полностью. Я не буду вдаваться в подробности и рассказывать как был вычислен вектор движения игрока. Но если вкратце, вектор использует синусы и косинусы для объединения направления игрока с векторами движения, чтобы сравниться с завершающими X- и Y-векторами движущей силы. Например, когда игрок зажимает клавишу вперед и направляется на север, X-координата вектора движения должна составлять значение, умноженное на косинус 90, что необходимо для обнуления. К сожалению, Doom не силен в сохранении углов и косинусов, он использует расчетные таблицы значений и, как оказалось, таблица синуса/косинуса фактически не включает 0 как значение (наиболее близкое ±0.0002).

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

Итак, зная о том, как для игроков работает СПТ, Вы можете увидеть, что происходит:
  • Игрок пытается переместиться в пространстве непосредственно между преградами
  • Движок использует СПТ, имеющую некоторый квантованый коэффициент удлинения, и определяет возможность перемещения в ту или иную позицию
  • Поскольку зазор имеет ту же ширину, что и игрок, последний должен ТОЧНО соответствовать проходимому пространству (неточные значения угла делают задачу реально сложной). Это означает, что для удачного прохождения Вам необходимо с идеальной точностью в 0.0002 единицы находиться западнее места между преградами

Понимание того, как заставить слаженно работать squeeze glide — это не наука, а настоящее искусство. Спидраннеры час за часом изучают тонкости перемещения, чтобы выполнять его за разумное количество времени

Line skipping (south / west)




трюк на 19:06

Понимание того, как движок использует СПТ игроков для тестирования новой позиции, лишь немного приблизит нас к пониманию line skip (пропуска границы). Может показаться, что игрок делает невозможное, когда выполняет line skip: бежит непосредственно через линию границы без ее фактической активации.

Чтобы понять, как это работает, Вам нужно посмотреть на порядок событий в движке:
  • Он использует СПТ игрока, чтобы протестировать его на потенциально новой позиции
  • В качестве части процесса, движок составляет список любого наложения отрезка потенциально новой позиции
  • Если это возможно, перемещает их на рассматриваемую позицию
  • Проводит соединительную линию между центрами старой и новой позиции игрока, после чего проверяет каждую границу в уже скомпилированном списке, чтобы увидеть пересекает ли линия соединения какую-то из них
  • Если пересекает, осуществляет проверку этих линий на специальные действия с дальнейшей их активацией

Таким образом, как Вы видите, существует две возможности пересечения специальной линии границы: 1) прикосновение к линии границы в точке обновленной позиции, и 2) граница пересекается линией, соединяющей старую и обновленную позиции.

Итак, задан алгоритм. Как Вы можете обойти его? Ну, вот как-то так.

Тик 1: СПТ игроков стремится передвинуть их на точку, где их центр практически, но все же не совсем, пересекает границу
Тик 1: движок определяет, что эта новая точка касается границы и добавляет ее в список
Тик 1: после успешного перемещения, движок набрасывает линию пересечения, но она не пересекает границу, так что это неважно
Тик 2: СПТ игроков перемещает их достаточно далеко, благодаря чему они передвигаются всецело *за* линию границы и больше ее не касаются
Тик 2: специальная линия границы не находится даже в новом составленном списке пересекаемых границ, в результате чего она не может быть активирована.

Итак Вы видите, что пока перемещаетесь быстрее, чем половина ширины игрока за тик (имея хорошее расположение и толику удачи), нет ничего невероятного в том, чтобы проскочить линию границы без активации ее специальных действий. Тем ни менее, помните, что ограничивающая коробка игрока всегда выравнивается с привязкой к осям координат. Иными словами этот прием проще выполнить вдоль осей, где “радиус” игрока составляет 16 единиц. Однако, даже по диагонали в 45 градусов, где “радиус” игрока составляет 22.6 единиц, стрейф бег SR50 с его 23+ единицами за движение все еще выполним.

Хочу обратить Ваше внимание на то, что описанное выше касается только движения на “юг/запад”. По причинам, которые мы еще не обсудили, данный прием НЕ БУДЕТ РАБОТАТЬ, при движении на север или восток.

Item bumping




трюк на 0:16

Вот еще одна несуразность, происходящая из-за странной и причудливой проверки Doom потенциально новой позиции игрока. Алгоритм выстраивается следующим образом:
  • Используется СПТ игрока для определения потенциально новой позиции
  • Проверяется, нет ли каких-либо препятствий в зоне посадки для того, чтобы занять эту точку
  • Одновременно с этим проводится проверка на наличие каких-либо других вещей, перекрывающих зону посадки
  • И также, если до какой-то из этих вещей можно дотянутся, то игрок их подбирает

Вы видите проблему? Игрок возьмет вещи, касающиеся потенциальной зоны посадки СПТ, даже если перемещение в конечном счете не происходит. Это означает, что если Вы несетесь на полной скорости в непроходимую границу c объектом, расположенным непосредственно по другую сторону от нее, движок (в момент столкновения с границей) предпримет попытку переместить игрока (СПТ) по другую сторону стены. Это даст возможность подобрать любые вещи, которые попадутся на месте потенциальной посадки.

image

Monsters opening doors




трюк на 0:09

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

В данном случае прием срабатывает, если дверь была создана способом, где одна сторона, к примеру, закрыта ключом, а другая сторона является «нормальной» дверью. В то время, как быстрый монстр (вроде archvile) проходит возле двери, его огромное СПТ указывает на попытки движка разместить этого монстра на значительном расстоянии с незакрытой стороны двери. И таким образом монстр в состоянии ее открыть.
Поделиться с друзьями
-->

Комментарии (8)


  1. marcor
    29.07.2016 06:28

    Спасибо, давно хотел почитать про движок DOOM, ибо в C я не настолько силён, чтобы читать сам движок.


  1. boris768
    29.07.2016 08:08
    +1

    Просто восхитительная статья про мою любимую игру! Хотелось бы даже изучить исходный код игры и найти там как эти «фичи» были реализованы


    1. saboteur_kiev
      29.07.2016 13:41

      https://github.com/id-Software/DOOM


      1. boris768
        29.07.2016 13:57

        Спасибо большое


  1. qwerty135
    29.07.2016 08:27

    Зачем писать такие статьи… они же вызывают непреодолимую ностальгию… так захотелось поиграть, вспомнить молодость )))


  1. Darth_Malok
    29.07.2016 10:34
    +1

    Спасибо, очень интересно. Вот бы подобное про quake 1,2,3 на русском языке почитать.


  1. msdos9
    29.07.2016 13:34

    На эту тему, на канале у Дмитрия Бачило, можно много чего почерпнуть. А также о других 3д движках.
    https://www.youtube.com/watch?v=rqx7r8TBsD4


  1. LostSenSS
    02.08.2016 10:20

    Ничего не знаю про конкретные формулы физики движения в Doom, но в плане восприятия и ощущения динамики это до сих пор работает отлично. Это не ностальгия, знаю про что говорю, т.к. недавно перепроходил оригинальный Doom2 и Brutal Doom.
    Но вершиной в этом плане до сих пор считаю Quake 3. Ни в одном другом шутере я не ощущал такой физики и контроля движения.