IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 在Unity中使用Excel表开发单选题和多选题 -> 正文阅读

[游戏开发]在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

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-28 11:36:17  更:2021-11-28 11:37:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 0:46:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码