Линия прицела, ошибавшаяся в 17 раз
· 4 мин чтения
В игре с рогаткой пунктирная линия — это весь договор с игроком. Вы оттягиваете, точки показывают, куда пойдёт выстрел, вы отпускаете. Если точки врут, учиться нечему — остаётся только удача и ощущение, что игра жульничает.
Первый плейтест ScaleSling вернулся с двумя фразами: «крепости падают сами» и «летит не туда, куда показывает линия». Обе были правдой, и ни одна не была вопросом подкрутки баланса. Этот пост — про вторую. Крепостям посвящён отдельный пост.
Что делала линия
Оттяните дракона под любым углом — и пунктирная подсказка уходила вверх за край экрана и больше не возвращалась. Ни под крутым углом, ни под пологим, ни на полной силе, ни при лёгком толчке. Дракон тем временем летел по совершенно обычной дуге и приземлялся там, куда линия никогда не указывала.
Подсказка работает так: она шагает воображаемым снарядом вперёд во времени: стартует с рогатки, прибавляет скорость, прибавляет к скорости гравитацию, повторяет и ставит точку каждые несколько шагов. В этом цикле гравитация прибавлялась как значение настройки гравитации, умноженное на длину одного шага.
Физический движок под игрой так не делает. Он применяет гравитацию как силу, а сила превращается в изменение скорости через длину шага в квадрате. При шестидесяти шагах в секунду один шаг — это около 16,7 миллисекунды, так что подсказка использовала примерно одну семнадцатую той гравитации, которую на самом деле ощущал дракон. Снаряд с одной семнадцатой своей гравитации делает ровно то, что показывала линия: поднимается и продолжает подниматься.
Поверх первой лжи была вторая, поменьше. У дракона есть сопротивление воздуха, которое съедает около половины скорости за две секунды. Подсказка его игнорировала. Исправь мы только гравитацию — получили бы линию, которая опускается правильной формы, но не в том месте.
Исправленный шаг — две строки: умножить скорость на сопротивление, затем прибавить гравитацию с квадратом шага. А значение сопротивления теперь живёт в одном общем файле настроек, а не внутри дракона, потому что оно нужно двум разным кускам кода, и они больше никогда не должны расходиться.
Проверять не на глаз
Соблазнительный способ проверить такое исправление — пару раз оттянуть рогатку и посмотреть, правильно ли выглядят точки. Именно так ошибка и выжила изначально: линия, которая ошибается немного, выглядит правдоподобно, а одинаковый выстрел дважды никто не повторит.
Поэтому теперь есть небольшой скрипт, который запускает настоящий физический движок — без браузера и без экрана, с собственными константами игры. Он запускает тело под набором углов и сил, шагает его вперёд и сравнивает, куда оно летит на самом деле, с тем, куда по арифметике подсказки должно.
До исправления расхождение через 90 шагов составляло 1300 пикселей. После — ноль, и точка приземления совпадает до пикселя. Скрипт отрабатывает меньше чем за секунду и запускается заново всякий раз, когда кто-то трогает подсказку, настройки физики или тело дракона.
Зонд, который тоже врал
Пока мы за этим гонялись, у нас был автоматический игрок — скрипт, который стреляет по каждому уровню и сообщает, какие можно пройти. Он доложил, что уровень 2 выиграть нельзя, а уровню 4 вообще невозможно нанести урон.
Оба вердикта были ложными, и причину стоит знать, если вы работаете с физическим движком, который позволяет телам засыпать. Чтобы экономить работу, движок перестаёт симулировать всё, что какое-то время оставалось неподвижным. Дракон, ждущий в рогатке, неподвижен, значит, он спит. Задание скорости спящему телу записывает число, которое движок никогда не читает: выстрел покидал рогатку со скоростью 17,9 и оставался ровно на месте — навсегда.
Человек этого ни разу не видел, потому что перетаскивание дракона двигает его каждый кадр и не даёт уснуть. Проваливался только выстрел без перетаскивания — а именно так и стреляет автоматический тестировщик. Каждое «непроходимо» в том отчёте было драконом, который ни разу не сдвинулся, а мы из-за этого уже начали менять материалы уровней.
Исправление — один вызов, который будит тело перед запуском. Урок шире: прежде чем верить машине, которая говорит, что что-то невозможно, проверьте, что проверенное ею действительно произошло. Теперь зонд записывает, сколько пролетел каждый выстрел. Нулевой урон при нулевой дистанции — это сломанные измерения, а не сложность.
Что изменилось для игрока
Пунктирная линия теперь и есть траектория. Где она заканчивается, там приземляется дракон, при любом угле и любой силе, и это проверяет машина, а не человек, щурящийся в экран. Если вы уже играли в ScaleSling и рогатка казалась вам нечестной, вам не показалось. Теперь это исправлено.
