Unity 中的回合制网络

Turn based networking in Unity

我想创建一个回合制网络游戏,所以我必须在某个地方告诉玩家等待回合。这看起来像一个简单的问题,但我现在已经为它苦苦挣扎了很多天。首先我跟你说说我目前的情况,如果我在任何阶段做错了什么,请告诉我。

在游戏开始之前,玩家进入大厅并可以加入游戏(或可能创建游戏)。目前我的大厅只有 2 个按钮,可以在其中创建服务器和检索服务器列表。一旦检索到服务器,就会显示在按钮中。

当玩家启动服务器时,此脚本运行:

private void StartServer()
{
    Network.InitializeServer(2, 25000, false);
    MasterServer.RegisterHost(gameName, "Test game 2", "Welcome!");
    GameObject canvas = GameObject.Find("Canvas");
    canvas.SetActive(false);
}

据我所知,该播放器目前是服务器。

其他玩家现在可以刷新列表并通过单击按钮加入MasterServer提供的HostData服务器:

private void HostButton(HostData hostData)
{
    Debug.Log(hostData.gameName);
    Network.Connect(hostData);
    Application.LoadLevel("GameScene");
}

可以看到,连接后加载了一个新的场景。建立连接后,我对服务器执行相同的操作:

private void OnPlayerConnected(NetworkPlayer player)
{
    Debug.Log(player.externalIP + " Has connected!");       
    Application.LoadLevel("GameScene");
}

现在两个玩家(客户端和服务器)都在游戏场景中,并且他们仍然处于连接状态。我如何将轮到服务器并让客户端等待?只需按下按钮的反馈就足以让我理解。比如只允许单个玩家输入反馈。这是我在新场景中尝试的:

void Start () 
{
    players = Network.connections;
    foreach (NetworkPlayer player in players)
    {
        Debug.Log(player.externalIP + ":" + player.externalPort + " entered the game!");
    }
    Debug.Log(players.GetLength(0)); //returns 1. Obviously because server to client is one conncetion. But what is the NetworkPlayer of the server?
}

很明显,以下检查哪个玩家可以行动的代码不起作用。

void Update () 
{
    if (players[currentPlayer] == Network.player)
    {
        if (Input.GetMouseButtonDown(0))
        {
            Debug.Log("Player " + (currentPlayer + 1) + " fired mouse!");
            currentPlayer++;
            if (currentPlayer > 1) currentPlayer = 0;
        }
    }
}

我完全被卡住了好几天,所有关于联网的教程都显示两个浮动立方体,但根本没有显示如何区分玩家。 如何限制单个玩家(无论是创建服务器还是作为客户端加入)在当前玩家行动之前不能移动

所以我想寻求有关如何在服务器播放器和连接的客户端之间实现非常基本的回合制系统的提示。

虽然我自己从未使用过 MasterServer,但我相信 NetworkView.RPC 调用应该有效。 RPC 或 远程过程调用 是通过网络发送的调用,如果找到匹配的 public 方法,则会在所有对等点上执行。

关于RPC这里就不多说了,在Unity's NetworkView.RPC docs here

上已经很详细了

针对您的问题,这里有一些您可以尝试的代码

创建一个新的 C# 脚本,并将其命名为 RPCExample

RPCExample.cs

using UnityEngine;
using System.Collections;

public class RPCExample : MonoBehaviour {

    public static RPCExample instance;

    //Assign this NetworkView in the inspector
    public NetworkView networkView;

    //A boolean that states whether this player is the server
    //or the client
    public bool thisIsTheServer = false;

    //This is a boolean that will control whether it is THIS
    //player's turn or not
    public bool isMyTurn = false;


    void Awake () {
        instance = this;
    }

    //This method will set whether this connected player is the server or not
    [RPC]
    public void SetAsServer (bool isServer) {
        thisIsTheServer = isServer;
    }

//
    [RPC]
    public void SetTurn (bool turn) {
        isMyTurn = turn;
    }

    [RPC]
    public void FinishTurn () {
        networkView.RPC("SetTurn", RPCMode.Others, new object[] { true });
        SetTurn(false);
    }


    public void Update () {

        //It is not this player's turn, so early exit
        if(!isMyTurn)
            return;

        //If execution reaches here, it is this player's turn
        Debug.Log (string.Format("It is {0}'s turn!", 
                             new object[] { (thisIsTheServer) ? "Server" : "Client" }));

    }
}

接下来,将此脚本附加到您的玩家游戏对象,并在检查器中分配 NetworkView 变量。

现在,将您的 StartServer 方法更改为

private void StartServer() {
    Network.InitializeServer(2, 25000, false);
    MasterServer.RegisterHost(gameName, "Test game 2", "Welcome!");
    GameObject canvas = GameObject.Find("Canvas");
    canvas.SetActive(false);

    //Set this player as server
    RPCExample.instance.SetAsServer(true);
}

如下更改您的 HostButton 方法

private void HostButton(HostData hostData) {
    Debug.Log(hostData.gameName);
    Network.Connect(hostData);
    Application.LoadLevel("GameScene");

    //This should only be executed on the client obviously.
    //Ensure you handle that scenario.
    RPCExample.instance.SetAsServer(false);
}

现在,对于这个例子,我们假设服务器总是先行(你可以很容易地改变它)。因此,当您的游戏场景加载时(您使用 Application.LoadLevel("GameScene") 加载的场景),请在客户端

上调用以下方法
void Start () {
    //By calling FinishTurn on the client, we pass the turn onto the server
    //You can always rewrite this to use a random value, like a coin flip
    RPCExample.instance.FinishTurn();
}

就是这样!当您完成当前玩家的回合后,只需调用

RPCExample.instance.FinishTurn();