保持Unity网络代码与游戏逻辑分离

时间:2017-05-05 11:18:51

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

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

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

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

1 个答案:

答案 0 :(得分:0)

您可以基于事件的方式构建代码。这将允许系统注册他们感兴趣的事件。这自然地将逻辑与网络代码分开。

作为一个例子,让我们说你要发射一枚射弹。 您可以通过致电:

来解雇它

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。注意服务器和客户端如何注册某些事件,这些事件将转发。