Отделение сетевого кода Unity от игровой логики

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

Мой текущий подход заключается в создании кода для моих классов, связанных с сетью, таким образом, чтобы существовала онлайн- и офлайн-версия. Я делаю это с помощью шаблонов T4.

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

Автономная/одиночная версия:

// T4 GENERATED CODE
// Head (Singleplayer version)
class StandaloneHelloWorld : MonoBehaviour, IHelloWorld
{
    private string name;

    public void SayHello()
    {
        SayHelloInternal();
    }

// Body
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    void SayHelloInternal()
    {
        Debug.Log(Name + ": Hello World");
    }
}

Многопользовательская версия:

// T4 GENERATED CODE
// Head (Multiplayer version)
class NetworkedHelloWorld : NetworkBehaviour, IHelloWorld
{
    [SyncVar]
    private string name;

    public void SayHello()
    {
        CmdSayHello();
    }

    [Command]
    void CmdSayHello()
    {
        RpcSayHello();
    }

    [ClientRpc]
    void RpcSayHello()
    {
        SayHelloInternal();
    }

// Body
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    void SayHelloInternal()
    {
        Debug.Log(Name + ": Hello World");
    }
}

Они оба имеют общий интерфейс, чтобы скрыть реализацию от вызывающих:

interface IHelloWorld
{
    string Name { get; set; }
    void SayHello();
}

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

Преимущества:

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

Недостатки:

  • Поддержка интерфейсов в Unity ограничена. Я бы не смог ссылаться на экземпляры сцены IHelloWorld из редактора.
  • Необходимость поддерживать отдельные префабы для режимов одиночной/многопользовательской игры.
  • Необходимость вмешиваться в T4/генерацию кода

Знаете ли вы лучшие способы справиться с этим? Как вы решили эту проблему?


person Thomas Hilbert    schedule 05.05.2017    source источник


Ответы (1)


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

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

new Event(EventType.FireProjectile, pos, dir, template)

Затем вы можете зарегистрировать системы, заинтересованные в этом событии:

CollisionSystem.Register(EventType.FireProjectile, (e) => {
  CollisionSystem.AddCollider(e.template.bounds); 
});

AudioSystem.Register(EventType.FireProjectile, (e) => {
  AudioSystem.PlaySound("Woosh"); 
});

AISystem.Register(EventType.FireProjectile, (e) => {
  AISystem.AlertAtPosition(e.pos); 
});

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

NetworkSystem.Register(EventType.FireProjectile, (e) => {
  NetworkSystem.Broadcast(e, Channel.Reliable); 
});

Это очень здорово, за исключением того, что вы скоро поймете, что это вызовет бесконечный цикл событий. Когда вы отправляете событие FireProjectile другому клиенту, они его перехватывают и запускают. Мгновенно их NetworkSystem ловит его и запускает по сети.

Чтобы исправить это, вам нужно два события для каждого действия — запрос: FireProjectile и ответ: ProjectileFired.

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

person Iggy    schedule 05.05.2017