Войти
UnityФорумСеть

[Unity3D] uNet - краткий обзор

Страницы: 1 2 Следующая »
#0
14:48, 1 окт. 2015

Добрый день!

  Ранее в своих проектах, для реализации сети, я использовал библиотеку uLink. Он показал себя очень удобным, надежным, хотя и местами "глючным".
Unity в версиях 5.х предлагают нам новое сетевое решение, выглядящее многообещающим. Однако, начав изучать его, не все моменты оказались явными. Что-то реализовано не очень удобно, например, невозможность явно отсылать RPC между клиентом и сервером разнесенными в разные классы, нет общей обертки для сериализации custom-классов.  Некоторые вещи просто отсутствуют, например: соединение серверов (p2p server connection), переброска клиентов между серверами (client handovering) и т.д. . Но, будем надеяться, что uNet просто молод и все будет вскоре.

  Обычно, из соображений безопасности, производительности, в конце-то концов - читабельности кода (без директив компилятора), я разношу клиент и сервер в разные классы разных пространств имен. И в этом случае, предложенная uNet модель общения клиента и сервера: [ClientRPC]-[Command] перестает работать. Я хотел бы поделиться своим знакомством с uNet, на примере такого случая, вдруг это сэкономит кому-то время...

  Итак, создаем проект, два класса отвечающих за клиентскую и серверную части: Client, Server. Пример будет на c#. Рассмотрим минимальные настройки клиентской части.

public class Client : MonoBehaviour
{

Для создания uNet клиента нам необходимо знать IP-адрес и порт сервера. Порт будем хранить в классе сервера, а IP-адрес определим в клиенте через синоним:

string mHostIpOrName = "localhost";

Объявим переменную для uNet клиента:

NetworkClient mUNetClient;

В имплементации начала работы MonoBehaviour создаем NetworkClient и запускаем соединение.

void Start()
  {
    mUNetClient = new NetworkClient();

Настраиваем каналы, по которым будут передаваться RPC, синхронизируемые данные и пр.

  ConnectionConfig config = new ConnectionConfig();

    // Медленный канал для RPC, но с гарантированной доставкой и очередностью
    config.AddChannel(QosType.ReliableSequenced);

    // Быстрый канал, без гарантии, например, для синхронизации положения
    config.AddChannel(QosType.Unreliable);

    // Устанавливаем каналы клиенту.
    // Второй параметр - количество соединений
    mUNetClient.Configure(config, 1);

Вся работа клиент-серверного общения строится на обработчиках команд клиента uNet по их идентификаторам. Для системных команд имеется энумератор QosType, для своих команд создаем свой энумератор.

// Регистрируем обработчики системных команд

    // Сообщение о том, что клиент присоединился к серверу
    mUNetClient.RegisterHandler(MsgType.Connect, OnConnect);

    // Сообщение о том, что клиент отсоединился от сервера
    mUNetClient.RegisterHandler(MsgType.Disconnect, OnDisconnect);

    // Сообщение о сетевой ошибке
    mUNetClient.RegisterHandler(MsgType.Error, OnError);


    // Регистрируем наши команды
    mUNetClient.RegisterHandler((short)EnumRpc.ClientHelloWorld, ClientHelloWorld);

Клиент настроен, пора присоединиться к серверу

mUNetClient.Connect(mHostIpOrName, Server.sHostPort);
  }

Далее опишем все обработчики

// Обработчик присоединения клиента к серверу
    public void OnConnect(NetworkMessage _msg)
    {
      Debug.Log("Client Connected");

      // Отошлем на сервер свою команду

      NetworkWriter writer = new NetworkWriter();

      // Начало сообщения, указываем идентификатор серверной команды-обработчика
      writer.StartMessage((short)EnumRpc.ServerHelloWorld);

      // Передаем данные команды, здесь можно сериализовать любые свои данные
      writer.Write("Hello World!");

      // Конец сообщения
      writer.FinishMessage();

      // Отправляем сообщение
      // В mUNetClient.connection содержится NetworkConnection, 
      // с помощью которого и отсылаются все сообщения
      mUNetClient.connection.SendWriter(writer, Channels.DefaultReliable);

      Debug.Log("Client send HelloWorld command");

    }

    // Обработчик отсоединения клиента от сервера
    public void OnDisconnect(NetworkMessage _msg)
    {
      Debug.Log("Client Disconnected");

    }

    // Обработчик сетевой ошибки
    public void OnError(NetworkMessage _msg)
    {

      Debug.Log("Client Error: " + _msg.ToString());

    }
    
    // Обработчик нашей команды с сервера
    public void ClientHelloWorld(NetworkMessage _msg)
    {
      // Получаем данные сообщения
      string serverMessage = _msg.reader.ReadString();

      Debug.Log("Server answer: " + serverMessage);
    }
  }

Энумератор клиент-серверных команд

public enum EnumRpc
  {
      ClientHelloWorld = 1001,

      ServerHelloWorld = 2001,

  }

Для сервера все аналогично

+ Показать

Здесь стоит отметить только строку

NetworkServer.SetNetworkConnectionClass<ServerConnection>();
. Чтобы не хранить свой список  присоединенных клиентов и их серверные данные, uNet любезно предоставляет возможность описать свой враппер сетевого соединения, в котором мы можем реализовать серверную часть игрока. Такой враппер должен наследоваться от NetworkConnection.

  
  public class ServerConnection : NetworkConnection
  {
    // Флаг того, что соединение активно
    public bool mIsConnected = false;

  }

Вот минимум для начала работы.

uNetExampleSource - исходники
uNetExample - проект

При более тесном знакомстве с uNet оказалось, что на данный момент почти отсутствует/крайне неудобна сериализация своих классов. А если у Вас более-менее сложные вложенные классы, то можно совсем закопаться. Я набросал небольшой регистратор для сериализации своих типов объектов. Если его развить, то он может оказаться жирным раком на безрыбье.

Итак, основной класс - это сам регистратор, через него мы регистрируем свои типы и удобно сериализуем/десериализуем:

+ Показать

Класс описатель типа объектов:

+ Показать

Использование выглядит следующим образом:

Регистрация:

NetworkCodec.Add<MyClass>(MyClass.Write, MyClass.Read);

Использование:

NetworkCodec.Write<MyClass>(writer, myClassObject);
MyClass myObject = (HelloWorld)NetworkCodec.Read<MyClass>(reader);


Архив с примером работы (можно подставить скрипты в начальный проект):
CodecExample


#1
19:03, 1 окт. 2015

О, спасибо. Как раз давно хотел поковырять uNet, а все лень было. А благодаря короткой статье уже какие то базовые знания появились.
> невозможность явно отсылать RPC между клиентом и сервером разнесенными в
> разные классы
вот это не совсем понял. Разнесенными но в пределах одного приложения?

#2
19:06, 1 окт. 2015

http://forum.unity3d.com/threads/unity-5-unet-multiplayer-tutoria… co-op.325692/

#3
20:48, 1 окт. 2015

kardinal
https://www.youtube.com/playlist?list=PLwyZdDTyvucyAeJ_rbu_fbiUtGOVY55BG

#4
22:29, 1 окт. 2015

kardinal
seaman
Ааааа! только не видео!

#5
9:47, 2 окт. 2015

у парня на сайте есть все исходники, что еще надо ?

#6
11:11, 2 окт. 2015

RadianTOR
> вот это не совсем понял. Разнесенными но в пределах одного приложения?

Да.  [ClientRPC]-[Command] работают только в одном классе.

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

#7
19:07, 3 окт. 2015

Femidko
> а.  [ClientRPC]-[Command] работают только в одном классе.
Я в четверке просто создаю одноименную пустышку этой же RPC в серверном скрипте и одноименная RPC может быть вызвана в клиентском скрипте. И классы вроде разные, сейчас не помню, надо посмотреть.

#8
19:27, 3 окт. 2015

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

#9
19:58, 3 окт. 2015

Femidko
> и о каком выделенном авторитарном сервере речи не могло быть.
Да ладно? Как же у меня все работает с авторитарным сервером. :) Ни о каком мастер сервере и речи не идет.

#10
20:59, 3 окт. 2015

RadianTOR
Есть вероятность, что я что-то путаю по старой сети, давно было. Но у меня есть устойчивое убеждение, что в старой сети не было возможности создать выделенный сервер и подключить к нему клиентов без мастер сервера. А это в два раза больший пинг. Сообщения же юньки невозможно было выдернуть в самом мастер сервере, чтобы использовать его как игровой. Я даже, помнится, начал переписывать ракнет на c# и даже уже подключил к нему клиента, но потом плюнул и стал использовать ulink. Все разьясниться, если вы покажете пример кода создание такого сервера и подключение к нему клиента.

#11
8:16, 4 окт. 2015

Femidko
Подключение как обычно, его писать не буду, но вот создание объекта игрока происходит не на клиенте как обычно, а по запросу клиента, на сервере.

//Скрипт на клиенте
public class ClientPlayer : MonoBehaviour 
{

  [RPC] //функция запроса на создание игрока
  void CreateMyPlayer()
  {
    //пустышка. Без нее клиент не может вызвать эту RPC
  }

  [RPC] //функция отправки нажатых клавиш
  void SendKeys(int hInp, int vInp, bool fire1)
  {
    //пустышка. Без нее клиент не может вызвать эту RPC
  }

}
//Скрипт на сервере
public class ServerPlayer : MonoBehaviour 
{

  [RPC] //функция запроса на создание игрока
  void CreateMyPlayer()
  {
    //Создаем игрока владельцем которого будет сервер
    Network.Instantiate(playerPrefab); 
  }

  [RPC]
  void SendKeys(int hInp, int vInp, bool fire1)
  {
    //тут много кода
    //обработки клавиш
    //клиента
    //.....
  }

}

#12
9:51, 4 окт. 2015

Именно создание и подключение нужно ))

#13
10:10, 4 окт. 2015

Упс, у меня там в посте опечатка была
Скрипт на сервере называется ServerPlayer и это не мешает ему взаимодействовать со скриптом ClientPlayer

Femidko
> Именно создание
всмылсе? Создать новый проект, создать пустой ГО, на него повесить скрипт ServerPlayer...

#14
21:51, 4 окт. 2015

Все понятно, вопросов больше не имею ))

Страницы: 1 2 Следующая »
UnityФорумСеть

Тема в архиве.