任务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中运行三个游戏程序进行测试哦!

本节最终效果,可以看到牌烂还是要强行抢到地主,地主头像拉风啊。。。

posted @ 2023-02-16 14:36  Domefy  阅读(44)  评论(0编辑  收藏  举报