在Unity3D联网中,如何拥有“多个播放器”?

时间:2018-08-08 16:20:08

标签: unity3d unity3d-unet unet

我有一个带有服务器的简单LAN拓扑,并说了三个客户端,

          -client
server    -client
          -client

(他们并不是字面上的“玩家”)

这三个客户端向服务器传达各种信息。当然,只使用[Command]调用。

但是它们“都是一样的”。拨打了相同的电话。

(服务器从ID知道哪个人发送了信息。)

首先,我只是生成了一个“玩家”对象,上面带有一个脚本,其中包含各种[Command]调用。

enter image description here
每个客户端都有一个抽象播放器对象

enter image description here
它是由NetworkManager以常规方式生成的

但是。

事实证明,服务器仅“相信”连接的最后一个

在Unity网络中似乎只能有一个“玩家”。

真的,您如何在Unity网络中做到这一点?

2 个答案:

答案 0 :(得分:2)

对于任何为此感到困惑的人,回答我自己的问题:

  • 关于网络自动生成的“播放器”预制件的自动生成。

实际上,您将为每个玩家获得 一位 。 (一旦您说了一遍,似乎就很明显了。)到处都是一大堆。

所以在这里:

          -client
server    -client
          -client

实际上,您将看到三个衍生的预制件。

(此外:在我的示例中,我将服务器用作“服务器”,而不是“主机”。

enter image description here

如果您使用的是“主机”概念,那儿还会有另一个衍生的播放器预制件。)

因此,当您启动该应用程序时,只有一台PC成为服务器,尚无连接..

enter image description here

然后您启动第二台PC,第一个客户端,它们就连接起来了……现在看起来像这样

enter image description here

然后启动第三台PC,第二台客户端,它们连接起来……现在看起来像这样

enter image description here

..依此类推。

因此,每个客户端都有一个克隆的播放器项。 (同样,如果对服务器使用“主机”概念,则仅意味着在与服务器相同的物理位置上还有另一个客户端。)

每个“玩家”都有自己的自动生成的玩家对象-,并且在所有设备上。五个客户端,每个设备(在所有六个设备上)将有五个自动生成的播放器对象。

那是什么引起了提到的特定错误?

Unet中隐藏的令人惊讶的困难是.isLocalPlayer概念。

假设在播放器预制件上的示例中确实有一个类似Comms的脚本,这似乎很自然。说游戏的其他部分必须与Comms联络(例如,向服务器发送消息)。

事实上,您必须找到自己的播放器预制件...。

您必须找到自己的播放器预制件...。

您可能会希望拥有类似这样的代码。

// laser blows up
// tell Comms to send a message to the server about that
// find Comms ..
Comms co = FindObjectOfType<Comms>();
// tell it to send the message
co.TellServerLaserBlewUp();

但是,这是错误的,调试将非常不稳定。

实际上您的代码如下:

// laser blows up
// tell Comms to send a message to the server about that
// find >> OUR << Comms ..
foreach (Comms co in FindObjectsOfType<Comms>() ) {

    string ilp = co.isLocalPlayer ? "YY" : "NN";
    Debug.Log("I tried one of the many Comms! .. " + ilp);
    if (co.isLocalPlayer) {

         Debug.Log("Found it!");
         co.TellServerLaserBlewUp();
    }
}

(注意-显然,每次生产时您都不会找到类似的东西,您将使用单身人士或您所喝的任何茶。)

请考虑您的脚本在播放器预制件上。在我们的示例中为“ Comms.cs”。

在大多数统一示例中,他们设想您“在该脚本中”进行操作。

在这种情况下,它更直观:如果您不是.isLocalPlayer,您就离开了。

但是,这非常幼稚,实际上,您周围的无数脚本希望/需要与您的“ Comms.cs”联系并进行处理(因为这是唯一连接到网络的脚本。

>

事实上,

您确实必须始终找到“您的” .isLocalPlayer

整rick!

要重复-Unity doco示例倾向于强调“在该脚本上”做事。实际上,在任何大型现实项目中,事实并非如此,您是在“联系”该脚本作为中央通信资源。

答案 1 :(得分:1)

不是一个完整的答案,因为您已经自己弄清楚了。

(您没有一个最小的代码示例,所以说实话我不完全遵循您的工作...但是)
无论如何,我想分享我通常的做法。

在播放器预制件上(在连接上生成),我放置了一个特殊的组件,例如

<span> {{ users && users.length ? users[0].event : '' }} </span>

,如果需要的话,其他using UnityEngine; using UnityEngine.Networking; [DisallowMultipleComponent] [RequireComponent(typeof(NetworkIdentity))] public class PlayerInformation : NetworkBehaviour { // simply a public accessor for isLocalPlayer public bool IsLocalPlayer { get {return isLocalPlayer;} } } 等其他NetworkBehaviour布尔也要这样做。

比每次必须从另一个组件调用方法时,我还要将其传递给isServer
->我可以直接检查它是否是本地播放器,例如

PlayerInformation

现在,仅向玩家传达某些东西的部分称为

不幸的是,您无法将public void DoSomething(PlayerInformation player) { // don't even go any further if not local player if(!player.IsLocalPlayer) return; // ... do your stuff } 传递给命令和Rpc ..,但是您可以传递Component(具有NetworkIdentity)。 NetworkIdentity确保可以在所有连接的实例上识别GameObject。

GameObjects

现在,只有调用命令的播放器才应执行Rpc方法的其余部分(通常由所有播放器执行)

这样做至少使事情变得轻松而整洁-至少对我来说。

希望有帮助