我正在寻求一些帮助,让我的游戏成为多人游戏。
我刚刚开始使用 Photon,虽然我理解为什么文档首先关注玩家移动之类的事情,但我对网络额外数据有点着迷。
例如,我正在创建的游戏是纸牌游戏,当前单例保存代表每个玩家手牌的列表,该列表不直接与 GUI 绑定。它存在于世界游戏对象上,然后用于跟踪所有当前的卡牌。虽然我认为这可以仅作为附加到实际游戏对象(例如每张卡)的数据存在,但我认为拥有一组数据然后根据 GUI 进行映射会更清晰。
因此,如果我有 p1、p2 和 p3,有 3 张牌,称为 k、j、j,我如何告诉 photon 这些数据应该只在主机上保存一份副本,而不是每个设备上每个人的手牌的四份副本。假设我正确理解 p1 将有一个用于其场景的 Gamecore 实例,而 p2 将有一个用于其场景的游戏核心实例,因为即使它是单例,每个客户端都有自己的实例。
orrrr
每个客户端是否应该生成自己的 p1、p2 和 p3 列表,然后传达要在所有客户端之间传播的更改,以确保所有客户端都具有相同的数据。如果是这种情况,它更像是 p1 说丢弃一张卡,更新自身,然后发送一条消息供客户端接收。作为参考,这里是我当前的游戏核心和玩家类别的缩减。
public class GameCore : MonoBehaviour{
public List<Player> playerList = new List<Player>();
public static GameCore Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
Destroy(gameObject);
else
Instance = this;
}
}
public struct Player {
//should initialize with a name and 7 blank cards
public Player(string playerName)
{
name = playerName;
playerCards = new List<Card>();
score = 0;
sticks = new Stick();
}
private string name;
private int score;
public List<Card> playerCards;
public Stick sticks;
}
Fusion 的要点是使用
[Networked]
属性,它会自动通过网络同步。网络属性必须位于 NetworkBehaviour
内部,具有 [Networked]
属性,并且是 兼容的可序列化类型。
因此,在您的情况下,您的
Player
结构可以是 NetworkBehaviour:
public class PlayerData : NetworkBehaviour {
private const int MAX_NAME_SIZE = 16;
private const int MAX_HAND_SIZE = 7;
[Networked, Capacity(MAX_NAME_SIZE)] private string Name { get; set; }
[Networked] private int Score { get; set; }
[Networked, Capacity(MAX_HAND_SIZE)] public NetworkList<Card> PlayerCards => default;
[Networked] public Stick Sticks { get; set; }
}
public struct Card : INetworkStruct {
public CardSuit Suit;
public int Value;
}
public enum CardSuit : byte {
Spades, Clubs, Hearts, Diamonds
}
public struct Stick : INetworkStruct {
// I'm not sure what a "stick" is...
}
然后对于加入的每个玩家,您可以使用类似
INetworkRunnerCallbacks.OnPlayerJoined:
之类的内容生成包含
Player
行为(以及协调同步的 NetworkObject
)的预制件副本
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) {
// Only spawn our own copy.
if (runner.LocalPlayer == player) {
Runner.Spawn(yourPlayerPrefab);
}
}
此解决方案是“共享”模式的首选,因为每个玩家都对自己的对象拥有 StateAuthority(允许他们修改网络变量)。
如果您使用主机模式,那么主机(或专用服务器)将在 PlayerData 类上拥有 StateAuthority,并且玩家想要做的任何事情都必须通过 inputs 或 RPC(使用
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
)。
这意味着服务器将负责卡牌的生成和修改,这使得客户端不可能通过直接修改自己的卡牌变量来进行作弊:
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) {
if (runner.IsServer) {
Runner.Spawn(yourPlayerPrefab, inputAuthority: player);
}
}