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

news2024/12/23 14:45:51

最终效果

在这里插入图片描述

系列导航

文章目录

  • 最终效果
  • 系列导航
  • 前言
  • 方法一、使用excel配置表
    • excel转txt文本
    • 读取txt数据
    • 按配置信息生成僵尸
  • 方法二、使用ScriptableObject 配置关卡信息
  • 源码
  • 结束语

前言

本节主要是推荐两种实现配置关卡信息,并按表生成僵尸和关卡波次

方法一、使用excel配置表

前面我们只是简单的随机生成僵尸,实际关卡编辑肯定不可能按这种方式,这里教大家一个读取excel配置表的方法,具体可以参考我之前的文章,这里就不过多介绍了:【unity小技巧】unity读excel配置表操作,excel转txt文本,并读取txt文本内容,实例说明

excel转txt文本

比如配置表大概样式
level.xlsx
在这里插入图片描述

转换为txt文本后效果
在这里插入图片描述

读取txt数据

新增GameConfigData游戏配置表类,每个对象对应一个txt配置表

public class GameConfigData
{
    // 存储配置表中的所有数据
    private List<Dictionary<string, string>> dataDic;
    // 构造函数,参数为字符串
    public GameConfigData(string str)
    {
        // 初始化数据字典
        dataDic = new List<Dictionary<string, string>>();
        // 按换行符切割字符串
        string[] lines = str.Split('\n');
        // 第一行是存储数据的类型
        string[] title = lines[0].Trim().Split('\t');//tab切割
        // 从第三行(下标为2)开始遍历数据,第二行数据是解释说明
        for (int i = 2; i < lines.Length; i++)
        {
            // 创建新的字典存储每行数据
            Dictionary<string, string> dic = new Dictionary<string, string>();
            // 按tab切割每行数据
            string[] tempArr = lines[i].Trim().Split("\t");
            // 将切割后的数据添加到字典中
            for (int j = 0; j < tempArr.Length; j++)
            {
                dic.Add(title[j], tempArr[j]);
            }
            // 将字典添加到数据列表中
            dataDic.Add(dic);
        }
    }

    // 获取所有行的数据
    public List<Dictionary<string, string>> GetLines()
    {
        return dataDic;
    }

    // 根据ID获取一行数据
    public Dictionary<string, string> GetOneById(string id)
    {
        // 遍历数据列表
        for (int i = 0; i < dataDic.Count; i++)
        {
            // 获取当前字典
            Dictionary<string, string> dic = dataDic[i];
            // 如果字典中的ID与参数相同,返回该字典
            if (dic["Id"] == id)
            {
                return dic;
            }
        }
        // 如果没有找到,返回null
        return null;
    }

    // 根据levelId获取数据
    public List<Dictionary<string, string>> GetListByLevelId(string levelId)
    {
        List<Dictionary<string, string>> levelList = new List<Dictionary<string, string>>();
        // 遍历数据列表
        for (int i = 0; i < dataDic.Count; i++)
        {
            // 获取当前字典
            Dictionary<string, string> dic = dataDic[i];
            // 如果字典中的levelID与参数相同
            if (dic["levelID"] == levelId)
            {
                // 将字典添加到数据列表中
                levelList.Add(dic);
            }
        }
        return levelList;
    }
}

新增GameConfigManager代码如下

// 游戏配置管理类
public class GameConfigManager
{
    public static GameConfigManager Instance = new GameConfigManager();
    
    private GameConfigData levelData;//关卡数据

    // 文本资源
    private TextAsset textAsset;

    // 初始化配置文件(txt文件 存储到内存)
    public void Init()
    {
        // 加载关卡数据
        textAsset = Resources.Load<TextAsset>("Data/level");
        levelData = new GameConfigData(textAsset.text);
    }

    // 获取关卡行数据
    public List<Dictionary<string, string>> GetLevelLines()
    {
        return levelData.GetLines();
    }

    // 根据ID获取关卡数据
    public Dictionary<string, string> GetLevelById(string id)
    {
        return levelData.GetOneById(id);
    }

	//根据关卡id获取数据
    public List<Dictionary<string, string>> GetLevelList(string levelId)
    {
        return levelData.GetListByLevelId(levelId);
    }
}

按配置信息生成僵尸

修改GameManager,初始化配置表信息并获取当前关卡数据

public int curLevelId = 1; //当前关卡
public int curProgressId = 1;//当前进度
public List<Dictionary<string, string>> listData;//当前关卡数据

private void Awake()
{
    Instance = this;
    //初始化配置表
    GameConfigManager.Instance.Init();
    //获取当前关卡数据
    listData = GameConfigManager.Instance.GetLevelList(curLevelId.ToString());
}

修改GenerateZombies,调用配置表信息并使用

public class GenerateZombies : MonoBehaviour
{
    public static GenerateZombies Instance { get; private set; }
    public GameObject zombiePrefab; //僵尸预制体
    public List<GameObject> curProgressZombie;//保存当前进度的敌人
    int zOrderIndex = 0;//排序
    

    private void Awake()
    {
        Instance = this;
    }

    private void Start()
    {
        curProgressZombie = new List<GameObject>();
        TableCreateZombie();
    }

    //生成僵尸
    private void TableCreateZombie()
    {
        //判断是否是最后一波敌人,如果表格中当前进度没有可以创建的敌人,及游戏胜利
        bool canCreate = false;
  
        //获取当前关卡数据
        GameManager.Instance.listData.ForEach(data =>
        {
            //属于当前进度的僵尸
            if (data["progressId"] == GameManager.Instance.curProgressId.ToString())
            {
                //延迟一段时间创建僵尸
                StartCoroutine(ITableCreateZombie(data));
                //代表当前进度有敌人
                canCreate = true;
            }
        });

        if(!canCreate){
            StopAllCoroutines();//停止所有的携程
            //TODO:游戏胜利处理
            Debug.Log("游戏胜利");
        }
    }

    IEnumerator ITableCreateZombie(Dictionary<string, string> levelItem)
    {
        yield return new WaitForSeconds(float.Parse(levelItem["createTime"]));

        //加载预制件:从Resources文件夹中加载,例如Zombie1
        GameObject zombiePrefab = Resources.Load("Prefabs/Enemy/Zombie" + levelItem["zombieType"]) as GameObject;

        //生成僵尸实例
        GameObject zombie = Instantiate(zombiePrefab);

        //根据配表的生成位置,找到父物体
        Transform zombieLine = transform.GetChild(int.Parse(levelItem["bornPos"]));
        zombie.transform.parent = zombieLine;
        zombie.transform.localPosition = Vector3.zero;
        zombie.GetComponent<SpriteRenderer>().sortingOrder = zOrderIndex;
        zOrderIndex ++;
        
        curProgressZombie.Add(zombie);
    }

    //消灭敌人
    public void ZombieDied(GameObject gameObject){
        if(curProgressZombie.Contains(gameObject)){
            curProgressZombie.Remove(gameObject);
        }
        //当前进度的僵尸全部消灭了,开启下一个进度
        if(curProgressZombie.Count == 0){
            GameManager.Instance.curProgressId += 1;
            TableCreateZombie();
        }
    }
}

修改Zombie里的OnDie方法,消灭敌人时调用下面代码,控制游戏进度变化

GenerateZombies.Instance.ZombieDied(gameObject);

记得配置敌人信息
在这里插入图片描述

效果,可以看到僵尸按配置表在指定位置逐渐生成
在这里插入图片描述

方法二、使用ScriptableObject 配置关卡信息

前面的excel转txt配置表使用比较方便,如果你觉得太复杂,并不理解他的用法。那也没关系,这里推荐另一种方法使用ScriptableObject 做配置关卡信息,省去了读excel和txt的步骤,也能实现一样的效果

新增LevelData

using System.Collections.Generic;
using UnityEngine;

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

// 表示单个关卡的数据
[System.Serializable]
public class LevelItem {
    public int id;           // 关卡ID
    public int levelId;      // 等级ID
    public int progressId;   // 进度ID
    public int createTime;   // 创建时间
    public int zombieType;   // 僵尸类型
    public int bornPos;      // 出生位置

    // 自定义的ToString方法,用于返回关键信息的字符串表示
    override
    public string ToString(){
        return "[id]: " + id.ToString();
    }
}

配置和excel表基本一样,然后再去获取这个LevelData 使用即可,使用方法和前面类似,这里就不多介绍了
在这里插入图片描述

源码

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

结束语

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

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1715630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

202474读书笔记|《我自我的田渠归来》——愿你拥有向上的力量,一切的好事都应该有权利发生

202474读书笔记|《我自我的田渠归来》——愿你拥有向上的力量 《我自我的田渠归来》作者张晓风&#xff0c;被称为华语散文温柔的一支笔&#xff0c;她的短文很有味道&#xff0c;角度奇特&#xff0c;温柔慈悲而敏锐。 很幸运遇到了这本书&#xff0c;以她的感受重新认识一些事…

手机拍照扫描成电子版,这三款软件助你轻松搞定!

在数字化时代&#xff0c;将手机拍照的内容快速转换为电子版已经成为许多人日常生活和工作中不可或缺的技能。无论是快速记录文档、合同&#xff0c;还是将纸质照片、笔记转化为电子格式&#xff0c;手机拍照扫描功能都为我们提供了极大的便利。今天&#xff0c;就为大家介绍三…

视频智能分析平台LntonCVS视频安防平台智慧农场应用方案

随着乡村振兴战略的深入实施&#xff0c;数字化发展已经成为农业生产的新方向和突破。在聚焦数字发展、探索数字应用、感受数字赋能的新发展模式下&#xff0c;“数字大田”融合了视频监控技术、5G通信技术、物联网技术和AI智能技术等先进技术&#xff0c;为春季农耕农管提供了…

失落的方舟 命运方舟台服封号严重 游戏封IP怎么办

步入《失落的方舟》&#xff08;Lost Ark&#xff09;&#xff0c;这款由Smilegate精心打造的宏大规模在线角色扮演游戏&#xff08;MMORPG&#xff09;&#xff0c;您将启程前往阿克拉西亚这片饱经沧桑的奇幻大陆&#xff0c;展开一场穿越时空的壮阔探索。在这里&#xff0c;一…

人脸识别——探索戴口罩对人脸识别算法的影响

1. 概述 人脸识别是一种机器学习技术&#xff0c;广泛应用于各种领域&#xff0c;包括出入境管制、电子设备安全登录、社区监控、学校考勤管理、工作场所考勤管理和刑事调查。然而&#xff0c;当 COVID-19 引发全球大流行时&#xff0c;戴口罩就成了日常生活中的必需品。广泛使…

实战解析:爬取音乐每日推荐歌单并自动分享

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、准备阶段 三、实战步骤 四、总结与展望 一、引言 在数字化时代&#xff0c…

揭开神秘的“位移主题”面纱 no.16

Kafka中神秘的内部主题&#xff08;Internal Topic&#xff09;__consumer_offsets。 consumer_offsets在Kafka源码中有个更为正式的名字&#xff0c;叫*位移主题*&#xff0c;即Offsets Topic。为了方便今天的讨论&#xff0c;我将统一使用位移主题来指代consumer_offsets。需…

新疆 | 金石商砼效率革命背后的逻辑

走进标杆企业&#xff0c;感受名企力量&#xff0c;探寻学习优秀企业领先之道。 本期要跟砼行们推介的标杆企业是新疆砼行业的龙头企业&#xff1a;新疆兵团建工金石商品混凝土有限责任公司&#xff08;以下简称&#xff1a;新疆金石&#xff09;。 从年产80万方到120万方&am…

OpenMv图片预处理

本博客讲述的是获取一张图片首先对图像进行处理,比如畸形矫正,图像滤波等操作。 1.histeq()自适应直方图均衡 # 自适应直方图均衡例子 # # 此示例展示了如何使用自适应直方图均衡来改善图像中的对比度。 #自适应直方图均衡将图像分割成区域,然后均衡这些区域中的直方图,…

React基础知识笔记

Reat简介 React&#xff1a;用于构建用户界面的 JavaScript 库。由 Facebook 开发且开源。是一个将视图渲染为html视图的开源库 第一章&#xff1a;React入门 相关js库 react.development.js &#xff1a;React 核心库react-dom.development.js &#xff1a;提供 DOM 操作的…

5月岚庭工人大会“安全就是效率、形象即是品质”

2024年5月18日、19日岚庭一月一期的“产业工人大会”和“工程大会”圆满举行初夏正当时&#xff0c;此次大会主要围绕“安全”与“形象”展开六场专题培训只为精益求精产业工人和装修管家全体到场。 岚庭 以绝对【安全】护家护园 安全就是生命&#xff0c;违章就是事故&#x…

想知道股指期货和期权有什么不同吗?

市场上目前有中金所的沪深300ETF&#xff0c;中证500和中证1000股指期货&#xff0c;期权市场有上证50ETF&#xff0c;沪深300etf和中证500ETF期权&#xff0c;股指期货和期权在买卖双方的权利义务、风险收益特征、保证金制度、上市合约数量等方面均有较大区别&#xff0c;下文…

地市新质生产力最新测算数据集-2005至2024年(基于工作BG)

数据简介&#xff1a;参考肖有智等&#xff08;2024&#xff09;的做法&#xff0c;查找各个地级市年工作BG进行词频分析&#xff0c;加总得到新质生产力总词频数据。数据来源&#xff1a;工作BG 时间范围&#xff1a;2005-2024年 数据范围&#xff1a;各地级市包含指标&…

最新!2023年台湾10米DEM地形瓦片数据

上次更新谷歌倾斜摄影转换生成OSGB瓦片V1.1版本&#xff0c;使用该版本生产了台北、台中、桃园三个地方的倾斜摄影OSGB数据&#xff0c;在OSGB可视化软件中进行展示&#xff0c;可视化效果和加载效率俱佳。已经很久没更新地形瓦片数据&#xff0c;主要是热点地区的原始数据没有…

竹云董事长在第二届ICT技术发展与企业数字化转型高峰论坛作主题演讲

5月25日&#xff0c;由中国服务贸易协会指导&#xff0c;中国服务贸易协会信息技术服务委员会主办的 “第二届ICT技术发展与企业数字化转型高峰论坛” 在北京隆重召开。 本次论坛以 “数据驱动&#xff0c;AI引领&#xff0c;打造新质生产力” 为主题&#xff0c;特邀业内200余…

el-pagination在删除非第一页的最后一条数据遇到的问题

文章目录 前言一、问题展示二、解决方案三、源码解析1、elementui2、elementplus 总结 前言 这个问题是element-ui中的问题&#xff0c;可以从源码中看出来&#xff0c;虽然页码更新了&#xff0c;active也是对的&#xff0c;但是未调用current-change的方法&#xff0c;这里就…

tinymce富文本编辑器使用

安卓富文本编辑器&#xff1a;npm i tinymce/tinymce-vue 当前项目中富文本是放在一个dialog中&#xff0c;因此部分样式会有层叠问题&#xff0c;该组件样式部分不添加scope。这里图片上传只是前端静态数据展示收集。 <template><div class"desc-editor"…

【算法工程师】(三年面试五年模拟版)总结

写在前面&#xff1a; WeThinkIn &#xff08;公主号&#xff09; 学习经验分享 目录 1、机器学习基础 2、深度学习基础 2.1 1*1卷积的作用 注&#xff1a;卷积核的个数对应输出的通道数&#xff08;channels&#xff09;&#xff0c;比如输入6*6*64&#xff0c;卷积核1…

C语言数据结构堆排序、向上调整和向下调整的时间复杂度的计算、TopK问题等的介绍

文章目录 前言一、堆排序1. 排升序&#xff08;1&#xff09;. 建堆&#xff08;2&#xff09;. 排序 2. 拍降序&#xff08;1&#xff09;. 建堆&#xff08;2&#xff09;. 排序 二、建堆时间复杂度的计算1. 向上调整时间复杂度2. 向下调整时间复杂度 三、TopK问题总结 前言 …

【数据结构与算法 | 链表篇】力扣876

1. 力扣876 : 链表的中间节点 (1). 题 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表…