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

Видимо с годами человек становиться всё более ленивым.

Был DOS, затем выскочил Windows, а вскоре выяснилось что есть ещё Linux. Нет, конечно уже который год многие "аналитики" уверяют что некая маленькая фирма всех мягко поглотит - но ведь не случиться этого никогда. Почему?

Нет, не из-за энтузиастов, а потому, что если у MicroSoft не будет хоть сколь-нибудь очевидных конкурентов - так сразу и попадёт она под анти-монопольное законодательство и уж тогда ей не отвертеться. Поэтому мудрый Билл по прозванию "Ворота" потихоньку конкурентов поддерживает на плаву (вспомните хотя бы как он спас от банкротства Apple, буквально в последний день купив 14% их акций по довольно приличной цене) - так ему выгоднее.

Ну а что с этого нам?

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

Вот так я и пришёл к Java.

Целевая аудитория

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

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

А кому нужна простая аркада?

Возможно вы не поверите мне сразу, но попробуйте сами вспомнить, не приходилось ли Вам "разгружать мозги" раскладывая "Пасьянс" или разминируя "Сапёра"?

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

Именно помня об этих игроках, я и принялся за разрабоку. А дальше я уже знал что им нужно.

Что хотят от игры?

Первое что моего игрока бесит в любой игре - её аппетит. С ностальгией вспоминаются старые добрые DOS'вские игры - потому что влезали на дискетку. Не я первый подметил это. Взять того же "Генерала" Дмитрия Гусарова (того самого, что нынче возглавляет Elemental Games создавшую "Космических рейнджеров"). Есть и зарубежные примеры.

И я решил, что моя игра тоже будет влезать на дискету.

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

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

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

И ещё, то, о чём всё время забывают - кроссплатформенность.

Нет, не переносимость, не несколько разных версий. И тут уже дело не только в моей лени.

Есть люди, которым удобно работать (или необходимо работать) совсем не под Windows - однако приходится держать "Must Die" на компьютере только ради игр. Ну а если хочется лишь немного отвлечься, что называется "на минуточку расслабиться"? Если ради этого приходится перезагружаться лишний раз - это уже нервирует.

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

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

Преимущества и недостатки

Даже если забыть про кроссплатформенность, то только одно требование компактного исполняемого кода не оставляет другой альтернативы, кроме Java. Проверьте сами - такая же по возможностям программа на C++ будет занимать раз в 10 больше места.

Говорят, она во столько же раз быстрее.

Но так ли это?

Давайте рассмотрим проблему не абстрактно, а конкретно - чем мы определяем быстродействие игры? Правильно, в основном кадрами в секунду. Так вот, как выяснилось, на современном компьютере этих самых пресловутых FPS мой движок на Java выдаёт 50 и больше. В теории программа на C++ могла бы выдать и 500 - но что нам проку с этого, если уже 25 кадров в секунду (телевизионная частота) воспринимаются нашими глазами как плавная анимация.

Получается, что для данного практического применения и Java и C++ равны. Но первый из них компактнее и никак не привязан к конкретной платформе.

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

Что делать?

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

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

Долой спрайты

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

Конечно, можно было бы и так. Тем более, что, на мой скромный взгляд, Java неплохо поддерживает спрайты (форматы GIF и PNG для Java как родные, а они поддерживают прозрачность и "изготавливаются" любым графическим или 3D-редактором). Разве что ещё бы пару-тройку методов для обработки спрайтов "на лету" добавить - типа вращения и отражения, но это совсем просто.

И если бы я разрабатывал какое-нибудь приключение в стиле анимэ - то однозначно выбрал бы не C++ и не DirectX, а именно Java и его спрайты (и вряд ли можно придумать что-либо более эффективное для реализации такой игры).

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

Ведь как самолёт производит манёвр в реальности? Он закладывает крен и... - понимаете "он закладывает крен". При единственном "виде сверху" никогда этот крен не показать.

А в каком случае крен будет выражен наиболее наглядным образом? При изометрической проекции, то есть "сверху-сзади" или "сверху-сбоку" (что в данном случае одно и тоже).

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

Теперь учтём, что игрок обычно желает видеть в игре несколько различного вида кораблей и несколько различного вида "врагов" - каков же будет суммарный объём спрайтов для простенькой такой аркады? Нет, это на дискетку никак не влезет.

Этот странный 3D

Говоря о Java и 3D нельзя упускать некоторую тонкость. Лишь с виду этот язык очень похож на C++ - оба имеют настолько близкий синтаксис, что отдельные фрагменты, перенесённые из Java в C++ будут откомпилированы без ошибки. Но когда я пытался применить к программе на Java все те приёмы оптимизации, которые выручали меня на C++, то либо не получал видимого эффекта (там, где на C++ имел бы явный выигрыш), либо даже некоторое замедление.

А дело в том, что Java честный язык высокого уровня. Ему что сложить, что возвести в степень - всё едино (почти) по затратам времени. Так что, пытаясь "разжевать" задачу, порою оказываешь компилятору "медвежью" услугу.

С другой стороны отпадает необходимость в хитростях - а значит можно сосредоточиться именно на осуществлении самой задачи (а не её оптимизации).

Вот этим я и воспользовался.

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

Поэтому когда говорят "для 3D-преобразования достаточно всего лишь перемножить матрицы" - учтите, что это отнюдь не перемножение нескольких цифр - это перемножение нескольких ДЕСЯТКОВ цифр.

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

Возможно я стал ленив, а может это специфика избранного языка, но мне захотелось вот чего:

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

Ведь это выглядит логичным, не правда ли?

Только не пытайтесь проделать такой трюк над матрицами - Ваш объекта "поедет" во все стороны. Увы, матрицы требуют внесения изменений только операцией над всей матрицей и никак иначе.

Поэтому мне был нужен принципиально иной движок, основанный на принципиально ином подходе.

И я его сделал.

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

Итак, у меня был движок

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

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

И ещё побольше удобства.

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

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

Удобство видеоигры - это прежде всего наглядность.

Игрок должен видеть пространство вокруг своего корабля, так чтобы метеорит не мог внезапно влететь в него "из-за края" - поэтому корабль должен быть закреплён в центре экрана.

Раз корабль закреплён в центре экрана - значит движение звёздного неба должно отображать его (корабля) движение.

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

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

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

Неплохо так же выводить полную статистику вылета после посадки (а кое-что и в полёте).

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

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

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

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

Наконец, игра должна поддерживать и управление мышью - ведь многие уже отвыкли играть клавиатурой :-) .

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

Да, это так, ничего революционного в изложенном выше нет - это именно то, что желает видеть каждый нормальный игрок в каждой игре.

А теперь ответьте, много ли Вы знаете игр, в которых всё это - само собой разумеющееся - сделано?

Теперь их стало на одну больше. Это мой MeteoRex, который любой желающий может найти на сайте конкурса java-программ, проводимого фирмой Sun по СНГ. Там же находится и документация, в частности содержащая и листинг исходного текста моего 3D-движка с необходимыми пояснениями.

© Rex
Игры и Java

PMG  14 ноября 2006 (c)  Rex