Почему наши крепости падали сами
· 4 мин чтения
У игры про физическое разрушение есть обещание, которое важнее всех остальных: то, что вы собираетесь снести, стоит. В ранних сборках ScaleSling некоторые крепости дрожали при загрузке уровня, а несколько просто падали, пока игрок ещё прицеливался для первого выстрела.
Очень легко свалить это на физический движок и начать крутить трение и плотность, пока шатание не прекратится. Мы чуть так и не сделали. Ни одна из причин в итоге не оказалась в движке.
Двадцать четыре пары блоков, рождённых друг в друге
Каждый уровень — это список блоков с позицией и размером, и каждое из этих чисел было набрано вручную. Доска шириной 42 пикселя, поставленная в 32 пикселях от соседней, заходит на неё на десять. На экране десять пикселей перекрытия не видно — спрайты просто выглядят соприкасающимися.
Два твёрдых тела не могут занимать одно и то же место. На самом первом шаге симуляции движок обнаруживает перекрытие и с силой расталкивает их. Этот толчок игрок и видел как дрожь, а в высокой стопке его хватало, чтобы обрушить всё целиком.
Скрипт, прочитавший все уровни, нашёл 24 перекрывающиеся пары в 16 уровнях из 24, с перекрытием от 3 до 11 пикселей. Второй скрипт разводит каждую пару вдоль той оси, где перекрытие меньше, — почти всегда это та ось, по которой автор уровня и хотел их состыковать, — по половине расстояния каждому блоку, за несколько проходов, чтобы исправление одной пары не создавало другую. Повёрнутые блоки он не трогает: реальный след наклонённой доски — это не её ширина и высота.
Забор без столба
Пять уровней делили забор, который копировали из одного в другой: балка длиной 130 пикселей, лежащая на единственном столбе. Её центр масс находился в 39 пикселях за опорой. Она опрокидывалась каждый раз и падала на 48 пикселей, прежде чем кто-нибудь к ней прикасался.
Первая мысль была сдвинуть балку обратно на столб. Попробовали — и вдвинули её в соседнюю, более длинную балку, что ещё хуже. Балка была не слишком длинной; у забора не хватало столба на дальнем конце. Его добавление исправило все пять уровней разом.
Откуда мы знаем, что исправлено
Не на глаз: теперь есть проверка устойчивости, которая загружает каждый уровень в настоящий физический движок, ничего не запускает, ждёт четыре секунды симуляции и измеряет, насколько сдвинулся каждый блок.
До исправлений шесть уровней рушились сами, а худший блок сдвигался на 48 пикселей. После — ни один, и самое большое смещение где-либо составляет 10 пикселей: обычная усадка стопки, которая находит покой. Проверка работает без браузера, поэтому запускается при каждом изменении уровня.
Блок, который вышел в полтора раза выше
Одна причина вообще была не в данных уровней. Доска, заказанная размером 26 на 88 пикселей, попадала в мир размером 26 на 131. У тролля с радиусом коллизии 27 радиус был 20.
Фреймворк, на котором построена игра, масштабирует физическое тело объекта вместе с его картинкой. Объявить тело нужного размера, а потом подогнать спрайт по размеру — значит отмасштабировать тело второй раз, на отношение между файлом изображения и заказанным размером. Никакой ошибки ничего не выводило. Башни просто стояли в двадцати пикселях над землёй, а выстрелы проходили сквозь то, что выглядело твёрдым.
Исправление — сначала задать размер картинки, а потом заменить тело точным. Найти проблему удалось только прямым измерением тел, а не чтением кода, который выглядел правильным, — и теперь так проверяется каждый новый тип объектов.
Ещё одна ловушка: звёзды
Каждый уровень даёт три звезды за 95% от общей суммы очков на поле. Эта цель записывается в файл уровня при его генерации, а не вычисляется при запуске игры.
Когда из шести уровней убрали дирижабли, каждый потерял 900 очков того, что можно разрушить, — а цель на три звезды по-прежнему их требовала. Шесть уровней, которые можно было пройти идеально, стали уровнями, где это невозможно, а само изменение выглядело как один удалённый объект. Теперь любая правка того, что стоит на поле уровня, пересчитывает его звёзды в том же изменении.
Общий вывод
Падающая конструкция выглядит как проблема физики и поэтому напрашивается на физические исправления: больше трения, блоки тяжелее, симуляция медленнее. Любое из них спрятало бы настоящие ошибки и сделало бы удары в игре хуже на ощупь. Причины были в данных (набранные вручную координаты, скопированный забор, устаревшее число), и нашло их измерение.
Крепости в ScaleSling теперь стоят, пока вы их не снесёте. Попробуйте снести одну.
