Истребители багов: фреймворк для размещения объектов

Истребители багов: фреймворк для размещения объектов

Истребители багов: фреймворк для размещения объектов

Выпуск от 17 января 2018г.

Марк Эйбент вернулся и в этом эпизоде «Истребителей багов» он покажет, что входит в процесс создания фреймворка, который позволит сразу нескольким кораблям появляться в игре практически без лага.

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

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

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

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

Избавимся от блочного механизма размещения сущностей. Для этого зададим функцию создания сущности и укажем ей параметры, которые определяют что, где и когда следует подгрузить. И тут есть еще один момент. Поскольку этот вызов будет асинхронным, мы как и прежде сможем получить к нему доступ только после завершения процесса размещения сущности. То есть придется подождать одну-две секунды. Это не позволяет нам обрабатывать что бы то ни было сиюминутно, поэтому нам нужно, чтобы после окончания размещения сущности функция возвращала ответ, сигнализирующий о выполнении задачи. Воспользуемся новой функцией C++, называемой Лямбдой, где возьмем сигнатуру обратного вызова. Эту роль выполняет ID сущности (в данном случае это будет ID нашего корабля).

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

Зафиксируем внесенные изменения, откомпилировав код. Однако на данный момент он еще не заработает. В текущем виде код позволит транспортному средству корректно появиться, но его предметы все еще будут заблокированы. Это проблема, потому что теперь мы размещаем корабль на стороне сервера, но одновременно с этим мы делаем блочное размещение всех его предметов. Так что мы не получим никаких преимуществ. Нам нужно также сконвертировать систему предметных портов в этот новый фреймворк.

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

Со стороны это все выглядит странно. Работает оно примерно так: "Эй, я хочу разместить эту штуку. – Хорошо, и вот обратный вызов, необходимый, чтобы в дальнейшем ее можно было обработать." Нужно написать код, который бы сказал: "Хорошо, размещение сущности завершилось, и что мне теперь нужно делать? Нужно запихнуть ее в какой-то объект, который мне понадобится в дальнейшем." И тут вступает другой фрагмент, который говорит: "Так, это произойдет позднее, поэтому мне нужно сохранить все эти вещи (переменные, параметры размещения), для чего используем механику захвата." Копируем параметры, а потом еще и еще... наконец у нас есть все, что нам нужно. Теперь после размещения сущности мы сможем получить доступ ко всем необходимым данным.

Прекрасно, но здесь используется предметный порт, поэтому нам нужно следить за тем, хотим ли мы отменить процесс. Сам корабль мы разместили и забыли, но что касается его предметов, то какая-то деталь может отделиться от корабля до того, как мы успеем прикрепить ее. Поэтому нам нужно знать, когда происходят подобные ситуации. К счастью, тут уже реализован механизм, который отменяет работу по размещению желаемой сущности. Это выглядит примерно так: "Я хочу разместить этот объект, но если в будущем что-то пойдет не так, я хочу иметь возможность отменить или сбросить весь процесс." Вот пример попроще. Я говорю: "Я хочу разместить на корабле эту кружку через 10 секунд." Но через 5 секунд я удаляю корабль. Без возможности отмены кружка появится и попробует получить доступ ко всему этому коду. Поэтому нам нужна функция, которая при удалении предметного порта отменит все запланированные для него задачи по размещению сущностей.

Теперь наши транспортные средства и предметные порты асинхронны. Снова компилируем код и загружаем тестовый уровень. Если все было выполнено верно, ничего не должно поменяться. Мы вызываем наш Cutlass, вызов обрабатывается, после чего корабль появляется в игре со всеми предметами на своих местах – все как раньше. Теперь у нас есть фреймворк для любых асинхронных размещений объектов.

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

Резюмируя выпуск, вы стали свидетелями небольшого забавного эксперимента, где мы пытаемся построить фреймворк для фонового асинхронного размещения сущностей. В результате вашей игре не помешает какой-то парень, пытающийся разместить свой Cutlass (а может быть и Idris, а это уже целый летающий уровень) в двух шагах за вашей спиной. Мы хотим создавать сущности в фоне, чтобы они просто интегрировались в существующее окружение, а не останавливали вашу игру на 10 секунд. Но потребуется некоторое время на реализацию этой технологии, а одним из ее элементов как раз является этот асинхронный фреймворк. Сначала мы создаем фреймворк, затем начинаем работать над небольшими фрагментами тут и там и в итоге получаем полную асинхронную систему, использующую фоновые потоки.

Надеюсь вам понравилось. Увидимся в следующий раз.

H_Rush administrator