园子荒废多年,闲来无事,用Unity3D来尝试做一个简单的小游戏,一方面是对最近研究的Unity3D有点总结,一方面跟广大的园友相互学习和提高。话不多说,进入正题~

创建项目

1.创建Unity2017的2D项目,暂且叫做ChinesePoker吧,就用自带的UGUI来编辑UI, 目前只导入iTween插件,用来方便控制动画效果。

目录结构如下:

  

考虑卡牌需要动态生成,我把图片资源放到Resource目录,并按照Card_类型(大小王,红桃,黑桃,方片,梅花 )_数字(卡牌所在类型中的数字)命名。

素材都是网上找的,没有美工基础,就是这么个意思,大家将就看吧,:)

2.建第一个场景,默认叫001_Playing,作为主要玩牌的场景,暂时作为第1个场景,后期新场景添加进来,我们可能再调整场景的顺序。

添加一个UI->Image,选择一个背景图片;

添加3个UI->Canvas,分别取名叫Player0,Player1,Player2,代表玩家,对手1,对手2;

每个Player底下,添加一个Image,选择卡牌背面图片,分别表示发牌时各自牌堆的位置,并在桌面放置一个总牌堆的位置,默认not active;

建一个卡牌的图片,命名为Card,并作为预制件,放入Player0中间一个,稍微偏移一定位置再放置一个,用来计算每张牌跟临牌相对位置,设置not active;

建一个卡牌的背面图片,命名Cover,也作为预制件;

添加一个测试按钮TestButton;

差不多了,大概结构如下:

创建卡牌、玩家信息

1.新建CardInfo类,主要不要继承默认的MonoBehaviour类,用来作为卡牌的实体类;

实现IComparable接口,后面手牌排序会用到。

public class CardInfo : IComparable
{public string cardName; //卡牌图片名public CardTypes cardType; //牌的类型public int cardIndex;      //牌在所在类型的索引1-13public CardInfo(string cardName){this.cardName = cardName;var splits = cardName.Split('_');switch (splits[1]){case "1":cardType = CardTypes.Hearts;cardIndex = int.Parse(splits[2]);break;case "2":cardType = CardTypes.Spades;cardIndex = int.Parse(splits[2]);break;case "3":cardType = CardTypes.Diamonds;cardIndex = int.Parse(splits[2]);break;case "4":cardType = CardTypes.Clubs;cardIndex = int.Parse(splits[2]);break;case "joker":cardType = CardTypes.Joker;cardIndex = int.Parse(splits[2]);break;default:throw new Exception(string.Format("卡牌文件名{0}非法!", cardName));}}//卡牌大小比较public int CompareTo(object obj){CardInfo other = obj as CardInfo;if (other == null)throw new Exception("比较对象类型非法!");//如果当前是大小王if (cardType == CardTypes.Joker){//对方也是大小王if (other.cardType == CardTypes.Joker){return cardIndex.CompareTo(other.cardIndex);}//对方不是大小王return 1;}//如果是一般的牌else{//对方是大小王if (other.cardType == CardTypes.Joker){return -1;}//如果对方也是一般的牌else{//计算牌力var thisNewIndex = (cardIndex == 1 || cardIndex == 2) ? 13 + cardIndex : cardIndex;var otherNewIndex = (other.cardIndex == 1 || other.cardIndex == 2) ? 13 + other.cardIndex : other.cardIndex;if (thisNewIndex == otherNewIndex){return -cardType.CompareTo(other.cardType);}return thisNewIndex.CompareTo(otherNewIndex);}}}}

View Code

2.Card预制件上,添加Card脚本,主要保存对应CardInfo信息、选中状态,并加载卡牌图片;

public class Card : MonoBehaviour
{private Image image;        //牌的图片private CardInfo cardInfo;  //卡牌信息private bool isSelected;    //是否选中void Awake(){image = GetComponent<Image>();}/// <summary>/// 初始化图片/// </summary>/// <param name="cardInfo"></param>public void InitImage(CardInfo cardInfo){this.cardInfo = cardInfo;image.sprite = Resources.Load("Images/Cards/" + cardInfo.cardName, typeof(Sprite)) as Sprite;}/// <summary>/// 设置选择状态/// </summary>public void SetSelectState(){if (isSelected){iTween.MoveTo(gameObject, transform.position - Vector3.up * 10f, 1f);}else{iTween.MoveTo(gameObject, transform.position + Vector3.up * 10f, 1f);}isSelected = !isSelected;}
}

View Code

3.考虑玩家分为2种类型,先创建一个公共的基类,实现玩家公共的方法,比如增加一张卡牌、清空所有卡片、排序等;

public class Player : MonoBehaviour
{protected List<CardInfo> cardInfos = new List<CardInfo>();  //个人所持卡牌private Text cardCoutText;void Start(){cardCoutText = transform.Find("HeapPos/Text").GetComponent<Text>();}/// <summary>/// 增加一张卡牌/// </summary>/// <param name="cardName"></param>public void AddCard(string cardName){cardInfos.Add(new CardInfo(cardName));cardCoutText.text = cardInfos.Count.ToString();}/// <summary>/// 清空所有卡片/// </summary>public void DropCards(){cardInfos.Clear();}protected void Sort(){cardInfos.Sort();cardInfos.Reverse();}
}

View Code

4.添加第一种玩家(自身玩家)PlayerSelf,继承Player,并挂载到Player0对象上;

实现整理手牌的逻辑:发牌后,从中间的位置,根据大小依次将牌展开;

获取牌面点击事件,将牌选中或取消选中;

public class PlayerSelf : Player
{public GameObject prefab;   //预制件private Transform originPos1; //牌的初始位置private Transform originPos2; //牌的初始位置private List<GameObject>  cards=new List<GameObject>();void Awake(){originPos1 = transform.Find("OriginPos1");originPos2 = transform.Find("OriginPos2");}//整理手牌public void GenerateAllCards(){//排序
        Sort();//计算每张牌的偏移var offsetX = originPos2.position.x - originPos1.position.x;//获取最左边的起点int leftCount = (cardInfos.Count / 2);var startPos = originPos1.position + Vector3.left * offsetX * leftCount;for (int i = 0; i < cardInfos.Count; i++){//生成卡牌var card = Instantiate(prefab, originPos1.position, Quaternion.identity, transform);card.GetComponent<RectTransform>().localScale = Vector3.one * 0.6f;card.GetComponent<Card>().InitImage(cardInfos[i]);var targetPos = startPos + Vector3.right * offsetX * i;card.transform.SetAsLastSibling();//动画移动
            iTween.MoveTo(card, targetPos, 2f);cards.Add(card);}}public void DestroyAllCards(){cards.ForEach(Destroy);cards.Clear();}/// <summary>/// 点击卡牌处理/// </summary>/// <param name="data"></param>public void CardClick(BaseEventData data){//叫牌或出牌阶段才可以选牌if (CardManager._instance.cardManagerState == CardManagerStates.Bid ||CardManager._instance.cardManagerState == CardManagerStates.Playing){var eventData = data as PointerEventData;if (eventData == null) return;var card = eventData.pointerCurrentRaycast.gameObject.GetComponent<Card>();if (card == null) return;card.SetSelectState();}}
}

View Code

5.添加另一种玩家(对手玩家)PlayerOther,继承Player,并挂载到Player1,Player2对象上;

暂时没有实现任何其他功能:

public class PlayerOther : Player
{}

View Code

实现发牌逻辑

在Camera上添加卡牌管理脚本:CardManager

1.实现洗牌逻辑,这里用生成GUID随机性后排序,达到洗牌的目的;

2.记录当前发牌回合,每发一张牌,跳转给下一个玩家;

3.记录当前玩牌回合(将来可能用到),每玩一局,跳转下个玩家开始发牌;

4.发牌逻辑:

设置牌堆的显示,动画依次给每位玩家发一张卡牌,发完牌后,隐藏牌堆,并将玩家的卡牌排序并展示;

public class CardManager : MonoBehaviour
{public float dealCardSpeed = 20;  //发牌速度public Player[] Players;    //玩家的集合public GameObject coverPrefab;      //背面排预制件public Transform heapPos;           //牌堆位置public Transform[] playerHeapPos;    //玩家牌堆位置public static CardManager _instance;    //单例public CardManagerStates cardManagerState;private string[] cardNames;  //所有牌集合private int termStartIndex = 0;  //回合开始玩家索引private int termCurrentIndex = 0;  //回合当前玩家索引private List<GameObject> covers = new List<GameObject>();   //背面卡牌对象,发牌结束后销毁void Awake(){_instance = this;cardNames = GetCardNames();}/// <summary>/// 洗牌/// </summary>public void ShuffleCards(){//进入洗牌阶段cardManagerState = CardManagerStates.ShuffleCards;cardNames = cardNames.OrderBy(c => Guid.NewGuid()).ToArray();}/// <summary>/// 发牌/// </summary>public IEnumerator DealCards(){//进入发牌阶段cardManagerState = CardManagerStates.DealCards;//显示牌堆heapPos.gameObject.SetActive(true);playerHeapPos.ToList().ForEach(s => { s.gameObject.SetActive(true); });foreach (var cardName in cardNames){//给当前玩家发一张牌
            Players[termCurrentIndex].AddCard(cardName);var cover = Instantiate(coverPrefab, heapPos.position, Quaternion.identity, heapPos.transform);cover.GetComponent<RectTransform>().localScale = Vector3.one;covers.Add(cover);iTween.MoveTo(cover, playerHeapPos[termCurrentIndex].position, 0.3f);yield return new WaitForSeconds(1 / dealCardSpeed);//下一个需要发牌者
            SetNextPlayer();}//隐藏牌堆heapPos.gameObject.SetActive(false);playerHeapPos[0].gameObject.SetActive(false);//显示玩家手牌Players.ToList().ForEach(s =>{var player0 = s as PlayerSelf;if (player0 != null){player0.GenerateAllCards();}});//动画结束,进入叫牌阶段yield return new WaitForSeconds(2f);covers.ForEach(Destroy);cardManagerState = CardManagerStates.Bid;}/// <summary>/// 清空牌局/// </summary>public void ClearCards(){//清空所有玩家卡牌Players.ToList().ForEach(s => s.DropCards());//显示玩家手牌Players.ToList().ForEach(s =>{var player0 = s as PlayerSelf;if (player0 != null){player0.DestroyAllCards();}});}/// <summary>/// 获取下个玩家/// </summary>/// <returns></returns>private void SetNextPlayer(){termCurrentIndex = (termCurrentIndex + 1) % Players.Length;}/// <summary>/// 切换开始回合玩家/// </summary>public void SetNextTerm(){termStartIndex = (termStartIndex + 1) % Players.Length;}private string[] GetCardNames(){//路径  string fullPath = "Assets/Resources/Images/Cards/";if (Directory.Exists(fullPath)){DirectoryInfo direction = new DirectoryInfo(fullPath);FileInfo[] files = direction.GetFiles("*.png", SearchOption.AllDirectories);return files.Select(s => Path.GetFileNameWithoutExtension(s.Name)).ToArray();}return null;}//for testpublic void OnTestClick(){ClearCards();ShuffleCards();StartCoroutine(DealCards());}}

View Code

总结

其实发牌后的动画,可以由override基类的方法,交由Player子类处理,不用CardManager集中管理,大家可以优化一下。

大体逻辑完成,我们验证下效果吧:

资源

项目源码

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

Unity3D手机斗地主游戏开发实战(01)_发牌功能实现相关推荐

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

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

  2. Unity3D手机斗地主游戏开发实战(02)_叫地主功能实现

    大体思路 前面我们实现了点击开始游戏按钮,系统依次给玩家发牌的逻辑和动画,并展示当前的手牌.这期我们继续实现接下来的功能--叫地主. 1.首先这两天,学习了DOTween,这是一个强大的Unity动画 ...

  3. [Unity3D]手机3D游戏开发:FPS射击游戏中瞄准镜CrossHair的制作

    学习Unity脚本推荐:Unity3D官网索引 首先创建脚本文件用来初始化瞄准镜,脚本CrossHairGUI源码如下: var crosshair = SwitchGUI(); var locati ...

  4. 【转】 [Unity3D]手机3D游戏开发:场景切换与数据存储(PlayerPrefs 类的介绍与使用)...

    http://blog.csdn.net/pleasecallmewhy/article/details/8543181 在Unity中的数据存储和iOS中字典的存储基本相同,是通过关键字实现数据存储 ...

  5. 《cocos2d-x手机游戏开发实战》直播课程第一期介绍

    史上最牛的cocos2d-x课程 <cocos2d-x手机游戏开发实战>直播课程第一期 史上最牛的优惠活动: 报名关东升老师iOS会员送cocos2d-x直播课程,而且终身会员价格只要19 ...

  6. 《Unity 2D与3D手机游戏开发实战》简介

    #好书推荐##好书奇遇季#<Unity 2D与3D手机游戏开发实战>,京东当当天猫都有发售.彩色印制,定价89元,网店打折销售更便宜.本书配套源码.PPT课件,适合Unity游戏开发初学者 ...

  7. 《大富翁手机游戏开发实战》 开始发售,目录如下

    <大富翁手机游戏开发实战--基于Cocos2d-x 3.2引擎>现已登陆各大网络销售平台发售! 部分网售地址: 当当:http://product.dangdang.com/2378178 ...

  8. 《大富翁手机游戏开发实战--基于Cocos2d-x 3.2引擎》现已登陆各大网络销售平台发售

    <大富翁手机游戏开发实战--基于Cocos2d-x 3.2引擎>现已登陆各大网络销售平台发售! 部分网售地址: 当当:http://product.dangdang.com/2378178 ...

  9. 从踩坑到填坑|淘宝Web 3D应用与游戏开发实战

    导读:本文是淘宝前端技术专家--徐乾伟(烧鹅)分享的淘宝 Web 3D 应用与游戏开发实战,这个话题在业界被谈及得比较少.今天将会从移动.3D.游戏三种交叉的话题来和大家探讨.接下来和小编一起从初试 ...

  10. 【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

    文章目录 一.前言 二.实现方案 1.无主之地,第一人称视角 2.我之前做的摇杆控制 3.第一人称视角 + 摇杆控制 三.开始实战 1.资源获取:Unity AssetStore 2.Low Poly ...

最新文章

  1. 迫于项目压力研究果园
  2. react mysql增删改查_react增删改查
  3. QT5生成可执行文件总结
  4. Styled Label
  5. 关于web自动化操作的分析和基类的实现
  6. 利用寄存器进入栈值交换
  7. 程序读取计算机设备管理器中各设备的状态(启用/禁用)?(转自大富翁)
  8. SteinerTree模板
  9. php 获取 get变量,php – 在Laravel中获取GET“?”变量
  10. 真假签到题(签到+打表)
  11. 400是什么错误_我想对您说小学作文400字7篇
  12. 怎么更改计算机上的限制应用,图文详解通过修改win10系统组策略实现限制指定应用程序的运行-系统操作与应用 -亦是美网络...
  13. 如何录制Gif动态图片
  14. 裸辞,在上海一点都不“难”
  15. win10休眠_硬派玩家 | Win 10关机速度变慢?这还真不是错觉
  16. 服务器设置虚拟ip,服务器设置虚拟ip
  17. STM32开发笔记112:ADS1258驱动设计——读寄存器
  18. python 剪刀石头布游戏设计
  19. 协方差矩阵的齐性检验(接上一篇文章)
  20. matlab中S-function的概念及使用

热门文章

  1. 因果推断-Uplift Model:Meta Learning
  2. 解决谷歌浏览器最新chrome94版本CORS跨域问题
  3. TP-LINK三层网管交换机通过console接口完成复位操作
  4. STC12C5A60S2 下载失败问题
  5. fsQCA+NCA方法的软件操作及注意事项、论文实证分析部分的写作范式
  6. 谷歌io大会 android p,十年最大革新 Android P现身谷歌I/O大会
  7. 企业宣传软文怎么写?手把手教大家撰写企业宣传软文
  8. JS 实现小游戏 打砖块
  9. ie浏览器的标准模式与怪异模式的设置与区分方法
  10. 看见更有远见的技术管理