基于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
相关产品推荐
相关产品推荐

