Истребители багов: прилепляем шасси

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

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

Тут у нас Gladius, для которого отображается вся информация из системы «Манекен». Вы видите переднее, левое и правое шасси и зеленый текст, который указывает, что шасси сдавлены. Сейчас шасси выпущены, а это значит, что проигрывается анимация сжатия, и при контакте с поверхностью шасси отреагируют на касание и спружинят. Если я уберу шасси, вы увидите, как при этом проиграется множество анимаций и звуков. Кажется, что все работает правильно, шасси продолжают убираться. И вот при попытке их снова выпустить они сломались. Теперь система пытается вытянуть их, но шасси резко перескакивают в конечное положение. Анимации выглядят нелепо. Кроме того, мы сломали сами состояния. К сожалению, «Манекен» указывает, что он пытается выпустить шасси, а значит, что корни проблемы уходят глубже. У нас есть уровень игрового кода, затем идет «Манекен», а еще выше стоит система анимации.

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

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

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

Работа посадочных шасси задумана следующим образом: у вас есть корабль, под днищем которого расположены шасси. При обработке анимаций мы спускаемся с уровня корабля до уровня прикрепленных объектов, которыми и являются шасси. Итак, когда наш корабль захочет обработать присоединенные объекты, шасси вызовут вот этот фрагмент кода, чтобы завершить воспроизведение анимации. То есть сначала анимируется транспортное средство, затем его шасси. Если я поставлю тут точку остановки, вернусь в игру, вызову Gladius, временно отключу точку остановки, чтобы все могло прогрузиться, переключусь на вид от третьего лица, а затем вновь активирую точку остановки, произойдет следующее: Шасси дойдут до этого момента, завершат свои анимации, а затем вызовут функцию расчета завершения анимации, которая должна будет прогнать через себя множество данных для синхронизации физики шасси и очистки всех старых анимаций. Давайте сделаем кое-что интересное. Поставим точку остановки вот тут, потому что у меня есть подозрение, что когда мы добираемся до этого момента, шасси не очищают старые анимации. Из-за этого алгоритм не должен проходить дальше этой точки. Следовательно я установлю отладочную точку остановки, чтобы узнать, что происходит. Перекомпилируем код.

Как вы могли видеть, я добавил точку остановки, и мои подозрения оказались верны – наш алгоритм практически мгновенно добирается до нее, тогда как шасси еще только начали выезжать. Что же тут происходит? Очевидно, что наша система анимации сказала функции, что действие завершено до того, как оно на самом деле завершилось. Эта часть не должна срабатывать, пока мы не закончим анимацию.

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

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

Итак, подытожим, что у нас получилось. Когда транспортное средство анимируется, оно говорит всем своим дочерним объектам, включая посадочные шасси, что им также следует запустить анимацию. В работу вступает обработчик присоединяемых объектов. Предполагается, что после завершения анимации будет вызываться функция расчета завершения, которая очистит все старые анимации для синхронизации потоков и окончательного завершения работы системы анимации. К сожалению, у нас тут остался некоторый старый код, который делал ту же самую работу поверх нового кода. Получилось вот что: мы завершили расчет анимации, всех костей, перемещений и сопутствующих вещей и вызвали новую функцию расчета завершения, однако алгоритм в действительности никогда не добирался до нее из-за действий старого кода. Конечно, самый простой путь исправить ошибку здесь – это удалить старый код и позволить работать новой функции. Давайте посмотрим, что же у нас вышло.

Мы вернулись в игру. Я обновлю корабль. Теперь мы выпустим шасси, и система будет проигрывать анимацию их выдвижения, что хорошо. Они больше не вытягиваются – теперь у нас осталось только положенное сжатие. Уберем их – всё работает исправно. Все присоединяемые предметы корректно очищают старые анимации, и всё хорошо. Надеюсь, вам понравилось.

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