• Unity3D手机斗地主游戏开发实战(03)_地主牌显示和出牌逻辑


    Hi,之前有同学说要我把源码发出来,那我就把半成品源码的链接放在每篇文件的最后,有兴趣的话可以查阅参考,有问题可以跟我私信,也可以关注我的个人公众号,互相交流嘛。当然,代码也是在不断的持续改进中~

    上期我们实现了叫地主功能,不过遗留了一个小功能:叫地主完成以后,要显示地主的3张牌,这期首先弥补这块的功能;

    接着我们要进入开发出牌逻辑的开发阶段,好了,废话不多说,继续我们斗地主开发之旅~

    地主牌的显示

    我们在玩家界面的顶部中间位置,放置一个新的GameObject,命名为BidCards,用来记录3张地主牌的显示位置。

    所以我们重构了CardManager中的发牌方法,在给地主发牌同时,生成地主牌的实例,放在BidCards相应位置:

        /// <summary>
        /// 发牌堆上的牌(如果现在不是抢地主阶段,发普通牌,如果是,发地主牌)
        /// </summary>
        /// <returns></returns>
        private IEnumerator DealHeapCards(bool ifForBid)
        {
            //显示牌堆
            heapPos.gameObject.SetActive(true);
            playerHeapPos.ToList().ForEach(s => { s.gameObject.SetActive(true); });
    
            var cardNamesNeeded = ifForBid
                ? cardNames.Skip(cardNames.Length - 3).Take(3)  //如果是抢地主牌,取最后3张
                : cardNames.Take(cardNames.Length - 3);         //如果首次发牌
    
            //计算每张地主牌的位置
            int cardIndex = 0;
            var width = (bidCards.GetComponent<RectTransform>().sizeDelta.x - 20) / 3;
            var centerBidPos = Vector3.zero;
            var leftBidPos = centerBidPos - Vector3.left * width;
            var rightBidPos = centerBidPos + Vector3.left * width;
            List<Vector3> bidPoss = new List<Vector3> { leftBidPos, centerBidPos, rightBidPos };
            foreach (var cardName in cardNamesNeeded)
            {
                //给当前玩家发一张牌
                Players[termCurrentIndex].AddCard(cardName);
    
                var cover = Instantiate(coverPrefab, heapPos.position, Quaternion.identity, heapPos.transform);
                cover.GetComponent<RectTransform>().localScale = Vector3.one;
                //移动动画,动画结束后自动销毁
                var tween = cover.transform.DOMove(playerHeapPos[termCurrentIndex].position, 0.3f);
                tween.OnComplete(() => Destroy(cover));
    
                yield return new WaitForSeconds(1 / dealCardSpeed);
    
                //如果给地主发牌
                if (ifForBid)
                {
                    //显示地主牌
                    var bidcard = Instantiate(cardPrefab, bidCards.transform.TransformPoint(bidPoss[cardIndex]), Quaternion.identity, bidCards.transform);
                    bidcard.GetComponent<Card>().InitImage(new CardInfo(cardName));
                    bidcard.GetComponent<RectTransform>().localScale = Vector3.one * 0.3f;
                }
                else
                {
                    //下一个需要发牌者
                    SetNextPlayer();
                }
    
                cardIndex++;
            }
    
            //隐藏牌堆
            heapPos.gameObject.SetActive(false);
            playerHeapPos[0].gameObject.SetActive(false);
    
            //发普通牌
            if (!ifForBid)
            {
                //显示玩家手牌
                ShowPlayerSelfCards();
                StartBiding();
            }
            //发地主牌
            else
            {
                if (Players[bankerIndex] is PlayerSelf)
                {
                    //显示玩家手牌
                    ShowPlayerSelfCards();
                }
                StartFollowing();
            }
        }
    View Code

    好的,我们地主牌显示已经没有问题了,接下来,我们要实现出牌回合逻辑

    出牌回合功能实现

    出牌回合其实跟叫地主回合类似,也是可以抽象出3种方法:进入出牌阶段、出牌、不出(比较进入叫地主阶段、叫地主、不叫地主);

    因此,我们参照叫地主的逻辑,再实现出牌逻辑:

    调整Player基类

    添加开始出牌ToFollowing、出牌ForFollow、不出NotFollow:

    • ToFollowing:进入自己回合,关闭其他人的倒计时,进入自己的倒计时阶段;
    • ForFollow:关闭自己的倒计时,然后将选择的牌添加到出牌区域,跳出自己回合;
    • NotFollow:关闭自己的倒计时,跳出自己回合;
        /// <summary>
        /// 开始出牌
        /// </summary>
        public virtual void ToFollowing()
        {
            isMyTerm = true;
    
            //关闭倒计时
            StopCountDown(CountDownTypes.Follow);
    
            //开始倒计时
            StartCountDown(CountDownTypes.Follow);
        }
        /// <summary>
        /// 出牌
        /// </summary>
        public void ForFollow()
        {
            //关闭倒计时
            StopCountDown(CountDownTypes.Follow);
    
            //选择的牌,添加到出牌区域
            var selectedCards = cardInfos.Where(s => s.isSelected).ToList();
            var offset = 5;
            for (int i = 0; i < selectedCards.Count(); i++)
            {
                var card = Instantiate(prefabSmall, smallCardPos.position + Vector3.right * offset * i, Quaternion.identity, smallCardPos.transform);
                card.GetComponent<RectTransform>().localScale = Vector3.one * 0.3f;
                card.GetComponent<Image>().sprite = Resources.Load("Images/Cards/" + selectedCards[i].cardName, typeof(Sprite)) as Sprite;
                card.transform.SetAsLastSibling();
    
                smallCards.Add(card);
            }
            cardInfos = cardInfos.Where(s => !s.isSelected).ToList();
    
            CardManager._instance.ForFollow();
            isMyTerm = false;
        }
        /// <summary>
        /// 不出
        /// </summary>
        public void NotFollow()
        {
            //关闭倒计时
            StopCountDown(CountDownTypes.Follow);
    
            CardManager._instance.NotFollow();
            isMyTerm = false;
        }
        /// <summary>
        /// 销毁出牌对象
        /// </summary>
        public void DropAllSmallCards()
        {
            smallCards.ForEach(Destroy);
            smallCards.Clear();
        }
    View Code

    调整PlayerSelf类

    实现ToFollowing:

    调用基类的ToFollowing,并显示出牌按钮以供玩家选择

        /// <summary>
        /// 开始出牌
        /// </summary>
        public override void ToFollowing()
        {
            base.ToFollowing();
            CardManager._instance.SetFollowButtonActive(true);
        }
    View Code

    调整PlayerOther类

    模拟出牌,随机选择除手牌中的一张

        void Update()
        {
            //如果当前是自己回合,模拟对手叫牌
            if (isMyTerm)
            {
                if (CardManager._instance.cardManagerState == CardManagerStates.Bid)
                {
                    if (Input.GetKeyDown(KeyCode.Q))    //叫牌
                    {
                        ForBid();
                    }
                    if (Input.GetKeyDown(KeyCode.W))    //不叫
                    {
                        NotBid();
                    }
                }
                if (CardManager._instance.cardManagerState == CardManagerStates.Playing)
                {
                    if (Input.GetKeyDown(KeyCode.Q))    //出牌
                    {
                        var rd1 = Random.Range(0, cardInfos.Count);
                        cardInfos[rd1].isSelected = true;
    
                        ForFollow();
                    }
                    if (Input.GetKeyDown(KeyCode.W))    //不出
                    {
                        NotFollow();
                    }
                }
            }
        }
    View Code

    调整CardManager

    实现卡牌管理对玩家出牌的控制:

    • 发完地主牌以后,开始出牌阶段,由地主先出牌;
    • 玩家选择出牌后,将上轮玩家的出牌堆清空,并将选择的牌添加到自己的出牌堆,轮转到下个玩家;
    • 玩家选择不出牌,将上轮玩家的出牌堆清空,轮转到下个玩家;
        /// <summary>
        /// 开始出牌阶段
        /// </summary>
        private void StartFollowing()
        {
            cardManagerState = CardManagerStates.Playing;
            //地主先出牌
            Players[bankerIndex].ToFollowing();
        }
        /// <summary>
        /// 玩家出牌
        /// </summary>
        public void ForFollow()
        {
            SetFollowButtonActive(false);
    
            //上轮玩家出牌清空
            Players[(termCurrentIndex + Players.Length - 1) % 3].DropAllSmallCards();
            if (Players[termCurrentIndex] is PlayerSelf)
                ShowPlayerSelfCards();
    
            SetNextPlayer();
            Players[termCurrentIndex].ToFollowing();
        }
        /// <summary>
        /// 玩家不出
        /// </summary>
        public void NotFollow()
        {
            SetFollowButtonActive(false);
    
            //上轮玩家出牌清空
            Players[(termCurrentIndex + Players.Length - 1) % 3].DropAllSmallCards();
    
            SetNextPlayer();
            Players[termCurrentIndex].ToFollowing();
        }
    View Code

    代码整理

    现在我们的代码具有一定的规模了,为了方便更好的管理,把现有的代码重新整理一下,并进行功能分类,比如:

        

    总结

    嗯,今天到此为止,我们再来测试验证下,当然,目前只是实现了出牌的功能,没有对牌力进行校验和出牌的控制,对手玩家随机模拟出牌,尚未加入AI。我们以后逐步去实现~来看看这期的效果吧~

    资源

    项目源码

  • 相关阅读:
    工作中用到知识点
    工作中遇到问题的解决办法
    透明度兼容性(ie8以上)
    js阻止浏览器默认行为
    js停止冒泡和阻止浏览器默认行为
    js添加事件通用方法
    jquery常用插件
    延迟加载、异步加载js
    JavaScript兼容性问题
    创建对象的一种方式&一种继承机制(代码实例)
  • 原文地址:https://www.cnblogs.com/lizzie-xhu/p/7794466.html
Copyright © 2020-2023  润新知