任务33:后端-实现抢地主功能与牌顺组件
服务端部分
添加服务端抢地主GamerGrabLandlordSelect_Handler
\Server\Hotfix\Landlords\Handler\Map\GamerGrabLandlordSelect_Handler.cs
1、保存了房间每个玩家抢地主状态,并发消息给前端
2、没人抢地主则重新发牌逻辑
3、确定地主发地主牌逻辑
using System; using System.Collections.Generic; using System.Threading.Tasks; using ETModel; namespace ETHotfix { [ActorMessageHandler(AppType.Map)] public class GamerGrabLandlordSelect_Handler : AMActorHandler<Gamer, Actor_GamerGrabLandlordSelect_Ntt> { protected override async ETTask Run(Gamer gamer, Actor_GamerGrabLandlordSelect_Ntt message) { Room room = Game.Scene.GetComponent<LandMatchComponent>().GetGamingRoom(gamer); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); GameControllerComponent gameController = room.GetComponent<GameControllerComponent>(); ActorMessageSenderComponent actorProxyComponent = Game.Scene.GetComponent<ActorMessageSenderComponent>(); if (orderController.CurrentAuthority == gamer.UserID) { //保存抢地主状态 orderController.GamerLandlordState[gamer.UserID] = message.IsGrab; if (message.IsGrab) { orderController.Biggest = gamer.UserID; gameController.Multiples *= 2; room.Broadcast(new Actor_SetMultiples_Ntt() { Multiples = gameController.Multiples }); } //转发消息 Actor_GamerGrabLandlordSelect_Ntt transpond = new Actor_GamerGrabLandlordSelect_Ntt(); transpond.IsGrab = message.IsGrab; transpond.UserID = gamer.UserID; room.Broadcast(transpond); //房间有三人抢地主操作后(抢或不抢) if (orderController.SelectLordIndex >= room.Count) { /* * 地主:√ 农民1:× 农民2:× * 地主:× 农民1:√ 农民2:√ * 地主:√ 农民1:√ 农民2:√ 地主:√ * * */ if (orderController.Biggest == 0) { //没人抢地主则重新发牌 gameController.BackToDeck(); gameController.DealCards(); //发送玩家手牌 Gamer[] gamers = room.gamers; List<GamerCardNum> gamersCardNum = new List<GamerCardNum>(); Array.ForEach(gamers, _gamer => gamersCardNum.Add(new GamerCardNum() { UserID = _gamer.UserID, Num = _gamer.GetComponent<HandCardsComponent>().GetAll().Length })); Array.ForEach(gamers, _gamer => { ActorMessageSender actorProxy = actorProxyComponent.Get(_gamer.CActorID); actorProxy.Send(new Actor_GameStartHandCards_Ntt() { HandCards = To.RepeatedField(_gamer.GetComponent<HandCardsComponent>().GetAll()), GamersCardNum = To.RepeatedField(gamersCardNum) }); }); //随机先手玩家 gameController.RandomFirstAuthority(); return; } else if ((orderController.SelectLordIndex == room.Count && ((orderController.Biggest != orderController.FirstAuthority.Key && !orderController.FirstAuthority.Value) || orderController.Biggest == orderController.FirstAuthority.Key)) || orderController.SelectLordIndex > room.Count) { gameController.CardsOnTable(orderController.Biggest); return; } } //当所有玩家都抢地主时先手玩家还有一次抢地主的机会 if (gamer.UserID == orderController.FirstAuthority.Key && message.IsGrab) { orderController.FirstAuthority = new KeyValuePair<long, bool>(gamer.UserID, true); } orderController.Turn(); orderController.SelectLordIndex++; room.Broadcast(new Actor_AuthorityGrabLandlord_Ntt() { UserID = orderController.CurrentAuthority }); } await ETTask.CompletedTask; } } }
Room实体中添加两个暂用地主牌属性
\Server\ET.Core\Landlords\Entity\Map\Room.cs
下节有出牌缓存组件后,将替换暂用属性
//暂用地主牌属性 public readonly List<Card> LordCards = new List<Card>(); public readonly List<Card> LordCache = new List<Card>();
GameControllerComponentSystem组件扩展类
\Server\Hotfix\Landlords\System\GameControllerComponentSystem.cs
下节有出牌缓存组件后,将替换暂用方法
1、添加抢地主相关扩展方法
//暂用存地主牌方法 public static void DealToLord(this GameControllerComponent self, long id){ Room room = self.GetParent<Room>(); Card card = room.GetComponent<DeckComponent>().Deal(); if (id == room.Id) { room.LordCache.Add(card); room.LordCards.Add(card); } } //暂用发地主牌方法 public static Card DealLord(this GameControllerComponent self,Room room) { Card card = room.LordCache[room.LordCache.Count - 1]; room.LordCache.Remove(card); return card; } /// <summary> /// 向客户端发地主牌 /// </summary> public static void CardsOnTable(this GameControllerComponent self, long id) { Room room = self.GetParent<Room>(); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); HandCardsComponent handCards = room.GetGamerFromUserID(id).GetComponent<HandCardsComponent>(); orderController.Start(id); for (int i = 0; i < 3; i++) { Card card = self.DealLord(room); handCards.AddCard(card); } //更新玩家身份 foreach (var gamer in room.gamers) { Identity gamerIdentity = gamer.UserID == id ? Identity.Landlord : Identity.Farmer; self.UpdateInIdentity(gamer, gamerIdentity); } //广播地主消息 room.Broadcast(new Actor_SetLandlord_Ntt() { UserID = id, LordCards = To.RepeatedField(room.LordCards) }); //广播地主先手出牌消息 room.Broadcast(new Actor_AuthorityPlayCard_Ntt() { UserID = id, IsFirst = true }); } /// <summary> /// 更新身份 /// </summary> public static void UpdateInIdentity(this GameControllerComponent self, Gamer gamer, Identity identity) { gamer.GetComponent<HandCardsComponent>().AccessIdentity = identity; } /// <summary> /// 场上的所有牌回到牌库中 /// </summary> public static void BackToDeck(this GameControllerComponent self) { Room room = self.GetParent<Room>(); DeckComponent deckComponent = room.GetComponent<DeckComponent>(); //回收玩家手牌 foreach (var gamer in room.gamers) { HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); while (handCards.CardsCount > 0) { Card card = handCards.library[handCards.CardsCount - 1]; handCards.PopCard(card); deckComponent.AddCard(card); } } }
2、修改发牌方法 DealCards 中的发地主牌部分
//发地主牌 for (int i = 0; i < 3; i++) { self.DealToLord(room.Id); }
添加出牌次序组件OrderControllerComponent
\Server\ET.Core\Landlords\Component\Map\LandlordsRoom\OrderControllerComponent.cs
看他的主要属性能明白此组件用途:
//先手玩家 public KeyValuePair<long, bool> FirstAuthority { get; set; } //玩家抢地主状态 public Dictionary<long, bool> GamerLandlordState = new Dictionary<long, bool>(); //本轮最大牌型玩家 public long Biggest { get; set; } //当前出牌玩家 public long CurrentAuthority { get; set; } //当前抢地主玩家 public int SelectLordIndex { get; set; }
完整OrderControllerComponent.cs
using System.Collections.Generic; namespace ETModel { public class OrderControllerComponent : Component { //先手玩家 public KeyValuePair<long, bool> FirstAuthority { get; set; } //玩家抢地主状态 public Dictionary<long, bool> GamerLandlordState = new Dictionary<long, bool>(); //本轮最大牌型玩家 public long Biggest { get; set; } //当前出牌玩家 public long CurrentAuthority { get; set; } //当前抢地主玩家 public int SelectLordIndex { get; set; } public override void Dispose() { if(this.IsDisposed) { return; } base.Dispose(); this.GamerLandlordState.Clear(); this.Biggest = 0; this.CurrentAuthority = 0; this.SelectLordIndex = 0; } } }
添加出牌次序组件扩展类与扩展方法
\Server\Hotfix\Landlords\System\OrderControllerComponentSystem.cs
using System; using ETModel; namespace ETHotfix { public static class OrderControllerComponentSystem { /// <summary> /// 初始化 /// </summary> /// <param name="self"></param> public static void Init(this OrderControllerComponent self, long id) { self.FirstAuthority = new System.Collections.Generic.KeyValuePair<long, bool>(id, false); self.Biggest = 0; self.CurrentAuthority = id; self.SelectLordIndex = 1; self.GamerLandlordState.Clear(); } /// <summary> /// 开始 /// </summary> /// <param name="self"></param> public static void Start(this OrderControllerComponent self, long id) { self.Biggest = id; self.CurrentAuthority = id; } /// <summary> /// 轮转 /// </summary> /// <param name="self"></param> public static void Turn(this OrderControllerComponent self) { Room room = self.GetParent<Room>(); Gamer[] gamers = room.gamers; int index = Array.FindIndex(gamers, (gamer) => self.CurrentAuthority == gamer.UserID); index++; if (index == gamers.Length) { index = 0; } self.CurrentAuthority = gamers[index].UserID; } } }
在RoomSystem的GameStart方法中添加牌序组件
\Server\Hotfix\Landlords\System\RoomSystem.cs
//出牌控制组件 self.AddComponent<OrderControllerComponent>();
前端部分
在前端增加完成抢地主,设置倍率,显示出牌按钮的Handler
Actor_GamerGrabLandlordSelect_NttHandler \Assets\Model\Landlords\Handler\Actor07_Grab_GamerLandlordSelect_NttHandler.cs
using System; using System.Collections.Generic; namespace ETModel { [MessageHandler] public class Actor_GamerGrabLandlordSelect_NttHandler : AMHandler<Actor_GamerGrabLandlordSelect_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_GamerGrabLandlordSelect_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); if (gamer != null) { if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { uiRoom.GetComponent<LandRoomComponent>().Interaction.EndGrab(); } gamer.GetComponent<LandlordsGamerPanelComponent>().SetGrab(message.IsGrab); } await ETTask.CompletedTask; } } }
Actor_SetMultiples_NttHandler \Assets\Model\Landlords\Handler\Actor04_SetMultiples_NttHandler.cs
using System; using System.Collections.Generic; namespace ETModel { [MessageHandler] public class Actor_SetMultiples_NttHandler : AMHandler<Actor_SetMultiples_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_SetMultiples_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); uiRoom.GetComponent<LandRoomComponent>().SetMultiples(message.Multiples); await ETTask.CompletedTask; } } }
LandlordsGamerPanelComponent 组件添加 SetGrab 方法
\Assets\Model\Sekia\Landlords\Component\LandlordsGamerPanelComponent.cs
/// <summary> /// 玩家抢地主 /// </summary> public void SetGrab(bool isGrab) { if (isGrab) { prompt.text = "抢地主"; } else { prompt.text = "不抢"; } } /// <summary> /// 设置玩家身份 /// </summary> public void SetIdentity(Identity identity) { if (identity == Identity.None) return; string spriteName = $"Identity_{Enum.GetName(typeof(Identity), identity)}"; Sprite headSprite = CardHelper.GetCardSprite(spriteName); headPhoto.sprite = headSprite; headPhoto.gameObject.SetActive(true); }
Actor_AuthorityPlayCard_NttHandler \Assets\Model\Landlords\Handler\Actor09_PlayCardAuthority_NttHandler.cs
using System; using System.Collections.Generic; namespace ETModel { [MessageHandler] public class Actor_AuthorityPlayCard_NttHandler : AMHandler<Actor_AuthorityPlayCard_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_AuthorityPlayCard_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); if (gamer != null) { //重置玩家提示 gamer.GetComponent<LandlordsGamerPanelComponent>().ResetPrompt(); //当玩家为先手,清空出牌 if (message.IsFirst) { gamer.GetComponent<HandCardsComponent>().ClearPlayCards(); } //显示出牌按钮 if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { LandInteractionComponent interaction = uiRoom.GetComponent<LandRoomComponent>().Interaction; interaction.IsFirst = message.IsFirst; interaction.StartPlay(); } } await ETTask.CompletedTask; } } }
抢地主相关的消息定义
\Proto\OuterMessage.proto
message Actor_AuthorityPlayCard_Ntt // IActorMessage { int32 RpcId = 90; int64 ActorId = 93; int64 UserID = 1; bool IsFirst = 2; } message Actor_SetMultiples_Ntt // IActorMessage { int32 RpcId = 90; int64 ActorId = 93; int32 Multiples = 1; }
生成消息文件后,复制到服务端,运行前后端项目,观察抢地主的消息收发情况。
初步实现了抢地主的效果,并且在每个玩家都同步了大家的抢地主状态:
运行问题一:做到这里,会发现前端点抢地主后,抢地主按钮不消失。
1、分析下,点抢地主按钮服务端收到消息的处理handler是GamerGrabLandlordSelect_Handler,应该下面判断中的逻辑没有执行,加一个Log信息。
2、我们看服务端输出可以发现,牌序组件的CurrentAuthority还没有数据:
3、初次确定牌序的是GameControllerComponentSystem组件扩展类中的RandomFirstAuthority方法,我们在这里修改加入牌序逻辑:
\Server\Hotfix\Landlords\System\GameControllerComponentSystem.cs
/// <summary> /// 随机先手玩家 /// </summary> public static void RandomFirstAuthority(this GameControllerComponent self) { Room room = self.GetParent<Room>(); OrderControllerComponent orderController = room.GetComponent<OrderControllerComponent>(); Gamer[] gamers = room.gamers; int index = RandomHelper.RandomNumber(0, gamers.Length); long firstAuthority = gamers[index].UserID; orderController.Init(firstAuthority); //广播先手抢地主玩家 room.Broadcast(new Actor_AuthorityGrabLandlord_Ntt() { UserID = firstAuthority }); }
运行问题二:完成抢地主确定地主后,服务端向玩家广播设置地主,前端有消息没处理
1、消息指令113没有handler,我们看看113消息是什么
2、前端添加Actor_SetLandlord_NttHandler
\Assets\Model\Landlords\Handler\Actor08_Grab_SetLandlord_NttHandler.cs
这样就在前端设置了玩家身份、农民与地主的UI,是地主的玩家添加了地主牌。
using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace ETModel { [MessageHandler] public class Actor_SetLandlord_NttHandler : AMHandler<Actor_SetLandlord_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_SetLandlord_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); Gamer gamer = room.GetGamer(message.UserID); if (gamer != null) { HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { //如果本地玩家是地主添加地主牌 Card[] Tcards = new Card[message.LordCards.Count]; for (int i = 0; i < message.LordCards.Count; i++) { Tcards[i] = message.LordCards[i]; } handCards.AddCards(Tcards); } else { //其他玩家设置手牌数 handCards.SetHandCardsNum(20); } } //设置值玩家身份 foreach (var _gamer in room.gamers) { HandCardsComponent handCardsComponent = _gamer.GetComponent<HandCardsComponent>(); LandlordsGamerPanelComponent gamerUI = _gamer.GetComponent<LandlordsGamerPanelComponent>(); if (_gamer.UserID == message.UserID) { handCardsComponent.AccessIdentity = Identity.Landlord; gamerUI.SetIdentity(Identity.Landlord); } else { handCardsComponent.AccessIdentity = Identity.Farmer; gamerUI.SetIdentity(Identity.Farmer); } } //重置玩家UI提示 foreach (var _gamer in room.gamers) { _gamer.GetComponent<LandlordsGamerPanelComponent>().ResetPrompt(); } //切换地主牌精灵 GameObject lordPokers = uiRoom.GameObject.Get<GameObject>("Desk").Get<GameObject>("LordPokers"); for (int i = 0; i < lordPokers.transform.childCount; i++) { Sprite lordCardSprite = CardHelper.GetCardSprite(message.LordCards[i].GetName()); lordPokers.transform.GetChild(i).GetComponent<Image>().sprite = lordCardSprite; } await ETTask.CompletedTask; } } }
包括前面几节和之后所有节的效果,都需要打包发布,在Release中运行三个游戏程序进行测试哦!
本节最终效果,可以看到牌烂还是要强行抢到地主,地主头像拉风啊。。。