任务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、通知客户端房间不存在,重新匹配房间的消息大家自己实现一下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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#获取程序所在的路径