在Unity中使用Excel表开发单选题和多选题

前言:

去年还是小菜鸡的我写过在Unity中单选题和多选题的开发。现在进步一点点,这次可以直接编辑表格,在表格中增删改查数据即可,无需再对代码进行更改!
下载链接在文章末尾,需要的可以直接划到最后!

废话不多说,开始~

首先需要配置三个文件

  1. 读取表格的程序集:EPPlus
  2. 处理Json数据的程序集:Newtonsoft.Json
  3. 表格文件:question.xlsx

大概流程如下

创建StreamingAssets文件

  1. 首先我们在工程文件Assets文件下创建一个StreamingAssets(这里我们默认使用此路径为加载路径)在这里插入图片描述

  2. 我们在此文件夹下创建一个表格。这里我使用的是.xlsx在这里插入图片描述
    下面是表结构,大家如果要修改的话,记得同时修改Question.cs哦!
    在这里插入图片描述

创建Plugins文件

  1. 在Assets文件下创建一个名为Plugins文件夹
    在这里插入图片描述

  2. 将刚才上面说了两个程序集EPPlus和Newtonsoft.Json放入此文件下在这里插入图片描述

做完上面那些就可以导入我给大伙准备的脚本了

这里一共有三个脚本:

  • ExcelMgr.cs
  • Question.cs
  • UIQuesPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OfficeOpenXml;
using System.IO;
using System.Data;
using System;
using Newtonsoft.Json.Linq;
using DialogEntity;

namespace vvb_ExcelMgr
{
    
    
    public class ExcelMgr : MonoBehaviour
    {
    
    
        static ExcelPackage package;//表文件缓存
        public static ExcelWorkbook dialogWorkbook;//表工作簿
        public List<Question> questionList = new List<Question>();//题目缓存列表
        public string quesPackPath="question";//题目文件地址,文件后缀默认为".xlsx"
        private void Awake()
        {
    
    
            ReadExcel(quesPackPath, () =>
            {
    
    
                UIQuesPanel.quesList = GetQuesList(package);
                questionList = UIQuesPanel.quesList;//这里为了在inspecter中能看到读取到达数据
            });
        }
        /// <summary>
        /// 打开表缓存
        /// </summary>
        public static ExcelWorkbook ReadExcel(string excelPath, Action action)
        {
    
    
            //Debug.Log(Application.streamingAssetsPath);
            excelPath = Application.streamingAssetsPath + "/"+excelPath+".xlsx";
            try
            {
    
    
                using (package = new ExcelPackage(new FileStream(excelPath, FileMode.Open)))
                {
    
    
                    action?.Invoke();
                    return package.Workbook;
                }
            }
            catch (NullReferenceException e)
            {
    
    
                Debug.LogError("空指针异常:" + e);
                return null;
            }
            catch (IOException e)
            {
    
    
                Debug.LogError("文件打开异常:" + e);
                return null;
            }
            catch (Exception e)
            {
    
    
                Debug.LogError("其他异常:" + e);
                return null;
            }
        }
        public List<Question> GetQuesList(ExcelPackage excelPackage)
        {
    
    
            List<Question> quesList = new List<Question>();
            int dataStartRow = 0;
            if (excelPackage.Workbook.Worksheets.Count < 0)
            {
    
    
                Debug.LogError("空表");
                return null;
            }
            ExcelWorksheet sheet = excelPackage.Workbook.Worksheets[1];
            for (int startRow = sheet.Dimension.Start.Row, endRow = sheet.Dimension.End.Row; startRow <= endRow; startRow++)
            {
    
    
                if (sheet.GetValue(startRow, 1).ToString().Equals("quesId"))//此行开始才是咱们真正需要的数据,当然在开发过程中这里是不需要的
                {
    
    
                    dataStartRow = startRow;
                    break;
                }
                //Debug.Log(sheet.GetValue(startRow, 1).ToString());
                //Debug.Log(sheet.GetValue(2, 1).ToString());
            }
            for (int startRow = dataStartRow+1, endRow = sheet.Dimension.End.Row; startRow <= endRow; startRow++)
            {
    
    
                JObject question = new JObject();//每一行实例化一个对象,用来存储到题目列表中
                for (int startColumn = sheet.Dimension.Start.Column, endColumn = sheet.Dimension.End.Column; startColumn <= endColumn; startColumn++)
                {
    
    
                    //这里要注意做一步特殊处理,因为当读取到选项内容那一行时,类型为数组,所以我们需要特殊处理一下
                    if (startColumn == 3)//在表中我们将选项内容放在C列,也就是第三项。或者各位也可以使用其他的方式判定
                    {
    
    
                        JArray options = new JArray();
                        string[] opsStr = sheet.GetValue(startRow, startColumn).ToString().Split(';');//这里先获取此处的字符串,然后再使用我们自定的符号切割以获得选项数组
                        options.Add(opsStr);
                        question.Add(sheet.GetValue(dataStartRow, startColumn).ToString(), options);
                    }
                    else
                    {
    
    
                        question.Add(sheet.GetValue(dataStartRow, startColumn).ToString(), sheet.GetValue(startRow, startColumn).ToString());
                    }
                }
                quesList.Add(JsonUtility.FromJson<Question>(question.ToString()));//转成Question对象添加入列表中
                Debug.Log(question);
            }
            return quesList;
        }
    }
}
using System;
[Serializable]
public class Question
{
    
    
    public int quesId;//当前题号
    public string tittleStr;//题目内容
    public string[] optionsStr;//选项内容
    public int quesType;//题目类型
    public string ans;//正确答案
    public string analysis;//解析
    public int nextQuesId;//下一题号
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIQuesPanel : MonoBehaviour
{
    
    
    //为了方便,所有变量均拖拽赋值。
    public static List<Question> quesList = new List<Question>();
    public Text tittleTxt;//题目内容
    public RectTransform optionsRoot;//选项生成根节点
    public List<Toggle> optionTogList;//选项
    public ToggleGroup toggleGroup;//根节点togglegroup组件
    public GameObject togPrefab;//选项预制体
    public Text analysisTxt;//解析文本
    public Button lastBtn;//上一题按钮
    public Button nextBtn;//下一题按钮
    public Button confirmBtn;//确认作答按钮
    public int curQuesIndex = -1;//当前题索引
    private void Start()
    {
    
    
        _UIQuesPanelInit();
    }
    /// <summary>
    /// 上一题
    /// </summary>
    public void OnLastBtnClick()
    {
    
    
        curQuesIndex--;
        if (curQuesIndex <0)
        {
    
    
            Debug.LogWarning("前面没有了!");
            curQuesIndex = 0;
            return;
        }
        _UpdateQues();
    }
    /// <summary>
    /// 下一题
    /// </summary>
    public void OnNextBtnClick()//这里加载下一题的方式也可以通过表中的字段nextQuesId,上一题同理(表中暂未设计)。
    {
    
    
        curQuesIndex++;
        if (curQuesIndex>quesList.Count-1)
        {
    
    
            Debug.LogWarning("后面没有了!");
            curQuesIndex = quesList.Count-1;
            return;
        }
        _UpdateQues();
    }
    /// <summary>
    /// 刷新题目
    /// </summary>
    private void _UpdateQues()
    {
    
    
        analysisTxt.gameObject.SetActive(false);
        tittleTxt.text = (curQuesIndex + 1).ToString() + "." + quesList[curQuesIndex].tittleStr;
        int optionsCount = optionsRoot.childCount;
        optionTogList.Clear();
        while (optionsCount > 0)
        {
    
    
            optionsCount--;
            Destroy(optionsRoot.GetChild(optionsCount).gameObject);
        }
        for (int i = 0; i < quesList[curQuesIndex].optionsStr.Length; i++)//根据选项个数创建相应的toggle数量
        {
    
    
            optionTogList.Add(Instantiate(togPrefab, optionsRoot).GetComponent<Toggle>());
            optionTogList[i].isOn = false;
            if (quesList[curQuesIndex].quesType == 0)
            {
    
    
                optionTogList[i].group = toggleGroup;
            }
            optionTogList[i].GetComponentInChildren<Text>().text = quesList[curQuesIndex].optionsStr[i];
        }
    }
    /// <summary>
    /// 确认作答
    /// </summary>
    public void OnConfirmBtnClick()
    {
    
    
        if (curQuesIndex<0||curQuesIndex>quesList.Count-1)
        {
    
    
            Debug.LogError("数组越界");
            return;
        }
        string selected=null;//用户选择
        analysisTxt.text = selected;
        analysisTxt.gameObject.SetActive(true);
        switch (quesList[curQuesIndex].quesType)
        {
    
    
            case 0://单选
                for (int i = 0; i < optionTogList.Count; i++)
                {
    
    
                    if (optionTogList[i].isOn)
                    {
    
    
                        selected = optionTogList[i].GetComponentInChildren<Text>().text[0].ToString();//默认第一个字符代表此选项,并与答案进行对比。这里正误判定也可以自己定义
                        Debug.Log("选择的答案为:" + selected);
                        break;
                    }
                }
                break;
            case 1://多选
                for (int i = 0; i < optionTogList.Count; i++)
                {
    
    
                    if (optionTogList[i].isOn)
                    {
    
    
                        selected += optionTogList[i].GetComponentInChildren<Text>().text[0].ToString();
                    }
                }
                break;
            default:
                break;
        }
        if (string.IsNullOrEmpty(selected))//没作答情况。具体可以自定义,比如你弹出一个面板提示什么的,强制答完才行。
        {
    
    
            selected = "未作答";
            Debug.LogError("请先选择选项!");
        }
        if (selected.Equals(quesList[curQuesIndex].ans))//与正确答案做比较
        {
    
    
            Debug.Log("正确");
            analysisTxt.text = "<color=green>回答正确</color>   ";
        }
        else
        {
    
    
            Debug.Log("错误");
            analysisTxt.text = "<color=red>回答错误</color>   ";
        }
        analysisTxt.text += "你的答案:" + selected + "   正确答案:" + quesList[curQuesIndex].ans+"\n解析:" + quesList[curQuesIndex].analysis;
    }
    /// <summary>
    /// 面板初始化
    /// </summary>
    private void _UIQuesPanelInit()
    {
    
    
    	//这里大伙儿可以自己默认调用一次“下一题”按钮的事件来默认更新第一道题。我就不写了
    	
        analysisTxt.gameObject.SetActive(false);
        if (optionsRoot.gameObject.GetComponent<ToggleGroup>())
        {
    
    
            toggleGroup = optionsRoot.gameObject.GetComponent<ToggleGroup>();
        }
        else
        {
    
    
            toggleGroup = optionsRoot.gameObject.AddComponent<ToggleGroup>();
        }
        toggleGroup.allowSwitchOff = true;
    }
}

具体的过程我就不详述了,大伙儿看代码自行体会!

有何疑问欢迎留言讨论!需要源码的可以评论区留言或者私聊。

下面是源工程的下载地址:
链接:https://pan.baidu.com/s/1hrmtq9PGJlDg4Ya_YO6FeQ
提取码:grnh

================================ 2023/04/18新增 ===============================
我已经将资源上传至CSDN了,各位需要的可以自行下载,放心进,我设置了0积分,纯免费下载。
创作不易,我就一个要求,如果对你有帮助的话,请给我点个赞,谢谢。下载地址

猜你喜欢

转载自blog.csdn.net/weixin_44870508/article/details/121562464