Player.IO. Основы или первая онлайн игра

Сегодня мы обсудим основы многопользовательских игр и рассмотрим пример многопользовательской игры с громким названием «Hello World!».

Если вы вдруг не знакомы с историей программирования и соглашений, то Hello World — это просто программа, которая выводит текст «Hello World». Таким образом, это считается эффективным средством вводного обучения. Наш «Hello World!» — это программа, которая будет отправлять сообщение на сервер, а сервер будет отвечать «Hello World!». Мы будем следить за этим сообщением и на этом простом примере узнаем необходимые нам основы об общении нашего приложения с сервером.

Если вы не хотите повторять все описанные шаги самостоятельно, то вы сразу можете загрузить готовый пример — CS4, *.zip, 1,5мб

Забегая вперед, хочу вам сказать, что если вам приходилось имплементировать в ваши игры, например API MochiMedia, то в данном случае работа с сервером Player.IO ни чуть не сложнее.

Давайте создадим новый Flash проект и добавим базовый класс, который будет точкой входа в наше приложение, назовем его HelloWorld.as и свяжем с HelloWorld.fla.

В первую очередь мы рассмотрим код, который нам понадобится на стороне клиента, то есть во Flash. Чтобы работать с сервером, нам необходимо подключить Player.IO API —это те классы, которые будут делать всю скучную техническую работу, реализуя наше общение с сервером.

import playerio.* // Подключаем PlayerIO API

Теперь добавим новую публичную переменную, в которой у нас будет храниться наш уникальный идентификатор игры. ?дентификатор игры необходимо получить, создав учетную запись на playerio.com и добавив туда свою игру. Данные добавленной игры могут быть отредактированны или удаленны в любой момент, так что не переживайте о корректности заполнения форм для получения идентификатора игры. В коде идентификатор игры назовем идентификатор так:

public var gameID:String = "вставьте сюда свой идентификатор";

Далее в конструкторе нашего HelloWorld.as нам следует вызвать метод PlayerIO.connect() для подключения:

PlayerIO.connect(
  stage, // Указатель на stage
  gameID, // ?дентификатор нашей игры
  "public", // Тип соединения, по умолчанию публичное
  "GuestUser", // ?мя пользователя в игре, который подключается
  "", // Авторизация пользователя. Может быть пустой, если авторизация не нужна.
  null, // Партнер для внутреигровых платежей (дополнительный сервис)
  handleConnect, // Указатель на метод, который будет вызван при успешном подключении
  handleError // Указатель на метод, который будет вызван, если подключение по какой-либо причине не удастся.
);

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

private function handleConnect(client:Client):void
{ 
  trace("Connected to server")
  
  // Устанавливаем подключение к локальному серверу для отладки
  client.multiplayer.developmentServer = "localhost:8184";
            
  // Создаем или подключаемся к игровой комнате "test"
  client.multiplayer.createJoinRoom(
    "test", // ?дентификатор комнаты. Если установить null, то идентификатор будет присвоен случайный
    "MyCode", // Тип игры запускаемый на сервере (привязка к серверному коду)
    true, // Должна ли комната видима в списке комнат?
    {}, // Какие-либо данные. Эти данные будут возвращены в список комнат. Значения могут быть изменены на сервере.
    {}, // Какие-либо данные пользователя.
    handleJoin, // Указатель на метод, который будет вызван при успешном подключении к комнате.
    handleError // Указатель на метод, который будет вызван в случае ошибки подключения
  ); 
}

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

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

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

Теперь посмотрим на метод обработчик успешного подключения:

private function handleJoin(connection:Connection):void
{
  // Отправляет "Hello" на сервер 
  connection.send("Hello World");

  // Слушает ответ сервера на сообщение "Hello World" и выводит его в trace
   connection.addMessageHandler("Hello World", function(m:Message){
     trace(m.type);           
  });
}

В данном методе первой строкой мы отправляем простое сообщение на сервер «Hello World». А второй строкой мы подписываемся на аналогичное сообщение от сервера, и при подписке указываем метод или указатель на метод, который будет вызван при получении указанного сообщения. В нашем случае мы просто выводим полученное сообщение в окно output.

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

private function handleError(error:PlayerIOError):void
{
  trace(">" + error);
}

Серверный код

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

player.Send("Hello World");

Но давайте посмотрим на весь код сервера:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using PlayerIO.GameLibrary;
using System.Drawing;
 
namespace MyGame {
  public class Player : BasePlayer {
    public string Name;
  }

  public class GameCode : Game {
    public override void GameStarted() {}
    public override void UserJoined(Player player) {}
    public override void UserLeft(Player player) {}

    public override void GotMessage(Player player, Message message) {
      player.Send("Hello World");
    }
  }
}

Здесь мы используем только один метод GotMessage(), в котором записана наша строка. Метод GotMessage() выполняется на сервере всякий раз, когда сервер получает сообщение от игрока. В этом методе мы имеем два аргумента — это указатель на игрока, от которого пришло сообщение и само сообщение.

Строка player.Send() делает фактически тоже самое, что и connection.send() во Flash. Только мы посылаем сообщение «Hello World» от сервера к указанному игроку.

Если мы хотим отправить сообщения всем игрокам, а не конкретному игроку, то нам вместо player.Send() следует использовать метод Broadcast(), который разошлет указанное сообщение всем игрокам в текущей игровой комнате.

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

Сообщения

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

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

Пример сообщения

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

Но все же в большинстве случаев мы будем отправлять сообщения с данными. Например, если вы хотите отправить сообщение о том, что вы двигаетесь к x=200 и y=300, то сообщение может выглядить следующим образом:

Пример отправки сообщения.

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

connection.send("move", 200, 300);

Получение и чтение сообщений

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

Давайте предположим, что мы можем отправить три таких сообщения:

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

connection.addMessageHandler("move", gotMove);
connection.addMessageHandler("points", getPoints);
connection.addMessageHandler("killed", gotKilled);

function gotMove(m:Message){
  x = m.getInt(0);
  y = m.getInt(1);
}
 
function gotPoints(m:Message){
  points += m.getInt(0);
}
 
function gotKilled(m:Message){
  // Обработчик смерти
}

В первых трех строках мы добавляем слушателей на сообщения. Они будут ожидать получения соотвествующих сообщений и вызывать указанные методы при их получении. То есть метод gotMove() будет вызван, когда мы получим сообщение «move» от сервера. Данные пришедшие с сообщением похожи на массив значений, но для обращения к ним мы используем метод get[dataType](index). Например, чтобы получить строку из нулевой ячейки, мы должны вызвать getString(0), или если нужна цифра из второй ячейки, то getInt(1).

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

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

Это вольный перевод статьи «Building Flash Multiplayer Games — Game Basics».

Содержание

  1. Как создать онлайн игру? Анонс
  2. Player.IO. Введение
  3. Player.IO. Основы или первая онлайн игра
  4. Player.IO. Пошаговые игры
  5. Player.IO. Сетевые архитектуры
  6. Player.IO. Безопасность в онлайн играх
  7. Player.IO. Пример пошаговой игры
  8. Player.IO. Онлайн игры в реальном времени
  9. Player.IO. Синхронизация игроков
  10. Player.IO. ?нтерполяция или удивительный мир обмана
  11. Player.IO. Решение проблемы задержек
  12. Player.IO. Советы и рекомендации

 

 

Отличная статья, не ожидал что выйдет сегодня:)

Pavel
13 Октября 2012
— 23:32
#

Смотрю в блоге появилась реклама)) Решил монетизировать?

pelican
14 Октября 2012
— 00:59
#

Что-то как-то слишком быстро статьи выходят. Читать не успеваю))

killer
14 Октября 2012
— 01:06
#

все нормально! мне нравится...

Wily
14 Октября 2012
— 01:39
#

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

Ant.Karlov
14 Октября 2012
— 01:56
#

Круто

Ден
14 Октября 2012
— 12:32
#

Побольше рекламы, постараемся покрыть тебе расходы за хорошие уроки! Спасибо!

Rokzero
14 Октября 2012
— 14:09
#

@Rokzero, только пожалуйста, не нужно специально кликать в рекламу, а то ничем хорошим для меня это не закончится.

Ant.Karlov
14 Октября 2012
— 14:47
#

Спасибо за урок! Рекламы не заметил :)

smck87
14 Октября 2012
— 19:38
#

А я заметил это уродство. Рекламные блоки - отстой.

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

noname
14 Октября 2012
— 19:42
#

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

ggman
14 Октября 2012
— 20:44
#

Антон, если укажешь номер кошелька (Яндекс-деньги, QIWI-кошелек) то я лично тебе переведу деньгу в благодарность за твои уроки и старания. Не миллион, конечно) Но и не 10 рублей.

vlev
15 Октября 2012
— 13:48
#

мда, вот нафига переводить официальные туториалы? Тебе не жалко тратить время на это?

Василий Петрович
15 Октября 2012
— 14:19
#

хм, не вижу рекламы, может, из-за adblock plus?.. а донат идея хорошая) за такую работу и офигенно полезный материал всего блога тоже не поскуплюсь) поддержим Антона!

Евгений
15 Октября 2012
— 14:27
#

@Василий Петрович, вы ведь не знаете что Антон затеял. К тому же я думаю многие рады и переводу.

?ван
15 Октября 2012
— 15:40
#

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

Но если очень хочется сделать донейт, то на zombotron.com висит кнопка. Если нет PayPal или кредитной карты, то не проблема, может быть когда-нибудь в будущем еще представится возможность сделать пожертвование ;)

Ant.Karlov
15 Октября 2012
— 17:08
#

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

Ant.Karlov
15 Октября 2012
— 17:11
#

@Ant.Karlov
Спасибо за перевод. На русском материал усваивается проще. Например был случай, что я переводил материал просто потому, что в процессе его оформления он куда лучше осмысливается и после к нему удобней возвращаться. Правда на это оформление тратиться уйма времени.

Mufasa
15 Октября 2012
— 20:02
#

Уже 16 октября почти, а следующего урока все нет.

Марк
15 Октября 2012
— 23:53
#

Еее. Наконец этого проклятого зомби победил мужик с пушкой))

Виталий
16 Октября 2012
— 10:04
#

Антон, получилось через PayPal заодно и зарегистрировался)

vlev
16 Октября 2012
— 10:54
#

Да все ок. Зачем торопиться?

killer
16 Октября 2012
— 13:04
#

Марк, если у вас родилась бы дочка, то скорее всего вам будет не до уроков.

Пятачок
16 Октября 2012
— 14:50
#

Может кому нибудь пригодится.

У меня локальный сервер с клиентом не обменивались сообщениями, я и так пробовал и эдак и VS переустанавливал.
У жены на ноутбуке работает, у меня нет.

Лечиться запуском VS "Run as administator".

Kickerua
10 Декабря 2012
— 23:26
#

Спасибо за хорошие уроки!
Только вот почему у меня Microsoft Visual C#
считает
...
public class GameCode : Game {
...
что "?спользование универсального тип "PlayerIO.GameLibrary.Game<P>" требует аргументы типа "1""

Дима
21 Января 2013
— 07:30
#

Я хочу поиграть в повторящего кота на ноут-буке

настя
4 Февраля 2013
— 16:36
#

акпауывсукпс

настя
4 Февраля 2013
— 16:36
#

купиправ

настя
4 Февраля 2013
— 16:37
#

Дима таже ошибка , наверно надо скачать исходник и та проверить

som911
7 Марта 2013
— 11:38
#

GameCode :Game <Player> { }

som911
7 Марта 2013
— 11:54
#

Все сделал правильно, но почему то сообщение Hello World в trace не выводит, хотя подключение происходит, комната создается.
? конечно же Connect to Server тоже выводит.

Dzamba
5 Апреля 2013
— 13:14
#

"MyCode", // Тип игры запускаемый на сервере (привязка к серверному коду) - ЧТО ЭТО?
у меня ошибки выбивает, пока не поменял на bounce

Дима
11 Ноября 2015
— 17:31
#

@Дима, Если мне не изменяет память то "MyCode" — это имя ключевого класса серверного кода. Хотя могу и ошибаться. На память уже не помню, нужно поднять исходники.

Ant.Karlov
11 Ноября 2015
— 21:56
#

Антон, подскажите, насколько сейчас актуален player.io. ? в чем его преимущество если, использовать передачу переменных из флеш в php и затем в mysql?
Насчет, "MyCode" — это имя ключевого класса серверного кода. Перепробовал все, пишет такое:
Sucessfully connected to player.io
got Error: The game MyTestGame doesn`t have server type with the name: "MyGame". The available server types are [].

Дима
13 Ноября 2015
— 16:03
#

@Дима,

> подскажите, насколько сейчас актуален player.io.

Поскольку player.io купил, поддерживает и развивает теперь Yahoo — я думаю, что он очень даже актуален.

> ? в чем его преимущество если, использовать передачу переменных из флеш в php и затем в mysql?

Приемущество Player.IO перед PHP и MySQL в том, что разработчику уже предоставляется сервер и весь необходимый для его работы API — разработчику остается только написать игру. Без Player.IO придется разбираться с покупкой/арендой сервера, с его настройкой, а так же с разработкой серверной части, не говоря уже о безопасности. С Player.IO всего этого делать не нужно.

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

Я считаю что Player.IO — это лучший способ попробовать свои силы в онлайновых играх, а далее уже действовать по ситуации.

> Перепробовал все, пишет такое:
Sucessfully connected to player.io


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

Ant.Karlov
18 Ноября 2015
— 14:26
#

Насчет, "MyCode" , я немного разобрался, чтобы сэкономить ваше время, напишу правильное подключение. Для начала можете почитать официальное руководство https://gamesnet.yahoo.net/documentation/tutorials/flashcombopackage.
Когда вы запускает сервер, выскакивет окошко с указанием пути к файлу с расширение *.dll. Далее мы открываем страницу с нашей игрой (где указана информация нашего Game ID), здесь мы находим и нажимаем синюю кнопку "Upload Game Code", находим файлик с расширением *.dll, о котором я писал выше, ...вот в принципе и все, ошибку не выдает

Дмитрий
19 Ноября 2015
— 15:58
#

Только не пойму в каком случае указывается вместо "MyCode" "bounce " (отскок)

Дмитрий
19 Ноября 2015
— 16:13
#

Вопрос про "bounce" снят, прочитал следующие статьи

Дмитрий
19 Ноября 2015
— 17:36
#