任务31:前端-将三个玩家的手牌发送到客户端并显示在牌桌
本节的内容都在前端完成。这节课前端增加了新的所有牌面的图集资源:
HandCard&PlayCard.zip (2.11KB)
上一节,服务端向前端玩家发送了手牌:
前端玩家收到自己的手牌数据:
前端添加接受消息的Actor_GameStartHandCards_NttHandler
本地玩家把服务端传来的手牌添加到handCards中,HandCardsComponent组件中的AddCards方法中有CardsSpriteUpdate来更新桌面上手牌的显示。
//显示牌背面或者手牌 handCards.Appear(); //添加与更新本地玩家的手牌 if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { //本地玩家添加手牌 Card[] Tcards = new Card[message.HandCards.Count]; for (int i = 0; i < message.HandCards.Count; i++) { Tcards[i] = message.HandCards[i]; } handCards.AddCards(Tcards); } else { //设置其他玩家手牌数 handCards.SetHandCardsNum(gamerCardNum.Num); }
本节后面有HandCardsComponent组件中有Appear方法。
因为本地玩家是handCards的UI,而另两个玩家是poker的UI,所以用 ?.
public void Appear() { _poker?.SetActive(true); _handCards?.SetActive(true); }
Actor_GameStartHandCards_NttHandler完整代码。
\Assets\Model\Landlords\Handler\Actor_GameStartHandCards_NttHandler.cs
using System; using System.Collections.Generic; using ETModel; using UnityEngine; using UnityEngine.UI; namespace ETModel { [MessageHandler] public class Actor_GameStartHandCards_NttHandler : AMHandler<Actor_GameStartHandCards_Ntt> { protected override async ETTask Run(ETModel.Session session, Actor_GameStartHandCards_Ntt message) { UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom); LandRoomComponent room = uiRoom.GetComponent<LandRoomComponent>(); //初始化玩家UI foreach (GamerCardNum gamerCardNum in message.GamersCardNum) { Gamer gamer = room.GetGamer(gamerCardNum.UserID); LandlordsGamerPanelComponent gamerUI = gamer.GetComponent<LandlordsGamerPanelComponent>(); gamerUI.GameStart(); HandCardsComponent handCards = gamer.GetComponent<HandCardsComponent>(); if (handCards != null) { handCards.Reset(); } else { //Log.Debug("没有可以复用的HandCardsComponent,创建一个。"); handCards = gamer.AddComponent<HandCardsComponent, GameObject>(gamerUI.Panel); } //显示牌背面或者手牌 handCards.Appear(); //添加与更新本地玩家的手牌 if (gamer.UserID == LandRoomComponent.LocalGamer.UserID) { //本地玩家添加手牌 Card[] Tcards = new Card[message.HandCards.Count]; for (int i = 0; i < message.HandCards.Count; i++) { Tcards[i] = message.HandCards[i]; } handCards.AddCards(Tcards); } else { //设置其他玩家手牌数 handCards.SetHandCardsNum(gamerCardNum.Num); } } //显示牌桌UI GameObject desk = uiRoom.GameObject.Get<GameObject>("Desk"); desk.SetActive(true); GameObject lordPokers = desk.Get<GameObject>("LordPokers"); //重置地主牌 Sprite lordSprite = CardHelper.GetCardSprite("None"); for (int i = 0; i < lordPokers.transform.childCount; i++) { lordPokers.transform.GetChild(i).GetComponent<Image>().sprite = lordSprite; } LandRoomComponent uiRoomComponent = uiRoom.GetComponent<LandRoomComponent>(); //设置初始倍率 uiRoomComponent.SetMultiples(1); await ETTask.CompletedTask; } } }
添加手牌组件HandCardsComponent
SetHandCardsNum方法:另两位玩家只有一个卡背,上面显示目前还有多少张牌
AddCards方法:添加牌到桌面手牌
PopCards方法:出牌后,更新桌面手牌
CardsSpriteUpdate方法:更新桌面上手牌的显示
Sort方法:对桌面手牌的左右排序和遮挡的显示层级
HandCardsComponent完整代码 \Assets\Model\Landlords\Component\HandCardsComponent.cs
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace ETModel { [ObjectSystem] public class HandCardsComponentAwakeSystem : AwakeSystem<HandCardsComponent, GameObject> { public override void Awake(HandCardsComponent self, GameObject panel) { self.Awake(panel); } } public class HandCardsComponent : Component { public const string HANDCARD_NAME = "HandCard"; public const string PLAYCARD_NAME = "PlayCard"; private readonly Dictionary<string, GameObject> cardsSprite = new Dictionary<string, GameObject>(); private readonly List<Card> handCards = new List<Card>(); private readonly List<Card> playCards = new List<Card>(); private GameObject _poker; private GameObject _handCards; private Text _pokerNum; public GameObject Panel { get; private set; } public Identity AccessIdentity { get; set; } public void Awake(GameObject panel) { this.Panel = panel; _poker = this.Panel.Get<GameObject>("Poker"); _handCards = this.Panel.Get<GameObject>("HandCards"); _pokerNum = _poker?.GetComponentInChildren<Text>(); //加载AB包 ResourcesComponent resourcesComponent = Game.Scene.GetComponent<ResourcesComponent>(); resourcesComponent.LoadBundle($"{HANDCARD_NAME}.unity3d"); resourcesComponent.LoadBundle($"{PLAYCARD_NAME}.unity3d"); resourcesComponent.LoadBundle($"{CardHelper.ATLAS_NAME}.unity3d"); } public override void Dispose() { if (this.IsDisposed) { return; } base.Dispose(); Reset(); } /// <summary> /// 重置 /// </summary> public void Reset() { ClearHandCards(); ClearPlayCards(); } /// <summary> /// 显示玩家游戏UI /// </summary> public void Appear() { _poker?.SetActive(true); _handCards?.SetActive(true); } /// <summary> /// 隐藏玩家游戏UI /// </summary> public void Hide() { _poker?.SetActive(false); _handCards?.SetActive(false); } /// <summary> /// 获取卡牌精灵 /// </summary> /// <param name="card"></param> /// <returns></returns> public GameObject GetSprite(Card card) { GameObject cardSprite; if(cardsSprite.TryGetValue(card.GetName(), out cardSprite)) { return cardSprite; } Log.Debug("没找到这张卡牌!"+card.GetName()); return cardSprite; } /// <summary> /// 设置手牌数量 /// </summary> /// <param name="num"></param> public void SetHandCardsNum(int num) { _pokerNum.text = num.ToString(); } /// <summary> /// 添加多张牌 /// </summary> /// <param name="cards"></param> public void AddCards(Card[] cards) { for (int i = 0; i < cards.Length; i++) { AddCard(cards[i]); } CardsSpriteUpdate(handCards, 50.0f); } /// <summary> /// 出牌后更新玩家手牌 /// </summary> /// <param name="cards"></param> public void PopCards(Card[] cards) { //ClearPlayCards(); //造成重复删除,先注释掉 for (int i = 0; i < cards.Length; i++) { PopCard(cards[i]); Log.Debug("更新一张牌:"+cards[i].GetName()); } CardsSpriteUpdate(playCards, 25.0f); CardsSpriteUpdate(handCards, 50.0f); //同步剩余牌数 GameObject poker = this.Panel.Get<GameObject>("Poker"); if (poker != null) { Text pokerNum = poker.GetComponentInChildren<Text>(); pokerNum.text = (int.Parse(pokerNum.text) - cards.Length).ToString(); } } /// <summary> /// 清空手牌 /// </summary> public void ClearHandCards() { ClearCards(handCards); } /// <summary> /// 清空出牌 /// </summary> public void ClearPlayCards() { ClearCards(playCards); } /// <summary> /// 卡牌精灵更新 /// </summary> public void CardsSpriteUpdate(List<Card> cards, float interval) { if (cards.Count == 0) { return; } Sort(cards); float width = GetSprite(cards[0]).GetComponent<RectTransform>().sizeDelta.x; float startX = -((cards.Count - 1) * interval) / 2; for (int i = 0; i < cards.Count; i++) { RectTransform rect = GetSprite(cards[i]).GetComponent<RectTransform>(); rect.anchoredPosition = new Vector2(startX + (i * interval), rect.anchoredPosition.y); } } /// <summary> /// 清空卡牌 /// </summary> /// <param name="cards"></param> private void ClearCards(List<Card> cards) { for (int i = cards.Count - 1; i >= 0; i--) { Card card = cards[i]; Log.Debug("删除卡牌"+card.GetName()); GameObject cardSprite = cardsSprite[card.GetName()]; cardsSprite.Remove(card.GetName()); cards.Remove(card); UnityEngine.Object.Destroy(cardSprite); } } /// <summary> /// 卡牌排序 /// </summary> /// <param name="cards"></param> private void Sort(List<Card> cards) { CardHelper.Sort(cards); //卡牌精灵层级排序 //Log.Debug("重设卡牌精灵层级"); for (int i = 0; i < cards.Count; i++) { //Log.Debug("卡牌精灵" + cards[i].GetName()); GetSprite(cards[i]).transform.SetSiblingIndex(i); } } /// <summary> /// 添加卡牌 /// </summary> /// <param name="card"></param> private void AddCard(Card card) { GameObject handCardSprite = CreateCardSprite(HANDCARD_NAME, card.GetName(), this.Panel.Get<GameObject>("HandCards").transform); handCardSprite.GetComponent<HandCardSprite>().Poker = card; cardsSprite.Add(card.GetName(), handCardSprite); handCards.Add(card); } /// <summary> /// 出牌 /// </summary> /// <param name="card"></param> private void PopCard(Card card) { //移除手牌 //因为对象并不是完全一致,不能用Contains来查找 foreach(var a in handCards) { if(a.Equals(card)) { Log.Debug("pop删除卡牌"+a.GetName()); GameObject handCardSprite = GetSprite(a); cardsSprite.Remove(a.GetName()); handCards.Remove(a); UnityEngine.Object.Destroy(handCardSprite); break; } } GameObject playCardSprite = CreateCardSprite(PLAYCARD_NAME, card.GetName(), this.Panel.Get<GameObject>("PlayCards").transform); cardsSprite.Add(card.GetName(), playCardSprite); playCards.Add(card); } /// <summary> /// 创建卡牌精灵 /// </summary> private GameObject CreateCardSprite(string prefabName, string cardName, Transform parent) { GameObject cardSpritePrefab = (GameObject)Game.Scene.GetComponent<ResourcesComponent>().GetAsset($"{prefabName}.unity3d", prefabName); GameObject cardSprite = UnityEngine.Object.Instantiate(cardSpritePrefab); cardSprite.name = cardName; cardSprite.layer = LayerMask.NameToLayer("UI"); cardSprite.transform.SetParent(parent.transform, false); Sprite sprite = CardHelper.GetCardSprite(cardName); cardSprite.GetComponent<Image>().sprite = sprite; return cardSprite; } } }
添加牌面比较和手牌排序的CardHelper
\Assets\Model\Landlords\Other\CardHelper.cs
using System.Collections.Generic; using UnityEngine; namespace ETModel { public static class CardHelper { //卡牌图集预设名称 public const string ATLAS_NAME = "Atlas"; /// <summary> /// 排序 /// </summary> /// <param name="cards"></param> public static void Sort(List<Card> cards) { for (int i = cards.Count; i > 0; i--) { for (int j = 0; j < i - 1; j++) { //先按照权重降序,再按花色升序 if (-CompareTo((int)cards[j].CardWeight, (int)cards[j + 1].CardWeight) * 2 + CompareTo((int)cards[j].CardSuits, (int)cards[j + 1].CardSuits) > 0) { Card temp = cards[j]; cards[j] = cards[j + 1]; cards[j + 1] = temp; } } } } /// <summary> /// int比较 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static int CompareTo(int a, int b) { int result; if (a > b) { result = 1; } else if (a < b) { result = -1; } else { result = 0; } return result; } /// <summary> /// 获取卡牌精灵 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="cardName"></param> /// <returns></returns> public static Sprite GetCardSprite(string cardName) { GameObject atlas = (GameObject)ETModel.Game.Scene.GetComponent<ResourcesComponent>().GetAsset($"{ATLAS_NAME}.unity3d", ATLAS_NAME); return atlas.Get<Sprite>(cardName); } } }
本节需要增加的bundles
所有牌的Atlas资源
PlayCard卡背资源
HandCard手牌资源
要挂上HandCardSprite脚本,本文后面有添加这个类
以上记得添加atlas.unity3d,handcard.unity3d,playCard.unity3d的bundle
其它新添加的类与原来一些类新添加的方,没什么含量:
LandRoomComponent增加属性和方法 \Assets\Model\Landlords\LandUI\LandRoom\LandRoomComponent.cs
增加倍数显示ui的属性
private Text multiples;
awake中获取multiples
multiples = rc.Get<GameObject>("Multiples").GetComponent<Text>();
前端显示本局牌的倍数方法
public void SetMultiples(int multiples) { this.multiples.gameObject.SetActive(true); this.multiples.text = multiples.ToString(); } public void ResetMultiples() { this.multiples.gameObject.SetActive(false); this.multiples.text = "1"; }
LandlordsGamerPanelComponent增加方法 \Assets\Model\Landlords\Component\LandlordsGamerPanelComponent.cs
清空三个玩家信息界面上的准备状态提示
/// <summary> /// 游戏开始 /// </summary> public void GameStart() { ResetPrompt(); }
EventIdType增加属性 \Assets\ET.Core\Base\Event\EventIdType.cs
选中与取消选中桌面上的牌的事件定义
public const string SelectHandCard = "SelectHandCard"; public const string CancelHandCard = "CancelHandCard";
添加游戏中牌的类,区别与消息体中牌的类Card
\Assets\ET.Core\Landlords\Card.cs
namespace ETModel { /// <summary> /// 参考Unit类 /// </summary> public partial class Card { public static Card Create(int weight, int suits) { Card card = new Card(); card.CardWeight = weight; card.CardSuits = suits; return card; } public bool Equals(Card other) { return this.CardWeight == other.CardWeight && this.CardSuits == other.CardSuits; } /// <summary> /// 获取卡牌名 /// </summary> /// <returns></returns> public string GetName() { return this.CardSuits == (int)Suits.None ? ((Weight)this.CardWeight).ToString() : $"{((Suits)this.CardSuits).ToString()}{((Weight)this.CardWeight).ToString()}"; } } }
添加前端斗地主需要的各种枚举定义类Type
\Assets\ET.Core\Landlords\Type.cs
namespace ETModel { /// <summary> /// 房间等级 /// </summary> public enum RoomLevel { Lv100 //100底分局 } /// <summary> /// 身份 /// </summary> public enum Identity { None, Farmer, //平民 Landlord //地主 } /// <summary> /// 出牌类型 /// </summary> public enum CardsType { None, JokerBoom, //王炸 Boom, //炸弹 OnlyThree, //三张 ThreeAndOne, //三带一 ThreeAndTwo, //三带二 Straight, //顺子 五张或更多的连续单牌 DoubleStraight, //双顺 三对或更多的连续对牌 TripleStraight, //三顺 二个或更多的连续三张牌 Double, //对子 Single //单牌 } public enum Weight { Three, //3 Four, //4 Five, //5 Six, //6 Seven, //7 Eight, //8 Nine, //9 Ten, //10 Jack, //J Queen, //Q King, //K One, //A Two, //2 SJoker, //小王 LJoker, //大王 } public enum Suits { Club, //梅花 Diamond, //方块 Heart, //红心 Spade, //黑桃 None } }
添加给桌面每张手牌挂的脚本HandCardSprite
\Assets\Model\Landlords\Other\HandCardSprite.cs
using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; namespace ETModel { public class HandCardSprite : MonoBehaviour { public Card Poker { get; set; } private bool isSelect; void Start() { EventTrigger eventTrigger = gameObject.AddComponent<EventTrigger>(); eventTrigger.triggers = new List<EventTrigger.Entry>(); EventTrigger.Entry clickEntry = new EventTrigger.Entry(); clickEntry.eventID = EventTriggerType.PointerClick; clickEntry.callback = new EventTrigger.TriggerEvent(); clickEntry.callback.AddListener(new UnityAction<BaseEventData>(OnClick)); eventTrigger.triggers.Add(clickEntry); } public void OnClick(BaseEventData data) { float move = 50.0f; if (isSelect) { move = -move; Game.EventSystem.Run(EventIdType.CancelHandCard, Poker); } else { Game.EventSystem.Run(EventIdType.SelectHandCard, Poker); } RectTransform rectTransform = this.GetComponent<RectTransform>(); rectTransform.anchoredPosition += Vector2.up * move; isSelect = !isSelect; } } }
运行上节的服务端,前端打包输出,Release中运行三个客户端,分别登录点准备游戏后,桌面上显示手牌: