Добрый день!
Меня зовут Сергей, я независимый разработчик игр. В сентябре 2014 года я поставил перед собой цель — реализовать игру во многом схожую с Hearthstone. Я долго размышлял перед тем, как взяться за этот проект: по силам ли мне это?! В тот момент задача казалась неподъемной для одного разработчика. Из титров к оригинальной игре было очевидно, что над ней трудятся не менее десяти человек, кроме того у Blizzard, есть уже сложившееся комьюнити и достаточно денег на маркетинг. Сама игра реализована по мотивам уже существующего игрового мира, что также несколько упрощает разработку самим близардам. Ничего из вышеописанных сопутствующих условий у меня нет и потому меня до сих пор преследуют сомнения относительно ожидаемого успеха от данной инициативы. Тем не менее, я все таки взялся за этот проект — прежде всего потому, что мне нравятся коллекционный карточные игры и сама работа над подобной игрой приносит мне удовольствие. Я решил, что этот проект, так или иначе, даст мне возможность получить практический опыт в разработке подобных игр. Даже если с первой попытки мне не удастся трансформировать это в какое-то коммерчески успешное предприятие, общий совокупный опыт, полученный в ходе работы над этим проектом, даст мне возможность, в будущем, экспериментировать в этом жанре и, в конечном счете, я так или иначе все равно нащупаю ту оригинальную механику и сеттинг, которые позволят создать собственную игровую студию, специализирующуюся на разработке оригинальных коллекционных карточных игр.
В настоящий момент я работаю над игрой уже восьмой месяц и хотел бы этой публикацией начать цикл статей, который открывает некоторые нюансы того, как работают коллекционные карточные игры в принципе. Наверное, для многих опытных разработчиков эти статьи вряд ли смогут рассказать что-то принципиально новое; и все же, я надеюсь, что если вы никогда ранее не сталкивались с подобными играми — у вас проснется дополнительный интерес к ним и, возможно, вы начнете разрабатывать свою игру или, может быть, присоединитесь к данному проекту.
В своей работе я использую python 3.3, django, redis и tornado для серверной части проекта action script + robotlegs для клиентской. Я не исключаю того, что в ближайшем будущем я также начну писать клиент на С++ под Unreal Engine 4. До последнего времени я был сфокусирован на работе непосредственно над кодом, обеспечивающим игровую механику и потому на данном этапе для меня было не слишком важно, какую технологию использовать для написания клиентской части игры. Я просто выбрал то, что лучше знал.
Django используется для административной панели, которая позволяет настраивать эффекты при розыгрыше тех или иных карт, а также работает с запросами касающимися создания новых карт самих игроков и создания определенной колоды из того набора, который открыт у игрока. Сами матчи не используют базу данных — вместо этого они просто кешируют собственную модель в redis. Бой между двумя реальными игроками осуществляется через приложение tornado, использующее постоянное сокетное соединение между двумя клиентами.
Совсем коротко архитектура игры выглядит так:
У клиентов есть как сервисы для установления контакта с торнадо приложением в рамках какого-то конкретного матча, так и отдельные сервисы, касающиеся коллекции карт. Совершая те или иные ходы, один из клиентов информирует об этом сервер, на основании полученных данных сервер анализирует сделанное игроком действие и формирует игровой сценарий, который отправляет обратно обоим клиентам. Получив от сервера игровой сценарий, клиент отдает его команде, отвечающей за его проигрывание. В целом, плавное и комплексное проигрывание всех игровых событий происходит за счет двух рекурсивных функций: одна функция находится на сервере, она анализирует настройки той или иной карты, проходит по всем переменным конкретного эффекта и формирует массив игровых действий; вторая рекурсивная функция клиентская, последовательно проигрывает каждое игровое действие, обнаруженное в игровом сценарии.
Вероятно, я еще напишу отдельную статью о том, что представляют собой настройки той или иной карты. Сегодня я ограничусь лишь одним поверхностным примером, того как это может выглядеть в реальном матче: предположим, у игрока на руках есть карта с простой способностью «Провокация». Эта защитная способность вынуждает сначала атаковать существо со способностью «Провокация» и только после его смерти уже других существ и героя противника.
Изначально один из игроков оповещает сервер о том что разыграна та или иная карта. По индексу карты сервер определяет набор ее настроек и отдает настройки карты особому игровому реактору. Игровой реактор пропускает все настройки карты через свою рекурсивную функцию и возвращает уже готовый игровой сценарий. Конкретно способность «Провокация» срабатывает таким образом. Сама карта хранит описание способности в константах:
В нашем случае реактор будет пропускать все способности через период EtitudePeriod.SELF_PLACED. Это значит, что он пытается найти способность, которую необходимо активировать сразу же, как только фишка оказалась на поле. Как только он обнаружит эту способность, по уровню влияния он сможет понять, к кому нужно будет применить эту способность. В данном случае по константе EtitudeLevel.SELF он поймет, что способность нужно применить к самому существу, спровоцировавшему срабатывание этой способности. На третьем этапе рекурсивная функция установит тип способности EtitudeType.Provocation, далее реактор изменит характеристики этого существа в своей модели и сформирует игровой сценарий, указав индекс существа и способность, которую необходимо применить к этому существу. Сформированный сценарий реактор вернет торнадо приложению, а тот в свою очередь отдаст его своим соединениям.
Немного кода для полноты картины:
На клиенте аналогичная рекурсивная функция перебирает компоненты игрового сценария, по индексу определяет, какой именно элемент (карта, существо) будет трансформирован и визуализирует тот или иной эффект в зависимости от его типа.
В общем, это все, что я хотел рассказать в своей вводной части. Благодарю за внимание!
Меня зовут Сергей, я независимый разработчик игр. В сентябре 2014 года я поставил перед собой цель — реализовать игру во многом схожую с Hearthstone. Я долго размышлял перед тем, как взяться за этот проект: по силам ли мне это?! В тот момент задача казалась неподъемной для одного разработчика. Из титров к оригинальной игре было очевидно, что над ней трудятся не менее десяти человек, кроме того у Blizzard, есть уже сложившееся комьюнити и достаточно денег на маркетинг. Сама игра реализована по мотивам уже существующего игрового мира, что также несколько упрощает разработку самим близардам. Ничего из вышеописанных сопутствующих условий у меня нет и потому меня до сих пор преследуют сомнения относительно ожидаемого успеха от данной инициативы. Тем не менее, я все таки взялся за этот проект — прежде всего потому, что мне нравятся коллекционный карточные игры и сама работа над подобной игрой приносит мне удовольствие. Я решил, что этот проект, так или иначе, даст мне возможность получить практический опыт в разработке подобных игр. Даже если с первой попытки мне не удастся трансформировать это в какое-то коммерчески успешное предприятие, общий совокупный опыт, полученный в ходе работы над этим проектом, даст мне возможность, в будущем, экспериментировать в этом жанре и, в конечном счете, я так или иначе все равно нащупаю ту оригинальную механику и сеттинг, которые позволят создать собственную игровую студию, специализирующуюся на разработке оригинальных коллекционных карточных игр.
В настоящий момент я работаю над игрой уже восьмой месяц и хотел бы этой публикацией начать цикл статей, который открывает некоторые нюансы того, как работают коллекционные карточные игры в принципе. Наверное, для многих опытных разработчиков эти статьи вряд ли смогут рассказать что-то принципиально новое; и все же, я надеюсь, что если вы никогда ранее не сталкивались с подобными играми — у вас проснется дополнительный интерес к ним и, возможно, вы начнете разрабатывать свою игру или, может быть, присоединитесь к данному проекту.
В своей работе я использую python 3.3, django, redis и tornado для серверной части проекта action script + robotlegs для клиентской. Я не исключаю того, что в ближайшем будущем я также начну писать клиент на С++ под Unreal Engine 4. До последнего времени я был сфокусирован на работе непосредственно над кодом, обеспечивающим игровую механику и потому на данном этапе для меня было не слишком важно, какую технологию использовать для написания клиентской части игры. Я просто выбрал то, что лучше знал.
Django используется для административной панели, которая позволяет настраивать эффекты при розыгрыше тех или иных карт, а также работает с запросами касающимися создания новых карт самих игроков и создания определенной колоды из того набора, который открыт у игрока. Сами матчи не используют базу данных — вместо этого они просто кешируют собственную модель в redis. Бой между двумя реальными игроками осуществляется через приложение tornado, использующее постоянное сокетное соединение между двумя клиентами.
Совсем коротко архитектура игры выглядит так:
У клиентов есть как сервисы для установления контакта с торнадо приложением в рамках какого-то конкретного матча, так и отдельные сервисы, касающиеся коллекции карт. Совершая те или иные ходы, один из клиентов информирует об этом сервер, на основании полученных данных сервер анализирует сделанное игроком действие и формирует игровой сценарий, который отправляет обратно обоим клиентам. Получив от сервера игровой сценарий, клиент отдает его команде, отвечающей за его проигрывание. В целом, плавное и комплексное проигрывание всех игровых событий происходит за счет двух рекурсивных функций: одна функция находится на сервере, она анализирует настройки той или иной карты, проходит по всем переменным конкретного эффекта и формирует массив игровых действий; вторая рекурсивная функция клиентская, последовательно проигрывает каждое игровое действие, обнаруженное в игровом сценарии.
Вероятно, я еще напишу отдельную статью о том, что представляют собой настройки той или иной карты. Сегодня я ограничусь лишь одним поверхностным примером, того как это может выглядеть в реальном матче: предположим, у игрока на руках есть карта с простой способностью «Провокация». Эта защитная способность вынуждает сначала атаковать существо со способностью «Провокация» и только после его смерти уже других существ и героя противника.
Изначально один из игроков оповещает сервер о том что разыграна та или иная карта. По индексу карты сервер определяет набор ее настроек и отдает настройки карты особому игровому реактору. Игровой реактор пропускает все настройки карты через свою рекурсивную функцию и возвращает уже готовый игровой сценарий. Конкретно способность «Провокация» срабатывает таким образом. Сама карта хранит описание способности в константах:
- EtitudeType (Вид способности)
- EtitudePeriod (Момент срабатывания способности)
- EtitudeLevel (Уровень влияния способности)
В нашем случае реактор будет пропускать все способности через период EtitudePeriod.SELF_PLACED. Это значит, что он пытается найти способность, которую необходимо активировать сразу же, как только фишка оказалась на поле. Как только он обнаружит эту способность, по уровню влияния он сможет понять, к кому нужно будет применить эту способность. В данном случае по константе EtitudeLevel.SELF он поймет, что способность нужно применить к самому существу, спровоцировавшему срабатывание этой способности. На третьем этапе рекурсивная функция установит тип способности EtitudeType.Provocation, далее реактор изменит характеристики этого существа в своей модели и сформирует игровой сценарий, указав индекс существа и способность, которую необходимо применить к этому существу. Сформированный сценарий реактор вернет торнадо приложению, а тот в свою очередь отдаст его своим соединениям.
Немного кода для полноты картины:
# match.py
def place_unit (self, index, attachment)
unit = self.get_unit(index, attachment)
scenario = []
reactor = new Reactor(scenario)
scenario = reactor.place_unit(unit)
return scenario
# reactor.py
def place_unit (self, unit)
self.initiator = unit
self.etitudes = initiator.etitudes[:]
self.activate(EtitudePeriod.SELF_PLACED)
return self.scenario
def activate(self, period):
if len(self.etitudes):
etitude = self.etitudes[0]
del self.etitudes[0]
if period == etitude.period:
targets = self.get_targets(etitude.level)
# some other etitudes ...
if etitude.type == EtitudeType.PROVOCATION:
for target in targets:
target.provocation = True
action = {}
action['type'] = 'provocation'
action['target'] = {'index':target.index}
self.scenario.append(action)
self.activate(period)
На клиенте аналогичная рекурсивная функция перебирает компоненты игрового сценария, по индексу определяет, какой именно элемент (карта, существо) будет трансформирован и визуализирует тот или иной эффект в зависимости от его типа.
В общем, это все, что я хотел рассказать в своей вводной части. Благодарю за внимание!
vvpoloskin
Так а игра-то где сама?
joint Автор
Я пока ее не опубликовывал, есть только несколько видео роликов о том как игра выглядит в настоящий момент: