Saturday, April 6, 2013

Задача 4: Обмен данными между приложениями в Unity3d

Практическое использование движка Unity3d.




Задача 4: Обмен данными между приложениями в Unity3d



Cодержание

  • Реализация первого приложения (А)
  • Реализация второго приложения (Б)
  • Замечания



Задача 4: Обмен данными между приложениями в Unity3d.

 В этой статье рассмотрим следующую задачу: необходимо реализовать передачу данных или сообщений между двумя приложениями, разработанными в Unity3d. Для демонстрации передачи сообщений между двумя приложениями необходимо будет создать две сцены. Каждая сцена будет в результате представлять собой отдельное приложение: А – посылает данные и Б – принимает данные. 

Настройка сцен 

Создадим пустой проект. Сразу создадим в этом проекте 2 папки: «Scenes» -для хранения сцен и «Scripts» - для хранения скриптов. Создадим первую сцену, нажав «File/Save Scene». Откроется диалог для сохранения сцены на диске. Выберем каталог «Scenes» и сохраним сцену под названием «AppFirstScene». Теперь нажмем на пункт меню «File/Save Scene as» и сохраним сцену под названием «AppSecondScene». Процесс сохранения сцены в файловой системе, показан на рис. 1.

Рисунок 1. – Сохранение сцены 

  Теперь нажмем двойным щелчком на сцену «AppFirstScene», для того чтоб ее выбрать ее как активную. В сцене у нас уже есть камера. Создадим входную точку для работы скриптов. Для этого выберем пункт меню «GameObject / Create Empty», и переименуем созданный объект в «AppRoot».

  Аналогично выберем сцену «AppSecondScene» и создадим точно такую же входную точку.

В результате в окне Hierarchy для обеих сцен можно будет увидеть объекты, показанные на рис. 2.
Рисунок 2. – Объекты в сцене «AppFirstScene»

  Приступим к созданию скриптов. Для каждой из сцен соответственно создадим два скрипта: «AppRootFirst» и «AppRootSecond». Для этого нажмем левую клавишу мыши на папке Scripts и выберем «Create/C# script». После этого создадим класс констант «Constants». И так, теперь файловая система будет иметь вид, показанный на рис. 3.

Рисунок 3. – Состояние файловой системы проекта

Теперь в сцене «AppFirstScene» переместим с помощью левой клавиши мыши скрипт «AppRootFirst» на объект «AppRoot». Теперь скрипт «AppRootFirst» будет нашей входной точкой в первой сцене «AppFirst». На рис. 4 показан процесс назначения скрипта «AppRootFirst» на объект «AppRoot».
Рисунок 4. – Назначение скрипта для входной точки приложения

  Также для работы с сетью назначим на объект «AppRoot» компонент «NetworkView». Для этого необходимо выбрать объект «AppRoot» и выбрать пункт меню «Component/Miscellaneous/Network View». Теперь если выбрать «AppRoot» и посмотреть в окне «Inspector» его свойства, можно увидеть состояние, показанное на рис. 5.

Рисунок 5. – Состояние объекта «AppRoot»

Теперь аналогичные действия нам нужно проделать в сцене «AppSecondScene». Заново повторять все действия не буду, но приведу краткий список действий:
  • Выбираем двойным щелчком сцену «AppSecondScene»;
  • Перемещаем скрипт «AppRootSecond» на объект «AppRoot» с помощью левой клавиши мыши;
  • Выделяем объект «AppRoot» и выбираем пункт меню «Component/Miscellaneous/Network View»;
  • Вот и всё, сцены настроены. Переходим к написанию скриптов.

Передача данных между приложениями

  И так, начнем с теории. Существует множество способов передавать данные между приложениями, но в данной статье я буду использовать один из нативных способов для Unity3d способ передачи данных между приложениями.
  Я буду использовать технологию под названием «RPC» или «Удалённый вызов процедур» (подробнее здесь: http://ru.wikipedia.org/wiki/Удалённый_вызов_процедур). Идея в том, чтобы вызвать в другом приложении процедуру с необходимыми нам параметрами. В движке Unity3d есть реализация RPC процедур (подробнее здесь: http://docs.unity3d.com/Documentation/Components/net-RPCDetails.html).
  При работе с этой технологией есть несколько особенностей. О них я также расскажу в этой статье.

Реализация первого приложения (А)

  Приложение будет иметь всего две кнопки и одно текстовое поле. При нажатии на первую кнопку, приложение посылает текстовое сообщение второму. При нажатии на вторую кнопку, приложение А посылает данные приложению Б. Основная часть приложения А расположена в классе «AppRootFirst». Рассмотрим его реализацию.
  Создадим три переменные, которые будут хранить позицию кнопок и текстового поля.
private readonly Rect cSendHelloRect = new Rect(20, 200, 200, 200); 
private readonly Rect cSendDataRect = new Rect(240, 200, 200, 200);
private readonly Rect cDebugMsgRect = new Rect(20, 420, 200, 100);
  Также создадим переменные, которые будут хранить отладочное сообщение и передаваемые данные соответственно.
private string mSendMessage = "No messages";
private byte[] mDataToSend = new byte[] { 0x0b, 0x1f, 0x3c };
  В функции «OnGUI» выведем две кнопки и одно текстовое поле:
public void OnGUI()
{
    …
    if (GUI.Button(cSendHelloRect, "Hello"))
    {
    …
    }

    if (GUI.Button(cSendDataRect, "Send data"))
    {
    …
    }
    GUI.Label(cDebugMsgRect, mSendMessage);
}
  Теперь реализуем отправку сообщений и данных через RPC функции. Для того чтоб передавать сообщения через RPC функции, одно из приложений должно выполнять роль сервера, а остальные приложения будут выполнять роль клиентов. Т.к. первое приложение будет реализовать функциональность сервера, создадим метод «InitNet» для инициализации сервера. Функция будет очень маленькая, но впоследствии, туда можно будет добавить дополнительный функционал, который необходим при инициализации сервера. Для инициализации сервера в Unity3d есть статический метод «Network.InitializeServer». Первый параметр – это количество соединений. В этом приложении мы поставим это значение равным «3». Вторым параметром функции является порт, по которому будут передаваться сообщения. Порт я вынес в класс констант.
private void InitNet()
{ Network.InitializeServer(3, Constants.cServerPort, false); }
  Метод «InitNet» вызовем в функции «Start».
public void Start()
{
    InitNet();
}
  Для того чтоб вызывать RPC функции, нужно использовать метод «RPC» у компонента «NetworkView», который мы добавили к объекту «AppRoot» при настройке сцены. Одной из особенностей RPC процедур является то что каждая RPC функция должна быть с атрибутом «[RPC]», причём этот метод должен быть И в классе который вызывает RPC процедуру И в классе в котором должна быть вызвана RPC процедура. Это очень важно, иначе вызов RPC процедур не будет работать.
  Отправка сообщений тоже имеет свои особенности. Если отправлять сообщение на каждом кадре, посылка сообщений прерывается. Поэтому необходимо поставить некоторую задержку перед отправкой сообщения повторно.
  Для реализации задержки передачи сообщений сразу создадим переменные, которые в себе будут хранить текущее время задержки, и максимальное время задержки.
// timer to create some delay for sending messages 
private float mWaitTimeUpdateLaserTracker = 0.0f;
private const float cMaxWaitTimeUpdateLaserTracker = 0.1f;
  И теперь метод «OnGUI» будет выглядеть следующим образом.
// increment wait time
if (mWaitTimeUpdate < cMaxWaitTimeUpdate)
{
    mWaitTimeUpdate += Time.deltaTime;
}
if (GUI.Button(cSendHelloRect, "Hello"))
{
    if (mWaitTimeUpdate > cMaxWaitTimeUpdate)
    {

        //
        this.networkView.RPC(Constants.cRPCSendMessage, Constants.cSendMessagesMode, "Hello");
        //
        mWaitTimeUpdate = 0.0f;

        //
        mSendMessage = "'Hello' message sent";
    } 
}

if (GUI.Button(cSendDataRect, "Send data")) 
{
    if (mWaitTimeUpdate > cMaxWaitTimeUpdate)
    {
        //
        this.networkView.RPC(Constants.cRPCSendData, Constants.cSendMessagesMode, mDataToSend);

        //
        mWaitTimeUpdate = 0.0f;

        //
        mSendMessage = "Data sent";
    }
}

GUI.Label(cDebugMsgRect, mSendMessage); 
  Хочу отметить, что названия RPC функций очень удобно хранить в классе констант, т.к. названия RPC функций могут использоваться в нескольких классах.
  Также в этом классе создадим пустые RPC функции.
[RPC]
public void RPCSendMessage(string msg)
{

}

[RPC]
public void RPCSendData(byte[] data)
{

}
  На этом разработка первого приложения закончена. Для того чтоб его собрать, необходимо выбрать пункт меню «File/Build Settings». В настройках, в поле «ProductName» указать «AppFirst», а в список сцен добавить сцену «AppFirstScene». И нажимаем кнопку «Build».
  Теперь рассмотрим реализацию второго приложения (Б)


Реализация второго приложения (Б)

  Реализация клиента тоже очень проста.
  Создадим переменную, которая будет хранить сообщение, которое прислали.
// received message 
private string mReceiveMessage = "No messages";
  Создадим переменную, которая будет хранить позицию текстового поля для сообщения.
// rect for displaying of received message 
private readonly Rect cMsgRect = new Rect(20, 420, 200, 100);
  Соответственно метод «OnGUI» будет выглядеть следующим образом.
public void OnGUI()
{
    GUI.Label(cMsgRect, mReceiveMessage);
}
  Соединяемся с сервером. Для этого в Unity3d есть статический метод «Network.Connect». Т.к. сервер находится на локальном компьютере то IP адрес сервера «127.0.0.1». Порт для соединения используем тот же что и север. Переменная, которая хранит IP адрес, также находится в константах.
//
Network.Connect(Constants.cServerIp, Constants.cServerPort); 
  Осталось только написать реализацию RPC процедур.
[RPC]
public void RPCSendMessage(string msg)
{
    mReceiveMessage = "Message received = " + msg;
}

[RPC] 
public void RPCSendData(byte[] data)
{
    mReceiveMessage = "Data received. Data length = " + data.Length;
}
  Вот и все. Для того чтоб собрать второе приложение, необходимо выбрать пункт меню «File/Build Settings». В настройках, в поле «ProductName» указать «AppFirst», а в список сцен добавить сцену «AppSecondScene». И нажимаем кнопку «Build».

Заключение

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

Демонстрацию работы можно скачать здесь:

Исходные коды Вы как всегда сможете бесплатно скачать здесь:

No comments:

Post a Comment