Используя атрибуты C #, как мне запускать функции, которые будут выполняться при изменении назначенного им поля - PullRequest
0 голосов
/ 24 июня 2018

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

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

Важным является наш ShatNetTransportLayer класс. Это статический класс, который внутренне использует два буфера, один для приема данных, один для передачи. Другой класс более высокого уровня работает во многом как «сетевой gc». Сообщения помещаются в список отправки, который регулярно проверяется и освобождается после отправки всех сообщений. Это, конечно, упрощено, на самом деле есть проверки для разных каналов и т. Д.

Пока что до настройки. Важно знать следующее.

Каждый объект в игре, который должен быть подключен к сети, будет содержать ShatNetIdentity. Это основной класс, который регистрирует сетевые объекты на сервере и поддерживает их синхронизацию на каждой стороне клиента. Хотя это еще не полностью реализовано, нам нужно преодолеть еще одну проблему. Планируется, что каждый ShatNetIdentity будет основным компонентом сетевого игрового объекта, так что любой ShatNetBehaviour сможет получить к нему доступ через игровой объект.

ShatNetBehaviour - это класс, производный от monobehaviour, который является классом Unity по умолчанию для работы с элементами сцены. Единственное, что он добавляет, это возможность получить ShatNetIdentity, связанный с объектом, к которому прикреплен скрипт.

Теперь идет решающая часть:

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

Внутренне все, что будет отправлено, - это сообщение с максимальным размером 128 байт. Частью этого является MessageType, целое число без знака длиной 1 байт, которое представляет тип сообщения, такой как переменная синхронизация, вызовы сообщения, отключенные проигрыватели и тому подобное. Вторым является идентификатор отправителя, представленный 2Byte. Отдых это само сообщение. Существует больше вовлеченных, но это ядро. Это еще предстоит полностью определить, но вы поняли идею. Эти сообщения записываются в буфер и отправляются, когда отправка запланирована или получена, когда произошло событие приема.

Назад, когда мы использовали собственный UNet Unity, вы могли просто добавить атрибут с именем SyncVar к любому полю класса, которое является частью объекта, к которому прикреплен ShatNetIdentity. Был также способ определить функцию обратного вызова, если значение изменилось.

Ссылка: https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute.html

* +1037 * Пример:
//When this is not our client, this value will be updated automatically across the net
[SyncVar(hook="OnHealthChanged")]
int health;


void OnHealthChanged()
{
doStuff();
}

Итак, мой вопрос, есть ли способ реализовать это самостоятельно?

Мы имели в виду имена NetVar и NetHook. Чтобы синхронизировать переменную по сети, нужно просто написать

[NetVar]
private int health;

void TakeDamage(int amount)
{
//Updates health for all clients
   health-=amount;
}

Чтобы это произошло, должно произойти несколько вещей.

  1. Когда создается новый ShatNetIdentity, зарегистрируйте его на сервере.
  2. Зарегистрируйте каждую переменную каждого класса, которая прикреплена к игровому объекту, с помощью ShatNetIdentity на эту личность. Вероятно, это невозможно, потому что уровни доступа? Использовать открытые свойства внутри атрибута?
  3. Когда приемный буфер заполнен, проверьте сообщения для типов обновления переменных, найдите индекс ShatNetIdentity и указанную переменную.
  4. Обновите эту переменную на каждом клиенте

Так что мне нужно получить подсказку:

  1. как ссылаться на частные переменные в ShatNetIdentity (конечно, это может быть любой класс). Поддерживаемые типы - это базовые типы, больше ничего, но должен быть один атрибут, который охватывает их все. Ничего подобного NetBool, NetInt ...
  2. Как мне ссылаться на функции обратного вызова с атрибутом и запускать их при изменении значения поля.
  3. Какие еще опции я могу использовать.

Идеи, которые у меня были до сих пор: Создание сетевых базовых типов для каждого базового типа. Это выглядело бы некрасиво и не помогло бы найти простой код.

Надеюсь, я все объяснил достаточно хорошо. Если нужна какая-то деталь, просто стреляйте.

Большое спасибо за вашу помощь.

Edit: Похоже, SynVar - это просто тег для генерируемого кода.

http://blogs.unity3d.com/2014/05/29/unet-syncvar

Как это можно сделать?

1 Ответ

0 голосов
/ 24 июня 2018

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

В сценарии Broadcast ваш коммуникационный компонент (класс) может объявить что-то вроде:

public event Action<(string shatNetIdentity, MessageType messageType, string message)> Message;

А затем вызвать, когда сообщение получено правильно:

Message?.Invoke((id, messageType, message));

Получающему объекту затем придется определить, предназначено ли ему сообщение, и если да, то каким оно было на самом деле и как с ним работать.

В сценарии Register ваши объекты могут регистрироваться в коммуникационном компоненте. В вашем компоненте связи может быть словарь:

private Dicitionary<string, INetworkObject> Registrations;

public void Register(INetworkObject o)
{
  Registrations[o.ShatNetIdentity] = o;
}

private void ReceiveMessage()
{
  // receive and parse message.
  if ( Registrations.TryGet(message.Id, out INetworkObject o )
  {
    switch (message.messageType)
    {
      case MessageType.Health: 
      o.Health = message.Health;
      break;
      // etc..
    }
  }
}

Где INetworkObject - что-то вроде:

public interface INetworkObject
{
  string ShatNetIdentity {get; set;}
  float Health { get; set; }
  Vector3 Position {get; set;}
}

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

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