netlib.narod.ru< Назад | Оглавление | Далее >

Введение в многопользовательские игры

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

Сказав это, важно отметить, что в XNA эта сложность не относится к кодированию установки соединения между машинами (PC или Xbox 360) или создания взаимодействия между ними. Это связано с тем, что XNA в данном случае скрывает от вас всю сложность, обрабатывая все необходимое внутри библиотек.

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

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

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

Выбор сетевой топологии

Наиболее распространенными топологиями для сетевых игр являются одноранговая (peer-to-peer) и клиент-серверная (client-server), а поскольку реализация работы с сетью в XNA не привязана к какому-либо типу соединения, вы можете программировать любой из этих типов в зависимости от способа организации вашего сетевого кода.

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


Рис. 5.1. Одноранговое соединение

Рис. 5.1. Одноранговое соединение


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

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

Главная проблема, которая встанет перед вами при программировании одноранговых игр, невозможность иметь много игроков в одном сеансе игры из-за экспоненциального увеличения количества сообщений с каждым новым присоединившимся игроком. Например, на рис. 5.1 у нас 4 игрока, так что каждый раз, когда игроку надо обновить свое состояние (например, переместиться), вы отправляете три сообщения, по одному для каждого игрока. Поскольку у вас 4 игрока, на каждом игровом ходе вы обмениваетесь 4 × 3 = 12 сообщениями. Выполнение тех же вычислений для игры с 5 игроками, увеличивает результат до 5 × 4 = 20 сообщений, а в случае 6 игроков вы получите 6 × 5 = 30 сообщений.

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

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


Рис. 5.2. Подключение клиент/сервер

Рис. 5.2. Подключение клиент/сервер


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

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

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

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


Рис. 5.3. Кольцевая топология сети

Рис. 5.3. Кольцевая топология сети


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

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


Рис. 5.4. Кольцевая топология сети

Рис. 5.4. Кольцевая топология сети


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

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

Походовая игра или игра реального времени

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

В походовых играх каждый игрок думает над своим ходом, выполняет соответствующие действия, и затем передает управление следующему игроку. Хотя первым типом таких игр, который приходит на ум, являются настольные игры, такие как шахматы или Монополия, есть сложные походовые стратегические игры, такие как старая серия X-COM, где вы передвигаете каждого из ваших солдат (тратя его энергию на ходьбу или стрельбу), а затем перемещаются враги, используя те же самые правила.

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

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

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

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

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

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

Несколько технических советов

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

В самом начале тщательно спланируйте игру

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

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

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

Программируйте сетевые возможности с самого начала

Гораздо лучше программировать все с самого начала, чем пытаться приспособить изолированную игру для поддержки сети, если она не была запланирована. Даже в простых программах вы можете столкнуться с ситуациями, когда адаптация программы приводит к менее оптимальному результату, чем написание игры сразу с мыслью о сети. Итак, если вы планируете создать игру с поддержкой сети только во второй версии, подготовьте весь ваш код из первой версии, чтобы он был «дружелюбным к сети». Например, изолируйте процедуры, которые имеют дело с пользовательским вводом, от остальной части игры, чтобы позднее вы могли менять эти процедуры для получения удаленного ввода. Также планируйте, как синхронизировать ввод от всех игроков, даже если в первой версии все игроки локальные.

ПРИМЕЧАНИЕ
Сетевые процедуры XNA позволяют вам создавать игры с несколькими локальными игроками. Лучший подход в этом случае заключается в том, чтобы сразу использовать эти процедуры при создании первой версии вашей игры. Таким образом, вы с самого начала обеспечиваете поддержку сетевой организации, даже если в первой версии нет никакой поддержки удаленных игроков.

Тщательно определяйте типы и размеры сообщений

Полоса пропускания — редкая и дорогая вещь, так что используйте ее экономно.

После определения на этапе проектирования всех сообщений, которыми будут обмениваться ваши программы, вы должны нарисовать полный поток для типичного игрового цикла (главный цикл игры, включая вызовы методов Update и Draw вашего класса XNA Game), чтобы вы могли проверить, не забыли ли чего-нибудь важного. Вы должны создать этот поток как минимум для двух – трех игроков, плюс сервер, если он есть, поскольку некоторые ситуации с тремя игроками никогда не возникают, если игроков два.

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

И заключительное замечание: убедитесь, что знаете реальный размер используемых типов данных. Например, Int32 занимает 4 байта, в то время как Int16 занимает два байта. Другой интересный пример относится к строкам: количество занимаемых ими байт не соответствует количеству символов. В них есть дополнительные внутренние контрольные байты помогающие, например, при определении длины строки.

ПРИМЕЧАНИЕ
В большинстве западных стран по умолчанию используются строки ANSI (один байт на символ), но этого недостаточно для того, чтобы представить каждый символ в алфавитах восточных стран, например, такой, как символ кана в Японии и Китае. Это вызвано тем, что в ANSI может быть только 256 символов. Для таких стран по умолчанию используется Unicode, и в этом случае каждый символ может принимать одно из 65 536 различных значений — достаточно для любого языка.

Скрывайте задержку от игрока

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

Поскольку вы никогда не знаете наверняка, сколько времени потребуется, чтобы получить следующее сообщение, вы можете использовать некоторые трюки, чтобы отвлечь игрока, пока он ждет. Предположим, у вас стратегическая игра, такая как серия Age of Empires, где игрок может отдавать приказы игровым персонажам. Однако, персонаж переместится только после того, как клиентская машина получит от главного узла подтверждение, что команда была получена. Так, вы можете заставить персонажи что-нибудь говорить («Да, командир!» будет достаточно, хотя и не слишком оригинально) после того как отдана команда, и у игрока будет создаваться впечатление, что реакция следует немедленно, хотя на самом деле все происходит (надеемся) на много миллисекунд позже.

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

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

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

ПРИМЕЧАНИЕ
XNA предоставляет способ имитировать задержку, так что вы легко можете протестировать вашу программу в «реальных условиях» с помощью NetworkSession.SimulatedLatency. Вы также можете имитировать другую общую проблему, процент потери сообщений между компьютерами, с помощью NetworkSession.SimulatedPacketLoss. Мы не будем использовать эти команды в данной главе, но важно, чтобы вы знали про их существование, когда будете тестировать ваши сетевые игры.

Включайте возможность одиночной игры в ваши многопользовательские игры

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

Возьмем простой пример: Halo — это великая игра и многопользовательские возможности дают игрокам полностью новый опыт, о чем знает каждый, кто играл в нее. Теперь вообразите Halo без истории, без управляемых компьютером персонажей, ограниченную до схваток и других вариантов «игрок против игрока». Это по-прежнему была бы хорошая игра, благодаря ее деталям, но великой ее уже назвать было бы нельзя.

Другой простой пример — набор для начинающего Net Rumble, выпущенный вместе с XNA 2.0. Это приятная игра, но если вы играете в одиночестве, все что у вас есть — это космический корабль с несколькими летающими камнями для стрельбы; никакой цели, никакого удовольствия. Программирование управляемого компьютером космического корабля может быть вызовом для новичка, но, конечно, создаст реальное отличие, если вы хотите играть один, или даже если вы хотите протестировать игру во время кодирования без партнеров.

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

Используйте отдельный поток для обработки сетевых сообщений

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

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

Тестирование, тестирование и еще раз тестирование!

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

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

ПРИМЕЧАНИЕ
XNA может выполнять гарантированную доставку пакетов (когда сообщения не теряются), используя флаг SendDataOptions.Reliable, и упорядочивание пакетов (чтобы сообщения всегда поступали в том порядке, в котором они были отправлены) с SendDataOptions.InOrder. Хотя это звучит заманчиво, чтобы сообщения всегда поступали и были упорядочены, установка обоих флагов может привести к резкому увеличению времени задержки, поскольку XNA Framework придется выполнять дополнительные проверки и периодически повторно отправлять сообщения. Лучше создавать игры, которые не зависят от этих возможностей.

Всегда является проблемой надежность многопользовательских игр. Предположим, вы создали игру у которой время безотказной работы составляет 99,9 процентов. Это значит, что ваша игра в среднем может работать без сбоев 23 часа 59 минут, и быть недоступной только одну минуту в день. Звучит достаточно хорошо?

Ладно, если у вас десять игроков на десяти разных машинах, скорее всего сбои у них будут происходить не одновременно. Итак, если вы разделите 24 часа на 10, то увидите, что сбои могут происходить каждые 2 часа 24 минуты. Если ваша игра достаточно хороша, другие игроки смогут продолжать игру — даже испытав разочарование, когда, играя в команде, видишь, что компаньон замер или исчез из команды.

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


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz