You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Photon的回合制卡牌游戏自定义结构体消息发送问题求助

在Photon中发送自定义结构体(card和Hand)的解决方案

嘿,我完全懂你纠结的点——Photon的文档关于自定义类型序列化确实有点绕,不过别担心,我一步步给你讲清楚:

核心结论:必须使用Photon.RegisterType()

Photon默认只支持基本数据类型(int、string、float等)和它内置的一些类型(比如Vector3)。对于你自己定义的card和Hand结构体,必须手动注册序列化/反序列化逻辑,否则Photon不知道怎么把这些对象转换成网络能传输的字节流,接收端也没法还原它们。

具体实现步骤

1. 先完善你的结构体定义

首先,把结构体的字段改成public会更稳妥(避免Photon访问权限问题),当然你也可以保持internal,但要确保项目内的代码都能访问到:

public struct Card 
{ 
    public string Suit; 
    public string Value; 
}

public struct Hand
{
    public List<Card> PlayerCards;
}

2. 编写序列化和反序列化方法

你需要为每个自定义结构体写两个方法:一个负责把对象数据写入Photon的数据流(序列化),另一个负责从数据流里读取数据重建对象(反序列化)。

针对Card结构体的方法:

// 序列化:把Card的字段写入流
private static void SerializeCard(PhotonStream stream, object customObject)
{
    Card card = (Card)customObject;
    if (stream.IsWriting)
    {
        // 发送数据时,依次写入花色和点数
        stream.SendNext(card.Suit);
        stream.SendNext(card.Value);
    }
}

// 反序列化:从流里读取数据,生成新的Card对象
private static object DeserializeCard(PhotonStream stream)
{
    Card card = new Card();
    card.Suit = (string)stream.ReceiveNext();
    card.Value = (string)stream.ReceiveNext();
    return card;
}

针对Hand结构体的方法:

因为Hand包含List<Card>,我们需要先传输列表的长度,再逐个传输每个Card(注意:必须先注册Card类型,这样Photon才能处理单个Card的传输):

// 序列化:把Hand的卡牌列表写入流
private static void SerializeHand(PhotonStream stream, object customObject)
{
    Hand hand = (Hand)customObject;
    if (stream.IsWriting)
    {
        // 先发送卡牌数量,方便接收端知道要读取多少个Card
        stream.SendNext(hand.PlayerCards.Count);
        // 逐个发送每个Card
        foreach (Card card in hand.PlayerCards)
        {
            stream.SendNext(card);
        }
    }
}

// 反序列化:从流里读取数据,生成新的Hand对象
private static object DeserializeHand(PhotonStream stream)
{
    Hand hand = new Hand();
    int cardCount = (int)stream.ReceiveNext();
    hand.PlayerCards = new List<Card>();
    // 逐个读取Card并添加到列表
    for (int i = 0; i < cardCount; i++)
    {
        hand.PlayerCards.Add((Card)stream.ReceiveNext());
    }
    return hand;
}

3. 在游戏初始化时注册自定义类型

你需要在Photon建立连接之前注册这些类型,比如放在一个全局单例的Awake()方法里,确保所有客户端都执行同样的注册逻辑:

void Awake()
{
    // 注册Card类型:参数分别是【类型】、【唯一ID(0-255之间,不能重复)】、【序列化方法】、【反序列化方法】
    PhotonPeer.RegisterType(typeof(Card), 10, SerializeCard, DeserializeCard);
    // 注册Hand类型,用另一个不重复的ID
    PhotonPeer.RegisterType(typeof(Hand), 11, SerializeHand, DeserializeHand);
}

⚠️ 重要提示:所有客户端使用的类型ID必须完全一致,否则会出现序列化失败的问题!

4. 发送和接收包含自定义结构体的消息

现在你就可以像发送基本类型一样发送Card或Hand对象了,比如用RPC:

发送消息的示例:

// 构建一个测试用的Hand对象
Hand myCurrentHand = new Hand();
myCurrentHand.PlayerCards = new List<Card>
{
    new Card { Suit = "Hearts", Value = "Ace" },
    new Card { Suit = "Spades", Value = "King" }
};

// 发送给所有玩家,更新他们的手牌显示
PhotonNetwork.RPC("UpdatePlayerHand", RpcTarget.All, myCurrentHand);

接收消息的RPC方法:

[PunRPC]
void UpdatePlayerHand(Hand receivedHand)
{
    // 这里直接就能使用接收到的Hand对象了
    foreach (Card card in receivedHand.PlayerCards)
    {
        Debug.Log($"收到卡牌:{card.Value} of {card.Suit}");
    }
}

额外小贴士

  • 如果你的结构体里有更复杂的类型(比如自定义类),逻辑是一样的:注册类型+写序列化/反序列化方法
  • 测试时如果出现序列化错误,先检查类型ID是否一致,再检查序列化/反序列化方法里的字段读写顺序是否对应(必须和发送顺序完全一致)

内容的提问来源于stack exchange,提问作者Shuba Friends

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:39:46