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

by admin on 2019年1月3日

园子荒废多年,闲来无事,用Unity3D来尝试做一个简易的小游戏,一方面是对最近探讨的Unity3D有点总计,一方面跟周边的园友互相学习和增强。话不多说,进入正题~

善与恶的言语混乱,我给您们提议国家的这些特点。真的,那么些特点代表着求死的心志!真的,这一个特性在向死之说教者招手!

创立项目

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

目录结构如下:

图片 1  图片 2

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

大抵了,大概结构如下:

图片 3

唉,在你们伟大的神魄里也响着它这阴沉的假话!唉,它猜透了那一个喜欢糜费的持有之心!

成立卡牌、玩家消息

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

兑现IComparable接口,前面手牌排序会用到。

图片 4图片 5

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

图片 6图片 7

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体系型,先创立一个国有的基类,实现玩家公共的主意,比如增添一张卡牌、清空所有卡片、排序等;

图片 8图片 9

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

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

收获牌面点击事件,将牌选中或收回选中;

图片 10图片 11

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

暂时并未落实任何另外成效:

图片 12图片 13

public class PlayerOther : Player
{

}

View Code

世间现行如故为巨大灵魂开放着,许多空座依旧留待孤独者和同行者,座位四周飘漾着平静大海的馥郁。

实现发牌逻辑

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

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

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

3.记下当前玩牌回合(以后可能用到),每玩一局,跳转下个玩家起初发牌;

4.发牌逻辑:

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

图片 14图片 15

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

什么地方还有民族,哪儿的部族就不懂国家,就憎恨国家,如同憎恨凶恶的眼神,如同憎恨对习俗和公理所犯的罪恶。

总结

实际发牌后的卡通片,可以由override基类的艺术,交由Player子类处理,不用CardManager集中管理,我们可以优化一下。

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

图片 16

她们全都争求王位,此乃他们的愚妄——似乎幸福在皇位上!王位上时不时沾满污秽,王位也经常置于污秽物上。

资源

连串源码

轻易的活着一如既往为英雄灵魂开放着,真的,何人占有的东西越少,什么人就越少被人占据。值得表扬的是小小的的贫寒!

瞧见那一个多余者吧!他们径直带病,呕出自己的胆囊还名为音讯。他们互相吞食而不可能消化。

它的一切都是虚伪的,它用偷来的牙齿咬啮,这个咬啮者。甚至于它的五脏六腑也是虚与委蛇的。

是呀,它也猜透了你们这一个战胜远古神明的人!你们厌倦了加油,而你们的厌倦现在可服务于新偶像!

离家这恶臭吧!离开多余者的偶像崇拜吧!

是呀,为广大人发明了一种死,这死又以自吹自擂为生。真的,对于死之说教者来说,此乃一种心灵服务!

在我看来,他们全是愚妄者、攀援之猴、热昏之徒。我闻到他们的偶像即冷酷的怪兽散发的臭味,我闻到这个偶像崇拜者一道散发的恶臭。

瞧见那多少个多余者吧!他们发了财,却因而而更贫穷;他们要抢劫权力,为此首先要拿到权力的撬棒,即大方的金钱,这个一无所有的人!

落地的人实在太多太多,国家是为多余者而发明的!

瞧他们攀登,这么些高速的猿猴!他们互相攀超,相互拉拽,终于一道掉进泥淖和深渊。

新偶像,它要在和谐周围建立英雄和光荣!冷酷的怪兽,它喜欢在灵魂的太阳中晒日光浴!

本人给您们说说这么些特性:每个民族说着善与恶的语言,邻近的部族对这语言不可以意会。每个民族是在风俗和公理中表明了祥和的言语。

江山?国家是什么?这好吧!请你们细致听着,现在自家要对您们说说民族的熄灭。

好几地方仍然存在着民族和部落,但大家这边不设有,弟兄们,那里唯有国家。

在国家没有之地才起来有人,这人不再是剩下的,才开首有必要之人的赞叹,才起来有无与伦比的、无可取代的灵气。

弟兄们,你们难道宁愿在你们的兽嘴和贪欲的云烟中窒息而死吗?最好破窗跳到露天吧!

自我把这地点名叫国家,所有的人不论善恶全是饮鸩者;我把这地方称为国家,所有的人不管善恶全都失掉了我;我把这地点叫作国家,人人慢性自杀,还把那名叫“生活”。

唯独国家在说善与恶时全是单向谎言,它不管说什么样都在撒谎,它兼具的一切都是它偷来的。

江山消亡的地点——你们朝这里看呀,弟兄们!你们尚未看见那超人的彩虹和桥梁吗?

国家是持有冷酷怪物中的最冷酷者。它也冷酷地撒谎,那么些谎言从自身的嘴里爬出来:“我,国家,国家即百姓。”

她用你们做钓饵去得到芸芸众生!是呀,发明了一种地狱艺术品,即一匹死神之马,上帝荣誉的装饰丁当作响!

“世间没有什么样比我更宏伟的了,我是上帝整饬的指尖。”这怪物如是咆哮。跪下来的也不单是长耳朵和短视眼!

你们假如向这新偶像顶礼膜拜,它愿意给您们所有,它为和谐收买你们的道德光辉和孤高眼神。

查拉图Stella如是说。

远离这恶臭吧!离开牺牲品——人的烟雾吧!

眼见这么些多余者吧!他们扒窃了发明者的作品和智者的瑰宝,他们把团结的偷盗叫做教化——一切都改成了他们的病症和灾难!

正是弥天大谎啊!这是些创立者,他们创建了各民族,并且在各民族的头顶高悬一种信仰和一种爱,就是说,他们服务于人生;这是些毁灭者,他们为无数人设下陷阱并把陷阱称之为国家,他们在诸几个人的头顶高悬一把剑和一百种贪求。

你们瞧呀,它是如何抓住太多太多的人到它身边!它是何等吞噬、咀嚼、再体会他们!

发表评论

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

网站地图xml地图