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

简介: Hi,之前有同学说要我把源码发出来,那我就把半成品源码的链接放在每篇文件的最后,有兴趣的话可以查阅参考,有问题可以跟我私信,也可以关注我的个人公众号,互相交流嘛。当然,代码也是在不断的持续改进中~ 上期我们实现了叫地主功能,不过遗留了一个小功能:叫地主完成以后,要显示地主的3张牌,这期首先弥补这块的功能; 接着我们要进入开发出牌逻辑的开发阶段,好了,废话不多说,继续我们斗地主开发之旅~ 地主牌的显示 我们在玩家界面的顶部中间位置,放置一个新的GameObject,命名为BidCards,用来记录3张地主牌的显示位置。

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。我们以后逐步去实现~来看看这期的效果吧~

资源

项目源码

img_8f0a90f3cbaa0e044fb8bf7b13c4317b.jpe

文章作者:原子蛋
文章出处:https://www.cnblogs.com/lizzie-xhu/
个人网站:https://www.lancel0t.cn/
个人博客:https://blog.lancel0t.cn/
微信公众号:原子蛋Live+
扫一扫左侧的二维码(或者长按识别二维码),关注本人微信公共号,获取更多资源。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

目录
相关文章
|
3月前
|
安全 API Python
详解手机状态查询API实战指南
手机状态查询API是一款高效接口,可实时识别手机号状态(实号、空号、风险号等),帮助企业筛选有效号码,提升业务触达率与客户体验。
337 0
|
16天前
|
小程序 PHP 图形学
热门小游戏源码(Python+PHP)下载-微信小程序游戏源码Unity发实战指南​
本文详解如何结合Python、PHP与Unity开发并部署小游戏至微信小程序。涵盖技术选型、Pygame实战、PHP后端对接、Unity转换适配及性能优化,提供从原型到发布的完整指南,助力开发者快速上手并发布游戏。
|
3月前
|
机器学习/深度学习 安全 API
通过UID非法获取手机号的违法行为与技术逻辑剖析别异想天开了-优雅草卓伊凡
通过UID非法获取手机号的违法行为与技术逻辑剖析别异想天开了-优雅草卓伊凡
849 0
通过UID非法获取手机号的违法行为与技术逻辑剖析别异想天开了-优雅草卓伊凡
|
8月前
|
存储 人工智能 编译器
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
324 10
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
|
10月前
|
数据采集 存储 XML
python实战——使用代理IP批量获取手机类电商数据
本文介绍了如何使用代理IP批量获取华为荣耀Magic7 Pro手机在电商网站的商品数据,包括名称、价格、销量和用户评价等。通过Python实现自动化采集,并存储到本地文件中。使用青果网络的代理IP服务,可以提高数据采集的安全性和效率,确保数据的多样性和准确性。文中详细描述了准备工作、API鉴权、代理授权及获取接口的过程,并提供了代码示例,帮助读者快速上手。手机数据来源为京东(item.jd.com),代理IP资源来自青果网络(qg.net)。
|
开发者 图形学 Java
揭秘Unity物理引擎核心技术:从刚体动力学到关节连接,全方位教你如何在虚拟世界中重现真实物理现象——含实战代码示例与详细解析
【8月更文挑战第31天】Unity物理引擎对于游戏开发至关重要,它能够模拟真实的物理效果,如刚体运动、碰撞检测及关节连接等。通过Rigidbody和Collider组件,开发者可以轻松实现物体间的互动与碰撞。本文通过具体代码示例介绍了如何使用Unity物理引擎实现物体运动、施加力、使用关节连接以及模拟弹簧效果等功能,帮助开发者提升游戏的真实感与沉浸感。
664 1
|
JavaScript
Vue 3:检验手机号码的逻辑
Vue 3:检验手机号码的逻辑
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
1176 0
|
图形学 开发者 UED
Unity游戏开发必备技巧:深度解析事件系统运用之道,从生命周期回调到自定义事件,打造高效逻辑与流畅交互的全方位指南
【8月更文挑战第31天】在游戏开发中,事件系统是连接游戏逻辑与用户交互的关键。Unity提供了多种机制处理事件,如MonoBehaviour生命周期回调、事件系统组件及自定义事件。本文介绍如何有效利用这些机制,包括创建自定义事件和使用Unity内置事件系统提升游戏体验。通过合理安排代码执行时机,如在Awake、Start等方法中初始化组件,以及使用委托和事件处理复杂逻辑,可以使游戏更加高效且逻辑清晰。掌握这些技巧有助于开发者更好地应对游戏开发挑战。
595 0
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
589 0

热门文章

最新文章

  • 1
    【02】整体试验思路,在这之前我们发现sec_uid,sec_uid是什么和uid的关系又是什么?相互如何转换?python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
    581
  • 2
    【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
    324
  • 3
    【01】整体试验思路,如何在有UID的情况下获得用户手机号信息,python开发之理论研究试验,如何通过抖音视频下方的用户的UID获得抖音用户的手机号-本系列文章仅供学习研究-禁止用于任何商业用途-仅供学习交流-优雅草卓伊凡
    1150
  • 4
    美团面试:手机扫描PC二维码登录,底层原理和完整流程是什么?
    568
  • 5
    MNN-LLM App:在手机上离线运行大模型,阿里巴巴开源基于 MNN-LLM 框架开发的手机 AI 助手应用
    5394
  • 6
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    194
  • 7
    ClKLog支持手机端查询统计数据啦!
    125
  • 8
    OmAgent:轻松构建在终端设备上运行的 AI 应用,赋能手机、穿戴设备、摄像头等多种设备
    681
  • 9
    Mobile-Agent:通过视觉感知实现自动化手机操作,支持多应用跨平台
    2943
  • 10
    HTML5实现的手机验证抽奖领券效果源码
    289