Unity3D手机斗地主游戏开发实战(01)_发牌功效实现

资源

项目源码

图片来自kodori_green@instagram

成立卡牌、玩家新闻

1.新建CardInfo类,紧要不要连续默认的MonoBehaviour类,用来作为卡牌的实体类;

落实IComparable接口,后边手牌排序会用到。

管理 1管理 2

public class CardInfo : IComparable
{
    public string cardName; //卡牌图片名
    public CardTypes cardType; //牌的类型
    public int cardIndex;      //牌在所在类型的索引1-13

    public 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消息、选中状态,并加载卡牌图片;

管理 3管理 4

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种品种,先成立一个公共的基类,实现玩家公共的方法,比如扩大一张卡牌、清空所有卡片、排序等;

管理 5管理 6

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对象上;

贯彻整治手牌的逻辑:发牌后,从中间的职务,依照大小顺序将牌展开;

赢得牌面点击事件,将牌选中或撤销选中;

管理 7管理 8

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对象上;

临风尚未兑现其他其他功效:

管理 9管理 10

public class PlayerOther : Player
{

}

View Code

绝色的手帐不仅可以记录点滴、规划日程,这种沉浸在挥洒中的愉悦感更令人快乐。

贯彻发牌逻辑

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

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

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

3.记录当前玩牌回合(将来或许用到),每玩一局,跳转下个玩家初步发牌;

4.发牌逻辑:

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

管理 11管理 12

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 test
    public void OnTestClick()
    {
        ClearCards();
        ShuffleCards();
        StartCoroutine(DealCards());
    }

}

View Code

不过,不会画画要肿样成功地用手帐毒害朋友圈呢?上面我要说说关于手帐的这么些事情。

创建项目

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

目录结构如下:

管理 13  管理 14

考虑卡牌需要动态变化,我把图片资源放到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;

基本上了,大概结构如下:

管理 15

不会画画也能做出周到手帐

▎手帐本

图表来源于1101.com

要做出系数的手帐你首先要挑一本颜值高的手帐本,常见的letts、hobonichi文库本、filofax、Midori
Traveler’s Note Book和smythson等都是难堪又好用的象征。

letts

为啥介绍letts?因为是它表明了世道上先是本手帐啊。从1812年始于,它直接是大英帝国皇室的御用手帐。当你在对象圈晒letts的时候,轻轻来一句:“维Dolly亚(维Dolly亚(Victoria))女皇鉴赏手帐的观点如故不错的。”啊~你可以协调体会一下。

●letts与《金融时报》合作款手帐

图表源于financialtimesdiaries.com

Letts的表征是四角的纯金配件,以及内页页边的金箔切口工艺。金箔啊,有钱任性有没有,其实这是为了以防万一水分进入本子。另外,第一次打开时还会有一种金属撕开的响声,套用一句酒界名言“那就是夫人的叹息”。

hobonichi文库本

图片来自1101.com

用来记日记或者做为个人时间管理以来,我最推荐的就是hobonichi的丛书本(简称hobo)啦,就是爱它一日一页的内页排版。每日都有日期记录,每成功一页都是满满的充实感。而且,每年一月、二月出售不同的本子,供本子控收藏

图形来源于1101.com

它家出的本子完全符合好剧本标准,比如可以摊平到180°;纸质丰饶,用钢笔书写也不会洇水,甚至有人把它作为写生本来画水彩。

图形来源1101.com

Midori Traveler’s Note

图表来源于meilishuo.com

诚如的话,一本hobo作为手帐足以应付大部分需要了。但是,你若出去旅行,带本Midori
Traveler’s Notebook(以下简称TN)将会越加便民。

与hobo关注每天记录不同,TN接纳满意手帐控们在旅行中著录的急需。它一般比索尼爱立信6
Plus胖上一圈,供给旅行者用来记录旅行,盖印章什么的。有一条橡皮筋捆着避免掉内芯出来,便于辅导。

图表来源mafengwo.com

厚厚的Traveler’s Note满满都是旅行的记念。

filofax metropol personal**

filofax最大的特性就是活页,你能体悟的保有能打孔的事物都能往里加。除了各类纸质内页,在里边加布料做成布料本啦,或者用来搜集明信片都是不错的精选。

图形来自官网

smythson

那也是一个大不列颠及苏格兰联合王国的世纪尽人皆知,与letts不同smythson的设计更为偏爱女性元素。无论是明媚的色彩仍旧温柔的纸质都是为着让妹纸们用得更开玩笑啊。女星格蕾丝(GraceKelly)和戴安娜(Anna)王妃都是它的忠诚粉丝。

图形源于smythson.com

smythson手帐内页用的是像羽毛一样轻但又不容易破的黑色羽毛纸,一百页的记录本也就两台HUAWEI6的轻重。
透过光就可以观望水印压花的纸张也可以算是小小的彩蛋呢

图表源于《我的士绅时髦》剧照

彩笔

说完手帐本,我们再来说说它的好盆友彩笔童鞋。不论是要用绘画来记录一天的生存仍然为手帐添加装修线条都离不开它。

图形来自kodori_管理,green@instagram

❶mildliner

Zebra旗下mildliner的卖点在于它的颜色丰硕淡,适合用来做标记。在急需标记文字上写道的时候不会把本来的文字覆盖掉,特别是淡色与和风多个色系,颜色怎么搭都窘迫。

其它,mildliner搭配hobo来做时间计划再好但是。每个颜色代表一个事项,完成后异常格子可以一笔填满,白羊座的童鞋不来一发么:)

❷吴竹信笔

图形来源于jetpens.com

这套笔是二〇一一年东瀛文具设计大赏设计组获奖小说。笔的前端和尾端会显示笔芯的一有的,可以知道地收看其中的颜料。笔尖有些许弹性,可以随便转换粗细,让字变得更灵敏。

图形来自byhayley.tumblr.com

譬如要写花体字就不要特别准备专用蘸水笔,也不用反复填涂,只要决定力度就能一笔完成粗细有其它笔画了。

和纸胶带

设若不会画画,这就去购买几卷和纸胶带呢,即便只是给内页包个边或者贴上两条分隔线也能提拔你手帐的美貌度。如果能在这基础上美观地拓展铺垫,这逼格不过蹭蹭往上涨了。

图片来自meilishuo.com

为了衬托不同的手帐内容,要准备不同的和纸胶带,比如用来包边的相似以简要的大头为佳,用来做标记的话就活该接纳纯色的。而且将它们简简单单地位于抽屉里可不行,你需要用架子把它们显示出来,取用方便的还要仍可以顺便晒晒限量版的胶带不是。

●胶带架

图表源于pcstore.com.tw

❶masking tape

江湖人称的MT的masking
tape可谓是和纸胶带界的一姐~她家发明了和纸胶带,目前以天马行空的绘画配色和巨大的产品谱系为特色。

图形来自X宝

MT每年都会发布大气新款,比如皆川明合作款等,用设计师的杰作来点缀手帐,不会画画又何以,照样让手帐逼格满满。

❷仓敷意匠

仓敷意匠的设计师井上阳子用日式杂货风来设计和纸胶带,配色与花纹简单清淡,有的直接是打印字体的作风。同时,它还以耐超高温,长年不脱落不变形,易于书写不褪色等质量拿到好评。

图表来自classiky.co.jp

假定对于MT少女系的画风不感冒可以尝试仓敷意匠的和纸胶带,配合它家的白瓷文具很容易就能将协调带走这种泛着淡淡文艺气息的空气中。

▎彩色贴纸

贴纸也是手帐中常用的宽广,初学者可以在购置手帐时一并收入,比如hobo就有专门的贴纸周边。

图表来源于X宝

❶Cavallini复古铁盒贴纸

喜欢复古风格的童鞋也得以采用cavallini的铁盒贴纸。这个起源于维也纳的文具品牌主打上世纪二十年间的老美风情,图案与质感都做得硬着头皮贴合这时的作风。

图形来源于X宝

越来越是复古邮票连串,不但将纸面做旧,花纹上还细心地加上了各地邮戳。用它搭配Traveler’s
Note总以为一不小心就会越过到上世纪,或者进入哈利波特的魔法世界。

图片来源于amazon.com

而是呢,贴纸的意趣更在于收集。有人跑遍世界收集不同的徽章、冰橱贴,而跑遍世界搜索不同的贴纸则是本身的最爱。

园子荒废多年,闲来无事,用Unity3D来品尝做一个简短的小游戏,一方面是对如今商量的Unity3D有点统计,一方面跟周边的园友相互学习和增长。话不多说,进入正题~

而对于男生以来手帐的留存简直是礼物界的良知。妹纸,尤其是这个软甜萌的妹纸,给他俩送上一本美貌又质量不错的手帐本,既能赢得欢心,又不容易落入俗套。

总结

实质上发牌后的卡通,可以由override基类的不二法门,交由Player子类处理,不用CardManager集中管理,我们可以优化一下。

大体逻辑完成,我们作证下效果啊:

管理 16

晒手帐的正统姿态

说到底,要记得手帐不仅是出自嗨的,我们的对象是麻醉朋友圈30年~本来嘛,你说你都把手帐画成这幅腔调了,不晒出去简直天理难容对吧。

图表源于pinterest.com

不过,要把手帐晒出人格晒出逼格还需要一点小技巧,比如说一定要把手机镜头擦干净、不要随便用滤镜等等。不过针对手帐你还要小心这样两点:

❶要在画面中留白

❷要记得与运用的工具合影

要在镜头中留白

方今网上看看晒手帐的肖像最多的就是摊开了第一手拍,整个画面完全被剧本的情节占据。这种构图简直是注解照的翻版,这样晒手帐就像在情侣圈晒证件照,都是当真勇士。

图片来自douban.com

应该在手帐周围留留白,让镜头不一定太拥堵。

图表来自douban.com

▎要记得与运用的工具合影

这是另一个晒手帐的科班动作,做了就能加分哦。因为制作手帐的工具无论是彩笔、和纸胶带仍然橡皮章、贴纸美貌才是它们存在的理由。用那么些来点缀照片既表达制作手帐的勤学苦练,也足以让附带晒晒家里的文具嘛。

图片来源于lucy-wonderland@tumblr

理所当然要记得配上一段这样的文案:“我的小伙伴萌在每一个夜间伴随我记下生命的点滴,感恩~”

笑妹的手帐小结

咳咳,说了如此多关于手帐的事体,其实做出一本可以手帐就是为着展现对于生活的疼爱。无论你会不会画画、能不可能做出令人眼睛一亮的柔美手帐,综上说述你做得喜出望外就好。

作者:笑妹 

网易新浪:@阁主妖娆

敢说你懂生活吧?

欢迎推荐给同样热爱生活的敌人们。

转载&合作,请联系我们:hedonist@mymanna.me

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注