目标

本章主要实现通过 Excel 配置关卡数据, Unity 解析 Excel 生成关卡,实现自动刷新敌人。

Excel 解析工具

在 Unity 中解析 Excel 需要使用 Excel.dll 和 ICSharpCode.SharpZipLib.dll ,这里附上下载链接:Unity 解析Excel工具:Excel.dll 和 ICSharpCode.SharpZipLib.dll

解压后直接放到Plugins文件夹下即可:
在这里插入图片描述

配置文件

用 Excel 写一个配置文件,上面预留两行,左侧预留一列方便后期做填写说明。
在这里插入图片描述

读取配置文件

首先根据配置文件写了两个匹配配置文件的类:LevelConfig、EnemyConfig。代码如下:

LevelConfig.cs

using System.Collections.Generic;

namespace TDGameDemo.GameLevel
{
    public class LevelConfig
    {
        private string _levelCode;
        private int _count;
        private float _cristalHP;
        private List<EnemyConfig> _enemyConfigs;

        public string LevelCode { get => _levelCode; set => _levelCode = value; }
        public int Count { get => _count; set => _count = value; }
        public float CristalHP { get => _cristalHP; set => _cristalHP = value; }
        public List<EnemyConfig> EnemyConfigs { get => _enemyConfigs; set => _enemyConfigs = value; }
    }
}

EnemyConfig.cs

using System.Collections;

namespace TDGameDemo.GameLevel
{
    public class EnemyConfig
    {
        private int _roundNum;
        private string _prefabPath;
        private int _enemyCount;
        private int _GenInterval;
        private float _enemyHP;
        private float _enemyAttack;
        private float _enemySpeed;
        public int RoundNum { get => _roundNum; set => _roundNum = value; }
        public string PrefabPath { get => _prefabPath; set => _prefabPath = value; }
        public int EnemyCount { get => _enemyCount; set => _enemyCount = value; }
        public int GenInterval { get => _GenInterval; set => _GenInterval = value; }
        public float EnemyHP { get => _enemyHP; set => _enemyHP = value; }
        public float EnemyAttack { get => _enemyAttack; set => _enemyAttack = value; }
        public float EnemySpeed { get => _enemySpeed; set => _enemySpeed = value; }
    }
}

然后再创建一个 LevelManager 脚本来读取配置,这个 LevelManager 后期将会实现关卡的加载、开始、结束,以及生成敌人等功能。目前代码如下:

LevelManager.cs

using Excel;
using System.Collections.Generic;
using System.IO;
using TDGameDemo.Enemy;
using TDGameDemo.Game;
using UnityEngine;

namespace TDGameDemo.GameLevel
{
    /// <summary>
    /// 关卡管理器
    /// 加载关卡数据、加载场景、生成敌人
    /// </summary>
    public class LevelManager : MonoBehaviour
    {
        private GameMain _gameMain;
        private EnemyGenerator _enemyGenerator;
        public Transform _generatePoint;
        public Transform _target;

        private LevelConfig _levelConfig;

        private int currRound = 0;

        //private int currEnemyIndex = 0;

        private float lastEnemyGenTim = 50f;

        private int genEnemyCount = 0;

        // Start is called before the first frame update
        void Start()
        {
            _gameMain = GetComponent<GameMain>();
            _enemyGenerator = GetComponent<EnemyGenerator>();
        }

        // Update is called once per frame
        void Update()
        {
            //Debug.Log("99999999999999----------" + Time.deltaTime);
            // 游戏开始,生成敌人
            if (_gameMain.GameStart)
            {
                // 如果当前为第0回合,证明游戏刚开始
                if (currRound == 0)
                {
                    currRound = 1;
                }

                // 累加生成时间
                lastEnemyGenTim += Time.deltaTime;

                // 如果上一个敌人的生成时间大于本轮的生成间隔,则生成新的敌人
                if (_levelConfig.EnemyConfigs[currRound - 1].GenInterval < lastEnemyGenTim)
                {
                    _enemyGenerator.GenerateEnemy(
                        _generatePoint,
                        Level.ENEMY_PREFAB_PREFIX + _levelConfig.EnemyConfigs[currRound - 1].PrefabPath,
                        _target
                    );
                    // 生成数量+1
                    genEnemyCount++;
                    // 生成时间重置
                    lastEnemyGenTim = 0;
                    // 如果生成数量大于等于本轮生成总量,则进行下一轮
                    if (genEnemyCount >= _levelConfig.EnemyConfigs[currRound - 1].EnemyCount)
                    {
                        currRound++;
                        genEnemyCount = 0;
                    }
                }
            }

        }

        public void InitLevel(string configPath)
        {
            // 创建关卡配置对象
            _levelConfig = new LevelConfig();
            // 解析Excel
            FileStream fs = new FileStream(Application.dataPath + configPath, FileMode.Open, FileAccess.Read);
            // 创建Excel读取类
            IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
            // 读取
            int index = 0;
            // 移动到第四行
            for (; index < 4; index++)
            {
                excelReader.Read();
            }
            _levelConfig.LevelCode = excelReader.GetString(1);
            _levelConfig.Count = excelReader.GetInt32(2);
            //Debug.Log(_levelConfig.Count + "======================" + _levelConfig.LevelCode);

            // 跳过空白行和标题行
            excelReader.Read();
            excelReader.Read();
            _levelConfig.EnemyConfigs = new List<EnemyConfig>();
            for (; index <= 4 + _levelConfig.Count; index++)
            {
                excelReader.Read();
                EnemyConfig emConfig = new EnemyConfig();
                emConfig.RoundNum = excelReader.GetInt32(1);
                emConfig.PrefabPath = excelReader.GetString(2);
                emConfig.EnemyCount = excelReader.GetInt32(3);
                emConfig.GenInterval = excelReader.GetInt32(4);
                emConfig.EnemyHP = excelReader.GetFloat(5);
                emConfig.EnemyAttack = excelReader.GetFloat(6);
                emConfig.EnemySpeed = excelReader.GetFloat(7);
                _levelConfig.EnemyConfigs.Add(emConfig);
            }
        }

    }
}

GameMain.cs

using TDGameDemo.GameLevel;
using UnityEngine;

namespace TDGameDemo.Game
{

    /// <summary>
    /// 游戏主程序
    /// 加载关卡、游戏的开始、暂停、通关、失败
    /// </summary>
    public class GameMain : MonoBehaviour
    {
        private LevelManager _levelManager;

        private bool gameStart = false;

        public bool GameStart { get => gameStart; set => gameStart = value; }

        void Start()
        {
            _levelManager = GetComponent<LevelManager>();
            _levelManager.InitLevel("/Resources/Level/LevelConfig/Level_1001.xlsx");
        }

        // Update is called once per frame
        void Update()
        {
            // 按 B 键开始游戏
            if (Input.GetKeyDown(KeyCode.B))
            {
                GameStart = true;
            }
        }
    }
}

附上脚本:
在这里插入图片描述
注意,EnemyGeneratePoint 的位置要放在地图上合理的位置上,不然导航将会报错。

最终效果

由于 GIF 大小有限制,只能少生成几个敌人了。前面三个小的是第一轮的,中间两个大的是第二轮的,后面小的是第三轮的。
在这里插入图片描述


更多内容请查看总目录【Unity】Unity学习笔记目录整理

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐