当前位置:   article > 正文

【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版9(附带项目源码)

【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版9(附带项目源码)

最终效果

在这里插入图片描述

系列导航

前言

本节主要实现添加选择植物功能

选择植物

简单绘制选择植物面板

每个卡牌插槽和前面植物卡牌类似,并配置为预制体
在这里插入图片描述

渲染卡牌数据

新增ScriptableObject,配置植物列表卡牌数据

[CreateAssetMenu(fileName = "CardData", menuName = "CardData", order = 0)]
public class CardData : ScriptableObject
{
    // 存储卡牌数据的列表
    public List<CardItem> cardItemDataList = new List<CardItem>();
}

// 表示单个关卡的数据
[System.Serializable]
public class CardItem
{
    public string name; //名称
    public float waitTime; //等待时间
    public int useSun;//需要阳光
    public GameObject prefab;//预制体
    public Sprite sprite;//图片
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

配置
在这里插入图片描述
新增ChooseCardPanel,渲染植物列表卡牌数据

public class ChooseCardPanel : MonoBehaviour
{
    public GameObject beforeCardPrefab;
    public CardData cardData;

    private void Start() {
       for (int i = 0; i < cardData.cardItemDataList.Count; i++)
        {
            GameObject beforeCard = Instantiate(beforeCardPrefab);
            beforeCard.transform.SetParent(transform, false);
            beforeCard.name = cardData.cardItemDataList[i].name;
            beforeCard.GetComponent<Image>().sprite = cardData.cardItemDataList[i].sprite;
            beforeCard.transform.Find("黑色进度").GetComponent<Image>().fillAmount = 0;
            beforeCard.transform.Find("黑色底图").gameObject.SetActive(false);

            Card card = beforeCard.GetComponent<Card>();
            card.waitTime = cardData.cardItemDataList[i].waitTime;
            card.useSun = cardData.cardItemDataList[i].useSun;
            card.prefab = cardData.cardItemDataList[i].prefab;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

配置
在这里插入图片描述
运行效果,可以看到卡牌数据都渲染上去了
在这里插入图片描述

点击选中和移除卡牌

注意:这里会使用DOTween实现动画效果,不会用的小伙伴可以查看我之前写的文章:【推荐100个unity插件之2】DoTween动画插件的安装和使用整合(最全)

修改UI

注意修改卡牌容器的顶点位置在左上
在这里插入图片描述
卡牌预制体轴心也在左上
在这里插入图片描述
选项卡牌面板顶点同样位置在左上
在这里插入图片描述

代码控制

这里改动比较大,我直接贴出代码

修改GameManager,新增字段用于判断是否开始游戏

public bool isStart;//是否开始游戏
  • 1

ChooseCardPanel代码

public class ChooseCardPanel : MonoBehaviour
{
    public static ChooseCardPanel Instance;
    public GameObject cardPrefab;//卡牌模板预制体
    public CardData cardData;//所有卡牌数据
    public GameObject useCardPanel;//选中的卡牌的对象父类
    public List<GameObject> useCardList;//选中的卡牌

    private void Awake()
    {
        Instance = this;
    }
    private void Start()
    {
        //渲染卡牌列表数据
        for (int i = 0; i < cardData.cardItemDataList.Count; i++)
        {
            GameObject beforeCard = Instantiate(cardPrefab);
            beforeCard.transform.SetParent(transform, false);
            beforeCard.GetComponent<Card>().cardItem = cardData.cardItemDataList[i];
        }
    }

    //添加卡牌
    public void AddCard(GameObject go)
    {
        int curIndex = useCardList.Count;
        if (curIndex >= 8)
        {
            Debug.Log("已经选中的卡片超过最大数量");
            return;
        }
        useCardList.Add(go);
        go.transform.SetParent(transform.root);//父级修改为当前对象所在的最顶层父对象,确保卡牌UI在最顶层显示
        Card card = go.GetComponent<Card>();
        card.isMoving = true;
        card.hasUse = true;
        // DoMove移动到目标位置
        go.transform.DOMove(useCardPanel.transform.position, 0.8f).OnComplete(
            () =>
            {
                go.transform.SetParent(useCardPanel.transform, false);
                //移动到其父对象的子物体列表的最前面
                go.transform.SetAsFirstSibling();
                card.isMoving = false;
            }
        );
    }

    //移除卡牌
    public void RemoveCard(GameObject go)
    {
        useCardList.Remove(go);
        go.transform.SetParent(transform.root);//父级修改为当前对象所在的最顶层父对象,确保卡牌UI在最顶层显示
        Card card = go.GetComponent<Card>();
        card.isMoving = true;
        card.hasUse = false;
        go.transform.DOMove(transform.position, 0.8f).OnComplete(
            () =>
            {
                go.transform.SetParent(transform, false);
                //移动到其父对象的子物体列表的最前面
                go.transform.SetAsFirstSibling();
                card.isMoving = false;
            }
        );
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

Card代码

public class Card : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler, IPointerClickHandler
{
    [HideInInspector] public CardItem cardItem;//卡牌信息
    [HideInInspector] public bool hasUse = false;//是否使用
    [HideInInspector] public bool isMoving = false;//是否正在移动
    private GameObject darkBg;//黑图
    private Image image;
    private GameObject progressBar;//进度对象
    private float waitTime; //等待时间
    private int useSun;//需要阳光
    private GameObject prefab;//预制体
    public LayerMask layerMask;//检测图层
    private GameObject thisObject;


    void Start()
    {
        darkBg = transform.Find("黑色底图").gameObject;
        progressBar = transform.Find("黑色进度").gameObject;
        image = progressBar.GetComponent<Image>();

        Init();
    }

    private void Init()
    {
        if (cardItem == null)
        {
            Debug.Log("找不到卡牌数据");
            return;
        }
        darkBg.SetActive(false);
        image.fillAmount = 0;
        GetComponent<Image>().sprite = cardItem.sprite;
        gameObject.name = cardItem.name;
        waitTime = cardItem.waitTime;
        useSun = cardItem.useSun;
        prefab = cardItem.prefab;
    }

    void Update()
    {
        if(!GameManager.Instance.isStart) return;
        UpdateProgress();
        UpdateDarkBg();
    }

    void UpdateProgress()
    {
        image.fillAmount -= 1 / waitTime * Time.deltaTime;
    }

    void UpdateDarkBg()
    {
        // TODO 且需要阳光数>useSun
        if (image.fillAmount == 0 && GameManager.Instance.sunSum >= useSun)
        {
            darkBg.SetActive(false);
        }
        else
        {
            darkBg.SetActive(true);
        }
    }

    //开始拖拽
    public void OnBeginDrag(PointerEventData eventData)
    {
        if(!GameManager.Instance.isStart) return;
        if (image.fillAmount != 0 || GameManager.Instance.sunSum < useSun) return;
        thisObject = Instantiate(prefab, transform.position, Quaternion.identity);

        //关闭运行
        thisObject.GetComponent<Plants>().isOpen = false;
        //关闭碰撞
        thisObject.GetComponent<Collider2D>().enabled = false;
        //关闭动画
        thisObject.GetComponent<Animator>().enabled = false;
    }

    //拖拽中
    public void OnDrag(PointerEventData eventData)
    {
        if (thisObject == null) return;
        //植物跟随鼠标
        Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        thisObject.transform.position = new Vector2(mousePosition.x, mousePosition.y); // 将物体位置设置为鼠标位置
    }

    //拖拽结束
    public void OnEndDrag(PointerEventData eventData)
    {
        if (thisObject == null) return;
        Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        Vector2 mousePosition2D = new Vector2(mousePosition.x, mousePosition.y);
        RaycastHit2D hit = Physics2D.Raycast(mousePosition2D, Vector2.zero, Mathf.Infinity, layerMask);//鼠标“点检测”射线
        if (hit.collider != null && hit.collider.transform.childCount == 0)
        {
            thisObject.transform.parent = hit.transform;
            thisObject.transform.localPosition = Vector2.one;

            //开始运行
            thisObject.GetComponent<Plants>().isOpen = true;
            //开启碰撞
            thisObject.GetComponent<Collider2D>().enabled = true;
            //开启动画
            thisObject.GetComponent<Animator>().enabled = true;

            thisObject = null;
            //重置进度
            progressBar.GetComponent<Image>().fillAmount = 1;

            //扣除阳光
            GameManager.Instance.SetSunSum(-useSun);

            AudioManager.Instance.PlaySFX("种植");
        }
        else
        {
            Destroy(thisObject);
        }

    }

    //点击事件
    public void OnPointerClick(PointerEventData eventData)
    {
        if (isMoving) return;
        if(GameManager.Instance.isStart) return;
        if (hasUse)
        {
            ChooseCardPanel.Instance.RemoveCard(gameObject);
        }
        else
        {
            ChooseCardPanel.Instance.AddCard(gameObject);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

配置
在这里插入图片描述
效果
在这里插入图片描述

开始战斗

修改GameManager,定义开始游戏事件,记得去除原来生成阳光和生成僵尸的代码

//开始游戏
public void StartGame(){
    AudioManager.Instance.PlayMusic("bgm1");
    isStart = true;
    //顶部随机位置掉落阳光
    InvokeRepeating("CreateSunDown", 10, 10);

    //生成僵尸
    GenerateZombies.Instance.TableCreateZombie();
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

配置
在这里插入图片描述
效果测试,一切正常
在这里插入图片描述

源码

源码不出意外的话我会放在最后一节

结束语

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/744620
推荐阅读
相关标签
  

闽ICP备14008679号