当前位置: 首页 > news >正文

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

最终效果

在这里插入图片描述

系列导航

文章目录

  • 最终效果
  • 系列导航
  • 前言
  • 选择植物
    • 简单绘制选择植物面板
    • 渲染卡牌数据
  • 点击选中和移除卡牌
    • 修改UI
    • 代码控制
  • 开始战斗
  • 源码
  • 结束语

前言

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

选择植物

简单绘制选择植物面板

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

渲染卡牌数据

新增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;//图片
}

配置
在这里插入图片描述
新增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;}}
}

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

点击选中和移除卡牌

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

修改UI

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

代码控制

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

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

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

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;});}
}

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 且需要阳光数>useSunif (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);}}
}

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

开始战斗

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

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

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

源码

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

结束语

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

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

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

在这里插入图片描述

相关文章:

  • 自动求导实现与可视化
  • 算法训练营day56
  • MT2096 数列分段
  • 六种图算法的python实现
  • 前端的强缓存和协商缓存
  • Pixi.js学习 (六)数组
  • 前端面试题日常练-day60 【面试题】
  • 鸿蒙轻内核M核源码分析系列六 任务及任务调度(2)任务模块
  • UnityAPI学习之Animator的基本使用
  • UE4获取动画序列资产的动画时长
  • 【Linux】I/O多路复用
  • B站画质补完计划(3):智能修复让宝藏视频重焕新生
  • SpringBoot整合SpringDataRedis
  • 附件采集文件类型识别方案
  • UML交互图-协作图
  • 《深入 React 技术栈》
  • Brief introduction of how to 'Call, Apply and Bind'
  • C# 免费离线人脸识别 2.0 Demo
  • CAP 一致性协议及应用解析
  • css系列之关于字体的事
  • Elasticsearch 参考指南(升级前重新索引)
  • JavaScript异步流程控制的前世今生
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Median of Two Sorted Arrays
  • Sass 快速入门教程
  • spring boot下thymeleaf全局静态变量配置
  • spring security oauth2 password授权模式
  • ⭐ Unity + OpenCV 实现实时图像识别与叠加效果
  • 程序员该如何有效的找工作?
  • 读懂package.json -- 依赖管理
  • 分类模型——Logistics Regression
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 基于axios的vue插件,让http请求更简单
  • 简单实现一个textarea自适应高度
  • 微服务核心架构梳理
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • (30)数组元素和与数字和的绝对差
  • (function(){})()的分步解析
  • (Matlab)使用竞争神经网络实现数据聚类
  • .FileZilla的使用和主动模式被动模式介绍
  • .htaccess 强制https 单独排除某个目录
  • .NET8 动态添加定时任务(CRON Expression, Whatever)
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • @vue/cli脚手架
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器
  • [1127]图形打印 sdutOJ
  • [202209]mysql8.0 双主集群搭建 亲测可用
  • [240903] Qwen2-VL: 更清晰地看世界 | Elasticsearch 再次拥抱开源!
  • [ACTF2020 新生赛]Include
  • [Android实例] 保持屏幕长亮的两种方法 [转]