c# - Unity 中的回合制网络

标签 c# unity3d network-programming

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

在游戏开始之前,玩家进入大厅并可以加入游戏(或可能创建游戏)。目前我的大厅只有 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;
        }
    }
}

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

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

最佳答案

虽然我自己从未使用过 MasterServer,但我相信 NetworkView.RPC 调用应该有效。 RPC 或 Remote Procedure Calls 是通过网络发送的调用,只要找到匹配的公共(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();

关于c# - Unity 中的回合制网络,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28023008/

相关文章:

c# - Azure AD - 从 token 获取客户端 secret 描述

c# - HttpBasicAuthenticator 身份验证问题

unity3d - 安装Unity后如何在Unity中添加Vuforia SDK?

c# - 是否可以使用枚举动态获取静态类?

networking - NAT 实现

c - 用C代码管理Linux网络接口(interface)

android - 假设所有支持网络的设备都可以访问 localhost/loopback/127.0.0.1 是否安全——即使 "networking"已关闭?

c# - WPF - 为 ItemsPresenter 实现 ItemTemplate?

java - 在 Java 中,通过 TCP 套接字从 C# 应用程序接收音频数据流并在接收时播放流媒体

c# - 每个 “foreach” 循环的每次迭代都会生成 24 字节的垃圾内存?