【问题标题】:Keeping Unity networking code separate from game logic将 Unity 网络代码与游戏逻辑分开
【发布时间】:2017-10-03 20:35:11
【问题描述】:

我想将网络代码与我的游戏逻辑分开。我不仅需要这样做以便能够在单人和多人游戏模式之间共享游戏逻辑,而且因为关注点分离的事情,我也想要它。

我目前的方法是为我的网络相关类生成代码,以便有在线和离线版本。我使用 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/代码生成

你知道更好的方法来解决这个问题吗?你是怎么解决这个问题的?

【问题讨论】:

    标签: unity3d t4 separation-of-concerns unity3d-unet


    【解决方案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++ 编写的,但如果您有兴趣,可以阅读更多 here。请注意服务器和客户端如何注册某些事件,它们将转发。

    【讨论】:

      猜你喜欢
      • 2016-02-05
      • 1970-01-01
      • 2017-08-15
      • 1970-01-01
      • 1970-01-01
      • 2015-09-25
      • 1970-01-01
      • 1970-01-01
      • 2020-07-14
      相关资源
      最近更新 更多