Новости :: Переводы :: Наши проекты :: Ромхакинг :: GameFAQs :: Документация :: Утилиты
Гостевая книга :: Форум :: 2RTeam :: Zelda64Rus :: О нас


Только свистни


Чёрный Плащ - прохождение игры за 10:59.5



РОМ: Darkwing Duck (U) [!].nes
Жанр: 2D-платформер
Страница пробега: 951M
Фильм опубликован: 24.08.2007
Автор: АнС и Randil
Комментарии авторов: вот

Характеристики фильма:

* Максимально быстрое прохождение
* Ради ускорения тратится энергия
* Эксплуатируются ошибки в программе
* Игрок манипулирует удачей

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

Записывал я его вместе с одним шведом - Randil'ом. До этого на сайте уже имелся пробег ЧП, но он был записан на устаревшем эмуляторе Famtasia, в котором отсутствовали весьма важные инструменты, такие как Frame Advance, Memory Watch и Frame Counter. Естественно, с новыми тулзами можно было улучшить пробег на 15-20 секунд без особых заморочек.

Я не планировал заниматься этой игрой в 2007 году, но в результате болтовни на форуме и по ЛС Рандил предложил делать пробег вместе. Раз такое дело, было решено напрячься и отшлифовать прохождение по максимуму.

Ещё первыми спидраннерами этой игры было замечено, что анимация героя достаточно тесно вплетена в геймплей. Если вы на бегу захотите выстрелить, то Чёрный Плащ сначала остановится, вытащит газовый пистолет и элегантно выпустит заряд с дымком. Кроме того, перед началом ходьбы ЧП на долю секунды разгоняется на месте (без замедления это практически не заметно). Такие задержки в пробеге совершенно ни к чему, поэтому в течение всего прохождения наш ЧП будет безостановочно прыгать на одной лапе - придётся привыкнуть. Если герой касается замли хоть на один кадр - этот кадр, считай, потерян. Поэтому мы с Рандилом, не договариваясь, отслеживали статус ЧП во время прыжков с помощью Memory Watch - как только статус меняется с "воздух" на "земля", нужно вновь прыгать.

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

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

Например, можно использовать тот факт, что при отключении дополнительного оружия все находящиеся на экране стрелы (максимум две) резко падают вертикально вниз. Сделано это, по-видимому, для того, чтобы как можно быстрее очистить экран от объектов - так как на экране может одновременно находиться не более трёх пуль ЧП (стрел, молний или простого газа). Мне неоднократно помогла такая стратегическая хитрость - прилепить стрелу к стенке над предполагаемым местом появления врага (ха, уж с сэйвами я могу "предполагать" на все сто) и отключить стрелы в нужный момент. Когда ЧП мирно прыгал где-то внизу, ничего не понимающий враг получал удар ценой в 3 хита откуда-то сверху.

Или вот ещё приёмчик - одной стрелой можно отнять сразу 6 хитов, если задержать её внутри врага на время, пока он мигает после получения урона от этой же стрелы. Задержать стрелу можно либо всадив её в стенку, у которой стоит несчастный вражина, либо отключив стрелы прямо перед тем, как спрайт стрелы вылетит из спрайта врага. Таким несложным приёмом можно мгновенно сразить почти любого врага в этой игре.


В связи со стрелами был замечен ещё один интересный приём. Если где-то наверху стоит враг (так, что на экране видны лишь его лапы/колёса), то можно поразить его падающей стрелой! Казалось бы, враг высоко, а стрела, выпущенная снизу под врагом, падает только вниз - как она может телепортироваться на самый верх экрана? Но не забывайте, что мы имеем дело с видеоигрой под сильно ограниченную платформу.

Вспоминаем курс информатики. Так как все переменные на 8-битной приставке представлены одним байтом, то они могут принимать либо значение от 0 до 255, либо от -128 до 127 - это уж зависит от интерпретации числа (либо рассматриваем байт как число со знаком, либо как число без знака). Причём числа со знаком физически ничем не отличаются от беззнаковых чисел - просто код построен так, что если значение байта больше 127, то это будет отрицательное число. То есть если x=130, то в знаковой интерпретации этот же x=-126.


И вот что происходит, когда стрела (объект с координатами X и Y) падает вниз. В ходе падения постоянно проверяются соприкосновения стрелы с врагами, но исчезает выключенная стрела только при достижении нижнего края экрана (при Y>240, в координатной сетке 256 x 240). Но это если число, содержащееся в переменной Y, рассматривать как беззнаковое. А если смотреть на него как на число со знаком? То при Y=239 (за миг до исчезновения) наш знаковый Y=-17. Если враг стоит выше нулевой точки (скажем, его текущая координата Y=-20), процедура проверки коллизий посчитает, что произошло пересечение стрелы и врага.

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

Ладно, этот трюк помог ускорить пробег всего на секунду. Гораздо больше времени удалось сэкономить на уровнях с шипами.

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

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

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

Дальше больше. Благодаря отслеживанию параметров боссов, удалось победить их предельно быстро - первый удар наносился как можно раньше, а все последующие удары совершались в тот же кадр, в котором таймер мигания босса доходил до нуля. Некоторыми боссами пришлось ещё и манипулировать во время боя - например, Квагу на мосту необходимо всё время держать на самом нижнем ярусе, чтобы после поражения ему недалеко было лететь с экрана (тут тоже ведь время тратится).

Пока я игрался с AI боссов, Рандил заметил одну ценную особенность прыжков по крюкам/фонарям/платформам. Чтобы ухватиться за фонарь, нужно держать кнопку прыжка достаточно долго, иначе не допрыгнешь. Отпустить её можно лишь в тот миг (кадр), когда статус ЧП, судя по состоянию памяти, изменится на "04" (т.е. "висит"). В следующем кадре приходится отпускать кнопку прыжка и ещё через один - опять нажимать, чтоб прыгать далее. Рандил раскопал, что если за миг до смены статуса отпустить кнопку прыжка, но нажать кнопку выстрела, то ЧП всё же уцепится за крюк, хотя прыжок уже отпущен. В итоге такой комбинации можно будет нажать кнопку прыжка (покинуть крюк) на 1 кадр раньше.

Скажете, 1 кадр - мелочи? Но таких мест в игре полным-полно! И если бы не эта находка, нам бы не удалось довести игру до отметки в 11:00.

Помимо поиска новых трюков, в течение работы над проектом шла постоянная шлифовка управления - где-то приходилось использовать гравитацию для быстрого спуска, где-то полезно на миг споткнуться о стенку, чтобы потерять полпикселя (даже на такой простой приставке, как Денди, координаты персонажа зачастую выражаются вовсе не целым числом!)

Вот примерно так выглядел экран в процессе записи прохождения.


Практически всё, что я знал о TAS на этот момент, пригодилось при работе над DW. Много новых идей нашли своё применение. Но самым заметным достижением конкретно этого спидрана я считаю управление лагом.

Лаг - это, грубо говоря, тормоза игры. В нормальном состоянии японские и американские NES-игры должны выдавать 60 FPS, но иногда в кадре собирается так много объектов, что слабенький процессор NES не успевает обработать их все до истечения одной шестидесятой секунды. А ведь 60 раз в секунду (NTSC) наступает VBlank, и на экране мы увидим недообработанный кадр, скорее всего - повторение предыдущего кадра (процессор ещё не записал новые данные в видеопамять). Если таких потерянных кадров немного - их трудно заметить, но если ситуация со скоплением объектов продолжается на протяжении долгого времени, любой зритель/игрок заметит "падение FPS" и общее замедление игрового процесса.

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

И тут Рандил подал идею интересного инструмента. Хорошо бы иметь, во-первых, чёткий индикатор лага (а то ведь по изображению на экране не всегда понятно), а во-вторых, некий счётчик потерянных от лага кадров. Этот счётчик и будет своеобразной функцией лага от времени! Управляя персонажем, мы будем манипулировать этой зависимостью, где-то позволяя лагу повысить своё значение, а где-то не позволяя ни в какую.

Чтобы реализовать такой инструмент, пришлось бы добавлять код в FCEU, да и формат сэйвов пришлось бы менять (очень важно связать состояние счётчика-Функции вместе с сэйвом-Аргументом). Тогда я вспомнил, что вообще-то занимаюсь, помимо всего прочего, ромхакингом. Можно же вставить инструмент не в эмулятор, а в саму игру!

Далее идут практические выкладки. Берём РОМ DarkwingDuck(U).nes, дебажим код и находим два местечка для инъекции кода своего инструмента. Первое место - обработчик NMI, то есть процедура, которая срабатывает раз в 1/60 секунды, аккурат при VBlank. В эту процедуру я добавил инструкцию увеличения новой переменной в неиспользуемой части памяти (слава богу, у ЧП было свободное место в RAM). То есть мы наращиваем значение этой переменной в каждом кадре, независимо от того - ценный ли это кадр, или это кадр с лагом.

Второе местечко - это процедура опроса кнопок джойстика. Давно замечено, если в кадре наблюдается лаг, то игра не будет реагировать на нажатия джойстика, т.к. процессор ещё не добрался до обработки процедуры опроса управления. В общем, в эту процедуру я добавил инструкцию уменьшения нашей новой переменной. Если в течение секунды игрового времени не будет лага, то эта переменная (счётчик) 60 раз увеличится и 60 раз уменьшится, оставаясь всё время на нуле. Ну а если в кадре будет лаг, счётчик будет уменьшаться реже, чем увеличиваться. Всё просто.

И вот, всё добавлено, всё работает, причём такой инструмент будет работать и на железной NES! И уже хочется ринуться в бой с лагом (а в этой игре лага МНОГО). Но есть одно "НО" - мы же записываем фильм для оригинального РОМа, а не этого хака. Конечно, отличия очень незначительны, и можно надеяться, что запись кнопочных нажатий одинаково сработает как в хаке, так и в оригинале.

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

Пришлось почесать башку и учесть количество процессорных тактов, затрачиваемых на дополнительные инструкции. Всего-то 10 тактов на INC и DEC! Тогда я проанализировал код обработчика NMI и обработчика ввода, нашёл способ оптимизировать этот код, в результате сэкономив ровно 10 тактов. Ура! Обе версии игры синхронизировались, и можно было без проблем играть в хакнутую версию, генерируя при этом кнопочные комбинации для оригинальной версии.

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

Было ещё много полезных находок, мы с Рандилом набрались опыта не только в конкретной игре, но и в совершенствовании искусства Superplay в целом.

Работа над проектом длилась около 4 месяцев, в основном из-за того, что оба автора одновременно работали над несколькими проектами. И хотя поначалу казалось, что рекорд прохождения даже теоретически невозможно перевалить за отметку в 11 минут, в ходе упорной оптимизации нашлось столько мелких улучшений, что в конце-концов нам удалось выйти за этот предел - ровно на полсекунды.

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

Но именно так, на грани поражения, в сфере инструментальной игры и создаются шедевры.


Возможные улучшения: сокращение лага в уровне Вульфдака.

АнС, 14.09.2007
Ссылками мы не меняемся.
Страница админа
        © 2001-2014 Shedevr Team.