Pull to refresh

Реверс-инжиниринг процедурной генерации в No Man's Sky

Reading time 20 min
Views 43K
Original author: gregkwaste


No Man’s Sky — это игра про исследование космоса, в которой используется технология процедурной генерации игрового окружения и ресурсов (текстур, моделей, рельефа и т.д.). Я был в восторге, когда объявили о её разработке в 2013 году, не только из-за самой игры, но в основном из-за возможности изучить игровые файлы и узнать, как она работает. После выпуска игра получила самые противоречивые отзывы, но мне всё равно интересно, что же происходит у неё внутри.

Если вы установите игру, то увидите, что по объёму она очень невелика, и это действительно так. Основная причина этого в том, что игра работает с очень ограниченным набором ресурсов и с помощью процедурной генерации создаёт на их основе буквально сотни вариантов. Я сосредоточусь на контенте, связанном с 3D-моделями игры, потому что для меня они всегда наиболее интересны. Статья будет разделена на три основные категории: геометрия, текстуры и анимации.

Геометрия


Итак, в игре файлы геометрии (вершин, буферов индексов и т.д.) хранятся в файлах с расширением ".GEOMETRY.MBIN". Уже с помощью этих файлов можно создать довольно простые парсеры, преобразующие геометрию для работы в ПО 3D-моделирования. Но этого файла недостаточно самого по себе. Такой файл с геометрией используется как контейнер для чистых геометрических данных.

Игра загружает ресурсы как сцены. Это значит, что все ресурсы моделей определяются как файлы отдельных сцен с собственными иерархиями объектов, несколькими частями сеток, несколькими типами объектов (шарниры, источники освещения, точки взаимодействия, коллизии, другие файлы сцены) и т.д. Этот тип информации хранится в файлах ".SCENE.MBIN". Они являются настоящими дескрипторами конкретной сцены, и обычно такие файлы ссылаются на один контейнер геометрии, из которого все части сеток в сцене получают соответствующую информацию о геометрии.

Пока в этом нет ничего нового. Такая же ситуация во множестве разных игр. No Man’s Sky отличается от других игр (по крайней мере, раньше я с таким не сталкивался) тем, что в этом файле сцены не просто одно готовое существо, которое можно заспаунить в игре при определённых условиях, здесь вступает в дело процедурная генерация.

Я кратко объясню, как это выглядит, приложив несколько изображений из созданного мной NMS Model Viewer.


Модель трицератопса

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

Самое важное в этих данных — фактические названия объектов. Можно чётко увидеть, что есть определённая связь между ними и способом, которым игра узнаёт о том, как комбинировать эти части и исключать ненужные при создании модели.

Заметив это, я начал искать другие файлы, которые могут управлять организацией сцены, и оказалось, что это файлы ".DESCRIPTOR.MBIN". Не у всех моделей есть такие файлы. Выяснилось, что они есть только у процедурно генерируемых моделей, а у статических моделей (таких как модель астронавта или материалы, созданные для трейлера) их нет.

Реверс-инжиниринг формата файлов NMS я начал с парсинга этих файлов дескрипторов. Но на тот момент они казались мне какой-то кашей, я не мог понять, что они делают. После парсинга сцен я уже осознал, что нужно искать, и вернулся к этим файлам дескрипторов. Они очень сильно похожи на файлы сцен. В них применён тот же способ определения частей и всего остального, но они могут ссылаться только на другие части сеток или целые файлы сцен. Эти файлы работают следующим образом:

<Data template="TkModelDescriptorList">
  <Property name="List">
    <Property value="TkResourceDescriptorList.xml">
      <Property name="TypeId" value="_HEAD_" />
      <Property name="Descriptors">
        <Property value="TkResourceDescriptorData.xml">
          <Property name="Id" value="_HEAD_ALIEN" />
          <Property name="Name" value="_Head_Alien" />
          <Property name="ReferencePaths" />
          <Property name="Chance" value="0" />
          <Property name="Children">
             ......;
          </Property>
        </Property>
        <Property value="TkResourceDescriptorData.xml">
          <Property name="Id" value="_HEAD_DIPLO" />
          <Property name="Name" value="_Head_Diplo" />
          <Property name="ReferencePaths" />
          <Property name="Chance" value="0" />
          <Property name="Children">
            ......;
          </Property>
        </Property>
        <Property value="TkResourceDescriptorData.xml">
          <Property name="Id" value="_HEAD_HIPPO" />
          <Property name="Name" value="_Head_Hippo" />
          <Property name="ReferencePaths" />
          <Property name="Chance" value="0" />
          <Property name="Children">
            ......;
          </Property>
        </Property>
        <Property value="TkResourceDescriptorData.xml">
          <Property name="Id" value="_HEAD_RHINO" />
          <Property name="Name" value="_Head_Rhino" />
          <Property name="ReferencePaths" />
          <Property name="Chance" value="0" />
          <Property name="Children">
            ......;
          </Property>
        </Property>
        <Property value="TkResourceDescriptorData.xml">
          <Property name="Id" value="_HEAD_STEG" />
          <Property name="Name" value="_Head_Steg" />
          <Property name="ReferencePaths" />
          <Property name="Chance" value="0" />
          <Property name="Children">
            .....;
          </Property>
        </Property>

Процедура такова: существует основная часть, которая решает, чем она будет являться, в нашем примере это _HEAD_. Обычно части, названия которых начинаются с нижнего подчёркивания, означают, что они относятся к группе дескрипторов и только одна из них будет выбрана для финальной модели. Как вы видите, это часть определена в списке TkResourceDescriptorList. Его элементы содержат свойство «Descriptors», дочерние элементы которого являются кандидатами на выбор. Дальше нужно просто выбрать один из дочерних элементов свойства Descriptors. Так, например, выбирается модель головы. После выбора этой конкретной модели головы есть ещё одно свойство «Descriptors», имеющее собственный список возможных вариантов, и из него опять нужно выбрать один. И так далее.

После выполнения этих операций для всех элементов в файле descriptor.mbin мы в результате получим набор выбранных частей, из которого создаётся уникальная полная модель.


Пример SHARKRIG


Истребитель, экспортированный из программы просмотра моделей No Man's Model Viewer


Стражи

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

Важнейшим в этой процедуре является способ выбора части. Если внимательно посмотреть на представленный выше XML-код, можно увидеть, что у каждой части есть свойство «Chance», но довольно у многих частей его значение равно 0. Думаю, что настоящие вероятности выбора частей или определяются движком при выполнении игры, или устанавливаются в других файлах параметров игры. В моей программе просмотра моделей я рандомизировал выбор. Все части имеют одинаковую вероятность выбора и это приводит к появлению довольно отличающихся моделей.


Случайная генерация существ на основании модели трицератопса


Процедурная генерация истребителей (кубы — это ненанесённые декали)


Процедурная генерация посадочных шлюпок


Процедурная генерация SHARKRIG

Я провёл в игре около 70 часов, и за всё это время я ни разу не встретил существо, похожее на диплодока. Это значит, что либо движок содержит ошибку и эти части не выбираются (в чём я сомневаюсь), либо вероятность выбора этих частей настолько мала, что они чрезвычайно редко появляются в игре. Множество обсуждений (в основном разгневанных) затрагивало отсутствующий в игре контент и контент, присутствующий только в игровых трейлерах, а также похожие темы. Я не могу судить о работе игры или о геймплейных возможностях, но судя по изученному мной количеству моделей существ, в игре существует КУЧА контента, который из-за решений движка (?) появляется в игре не очень часто (или не появляется совсем). По-моему, процедурно генерируемые модели диплодоков в 10 раз лучше статических, и если бы разработчики захотели, они смогли бы заставить движок загружать статические модели (и, разумеется, весь контент из трейлера) в любой момент, поэтому, хорошо это или плохо, но это, скорее всего, дизайнерское решение.


Модель диплодока из трейлера на E3

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

Текстуры


Текстуры — это ещё один сложнейший аспект моделей NMS. Как я упомянул в разделе «Геометрия», все части сеток определяются в файлах SCENE.MBIN. Элементы этих файлов выглядят примерно вот так:

<Property value="TkSceneNodeData.xml">
      <Property name="Name" value="_Head_Tri" />
      <Property name="Type" value="MESH" />
      <Property name="Transform" value="TkTransformData.xml">
        <Property name="TransX" value="0" />
        <Property name="TransY" value="0" />
        <Property name="TransZ" value="0" />
        <Property name="RotX" value="0" />
        <Property name="RotY" value="0" />
        <Property name="RotZ" value="0" />
        <Property name="ScaleX" value="1" />
        <Property name="ScaleY" value="1" />
        <Property name="ScaleZ" value="1" />
      </Property>
      <Property name="Attributes">
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="BATCHSTART" />
          <Property name="AltID" value="" />
          <Property name="Value" value="19140" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="BATCHCOUNT" />
          <Property name="AltID" value="" />
          <Property name="Value" value="5772" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="VERTRSTART" />
          <Property name="AltID" value="" />
          <Property name="Value" value="3777" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="VERTREND" />
          <Property name="AltID" value="" />
          <Property name="Value" value="4894" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="FIRSTSKINMAT" />
          <Property name="AltID" value="" />
          <Property name="Value" value="97" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="LASTSKINMAT" />
          <Property name="AltID" value="" />
          <Property name="Value" value="124" />
        </Property>
        <Property value="TkSceneNodeAttributeData.xml">
          <Property name="Name" value="MATERIAL" />
          <Property name="AltID" value="" />
          <Property name="Value" value="MODELS\PLANETS\CREATURES\TRICERATOPSRIG\TRICERATOPS\HEADTRIMAT.MATERIAL.MBIN" />
        </Property>
      </Property>
      <Property name="Children">

Как вы видите, здесь есть несколько атрибутов, которые я сейчас описывать не буду, и поговорю только о последнем. Он определяет материал, используемый для сетки. Как видно, этот узел ссылается на файл материала, который нужно использовать. Давайте рассмотрим этот файл материала:

<?xml version="1.0" encoding="utf-8"?>
<Data template="TkMaterialData">
  <Property name="Name" value="DiploHeadMat" />
  <Property name="Class" value="Opaque" />
  <Property name="TransparencyLayerID" value="0" />
  <Property name="CastShadow" value="True" />
  <Property name="DisableZTest" value="False" />
  <Property name="Link" value="" />
  <Property name="Shader" value="SHADERS/UBERSHADER.SHADER.BIN" />
  <Property name="Flags">
    ...
  </Property>
  <Property name="Uniforms">
    ...
  </Property>
  <Property name="Samplers">
    <Property value="TkMaterialSampler.xml">
      <Property name="Name" value="gDiffuseMap" />
      <Property name="Map" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/DIPLOHEAD.DDS" />
      <Property name="IsCube" value="False" />
      <Property name="UseCompression" value="True" />
      <Property name="UseMipMaps" value="True" />
      <Property name="IsSRGB" value="True" />
      <Property name="MaterialAlternativeId" value="" />
      <Property name="TextureAddressMode" value="Wrap" />
      <Property name="TextureFilterMode" value="Trilinear" />
      <Property name="Anisotropy" value="0" />
    </Property>
    <Property value="TkMaterialSampler.xml">
      <Property name="Name" value="gMasksMap" />
      <Property name="Map" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/DIPLOHEAD.MASKS.DDS" />
      <Property name="IsCube" value="False" />
      <Property name="UseCompression" value="True" />
      <Property name="UseMipMaps" value="True" />
      <Property name="IsSRGB" value="False" />
      <Property name="MaterialAlternativeId" value="" />
      <Property name="TextureAddressMode" value="Wrap" />
      <Property name="TextureFilterMode" value="Trilinear" />
      <Property name="Anisotropy" value="0" />
    </Property>
    <Property value="TkMaterialSampler.xml">
      <Property name="Name" value="gNormalMap" />
      <Property name="Map" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/DIPLOHEAD.BASE.NORMAL.DDS" />
      <Property name="IsCube" value="False" />
      <Property name="UseCompression" value="True" />
      <Property name="UseMipMaps" value="True" />
      <Property name="IsSRGB" value="False" />
      <Property name="MaterialAlternativeId" value="" />
      <Property name="TextureAddressMode" value="Wrap" />
      <Property name="TextureFilterMode" value="Trilinear" />
      <Property name="Anisotropy" value="0" />
    </Property>
  </Property>
</Data>

Важная часть файлов материалов — раздел «Samplers». Очевидно, что в этом разделе определяются текстуры, используемые в части модели. И вот что интересно: для статических моделей все текстуры являются очень качественными текстурами, которые без всяких проблем можно использовать непосредственно на моделях. Но если сетка используется для процедурно генерируемой модели, правильной является только текстура нормалей. Диффузная текстура, которая содержит всю цветовую информацию о части, представляет собой пустую белую текстуру.

Сначала я решил, что все цвета и текстуры тоже выбираются при выполнении игры, но на самом деле это не так. Эти файлы текстур всегда сопровождаются файлами ".TEXTURE.MBIN", которые, как можно догадаться, работают в точности как файлы дескрипторов моделей. Они определяют способ комбинирования текстур для создания финальной диффузной текстуры модели. Художники игры не только создали различные части моделей, но и нарисовали множество разных текстур для каждой части. Поэтому, проходя по этому файлу таким же образом, как и по файлу дескриптора, можно вычислить финальную диффузную текстуру процедурно генерируемой модели. Более того: даже если две модели одинаковы с точки зрения геометрии, благодаря процедурной генерации текстур они могут иметь абсолютно разные цвета, отметины, формы и т.д.


Пример текстуры с пятнами

Детали текстуры при процедурной генерации могут очень сильно отличаться. Похоже, что текстуры создаются послойно. Я покажу пример сборки процедурно генерируемой текстуры существа. Обычно нижним слоем служит базовая текстура, добавляющая основной цвет и оттенок модели (имена таких текстур заканчиваются на .BASE.DDS). На следующем слое находится текстура подбрюшья (.UNDERBELLY.DDS), которая добавляет деталей на брюхе животного. Затем идёт ещё один слой, добавляющий больше деталей на случайных участках модели (.UNDERLAYER.X.DDS). Потом идут отметины (.MARKINGS.X.DDS), определяющие наиболее заметные детали кожи модели. На следующем слое снова находятся детали кожи, расположенные поверх отметин (.SKIN.DDS), а на последнем слое накладывается ещё одна текстура (TOP.X.DDS), добавляющая деталей на отдельные части модели.

Похоже, что максимальное количество слоёв в процедурных текстурах равно 8 (обычно используется 5 или 6). Очевидно, что существует множество текстур, которые нужно смешать вместе. Поэтому все текстуры сопровождаются соответствующей текстурой маски, содержащей необходимую информацию об альфа-канале, чтобы смешивание было как можно более точным. Чаще всего текстуры также сопровождаются соответствующей картой нормалей, которая создаёт детали на каждой части.

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


FURPALETTE.DDS


PAINTPALETTE.DDS

Итак, в процессе создания планеты (или создания звёздной системы) выбираются определённые цвета, которые будут использованы для живых существ всей планеты. Я говорю о выборе цвета, потому что в игровых файлах есть конкретные цветовые палитры размером 8×8 (в папке PCBANKS/TEXTURES/PALETTES). Вероятно, эти палитры составляют различные формы. Это значит, что 64 цвета, содержащиеся в палитрах, обычно объединяются в группы по 4 или 8 цветов. Поэтому когда игра создаёт среду планеты, она выбирает из этих групп, которые будут использованы позже. Эти группы легко определить, глядя на палитры, потому что они на самом деле являются градиентом двух граничных цветов.

Индексирование в выбранной группе выполняется с информацией, содержащейся в файле .texture.mbin file. Описание текстуры в таких файлах выглядит следующим образом:

<Property value="TkProceduralTexture.xml">
        <Property name="Name" value="GRADIENT" />
        <Property name="Palette" value="TkPaletteTexture.xml">
          <Property name="Palette" value="Fur" />
          <Property name="ColourAlt" value="Alternative1" />
        </Property>
        <Property name="Probability" value="1" />
        <Property name="TextureGameplayUse" value="IgnoreName" />
        <Property name="OverrideAverageColour" value="False" />
        <Property name="AverageColour" value="Colour.xml">
          <Property name="R" value="0" />
          <Property name="G" value="0" />
          <Property name="B" value="0" />
          <Property name="A" value="0" />
        </Property>
        <Property name="Diffuse" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/ALIENDIPLO.MARKINGS.GRADIENT.DDS" />
        <Property name="Normal" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/ALIENDIPLO.MARKINGS.GRADIENT.NORMAL.DDS" />
        <Property name="Mask" value="TEXTURES/PLANETS/CREATURES/TRICERATOPSRIG/ALIENDIPLO.MARKINGS.GRADIENT.MASKS.DDS" />
      </Property>

Я снова не буду говорить о том, что делают остальные опции. Нас волнует только описание в свойстве «Palette». Из информации в этом свойстве мы можем узнать, какой цвет нам нужно выбрать для части. В нашем случае видно, что требуется индекс палитры «Fur» (мех) (которая, как мы видели ранее, состоит из групп по 4 цвета), и в выбранной группе нам нужен цвет «Alternative1». Вот как индексируются цвета в палитрах. Эти значения свойства «ColourAlt» могут быть равны «Primary», «Alternative1», «Alternative2», «Alternative3» и т.д. Значит, primary будет первым цветом в группе, alternative1 — вторым, и т.д. Тут я тоже не на 100% уверен, но я использую палитры так, и это логично, исходя из принципа групп цветов в палитре.

Итак, у нас есть один базовый цвет для определённой текстуры, что же нам с ним делать? Нужно просто умножить его на цвет текстуры. Обычно игровые текстуры по умолчанию имеют голубоватый цвет, который выглядит нейтральным. После умножения на цвет палитры текстура получает нужный насыщенный цвет.

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

Благодаря таким комбинациям текстур диффузные текстуры могут очень отличаться и у геометрически одинаковых моделей может быть совершенно разная кожа. Такой техники процедурной генерации текстур вмести с процедурной генерацией моделей достаточно для придания сгенерированным моделям уникального внешнего вида. Как я писал выше, если бы художники работали только над добавлением вариаций слоёв и более детализированными цветовыми палитрами, получившийся мир стал бы богаче и детализированней.

Анимации



Скелетная анимация модели трицератопса


Цикл анимации ходьбы астронавта


Анимация медленной ходьбы процедурно сгенерированной модели Spiderrig

Честно говоря, я не исследовал анимации так подробно, как геометрию и текстуры. Всё что я сделал (и это было не так просто, как кажется :P) — выполнил парсинг анимаций и успешно воспроизвёл их в моей программе просмотра моделей. В этой категории тоже есть множество интересных и уникальных деталей.

Во-первых, скелеты моделей определяются в файлах «SCENE.MBIN». Они представляют собой иерархическую систему шарниров, к которой с помощью скиннинга вершин прикрепляются части моделей. В этом нет ничего удивительного. Интересно то, что, как я упомянул в разделе «Геометрия», в файле SCENE.MBIN есть множество частей сеток. Поэтому для управления анимацией и движением всех этих частей назначенная система шарниров влияет на все части сцены.

Анимации работают точно так же, они анимируют весь скелет, даже если не все шарниры используются для финальной сгенерированной модели. На первый взгляд это выглядит бессмысленной тратой ресурсов. На самом деле единственной тратой является здесь парсинг всего файла анимации. При выполнении игры файлы MBIN SCENE и GEOMETRY предоставляют достаточно информации, чтобы загружать в графический процессор только данные, необходимые для каждой конкретной части модели.

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

Выводы


Я попытался объяснить, как работает игра, насколько я понял её после трёх недель работы. Я сосредоточился на генерировании существ, но те же принципы применяются и к другим аспектам (корабли, NPC, здания, растения). Когда я начинал работать с файлами, все были восхищены игрой, искали новизны и разнообразия. Прошли недели, и игру стали считать монотонной. Вопрос в следующем: стоила ли процедурная генерация использования в NMS?

Однозначного ответа на этот вопрос нет.

Как разработчик ПО и реверс-разработчик видеоигр я отвечаю: да, определённо стоила. С технической точки зрения я никогда не видел игровых механик, похожих на механику NMS, и я сомневаюсь, что увижу в новых играх, использующих технологию процедурной генерации. Потому что никто не будет пытаться создавать вымышленные миры с таким уровнем случайности. Технически No Man’s Sky стала настоящим сокровищем, и каждый, кто пытается отрицать это, просто лжёт себе, или не знает о том, как работает игра (или не интересуется этим). Игровой движок имеет очень большой потенциал, и я постоянно думаю о том, какой могла стать игра, если бы движок был в руках большой игровой студии. Даже с учётом ограниченности ресурсов мне всё равно нравится многообразие существ, которое я вижу в игре (и я считаю, что с помощью более тонкой настройки мы можем выжать даже больше из имеющихся ресурсов).

Мои чувства как игрока противоречивы. Хотя это и не мой стиль игры, но с самого предзаказа No Man’s Sky я знал, что это будет игра, в которой я смогу просто расслабиться. Изучать окружающую среду, растения и животных. Сначала все они кажутся одинаковыми, но приглядевшись, мы увидим в большинстве из них отличия. Может быть, отличаются только текстуры, но они разные: это может быть маленький рог на голове существа, или разные пятна, или отличающаяся деталь корабля. Контент есть в игре (даже геймплейный, который бы разработчики могли заспаунить, если бы хотели), нельзя сказать, что его нет. Всё, что мы получаем — это результат чрезвычайно хорошей процедуры генерации. Фактически, контент, создаваемый движком NMS для системы из 2-3 планет, намного превосходит ресурсы, которые можно видеть, например в ARK. Разумеется, никакие существа не будут полностью идеальными, как динозавры в ARK, но в этом и состоит их прелесть. Именно движок может создавать роскошных и величественных созданий, и в то же время — самых невразумительных животных, которые были в компьютерных играх. Вот почему я купил эту игру, и почему я люблю её. Я живу ради тех моментов, когда после исследования разной утомительной ерунды я внезапно приземляюсь на самую красивую планету, которую когда-либо видел. Повторюсь, я не сравниваю элементы RPG или возможности геймплея. Я говорю только о процедурном контенте. Это не значит, что игра не могла быть лучше. Я верю, что могла, и жду, что разработчики её улучшат.

С другой стороны, если вы не настроены на расслабление, терпеливость и внимание к деталям, игра не стоит покупки, и вся процедурная генерация для вас будет простой тратой ресурсов. Это не шутер, где за вами постоянно охотятся и вы постоянно находитесь в напряжении. Разработчики чётко дают это понять. Для тех, кто выбрал такой стиль игры, всё будет выглядеть одинаковым. Даже если бы контент из трейлера создавался в игре, и на каждой второй планете была пышная растительность, динозавры и песчаные черви, игра всё равно наскучила бы вам. После третьей звёздной системы вам показалось бы всё одинаковым, потому что вы бы не приглядывались внимательно. Нельзя винить игру или разработчиков за то, что вы не занимаетесь исследованиями в игре про исследования. Кроме того, процедурная генерация никогда не была и не станет способом создания достойного контента «с нуля». По крайней мере, не в ближайшем будущем. Нельзя просто написать математическое выражение и создать новое животное. Такое сгодится для рельефа, растений, камней или кораблей (при этом текстурирование всё равно будет под большим вопросом), но для живых подвижных объектов, таких как существа или NPC всё становится настолько сложным, что это практически невозможно. (Разумеется, если бы это было возможно, у разработчиков не было причин не сделать этого, ведь они уже сделали подобное с остальными объектами.) Поэтому если вы ожидаете через каждые пять шагов видеть нового инопланетянина, то простите, это проблема ваших ожиданий, а не разработчиков. Если и есть способ создания такого контента, то это именно способ движка NMS.

В конце концов, после всех моих исследований я знаю, что в игре есть достаточно контента, чтобы по крайней мере создавать всё по-новому на каждой планете, так что я не могу винить игру или движок. Я могу обвинить настройку и конфигурирование движка. Ещё я могу обвинит проклятые многоплатформенные релизы и издателя. Я уверен на 10000000000000000000%, что разработчики были вынуждены торопиться при выпуске игры. Игра, которую мы получили, далека от завершённости, и даже не приближается на 80% к возможностям, которые предусмотрены движком. После изучения файлов мне это абсолютно ясно. Она ближе к технической демке, а не к игре. Попытка запихнуть одинаковый контент на PC и PS4 просто растерзала игру, а стремление заставить работать её на слабых машинах ещё больше снизило качество. Лично я жду обновлений, и их должно быть много. Я могу простить множество ошибок Hello Games при релизе игры, завышенную стоимость, нехватку связи с потребителями, даже отсутствие многих возможностей (например, многопользовательского режима, на который, честно говоря, мне наплевать). Но даже с учётом неразберихи и напряжения перед релизом я не могу простить того, что они не смогли полностью показать, на что способен движок с правильной конфигурацией. Моддеры сейчас погружаются в дебри файлов и пытаются найти способы создания тем же самым движком более богатого и разнообразного контента. И чаще всего у них это получается, потому что движок может обеспечивать гораздо более качественную игру. Все эти опции должны быть доступными любому игроку, а не только моддерам. Очевидно, разработчики решили не давать игрокам такой возможности, чтобы все они находились в одинаковой вселенной и могли иметь общие точки маршрута, существ и планеты. Но они должны были оставить такую возможность. Сделать упор на одиночной игре и показать всем игрокам, на что способен движок.

Почему-то мне кажется, что рано или поздно HG так и поступят. Они не могут просто забыть четыре с лишним года работы над движком, который на самом деле замечателен. А всем любителям конспирологических теорий о Hello Games я могу сказать, что у разработчиков была тысяча способов получить ваши деньги, и если бы у них было такое желание, это могло бы произойти гораздо раньше.

Ещё до того, как движок игры станет настолько прекрасным…
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+90
Comments 39
Comments Comments 39

Articles