任务40:牌局结束后房间内玩家选择继续匹配新的牌局

前端部分

前端项目新增加的LandEnd界面,Content界面

LandEnd&ContentUI.zip (4.66KB)

下载后放到 \Assets\Bundles\Landlords,如果ReferenceCollector脚本丢失,参考下面的图自己挂一下并绑定好UI

添加前端收到Actor_Gameover_Ntt消息的Actor_Gameover_NttHandler

\Assets\Model\Landlords\Handler\Actor12_Event_Gameover_NttHandler.cs

复制代码
using System;
using System.Collections.Generic;
using ETModel;
using Google.Protobuf;
using UnityEngine;
using UnityEngine.UI;

namespace ETModel
{
    [MessageHandler]
    public class Actor_Gameover_NttHandler : AMHandler<Actor_Gameover_Ntt>
    {
        protected override async ETTask Run(ETModel.Session session, Actor_Gameover_Ntt message)
        {
            UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom);
            ReferenceCollector rc = uiRoom.GameObject.GetComponent<ReferenceCollector>();
            //隐藏发牌桌
            rc.Get<GameObject>("Desk").SetActive(false);

            LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>();
            Identity localGamerIdentity = LandRoomComponent.LocalGamer.GetComponent<HandCardsComponent>().AccessIdentity;
            UI uiEndPanel = LandEndFactory.Create(LandUIType.LandEnd, uiRoom, message.Winner==(int)localGamerIdentity);
            LandEndComponent landlordsEndComponent = uiEndPanel.GetComponent<LandEndComponent>();

            foreach (GamerScore gamerScore in message.GamersScore)
            {
                Gamer gamer = room.GetGamer(gamerScore.UserID);
                //更新玩家信息(金钱/头像)
                gamer.GetComponent<LandlordsGamerPanelComponent>().UpdatePanel();
                //清空出牌
                gamer.GetComponent<HandCardsComponent>().ClearPlayCards();
                gamer.GetComponent<HandCardsComponent>().Hide();
                //根据GamersScore中玩家顺序进行排列
                landlordsEndComponent.CreateGamerContent(
                    gamer,
                    localGamerIdentity,
                    message.BasePointPerMatch,
                    message.Multiples,
                    gamerScore.Score);
            }
            
            room.Interaction.Gameover();
            room.ResetMultiples();
            await ETTask.CompletedTask;
        }
    }
}
复制代码

LandInteractionComponent组件中添加Gameover方法隐藏手牌交互按钮

\Assets\Model\Landlords\LandUI\LandInteraction\LandInteractionComponent.cs

复制代码
        /// <summary>
        /// 游戏结束
        /// </summary>
        public void Gameover()
        {
            promptButton.gameObject.SetActive(false);
            playButton.gameObject.SetActive(false);
            discardButton.gameObject.SetActive(false);
        }
复制代码

LandEndComponent界面组件与工厂方法

LandEndComponent  \Assets\Model\Landlords\LandUI\LandGameEnd\LandEndComponent.cs

如果玩家选择继续游戏,将向服务端发消息

//发消息到服务器继续游戏

SessionComponent.Instance.Session.Send(new Actor_GamerContinue_Ntt());

复制代码
using UnityEngine;
using UnityEngine.UI;
using System;

namespace ETModel
{
    [ObjectSystem]
    public class LandEndComponentAwakeSystem : AwakeSystem<LandEndComponent, bool>
    {
        public override void Awake(LandEndComponent self, bool isWin)
        {
            self.Awake(isWin);
        }
    }

    public class LandEndComponent : Component
    {
        //玩家信息面板预设名称
        public const string CONTENT_NAME = "Content";

        private GameObject contentPrefab;  //每个玩家的统计
        private GameObject gamerContent;  //计分面板

        public void Awake(bool isWin)
        {
            ReferenceCollector rc = this.GetParent<UI>().GameObject.GetComponent<ReferenceCollector>();
            ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
            resourcesComponent.LoadBundle($"{CONTENT_NAME}.unity3d");

            if (isWin)
            {
                rc.Get<GameObject>("Lose").SetActive(false);
            }
            else
            {
                rc.Get<GameObject>("Win").SetActive(false);
            }

            gamerContent = rc.Get<GameObject>("GamerContent");
            Button continueButton = rc.Get<GameObject>("ContinueButton").GetComponent<Button>();
            continueButton.onClick.Add(OnContinue);
            contentPrefab = (GameObject)resourcesComponent.GetAsset($"{CONTENT_NAME}.unity3d", CONTENT_NAME);
        }

        public override void Dispose()
        {
            if (this.IsDisposed)
            {
                return;
            }

            base.Dispose();

            ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
            resourcesComponent?.UnloadBundle($"{CONTENT_NAME}.unity3d");
            LandEndFactory.Remove(LandUIType.LandEnd);
        }

        /// <summary>
        /// 创建玩家结算信息
        /// </summary>
        /// <returns></returns>
        public GameObject CreateGamerContent(Gamer gamer, Identity winnerIdentity, long baseScore, int multiples, long score)
        {
            //实例化prefab
            GameObject newContent = UnityEngine.Object.Instantiate(contentPrefab);
            newContent.transform.SetParent(gamerContent.transform, false);
            
            //获取玩家身份/设置头像
            Identity gamerIdentity = gamer.GetComponent<HandCardsComponent>().AccessIdentity;
            Sprite identitySprite = CardHelper.GetCardSprite($"Identity_{Enum.GetName(typeof(Identity), gamerIdentity)}");
            newContent.Get<GameObject>("Identity").GetComponent<Image>().sprite = identitySprite;

            //设置昵称/底分/倍数/积分
            string nickName = gamer.GetComponent<LandlordsGamerPanelComponent>().NickName;
            Text nickNameText = newContent.Get<GameObject>("NickName").GetComponent<Text>();
            Text baseScoreText = newContent.Get<GameObject>("BaseScore").GetComponent<Text>();
            Text multiplesText = newContent.Get<GameObject>("Multiples").GetComponent<Text>();
            Text scoreText = newContent.Get<GameObject>("Score").GetComponent<Text>();
            nickNameText.text = nickName;
            baseScoreText.text = baseScore.ToString();
            multiplesText.text = multiples.ToString();
            scoreText.text = score.ToString();

            //本地玩家的统计结果高亮
            if (gamer.UserID == LandRoomComponent.LocalGamer.UserID)
            {
                nickNameText.color = Color.red;
                baseScoreText.color = Color.red;
                multiplesText.color = Color.red;
                scoreText.color = Color.red;
            }
            
            return newContent;
        }

        /// <summary>
        /// 继续游戏
        /// </summary>
        private void OnContinue()
        {
            UI entity = this.GetParent<UI>();
            UI parent = (UI)entity.Parent;
            parent.GameObject.Get<GameObject>("Ready").SetActive(true);
            parent.Remove(entity.Name);

            //发消息到服务器继续游戏
            SessionComponent.Instance.Session.Send(new Actor_GamerContinue_Ntt());
        }
    }
}
复制代码

LandEndFactory \Assets\Model\Landlords\LandUI\LandGameEnd\LandEndFactory.cs

复制代码
using UnityEngine;

namespace ETModel
{
    public class LandEndFactory
    {
        public static UI Create(string type, UI parent, bool isWin)
        {
            ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
            resourcesComponent.LoadBundle($"{type}.unity3d");
            GameObject prefab = (GameObject)resourcesComponent.GetAsset($"{type}.unity3d", $"{type}");
            GameObject endPanel = UnityEngine.Object.Instantiate(prefab);

            endPanel.layer = LayerMask.NameToLayer("UI");

            UI ui = ComponentFactory.Create<UI, GameObject>(endPanel);
            parent.Add(ui);
            ui.GameObject.transform.SetParent(parent.GameObject.transform, false);

            ui.AddComponent<LandEndComponent, bool>(isWin);
            return ui;
        }

        public static void Remove(string type)
        {
            Game.Scene.GetComponent<ResourcesComponent>().UnloadBundle($"{type}.unity3d");
        }
    }
}
复制代码

后端部分

GameControllerComponentSystem的Continue方法中加一个游戏结束的测试

\Server\Hotfix\Landlords\System\GameControllerComponentSystem.cs

不可以为了测试游戏结束每次都把牌打完吧!!

测试完游戏结束逻辑要注释或者删除哦,不然不能正常打牌了!!

//测试游戏结束
lastGamer.GetComponent<HandCardsComponent>().library.Clear();

添加服务端收到Actor_GamerContinue_Ntt消息的GameContinueHandler

\Server\Hotfix\Landlords\Handler\Map\GameContinueHandler.cs

有人继续游戏,就取消RoomWaiting,这样房间就不会清除了。

复制代码
using ETModel;

namespace ETHotfix
{
    [ActorMessageHandler(AppType.Map)]
    public class GameContinue_Handler : AMActorHandler<Gamer, Actor_GamerContinue_Ntt>
    {
        protected override async ETTask Run(Gamer gamer, Actor_GamerContinue_Ntt message)
        {
            Room room = Game.Scene.GetComponent<LandMatchComponent>().GetGamingRoom(gamer);

            //如果消息到达时,正好2分钟到房间已经清除
            if(room == null){
                //通知客户端房间不存在,重新匹配房间
                //...
            }else{
                room.CancellationTokenSource?.Cancel();
            }
            
            await ETTask.CompletedTask;
        }
    }
}
复制代码

本节需要定义的消息体

D:\Users\taikr008\Desktop\Landlords_Client02_05\Proto\HotfixMessage.proto

message Actor_GamerContinue_Ntt // IActorMessage
{
    int32 RpcId = 90;
    int64 ActorId = 93;
}

观察本节效果:

因为服务端加了测试游戏结束的代码,所以开始牌局后,随便出个牌即可触发游戏结束效果。

发现存在Actor_GamerPlayCard_Ntt = 109的消息未处理报错,因为是强行测试游戏结束效果引起不用介意。

课外练习:

1、通知客户端房间不存在,重新匹配房间的消息大家自己实现一下

 

posted @   Domefy  阅读(17)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
历史上的今天:
2022-02-21 C#获取程序所在的路径
点击右上角即可分享
微信分享提示