[Unity3D]卡牌游戏中有关卡牌类的制作


还没学到设计模式,所以自己和同学捣鼓了一个,请各位大神多多指教!
源码已经添加到我的github,跳转至https://github.com/HMY777/GameProject.git下载

整个项目的压缩包下载:
链接:https://pan.baidu.com/s/1pMB82nCCOSgLOP_UL8wxmA
提取码:w8a1

展示卡牌

卡牌展示效果

在这里插入图片描述

打出卡牌

在这里插入图片描述

敌人释放技能

在这里插入图片描述

需求

制作一款卡牌游戏最核心的地方就是卡牌,游戏中有生成卡牌,获得卡牌,销毁卡牌,查看卡牌,打出卡牌,卡牌音效等几大功能,但是最核心的可以分成三个:卡牌的生成与销毁,卡牌的特效,卡牌的查看,下面我们就遵循这三个核心来设计卡牌类

制作

  • 将卡牌做成Prefab,这里面包括卡牌名称,信息,卡牌框图片,卡牌图片
    在这里插入图片描述
  • 我们设计了一个Card类来存放所有卡牌的基础信息,这个类不继承MonoBehaviour,因为它不需要挂载在某个物体上,这里面包括:名称,介绍,类型,效果,卡牌框图片,卡牌图片,卡牌ID
    在这里插入图片描述
  • 需要一个脚本用来对卡牌实例化的操作,因为每张卡牌都不一样,所以生成时候都需要再设置一下它的信息,这个脚本我们叫CardInstance
    在这里插入图片描述
  • 需要一个脚本来管理卡牌的抽取,丢弃,销毁等,同时它还有 当前使用了多少张卡牌,卡牌总数,当前卡牌数等等所有游戏运行时候卡牌的信息,这个脚本我们叫CardManager,它是个单例脚本
    在这里插入图片描述
  • 需要一个脚本来对卡牌的展示进行操作,如鼠标放上去卡牌会放大,打出卡牌时会有音效等等,这个卡组我们叫做CardListen
  • 在这里插入图片描述
    上述脚本编写完以后,把CardInstance和CardListen添加到卡牌的Prefab上,设置卡牌信息。

流程

整个大体流程是这样的,玩家选择英雄,CardManager设置初始化卡组,进入游戏,玩家抽牌,CardManager生成一张卡牌,并且传递Card类的信息给CardInstance,然后CardInstance在Start()方法中设置自己的所有信息,就有一张卡牌生成了,接着打出一张卡牌的时候,就播放动画移到弃牌堆,然后摧毁这张卡牌,这样就能实现卡牌的抽取和打出了。

  • 选择英雄并初始化不同卡组
    在这里插入图片描述
  • 手动初始化卡组
    在这里插入图片描述

抽牌:

  1. CardManager实例化一个卡牌Prefab
    在这里插入图片描述
  2. 卡牌Prefab上的CardInstance被调用,然后设置自身的信息
    在这里插入图片描述

弃牌:

  1. 卡牌打出后,通知CardManager,然后CardManager将这种卡牌的信息放入弃牌堆,播放卡牌动画,然后摧毁这张卡牌

一些问题

我们根据选择的职业生成了不同的初始卡组,然后把它存放在一个Card类型的数组里面,以后增加删除都可以用它来操作,注意它的属性是Card,就是之前说的静态脚本

public Card[] CardGroup;

怎么实现抽牌和弃牌呢?
我们先想到,用Random.Range来随机获得一张卡牌,但是随机又有新的问题,如果运气不好,获取到了已经抽过,或者已经打出的卡牌怎么办?这里我们用了一个List来解决这个问题

抽牌

  1. public List<int> CardToDrugList; int类型的List,里面存放的是待抽取的牌的数组序号
    想象一下你现在有6张牌,那么这个list的Index和value就为
index 0 1 2 3 4 5
value 0 1 2 3 4 5

假设我们抽到2号牌,那么list的变化就为

index 0 1 2 3 4
value 0 1 3 4 5

其中2号牌从list中删除了,从上面我们可以看出,真正要传给CardGroup的是list的value,使用这个方法就能避免抽到重复的牌了。
3. 初始化这个List,从0开始到卡牌总数,
for (int i = 0; i < HowManyCard; i++) { CardToDrugList.Add(i); }
这里要注意,Unity数组和List似乎必须要在方法里面初始化,如果在Start里面初始化的话,接下来使用的过程中会报空指针错误。

  1. 现在假设有15张牌,我们从中抽取一张,那么先用Random.Range来获得一个随机数,假设是9,
    int j = Random.Range(0, CardToDrugList.Count);

  2. 然后初始化CardGroup里面的第CardToDrugList中的第j个元素,这么说可能有点绕,要好好想一下 SetCard((CardGroup[CardToDrugList[j]]));
    前面说了,因为List里面存放的是数组序号,而且经过几次抽牌后,List的下标会改变,导致不能跟List下标对应的值一一对应,但是不管它改变多少次,它里面存放的值始终是不变的,我们只要取得它里面的值(即为卡组中的第几张牌),然后再传递给卡组就行了

  3. 如果抽完牌了,就初始化List就行了,因为CardGroup是始终不变的
    ##弃牌
    我们每打出一张牌,就把这张牌的添加到弃牌堆里面,这里不用用到int类型的LIst,直接用Card类型的List,每次打出一张牌就添加进这个List里面就行了

public List<Card> CardToLoseList;

查看卡牌

战斗中我们常常需要查看卡组和弃牌堆,这里面怎么操作呢?

查看抽牌堆

用我们之前提到的抽牌List,获取LIst的长度,然后按顺序实例化数组中的List序号的卡牌,接着设置它的位置和父物体就行了。由于是用生成的方法,所以每次都要先清空已经生成的卡牌,很简单,直接Destroy就行了。

  /// <summary>
    /// 生成预制卡片,设置信息
    /// </summary>
    public void CreatCardToSee()
    {
        DestroyCardToSee();
        //获得待抽牌长度
        CardGroupLength = CardManager.Instance.CardToDrugList.Count;
        for (int i = 0; i < CardGroupLength; i++)
        {
            //实例化生成一个物体
            GameObject go = GameObject.Instantiate(CardToSeePrefab, Vector3.zero, Quaternion.identity);
            //设置Card属性
            go.GetComponent<CardToSeeInstance>().card = CardManager.Instance.CardGroup[CardManager.Instance.CardToDrugList[i]];
            go.transform.GetChild(1).transform.position = new Vector3(0, -45, 0);
            //TextTitle.transform.position = new Vector3(0, -45, 0);
            go.GetComponent<CardToSeeInstance>().SetAllInfomation();
            //设置到CardGroupPanel上
            go.transform.SetParent(CardPanel.GetComponent<Transform>());
            // CardManager.Instance.CardGroup[CardManager.Instance.CardToDrugList[i]];
        }
    }
 /// <summary>
    /// 清空已有的卡组信息
    /// </summary>
    public void DestroyCardToSee()
    {
        GameObject go = CardPanel;
        for (int i = 0; i < go.transform.childCount; i++)
        {
            Destroy(go.transform.GetChild(i).gameObject);
        }
    }

查看弃牌堆

弃牌堆要用到之前的弃牌LIst,这个简单,因为它是Card类型的List,我们直接生成就行了

  /// <summary>
    /// 生成预制卡片,设置信息
    /// </summary>
    public void CreatLoseCardToSee()
    {
        DestroyLoseCardToSee();
        //获得弃牌List长度
        CardGroupLength = CardManager.Instance.CardToLoseList.Count;
        if(CardGroupLength>0)
        {
            for (int i = 0; i < CardGroupLength; i++)
            {
                //实例化生成一个物体
                //GameObject go = GameObject.Instantiate(CardToSeePrefab, Vector3.zero, Quaternion.identity);
                //go.GetComponent<CardToSeeInstance>().card = CardManager.Instance.CardGroup[CardManager.Instance.CardToLoseList[i]];
                //go.GetComponent<CardToSeeInstance>().SetAllInfomation();
                //go.transform.SetParent(GameObject.Find("Lose_CardPanel").GetComponent<Transform>());
                GameObject go = GameObject.Instantiate(CardToSeePrefab, Vector3.zero, Quaternion.identity);
                go.GetComponent<CardToSeeInstance>().card = CardManager.Instance.CardToLoseList[i];
                go.GetComponent<CardToSeeInstance>().SetAllInfomation();
                go.transform.SetParent(Lose_CardPanel.GetComponent<Transform>());
            }
        }
        
    }

卡牌效果的实现

这里是我们没做好的地方,我们原来想的是即将打出的卡牌和卡牌指向的目标之间需要一个消息脚本来给他们发消息,传递当前卡牌效果,于是就能把所有的卡牌信息写在那个脚本里面,然后每次监听鼠标,每次鼠标选中一张卡牌指向一个目标的时候,我们就发消息给这个消息脚本,传递选中的卡牌和目标,然后根据卡牌的ID发动效果。
但是当时没想好,直接把每张卡牌的效果写在了人物目标身上,导致这个脚本有点逻辑不通(运行时没问题的,但是自己感觉很不舒服),过程还是一样,直接传卡牌ID到人物身上就行了

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值