gamedev Tutorials Программирование игр

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

Не знаю, что на этот счёт думаете Вы - но я теперь в этом убеждён и, увы, на собственном опыте.

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

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

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

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

Но это - огромный объём работы. Тогда как первоначальная версия была написана (самим заказчиком) за 3 часа, и время на создание нынешней версии было им оценено в один день. А надо заметить, что в своём основном бизнесе мой друг большой специалист по части управления проектами и обычно в его оценках сомневаться не приходится.

Простите за каламбур, но: вероятно игры серьёзно отличаются от серьёзных проектов...

И вот он - новый герой

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

Ох, лучше бы я не поленился тогда и писал бы всё с нуля. Вы спросите: "Почему, если всё работало?" Да, вот именно потому что работало!

В ходе решающего разговора

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

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

К утру четверга я уже был уверен, что несколько статичных лабиринтов можно включить в игру в качестве обучающих, а вот далее таракан должен бегать по динамически изменяющимся лабиринтам (изменения производились по завершению очередного тараканьего хода). Плотность препятствий изначально была выбрана довольно высокой (2/5 от общей площади лабиринта), а единственным новым ограничением стал запрет таракану оставаться на месте - за такое таракан зарабатывал череп, что могло быстро привести к проигрышу.
За четверг игра была исправлена и вроде бы всё было хорошо.
Но ведь нынче игрок любит, чтобы всё в игре имело логическое объяснение - а как логически объяснить появление черепа?

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

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

Но расслабляться было рано

Лишь в воскресенье заполночь (если быть точным, то в 0:37 - так что это было уже не совсем воскресенье :-) ) наконец-то были решены проблемы с экранным буфером и "залипаниями" клавиш мыши.
Первая проблема выявилась чуть ли не в первый же день начала собственно программирования игры - и тем не менее, сначала была сочтена непонятным глюком компьютера и свалена на Windows (тем более, что под Linux'ом всё вроде запускалось без проблем). Разница была в том, что, на основном рабочем компьютере, под Windows у меня специально оставлена её родная Java-машина (давно уже устаревшая). С другой стороны прочие программы гарантированно запускались без проблем, а вот "Таракан" иногда выдавал ошибку при запуске.
Не сразу я понял, что проблема не в спрайтах (сначала я грешил на фрагмент, где происходило их масштабирование), а в самом экранном буфере. Чего только не делалось для исправления - изменялся порядок масштабирования спрайтов, фрагмент переносился из одного метода в другой, изменялся и фрагмент создания экранного буфера. В результате уже почти завершённая игра устойчиво отказалась запускаться под Windows XP с Sun Java-машиной 1.4. В конечном счёте выяснилось, что проблему создавала попытка определения размеров выделенного апплету экранного пространства, предшествующая созданию экранного буфера. Всего-то и нужно было задать его размеры в виде констант (а не переменных).
Если эту проблему ещё можно отнести к категории "хотел как лучше...", то "залипание" клавиш мыши - это в чистом виде недостаток планирования. Для скорейшей разработки, обработчик событий, с минимальными изменениями, был взят из другой программы. По ходу работы он слегка приспосабливался под текущие нужды - между тем как эти самые нужды так же менялись. Естественно, одни фрагменты программы начинали "отставать" в своём развитии от других, и оказывалось проще переписать всю "эту маленькую игру" заново с самого начала, чем попытаться найти и "быстренько исправить" нестыковку. Всего же, вот таким вот образом, игра была переписана четырежды для исправления тех нестыковок, которые и не возникли бы не поленись я спланировать всю работу заранее.

Дембельский аккорд

который, как известно, завершающий, для этой игры выглядел почти прозаично, в сравнении с предыдущими "приключениями" на тернистом пути к релизу.

Ночь с воскресенья на понедельник лично для меня выдалась бессонной (не мог заснуть даже несмотря на переутомление Quake 2) - благодаря чему к рассвету понедельника игра уже обзавелась необходимой для shareware продуктов защитой от несанкционированного использования, а заодно был полностью написан и генератор регистрационных кодов (кстати, тоже в виде апплета - и технические моменты этой реализации заслуживают того, чтобы быть подробно рассмотренными - но уже в другой статье).

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

В ночь со вторника на среду, погоняв таракана 4 ранга по лабиринтам, осознали необходимость динамита в игре (для подрыва стен). Добавление динамита заняло совсем немного времени.
В отличие от вдруг проявившейся ошибки с регистрационным кодом. Что только не делалось для её преодоления! Жена мудро заметила, что спокойно спать - при наличии такой недоработки - я не буду. Поэтому попытки продолжались до 5 часов утра. И всё же, согласно одному из законов Мэрфи, они были безрезультатны, а успех был достигнут на утро, всего-то за час (параллельно с приготовлением завтрака - если быть точным, в период с 12:00 до 13:00).
Ошибка оказалась необыкновенно смешной и крылась не во встроенном в игру блоке декодирования ключа, а в программе-кодогенераторе, а точнее в том, как или ещё вернее КОГДА она производила обсчёт очередного введённого оператором символа. Поначалу кодогенератор работал исправно, но затем в него были внесены улучшения. Они уже породили проблему, но она не проявлялась, так как гоняли программу "по полной программе". А вот позже, когда входные параметры стали вводить быстро, последний вводимый символ не обсчитывался. Естественно, небольшое изменение обработчика нажатия клавиш исправило эту проблему - но можете себе представить, сколько сил было потрачено на поиски в совсем других блоках (особенно в страшном алгоритме шифрования!).

Да, не стоит обманываться простотой. И уж тем более - пренебрегать планированием.

Это история со счастливым концом

а бывает и иначе.
Вот небольшой фрагмент рассказа одного из разработчиков игры "Red Baron II":

Игре Red Baron II очень не повезло. Изначально разработанная под DOS, позже она переделывалась сначала под Windows 95, потом под DirectX и, наконец, под 3D-платы. В итоге код игры перекраивался столько раз, что под конец стал невообразимо громоздким. Основная проблема заключалась не в самой реконструкции, а скорее в том, что технология привела, как оказалось, к "переоценке" возможностей повторного использования кода и нарушению модульности. Вырезая некоторые части, приходилось сохранять остальные, склеивая их кое-как. В результате программу стало очень трудно читать и понимать. Если бы код с самого начала был хорошо продуман и выстроен по модульному принципу, то игра могла бы быть завершена гораздо быстрее, независимо от преимуществ технологии, которая вдохновляла на эти изменения. Не следует понимать это утверждение в смысле, что "нужно было использовать C++". Мы пользовались этим языком. Однако объектно-ориентированное программирование со всеми его достоинствами может стать настоящим кошмаром при злоупотреблении плохо продуманным проектом и применением технологии "заплаток". Никогда не позволяйте "завораживающим" возможностям языков программирования убедить вас, что соблюдение принципа "бритвы Оккама" и удобочитаемость программы уже не существенны. Никогда.

© Rex
Игры и Java

PMG  8 февраля 2007 (c)  Rex