Старкрафт, как он был показан в июне 1996 на Electronic Entertainment Expo - да, я тоже не стал бы в это играть.
Но проект с более высоким приоритетом затмевал Старкрафт и крал его разработчиков одного за другим. Diablo. РПГ, разрабатываемая Condor Studios в Редвуде Калифорния, требовал дополнительных усилий. Condor, кампании созданной Dave Brevik вместе с Max Schafer, был выделен бюджет 1,2 миллиона долларов - смешной даже по меркам того дня.
У Condor не было шансов в одиночку создать ту игру, которая от них требовалась, но они проделали такую потрясающую работу по разработке чего-то очень захватывающего, что Blizzard решила поглотить Condor, переименовать его в Blizzard North и начать вливать в проект те деньги и ресурсы, которые он действительно заслуживал.
Изначально Collin Murrey, программист Старкрафта, и я полетели в Редвуд помогать, пока остальные разработчики в штаб-квартире Blizzard в Ирвине, Калифорния работали над сетевыми провайдерами для battle.net, модемами, LAN играми, экранами интерфейса, ответственными за создание персонажа, присоединению к игре и другими мета-функциями.
По мере того как Diablo рос, все в Blizzard: художники, программисты, дизайнеры, инженеры по звуку, тестеры; начали работать над ним, и в конце концов в штаб-кваритре Blizzard не осталось никого, кто работал бы на Starcraft. Даже глава проекта был переведен, чтобы помочь закончить инсталлятор игры, который я написал наполовину, но был слишком занят, чтобы закончить.
После выхода Diablo в конце 1996 разработка Старкрафта была начата заново. Все увидели во что превращается игра и это никому не понравилось. Игра была устаревшей, совершенно не впечатляющей, особенно в сравнении с проектами вроде Dominion Storm, который выглядел превосходно в демо-версии, показанной на E3 шестью месяцами ранее.
Огромный успех Diablo повысил ожидания от проекта следующей игры Blizzard. Старкрафт стал игрой, которая определила политику Blizzard не выпускать игр, пока они не будет полностью готовы. Но много неприятностей случилось на пути к осуществлению этой стратегии.
Есть что доказывать
Все смотрели на Старкрафт очень критически, было очевидно, что проект должен быть гораздо амбициознее, чем наши предыдущие потрясающие достижения, определившие облик RTS, которыми были первые два Warcraft.
На момент перезагрузки Старкрафт по данным John Wilson, главного редактора Computer Gaming World, самого многотиражного журнала о компьютерных играх того времени, в разработке находилось более 80 (!!!) RTS. Имея столько конкурентов, включая Westwood Studios, кампании которая буквально придумала жанр RTS, мы должны были сделать что-то, что надрало бы всем задницы.
Мы больше не были темной лошадкой: после успеха Diablo и Warcraft нам не было бы никакой поблажки от игровой прессы и игроков. В мире игр ты хорош настолько насколько хороша была твоя последняя игра. Нам нужно было шагнуть гораздо дальше чем ранее, и для этого нужно было пойти на риск.
Новые лица.
У Warcraft II было только шесть программистов, которые работали над движком и два программиста поддержки. Этого было явно недостаточно для масштаба Старкрафт, так что команда разработчиков получила пополнение - новых и непроверенных программистов, которым еще предстояло научиться писать игровой код без постоянного руководства.
Но наше управление было слабовато: мы сами еще не поняли как важно дать нужные наставления менее опытным членам команды, чтобы они могли выучить важные уроки ДО того как игру нужно выпустить, так что во многом это было предложением типа “выплыви или утони” для новых падаванов. Большую проблему представляло то, что под нами горела земля - каждый программист кодил как сумасшедший, чтобы выполнить поставленные задачи, не отвлекаясь на то, чтобы перепроверить код или на обучение.
Но опыта недоставало не только молодым членам команды. Ведущим программистам Старкрафт до тех пор ни разу не приходилось разрабатывать полностью готовый к выпуску игровой движок. Bob Fitch писал игры уже несколько лет и с большим успехом, но он только портировал игры, где он работал с уже готовым движком, он разрабатывал вспомогательные элементы Warcraft I и II, не занимаясь полномасштабным написанием движка. И хотя он был главным программистом Shattered Nations, этот проект был отменен, и никакой проверки разработанной архитектуры не проводилось.
Команда проекта проявляла небывалую самоотдачу и прилагала неслыханные усилия чтобы завершить проект, жертвуя своим здоровьем и личным временем. Никогда более я не работал над проектом, где каждый проявлял бы такую самоотдачу. Однако несколько ключевых решений этого проекта, которые я хочу описать, будут преследовать команду разработчиков до самого выхода игры.
Кое-что надо поменять.
Итак, потратив месяцы на выпуск Diablo, и еще месяцы на зачистку концов и выпуск патчей, я вернулся, чтобы помочь перезапустить Старкрафт. Я не предполагал, что меня бросят в полный багов муравейник, но так оно и вышло.
Я думал, что без труда погружусь в этот проект, поскольку великолепно знал код Warcraft - я сам работал над всеми компонентами игры. Но вместо этого я в ужасе выяснил, что большинство компонентов движка были выброшены и переписаны.
Классы юнитов были написаны с чистого листа, диспетчер юнитов выброшен и переписан. Диспетчер юнитов — это механизм, который обеспечивал выделение каждому юниту времени, чтобы он мог осуществить планирование своего поведения. Каждый юнит периодически спрашивает: “Что я должен делать, когда закончу делать то, что я делаю?”, “Нужно ли мне пересчитать путь туда, куда я направляюсь?”, “Нет ли цели получше той, которую я атакую сейчас?”, “Не дал ли мне пользователь новую команду?”, “Я умер. Как мне прибрать за собой?” и так далее.
Есть хорошие причины на то, чтобы переписать код, но это несет огромные риски. Как отлично сказал Joel Spolsky в “Вещах которые тебе не следует делать” :
“Важно помнить, что когда вы начинаете с чистого листа нет никакой гарантии, что вы сделаете все лучше, чем в прошлый раз. Во-первых у вас скорее всего не совсем та команда, что в прошлый раз, так что неверно говорить что “теперь у нас больше опыта”. Вы заново сделаете все старые ошибки и добавите несколько новых проблем, которых не было в оригинале”
Месяцами движок Warcraft доводился до ума, и хотя его надо было переработать, чтобы добавить в игру новые фичи, новой команде программистов предстояло потратить очень много времени чтобы на своей заднице понять как был создан движок Warcraft и почему его архитектура такова как она есть.
Архитектура движка игры
Я написал оригинальный движок Warcraft для MS DOS на С, использовав компилятор Watcom. C переходом на Microsoft Windows Боб выбрал компилятор Visual Studio и переделал движок на С++. И то и другое было разумным решением, но факт был в том, что на тот момент у немногих разработчиков был опыт работы с этим языком и всеми его ловушками.
С++ имеет преимущества, но его легко использовать неверно. Как сказал создатель языка Bjarne Sroustrup : “На С легко выстрелить себе в ногу. На С++ это сложнее, но если это произойдет, вам оторвет всю конечность”
Опыт говорит, что программисты чувствуют обязанность использовать каждую фишку нового языка во время первого же проекта, и именно так произошло с наследованием классов в Старкрафт. Опытный программист вздрогнет, когда увидит дерево наследования юнитов старкрафта:
CUnit < CDoodad < CFlingy < CThingy
CThingy были спрайтами, которые могли появляться в любом месте карты, но не двигались и не имели поведения, CFlingy использовались для создания частиц, когда происходил взрыв несколько разлетались в разных направлениях. CDoodad (по прошествии 14 лет мне кажется, что именно так назывался этот класс) был абстрактным классом содержащим важные поведения, необходимые для функционирования экземляров наследуемых классов. А CUnit был поверх всего этого нагромождения. Его поведение было разбросано по всем этим модулям и необходимо было полное понимание как работает каждый из них, чтобы добиться от него хоть чего-нибудь.
А поверх всего ужаса сложной иерархии наследования сам класс CUnit был богомерзким нагромождением разбросанным по множеству файлов:
class CUnit ... {
#include "header_1.h"
#include "header_2.h"
#include "header_3.h"
#include "header_4.h"
};
Каждый из этих заголовков содержал по несколько сот строк, так что объявление класса можно назвать, мягко говоря, занимательным.
Лишь много лет спустя мантра “структура превыше наследования” (favor composition over inheritance) набрала авторитет среди программистов, но те, кто работал на Старкрафтом выяснили это тяжелым путем гораздо раньше.
Два месяца до выхода
Испытывавшая проблемы с самого начала, после перезагрузки команда разработчиков находилась под огромным давлением. Чтобы сдать игру вовремя, сроки были сжаты до предела — по плану до выхода игры оставалось два месяца.
С учетом количества поведений, которые предстояло еще дописать, изменений необходимых, чтобы перейти от плоских макетов к изометрическим, с новым редактором карт и добавлением battle.net для игры по интернету это было совершенно нереально, даже если предположить что художники, дизайнеры, ответственные за баланс и тестеры могли завершить свою работу к сроку. Но команда программистов постоянно работала исходя из предпосылки о выходе игры через два месяца в течении четырнадцати месяцев!
Вся команда работала сверхурочно, а Боб программировал по 40, 42 и даже 48 часов к ряду. На моей памяти никто не более не предпринимал более подобные мазохистские попытки, каждый перерабатывал огромное, нереальное количество времени.
Мой опыт разработки Warcraft и Diablo с частым всенощным программированием, когда я кодил по 14 лишних часов кряду 7 дней в неделю, заставили меня думать, что такое геройство совершенно бессмыленно. Весь код, написанный после какой-то точки, будет проклят и переписан на свежую голову следующим днем.
Отрабатывая такие длинные часы люди переутомляются, а это плохо, когда требуется решать задачи, требующие знаний, интеллекта и креативности, так что не приходится удивляться количеству ошибок, багов и неверных решений.
Кстати, никто не принуждал нас работать сутками — мы сами так делали, потому что хотели делать великолепные игры. Задним умом ясно, что это было глупостью. Мы могли достичь лучших результатов меньшими усилиями.
Моим лучшим достижением я считаю выпуск четырех частей Guild Wars за два года, не ведя команду разработчиков по этому темному пути.
Наиболее распространенные причины вылета в Старкрафт
И хотя я разработал такие важные компоненты Старкрафта как туман войны, линия видения, поиск пути и столкновение летающих юнитов, голосовой чат, точки для подкреплений ИИ и многое другое, моей главной работой был отлов багов.
Стоп? Голосовой чат?! В 1998?! Ага — я проделал всю работу в декабре 1997. Я использовал компрессор стороннего разработчика, и написал код рассылки фонем на компьютеры семи остальных игроков, распаковки и проигрывания.
Но каждая звуковая карта в нашем офисе потребовала обновления драйверов чтобы это работало, даже если она имела полный дуплекс (возможность писать и проигрывать звук одновременно). И я с сожалением принял решение отказаться от этой идеи. Тех. поддержка была бы так завалена работой, что мы потратили бы больше денег на неё чем заработали от продажи игры.
Как бы то ни было я пофиксил кучу багов. Некоторые, конечно, свои, но в основном сложные для поимки ошибки написанные усталыми программистами. Один из лучших комплиментов я получил всего несколько месяцев назад, когда Brian Fitzgerald, один из двух лучших программистов с которыми мне довелось работать упомянул об обзоре кода Starcraft. У них крышу снесло от количества исправлений и изменений, которые я сделал по всему базовому коду. Ну хоть с опозданием я получил должную оценку!
С учетом всех описанных проблем, вам может показаться сложным идентифицировать единый источник большинства багов, но по моему опыту самые большие проблемы в Старкрафте были связаны со списками с двойным связыванием.
Связные списки активно использовались в движке для отслеживания юнитов с совместным поведением. Так как Старкрафт имел максимум в 1600 юнитов, вдвое больше, чем в его предшественнике - Warcraft II, стало необходимым оптимизировать поиск юнитов определенных типов с помощью содержания их в связных списках.
Насколько я помню, там были списки юнитов и строений каждого игрока, списки генерирующих зданий, списки перехватчиков каждого авианосца и многие другие.
Все они были списками с двойным связыванием, дабы можно было добавлять и удалять элементы за постоянное время, не проматывая все элементы с первого, чтобы найти элемент для удаления.
К сожалению каждый список создавался вручную без использования единого набора функций для привязывания и отвязывания элемента от списка — программисты каждый раз вручную писали такие методы, когда возникала необходимость. Такой код гораздо больше подвержен ошибкам, чем использование единого набора отлаженных функций.
Многие связи были общими для нескольких списков, так что было необходимо точно знать к какому списку привязан объект, чтобы безопасно исключить его из списка. А некоторые связи хранились в объединениях Си (unions) вместе с другими данными, чтобы свести к минимуму использование памяти.
Так что, игра падала все время. Все время.
Так зачем вы сделали это таким образом?
Трагедия в том, что не было никакой особой причины для всех этих проблем со связными списками. Mike O’Brien, который вместе со мной и Jeff Strain основал ArenaNet, написал библиотеку под названием Storm.DLL которая поставлялась вместе с Diablo. Помимо многих других вещей она содержала отличную реализацию списков с двойным связыванием на основе шаблонов С++.
На ранних этапах разработки Старкрафт мы использовали эту библиотеку. Но в какой-то момент команда разработчиков выкинула эту библиотеку и вручную реализовала все связные списки, это помогло, к примеру, упростить сохранение игры.
Позвольте немного подробнее сказать о сохранении, чтобы прояснить это.
Сохранение игр.
У многих игр в, которые я играл, был дерьмовый функционал сохранения. Игроманы, которые играли в любую игру от Orign помнят, как доооолго записывался сэйв. Понятно, их писали медленные микропроцессоры на жесткие диски которые по сегодняшним стандартам выглядят как трехколесные велосипеды по сравнению с мотоциклом. Но не было реальных причин для них быть таким отстоем. Я сразу решил что в Warcraft не будет подобных проблем.
Warcraft использовал пару приемчиков, чтобы писать большие блоки данных одним куском, вместо того, чтобы метаться по памяти, записывая байт там, байт здесь. Весь массив юнитов (600 юнитов по паре сотен байтов на юнит) должен был записываться в один проход. И все глобальные переменные, не основанные на указателях, тоже писались в один проход — как местность и туман войны на картах.
Удивительно, что способность писать все юниты на диск разом не так уж влияла на скорость записи сохранений, но драматически упрощала код. Это работало в основном из-за того, что юниты Warcraft не содержали указателей.
Юниты Старкрафт, как я уже сказал, были совсем другим зверем и содержали кучу указателей для реализации связных списков. Необходимо было произвести адресную привязку для всех этих указателей (а еще были поля указателей в объединенниях), записать 1600 юнитов за один раз, а потом вернуть указатели на место, чтобы продолжить игру. Всего-то.
Вернемся назад!
Так что, пофиксив много много много багов в связных списках, я неистово убеждал всех, что мы должны вернуться к использованию Storm.DLL, даже если это усложнит код сохранения. Когда я говорю “неистово убеждал”, я должен упомянуть, что это единственный способ спорить, который использовался в Blizzard - со всей юношеской горячностью и заносчивым высокомерием. Единственным вопросом, по которому в Blizzard не возникало неистовых споров это то, что есть на ланч.
Я не выиграл тот спор. Так как до выхода оставалось всего “два месяца” никто не хотел переписывать движок, довольствуясь местным заплатками: псевдо-оптимальное решение, которое породило месяцы страданий и кардинально повлияло на мой личный подход к программированию.
Еще заплатки: нахождение пути в Старкрафт
Хочу привести еще пример того как залатывание дырок выбирается вместо устранения источника проблемы: когда Старкрафт переключился от плоской графики к изометрической, основанный на разбитом на квадраты поле движок рендеринга, который основывался на коде, написанным мною в 1993/94 остался неизменным.
Рендерить изометрически нарисованные квадратики используя такой движок не так сложно, но возникают проблемы если вы хотите заставить работать такие вещи как редактор карт: разместив один такой квадрат рядом с другим, нужно применить много исправлений стыков, так как редактор пытается разместить диагональные картинки в квадратные поля.
И хотя с рендерингом не все так плохо, прокладка изометрического пути по плоскому полю по настоящему сложна. Вместо крупных (32х32 пикселя) квадратов, которые были либо проходимы, либо непроходимы карты были разбиты на маленькие (8х8) квадраты — умножив сложность нахождения пути на 16, а также создавая проблемы, когда крупные юниты не могли протиснуться в узкие проходы.
Если бы Brian Fitzerald не был звездой программирования, проблема прокладки пути не давала бы выпустить игру неопределенно долго.
Так как пути были проблемой с которой нам удалось разобраться только к самому выходу игры, я хочу как-нибудь написать отдельную статью о прокладке путей в Старкрафт, поскольку мы применили много интересных решений.
Источник.