Пятничные факты #324 — Звуковое оформление, Анимированные деревья, Оптимизация

опубликовали Jitka, Ian, Albert, Allaizn, Rseding

Логотип Факторио Jitka

Мы хотели бы представить наши новые нашивки на ткани Factorio, которые теперь доступны в нашем интернет-магазине. Эти вышитые нашивки идеально подходят для одежды, головных уборов, рюкзаков и т. Д. Размер  2,5 х 12 см.

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

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

Звуковое оформление Ian

Меня пригласили в Factorio, чтобы закончить звук для игры для версии 1.0. Чувствовалось, что звукорежиссер нужен для работы в пражском офисе, чтобы помочь реализовать и улучшить звуки.

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

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

Звуки у каждого врага разные, но у кусак больше треска, а у плевак больше стука. Чем больше враг, тем больше слышно шаги. Эти звуки должны дать вам больше ощущения погружения в их мир, поскольку вы слышите, как они снуют к вам как раз перед тем, как вы их видите.

Что касается других звуков врага, мне показалось, что кусаки и плеваки звучали слишком похоже, и это то, что я хотел изменить. Если мы можем различить звуки, издаваемые врагами, это добавляет разнообразия, а также забавного фактора. Другой звукорежиссер Val сделал новые звуки для плевак на холостом ходу, чтобы подчеркнуть их мягкость и сделать их более отвратительными. А пока я добавляю эти звуки в игру, тестирую и настраиваю их. Например, я выбрал получше звуки для умирающих врагов, чтобы дать игроку более четкую обратную связь при убийстве.

Что касается других новостей, я был занят работой над улучшением всех звуков, как я считаю нужным, но я оставлю это на другой раз!

Анимированные деревья. Конечно Albert

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

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

Идея одушевления листвы деревьев старая как Factorio, но у нас никогда не было времени из-за очевидных других приоритетов. Однажды Том (wheybags) пришел с очень хорошим прототипом, и я очень увлекся этой идеей. Некоторое время спустя Ernestas создал новый прототип, основанный на различных методах. Это было также действительно интересно. Объект двигался довольно твердо, но этого было недостаточно. Затем у Виктора (Allaizn) возникла идея использовать карты нормалей деревьев вместо общего шума для перемещения листьев, и результат этого эксперимента был фантастическим.

Остальное объяснит сам Allaizn.

Интеграция шейдеров дерева — вы, вероятно, что-то забыли, если это работает с первой попытки Allaizn

Моей первой «большой» задачей было интегрировать созданный Ernestas шейдер в игровой движок, который был впечатляющим из-за его внешнего вида, но также несколько напряженным, учитывая, что до этого момента я редко смотрел на эту часть игрового кода. Первый шаг в этом, как правило, состоит в том, чтобы понять, как работает сам шейдер, поэтому позвольте мне дать вам небольшое объяснение того, что происходит.

Графический процессор визуализирует текстуру пиксель за пикселем, и каждый пиксель (грубо говоря) изначально знает только, где он находится на экране. Затем необходим шейдер, чтобы дать ему дополнительную информацию, необходимую для получения цвета, который он в конечном итоге должен иметь, где он немного напоминает игру «цвет по номерам» — мы готовим текстуру в качестве используемых цветов, а также передайте несколько чисел, которые сообщают ему, какую часть текстуры использовать (их техническое название — UV-координаты). При рендеринге спрайтов мы почти всегда хотим передавать эти числа таким образом, чтобы текстура копировалась на экран (см. Изображение ниже) — но ничто не мешает нам заранее связываться с этими числами;).

Передача в случайные UV-координаты обычно приводит к совершенно неузнаваемому изображению, но мы можем быть хитрее, чем это: мы можем передать число, проходящее в пиксель ниже обычного, или в пиксель выше — и если мы сделаем это стратегически, изображение выглядит так, как будто оно немного смещено. Измените это смещение во времени, и в результате появится небольшое движение по вашему изображению! Это смещение на один или два пикселя называется искажением, и оно обычно передается шейдеру с помощью второй текстуры (значения цвета которой мы просто интерпретируем как нужные нам значения сдвига), называемой «картой искажения».

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

  • Тестуры высокого против низкого качества
  • Без, высокое или низкое сжатие
  • Полная или половина глубины цвета.
  • Листья уменьшаются в количестве из-за повреждения загрязнения и имеют в общей сложности 4 стадии.
  • Листья обесцвечиваются с ростом загрязнения.
  • Деревья могут быть отображены как часть игрового мира или как часть графического интерфейса.

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

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

Три цветовых канала кодируют три основных свойства, с которыми должен работать шейдер:

  • Красный канал сообщает о смещении в горизонтальном направлении -отсутствие красного означает смещение в одном направлении, полный красный — в другом.
  • Зеленый канал сообщает то же самое для вертикального смещения.
  • Синий канал сообщает, насколько необходимо масштабировать искажения, чтобы учесть различные разрешения текстур — полный синий цвет приводит к отсутствию масштабирования, половинный синий — к 0,5, отсутствие синего не приводит к никаким искажениям вообще, поскольку он масштабируется на 0 ,

Тема заголовка также поразила меня по-другому: эффект сильно зависит от предоставленной карты искажений, и наша первая версия привела к тому, что внешний вид был лучше всего описан как «он выглядит хорошо, если вы выключите его, пока он не станет почти невидимым» — даже лучшая реализация в мире просто не имеет значения, если конечный эффект тоже не выглядит великолепно. Учитывая, что исходная карта искажений была в основном шумовой, я вместо этого попробовал совершенно противоположный подход: найти текстуру, которая сильно коррелирует с текстурой листьев дерева, и использовать ее вместо этого — в конечном итоге, расположившись на карте нормалей листьев дерева.

Сам шейдер использует только красный и зеленый каналы карт нормалей, что делает их прекрасной целью для сжатия BC5, которое мы до сих пор не использовали в игре (см. FFF-281 для получения дополнительной информации о сжатии). Через удивительно короткое время компрессия была запущена и работала — или я так подумал, поскольку меня снова поразило «Вы думали об этом?». На этот раз виновником было mipmapping, который не знал о сжатии и, таким образом, уменьшал масштаб сжатого изображения вместо того, чтобы снова распаковывать, уменьшать масштаб и снова сжимать.

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

Оптимизация Rseding

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

«Почему … так медленно?» -> «Потому что он должен проверять A, B, C и D каждый раз, когда выполняет какую-либо логику».
«Почему? Такого почти не бывает» -> «Потому что без проверок логика просто неверна».

Около 3 лет назад у меня была эта версия с манипуляторами. Манипулятор становятся одной из наиболее распространенных сущностей по простому факту: они должны быть, если вы хотите, чтобы что-то запускалось. Тем не менее, манипуляторы также являются одной из этих «более медленных» сущностей, где основная идея того, что они делают, кажется такой простой; «Как перемещение предметов от А до Б по дуге может оказаться таким медленным?» (относительно всего остального конечно).

И поэтому я посмотрел на результаты профилирования и код, на который он указывал:

  • Каждый тик; проверьте, есть ли в манипулятора источник энергии горения. Если это так:
  • Проверьте, не является ли источник энергии полностью пустым, и, если это так, отключите (нет топлива, он не может переместить топливо в себя, поскольку он никогда не может двигаться).
  • Проверьте, можно ли использовать предмет в руке манипулятора в качестве топлива для этого манипулятора. Если это возможно:
  • Переместите руку к самому манипулятору.
  • Каждый тик; проверьте, не удалился ли пункт назначения (телепортировался / автомобиль уехал).
  • Каждый тик; проверьте, не удалился ли источник (телепортировался / автомобиль уехал).
  • Каждый тик; проверьте, помечен ли объект или источник для деконструкции.
  • Каждый тик; проверьте, изменился ли источник силы.
  • Каждый тик; проверьте, существует ли включенное условие и не отключен ли он им.

Если все это пройдет, то переместите руку к исходной / конечной позиции.

Не удивительно, что в конечном итоге «медленно». Но вы не можете просто не делать ничего этого и говорить «все в порядке». Очевидный ответ на все эти вопросы: «это случается не часто / это крайние случаи; все они должны быть выполнены через события». Но тогда — как? Проще говоря; не было никакого события, которое мог бы использовать манипулятор, чтобы знать, когда эти вещи происходят, поэтому он должен был проверять каждый тик, если какой-либо из них произошел. Я закончил тем, что оставил это в покое и вернулся к работе над другими вещами. Но это всегда оставалось в моей голове; Готовим — пытаемся найти решение. Спустя 3 года я обнаружил это: таргетирование.

Целевые события.

Цели — это то, что мы создали и усовершенствовали за последние 2 года:

  1. Это указатели на исчезновение; когда объект, на который вы «нацелены», удаляется, ваш указатель очищается (устанавливается на nullptr).
  2. Это сохраняемые / загружаемые указатели; Вы можете сохранить указатель через save -> quit -> load.

Большинство вещей в игре, которые должны «указывать» на что-то еще, будут использовать их (конечно, за некоторыми исключениями). Манипуляторы используют их. Моя идея была довольно простой: все, что может быть «нацелено», может превзойти все, «нацеливаясь» на него, и дать ему знать, когда происходит какое-то редкое событие (все, что должен был проверять манипулятор, и некоторые другие). События не являются бесплатными — но поскольку эти случаи обычно не случаются, манипулятор, не выполняющий эти проверки, делает стоимость событий, когда они происходят, не имеет смысла в общих диаграммах производительности.

Как снежный ком

С радикально упрощенной логикой обновления манипулятор и с новыми событиями, управляемыми Целями, я начал замечать вещи:

  • В Бурах используются те же проверки, что и в манипуляторах, — поэтому они получили такую же обработку.
  • Локомотивы, грузовые вагоны и жидкостные вагоны имели такие же проверки; так что теперь им не нужно быть активными в 99% + случаях.
  • Модуль-запросчик синего треугольника имел такие же проверки; так что теперь им не нужно быть активными в 99% + случаях.
  • Роботы логистичиские и строительные проходили одинаковые проверки (перемещалась ли цель?), Поэтому теперь им не нужно это проверять.

После окончания тех, которые я перепрофилировал и у тех сущностей, которые занимали гораздо меньше времени, начали появляться разные интересные вещи:

  • Логика источника энергии горелки выполняла несколько медленных проверок поведения краевого случая.
  • Конвейеры проверяли O (N) каждый раз, когда они перемещали предметы, когда это можно было сделать за O (1) раз.
  • Все, что производило дым от потребления энергии, делало несколько медленных проверок, чтобы попытаться сделать дым, когда в большинстве случаев это было не нужно.

И, наконец, появилась последняя вещь: тепловые трубки. Каждый раз, когда я заставляю что-то работать быстрее, что-то еще занимает свое место в «сколько времени тратится на каждый тик» (это ожидаемо), но это также означает, что оно открывает новые вещи, которые я, возможно, раньше не замечал.

Тепловые трубы

Первое, что я заметил с тепловыми трубками: вся логика для тепловых труб даже не в самом объекте тепловых труб. Сущность просто переносит логику на класс «Тепловой буфер». Это заставило меня задуматься: почему вообще логика «обновления» проходит через сущность, если она ничего не делает? Несколько дней спустя и намного больше кода, чем я намеревался написать; Я переместил всю логику обновления для теплового потока в его собственный выделенный класс менеджера (почти идентичный тому, как у жидкостей и электрического потока есть класс менеджера).

Это выглядело слишком хорошо, чтобы быть правдой; то, что было 0,55 мс / тик, показывало 0,17 мс / тик (чуть более чем в 3 раза быстрее), просто не проходя через сущность каждый тик для создания теплового потока. Много тестов позже и результаты были правильными; это было намного быстрее. Базовый алгоритм не изменился, но теперь он работал в 3 раза быстрее, затрагивая меньше памяти. Это еще один хороший пример того, что «Factorio не связан с процессором, он связан с задержкой памяти». Больше ядер не делают быстрее из за скорости процессора ограничена

Итог

Электрические сети … Жидкостные сети … Сети с тепловыми трубами … ни одна из них не взаимодействует друг с другом или чем-то вне их. Что произойдет, если я просто обновлю все 3 параллельно? Не удивительно; каждый из них стал немного медленнее (потому что они конкурируют за доступ к памяти), но в целом он все еще был заметно быстрее. Интересная часть об этом, хотя: 1 из 3 всегда занимает значительно больше времени, чем остальные. Это означает, что другие в конечном итоге становятся «свободными»; в любом случае игра должна дождаться окончания самого медленного, поэтому более быстрые 2 из 3 завершают «бесплатную поездку» задолго до того, как заканчивается игра, ожидая самого медленного.

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

Как всегда, дайте нам знать, что вы думаете на форуме

Comments: