1. Введение

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

Что касается реализации, проект будет создаваться на базе связки PHP+Java.

Выбор PHP обоснован следующими аргументами:

  1. Наличие готовых фреймворков. Не надо писать свой движок.
  2. Простота языка.
  3. Доступность бесплатного хостинга.

Выбор Java обоснован тем, что:

  1. Клиент с точки зрения траффика более практичен, чем браузер.
  2. Язык кроссплатформенный и удобный.
  3. Имеются удобные заготовленные подсистемы.

Сектор ММОG сейчас активно развивается. Для создания ММОG совсем не обязательно создавать проект, развивающий мультиплеерную составляющую какой-либо стандартной сингл-концепции (например WoW, Lineage). Такие проекты достаточно трудоемки и не всегда, как ни странно, имеют широкий успех. Дело в том, что ММОG – это не совсем игра, это своеобразный развлекательный сервис. Например, тоже ММОG.

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

Конечно, в аудитории ММОG есть небольшой процент игроков, любящих хардкор, но подавляющее большинство приходит за тремя вещами:

  1. Расслабиться
  2. Пообщаться
  3. Похвастаться перед другими новой обновкой своего аватара.

Соответственно, при создании ММОG проекта, нужно отталкиваться от этих требований.

2. Составление концепции

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

2.1 Зарождение идеи

Одной из распространенных ошибок является преждевременная детализация проекта на этапе составления концепции. Тут очень хорошо просматривается аналогия с одним из принципов хорошего программиста: «Преждевременная оптимизация – корень зла». Любая концепция должна начинаться с идеи, причем эта идея должна описывать только ключевые особенности игры, которыми она будет выделяться, привлекая игроков. По сути, мы придумываем те самые USP. Одновременно с этим мы должны оценить, а будет ли кому-то интересна эта идея (т.е. делаем анализ аудитории).

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

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

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

Кто? – обычный житель одного города.

Где? – в городе, расположенном рядом с мистическим лабиринтом.

Когда? – в недалеком будущем.

Что? – совершает вылазки в лабиринт

Зачем? – чтобы не только выживать, но и хорошо жить.

2.2 Костяк геймплея

После составления представления о том, куда мы движемся, переходим к созданию скелета геймплея. Различные теории приводят 4 основных типа игроков по их интересам в области геймплея: Киллеры, Манчкины, Исследователи и Социофилы. (Точные названия могут меняться).

Первую группу в основном интересует PvP. Вторая группа – люди, которые любят игровые достижения: раскачаться до максимума, получить все медальки с квестов и прочее. Третью группу интересуют игровые секреты, пасхалки и даже просто недокументированные возможности игры. Четвертая группа в основном играет более ради общения, нежели игрового процесса.

Исходя из этого получаем следующую картину:

USP уже определенным образом ориентирована на группу Исследователей. Мы имеем Мистический лабиринт, который при каждом попадании в него выдает новую последовательность комнат. Для простоты реализации будем применять инстансы для каждого игрока. Инстанс – отдельный, изолированный от других кусок мира. Функционал лабиринта: фарминг и секреты. Секреты – полностью для удовлетворения потребностей Исследователей, а фарминг – основа экономики игры, сбор предметов, на которые персонаж будем потом «жить».

Для киллеров придется придумать арену. Т.к. лабиринт – это инстансы, два игрока в лабиринте не встретятся. Тут дело не только в простоте реализации, но и в том, что остальные группы игроков, например Манчкины, будут очень расстроены, если какой-то шустрый Киллер снимет их во время получения очередной медальки. Именно поэтому в играх с общим миром применяются нуб-зоны, звания PK и прочие ограничения на PvP. Арена также служит удобным местом сбора всех, кто желает подраться, не надо ни за кем охотиться. Остается проблема: как затащить остальные группы игроков на арену. Добьемся этого введением следующего условия: пусть для доступа к более «жирным» участкам лабиринта надо набрать очки на арене. Кстати, распространенная система, я встречал ее даже в синглах типа Механоидов. В свою очередь, для обеспечения некоторого замкнутого цикла, лабиринт будет давать игроку предметы, которые позволят ему быть сильнее в PvP бою. Т.е. получится некая восходящая спираль: лабиринт->предметы->арена->доступ->лабиринт.

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

  1. Самый крутой гладиатор на арене
  2. Больше всего утащил лута с лабиринта
  3. Самый кровожадный охотник на монстров лабиринта
  4. Самый … (нужное вставить)

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

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

  1. Внутренняя иерархия
  2. Совместные сражения против других кланов (CvC)
  3. «Совместное хозяйство» (не очень распространенная вещь).

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

В нашем примере применим такую схему:

Город разделен на некоторое количество областей (их число статично). Кланы бьются друг с другом на арене за право «крышевать» эти районы путем вызова «на стрелку». (Главное, поставить условие – не больше N нападений за К часов, чтобы не заДДоСили клан-хозяин). Для развития пункта 3 в контролируемых районах можно заниматься экономикой. Для упрощения система будет построена по принципу «вкладов». Клан вкладывает в район Х денег и через время Т получает K1*X – K2*X денег. Коэффициенты зависят от «жирности» района.

Исходя из вышесказанного, составим следующую иерархию клана

1 уровень: участник

2 уровень: казначей, который может заниматься экономикой, военачальник, который может назначать «стрелки другим кланам»

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

4 уровень: лидер, локальный Бог.

2.3 Персонализация аватара персонажа

Наверное, это самый важный элемент ММОG. Это то, что будет задерживать игрока именно в вашем проекте (помимо ураганного геймплея ). Как говорилось выше, одна из целей игрока ММОG – выделиться среди окружающих. Есть два способа: либо быть успешным игроком, либо каким-то образом изменить стандартный аватар. Под аватаром мы понимаем не картинку, а общий набор презентационных характеристик персонажа. Исходя из тенденции все большего распространения системы микротранзакционной оплаты (смски и прочее), а также ее большой обоснованности для небольших проектов, платная персонализация становится основным источником доходов проекта. При этом надо учитывать, что под персонализацией мы имеем в виду изменения внешних качеств персонажа, не влияющие на баланс игрового процесса. Продажа игровых предметов – это отдельная, достаточно сложная тема, требующая детального рассмотрения внутриигровой экономической системы.

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

3. Технологическая демка

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

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

3.1 Java HTTP и PHP сессии

Система будет построена по трехзвенной архитектуре клиент-сервер-база данных.

В качестве клиента будет выступать Java-приложение, в качестве сервера – Apache с бизнес-логикой, заключенной в PHP-скриптах. Наибольший интерес сейчас представляет вопрос скрещивания Java-клиента с PHP-скриптами, т.к. обычно для PHP в качестве клиента выступают немного более высокоуровневые системы, которые скрывают в себе некоторые детали, которые нам придется реализовывать вручную.

В этой части мы рассмотрим базовые принципы сетевого взаимодействия Java клиента с PHP скриптами.

Наша задача научиться вызывать PHP-скрипты с POST и GET параметрами и читать результат их работы. При этом необходимо учитывать, что в PHP существует механизм сессий, который позволяет «запоминать» значения отдельных переменных при вызове разных скриптов.

Проведем тест, который позволит разобраться с данными технологиями.

Напишем два PHP-скрипта:

test.php

Данный скрипт создает сессию, т.е. некий контейнер для междускриптовых переменных, затем создает в ней переменную counter, в которую мы запишем сумму двух входных параметров: x – переданного через GET и y – переданного через POST. Еще одну переменную, аргумент some, мы выведем в качестве результата работы этого скрипта

test2.php

А этот скрипт попытается загрузить существующую сессию (в случае неудачи создаст новую), прочитает из нее переменную counter и выведет ее в качестве результата

Теперь рассмотрим Java-код клиента, который вызовет по очереди два скрипта и напечатает в консоли результаты их работы.
Для начала отправляем вызов первого скрипта

// создаем объект URL
// заметьте, GET переменные указываются тут же
URL url = new URL("http://www.localhost/my/test.php?x=89");

// открываем соединение
URLConnection connection = url.openConnection();

// важный момент, данный флаг нужен, чтобы писать в поток ввода на сервер
// т.е. сюда будут записываться POST переменные
connection.setDoOutput(true);

// берем открытый поток
PrintWriter pw = new PrintWriter(connection.getOutputStream());

// и пишем туда пару POST переменных в указанном формате
pw.println("y=150&some=859524");

// обязательно надо закрыть этот поток, иначе POST не передастся,
// хотя все остальное сработает
pw.close();

Теперь, когда вызов осуществлен, необходимо сперва подцепить ID PHP сессии

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

// теперь нам надо подцепить Куки, чтобы работать с сессиями
String sid = null;

// читаем заголовки ответа сервера
Map> headers = connection.getHeaderFields();

// находим заголовок с куками
List cookies = headers.get("Set-Cookie");
String cookie = cookies.get(0);

// из строки куков вырезаем значение сида
int index = cookie.indexOf("PHPSESSID=")+"PHPSESSID=".length();
int index2 = cookie.indexOf(";");
sid = cookie.substring(index, index2);

Дальше просто читаем результаты работы первого скрипта

// принимает входной поток сервера, в котором лежит его ответ.
Scanner in = new Scanner(connection.getInputStream());
while(in.hasNextLine())
System.out.println(in.nextLine());

Далее нам надо вызвать второй скрипт, при этом мы должны передать ему ID сессии через заголовок HTTP запроса

// создаем урл к другом скрипту
url = new URL("http://www.localhost/my/test2.php");
connection = url.openConnection();

// передаем в куках серверу наш сид
connection.setRequestProperty("Cookie", "PHPSESSID="+sid+"; path=/");

Ну и прочитать результат работы второго скрипта

// читаем ответ и радуемся
in = new Scanner(connection.getInputStream());
while(in.hasNextLine())
System.out.println(in.nextLine());

Вот что получилось в результате работы нашего клиента

run:
some = 859524
answer: 239

3.2 Общая архитектура системы

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

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

Закачка файлов в Java происходит с помощью простой функции:

public static InputStream downloadFile(String path) throws IOException
{
   URL url = new URL("http://"+mServerHostName+'/'+path);
   return url.openStream();
}

4. Игровая демка

В отличие от технологической, она показывает не основные технологические достижения игры, а основные геймплейные. Во-первых, это дает ответ на вопрос: «Может ли команда реализовать заявленный геймплей». Во-вторых, мы можем «пощупать» сам геймплей. Оценить его играбельность, хардкорность и прочие параметры. Чаще всего именно после реализации игровой демки команда идет к издателю, т.к. если игровая демка отвечает всем требованиям, заложенным в концепте, то дальнейшая реализация – дело техники. Т.е. процесс становится достаточно прогнозируемым с точки зрения рисков. А для издателя как раз главное не столько масштаб проекта, сколько именно возможность спрогнозировать время разработки, затраты, ну и, конечно, предполагаемую прибыль.

Помимо собственно геймплея, в игровой деме должен быть отражен интерфейс продукта для оценки юзабельности (но совсем не обязательно красоты). Это тоже немаловажный фактор, т.к. гениальный геймплей с ужасным управлением смотрится ужасно.

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

4.1 Лабиринт

Переходим к первому и достаточно серьезному геймплейному модулю.

Серьезность его состоит в следующих пунктах:

  1. USP
  2. «добывающая» часть экономики
  3. влияние на общий баланс игры (уровни с арены)

Теперь о том, как реализуется лабиринт. Вернее, это будет не совсем лабиринт в классическом понимании, но нечто похожее.

Представим себе бесконечный тетрадный лист. Каждая клетка – комната. Линии разметки – стены. При этом в каждой из 4-х смежных стен комнат сделан проем, таким образом можно перемещаться из комнаты в комнату, каждый раз выбирая одно из четырех направлений.

При входе в лабиринт игрок попадает в комнату с координатами 0,0 и далее свободен ходить куда хочет, учитывая ограничение. Уровень доступа, полученный на арене, определяет максимальное расстояние, на которое может отойти игрок от начальной точки в любом направлении.

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

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

Каждая комната имеет «тип». От этого типа зависит ее функционал. Обычно он состоит в выдаче игроку каких-либо предметов или их обмене. Возможны также комнаты-телепорты и т.д. Но это уже игровой контент, и о нем позже.

4.1.1 Генератор комнат

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

Начнем с комнат. Т.к. разные типы комнат несут разный функционал, возможно даже связанный (т.е. будут «производственные» цепочки), необходимо задать некоторое примерное расположение типов комнат. И сделаем мы это с помощью функции распределения. Пусть имеются некие «центры» (клетки на тетрадном листе), в которых вероятность появления данного типа комнаты равна 100% или 1. Для каждого типа комнаты имеется некая функция, которая определяет вероятность появления комнаты в заданной точке, в зависимости от ее расстояния до «центра ареала обитания» данного типа комнат. В соответствии с полученными вероятностями по каждому типу для данной клетки мы «пробрасываем кости» и выбираем конкретный тип. «Центры ареалов обитания» задаются заранее жестким образом. Т.к., во-первых, это ориентир для игроков, а во-вторых, располагая различные «центры» в различных областях доступа (уровней доступа), мы даем игроку направление, куда стремиться.

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

4.1.2 PvE:Боевая система

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

В идеале бой должен происходить так:

1ый клик – выбор жертвы

2ой клик – выбор орудия убийства

3 – просмотр отчета о гибели противника

С другой стороны, надо обеспечить хотя бы минимальную тактическую составляющую. Как видно отсюда, она будет состоять в поиске решений «кого и чем бить» и «убить или обойти».

Получаем следующую систему:

  1. И монстр, и игрок имеют здоровье.
  2. Игрок имеет несколько видов оружия с боеприпасами. Одно из них самое слабое, но имеет бесконечный боезапас. Так сказать, «нож/монтировка/топор»
  3. Монстр имеет одну атаку определенного типа из нескольких возможных.
  4. Игрок имеет броню с несколькими (по числу типов атак монстров) сопротивлениями к атакам монстра (т.е. определенным коэффициентом снижения урона).
  5. Монстр имеет также броню с несколькими (по числу типов оружия игрока) сопротивлениями к атакам игрока.
  6. У монстра «патроны» бесконечные.

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

Для сокрытия детерминированности расчетов предполагается, что урон от любой атаки принадлежит интервалу между минимальным и максимальным уроном с равномерным распределением.

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

Извините, комментарии отсутствуют.

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