推荐阅读
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
【GameFramework框架】系列教程目录:
https://blog.csdn.net/q764424567/article/details/135831551
二、正文
2-1、简介
DataTable数据表
是一个表的数据集合,可以理解为DataRow
的字典集合比如Dictionary<int,T> m_DataRow
。
DataTable数据表
是数据集合的访问查询类,主要操作有数据的增删改查
方法(虽然框架提供了删除的接口,但是一般是不需要进行修改或者删除的,读取就好了)。
框架推荐我们使用DataTable数据表
来设置初始数据,比如玩家初始生命值、初始蓝量、初始防御力等,下面就来看一下如何使用吧。
2-2、使用说明
2-2-1、准备数据表及生成实体类
以StarForce项目为例,数据表存放在这个位置:
每一个数据表都对应一个C#,这个C#对应表里面的字段,这样才能使用里面的信息,比如Music数据表
:
对应的C#类在这个目录下:
DRMusic.cs
csharp
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
// 此文件由工具自动生成,请勿直接修改。
// 生成时间:2021-06-16 21:54:35.591
//------------------------------------------------------------
using GameFramework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace StarForce
{
/// <summary>
/// 音乐配置表。
/// </summary>
public class DRMusic : DataRowBase
{
private int m_Id = 0;
/// <summary>
/// 获取音乐编号。
/// </summary>
public override int Id
{
get
{
return m_Id;
}
}
/// <summary>
/// 获取资源名称。
/// </summary>
public string AssetName
{
get;
private set;
}
public override bool ParseDataRow(string dataRowString, object userData)
{
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
for (int i = 0; i < columnStrings.Length; i++)
{
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
}
int index = 0;
index++;
m_Id = int.Parse(columnStrings[index++]);
index++;
AssetName = columnStrings[index++];
GeneratePropertyArray();
return true;
}
public override bool ParseDataRow(byte[] dataRowBytes, int startIndex, int length, object userData)
{
using (MemoryStream memoryStream = new MemoryStream(dataRowBytes, startIndex, length, false))
{
using (BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8))
{
m_Id = binaryReader.Read7BitEncodedInt32();
AssetName = binaryReader.ReadString();
}
}
GeneratePropertyArray();
return true;
}
private void GeneratePropertyArray()
{
}
}
}
大家注意,这个配置表的实体类DRXXX类
是工具生成的,不需要手动修改的,如果需要重新生成,可以点击菜单的Star Force→ Generator DataTables
:
DataTable数据表
读取数据是分成一行一行的,每一行就是一个DataRow,在代码中对应的其实就是一个数据对象,index++是指同一行数据列数增加,一一对应了数据类型。
OK,数据表准备好之后,我们就在框架中加载配置。
2-2-2、ProcedurePreloa预加载
新建脚本,命名为Test04.cs,编辑代码:
csharp
using StarForce;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using UnityEngine;
using UnityGameFramework.Runtime;
public class Test04 : MonoBehaviour
{
DataTableComponent DataTable;
string[] DataTableNames;
void Start()
{
DataTable = UnityGameFramework.Runtime.GameEntry.GetComponent<DataTableComponent>();
DataTableNames = new string[2] { "Scene", "Music" };
// 预加载 data tables数据表
foreach (string dataTableName in DataTableNames)
{
LoadDataTable(dataTableName);
}
}
/// <summary>
/// 加载表数据
/// </summary>
/// <param name="dataTableName"></param>
private void LoadDataTable(string dataTableName)
{
string dataTableAssetName = AssetUtility.GetDataTableAsset(dataTableName, false);
// 表名、表名资源路径、用户自定义数据
DataTable.LoadDataTable(dataTableName, dataTableAssetName, this);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
QueryData();
}
}
/// <summary>
/// 查询表数据
/// </summary>
private void QueryData()
{
//获取指定表数据列表
var drScene = DataTable.GetDataTable<DRScene>();
//根据行号查找数据
DRScene scene1 = drScene.GetDataRow(1);
Debug.Log("获取第一行数据的ID值:"+scene1.Id);
//根据指定条件,比如按id查找,按名字查找
DRScene scene2 = drScene.GetDataRow(x => x.Id == 1);
DRScene scene3 = drScene.GetDataRow(x => x.AssetName == "Menu");
Debug.Log("获取id等于1的场景名:"+scene2.AssetName);
Debug.Log("获取场景名等于Menu的id值:" + scene3.Id);
//根据条件查找多条数据
DRScene[] scene4 = drScene.GetDataRows(x => x.Id > 0);
for (int i = 0; i < scene4.Length; i++)
{
Debug.Log("获取id大于0的背景音乐编号:" + scene4[i].BackgroundMusicId);
}
}
}
运行结果:
对应的Scene.txt数据表:
PS:这里补充说明一下,加载表数据用了StarForce项目的拓展函数,方便加载,所以需要引用StarForce命名空间,推荐大家下载StarForce项目来学习这个框架。
2-3、实现及代码分析
接下来,就分析一下这个数据表DataTable
是如何加载及代码分析。
首先,用流程图说明一下:
Created with Raphaël 2.3.0 开始 创建数据节点表 编写数据表对应的cs文件 加载数据表 使用数据表
(1)第一步,创建数据节点表
数据表需要严格按照它这个格式来,行是字段,列是每条数据:
这样看,不太清晰,我们用Excel看一下:
说到这,有一点想要说明一下,因为这些数据表一般是策划来修改,然后导入到我们项目中的。
而策划一般使用Excel表,而程序需要csv、xml、Json、txt格式,所以就需要一个Excel导出这个文件的工具,这个工具我们后续也会进行拓展开发,框架没有带这些,但是框架提供了接口,也是很方便的。
(2)第二步,编写数据表对应的CS文件
这个是工具自动生成的,点击菜单的Star Force→Generate DataTables
,来看一下代码如何实现的:
DataTableGenerator.cs
脚本,获取到表里面的行的数据保存到字符序列中,然后保存到文件中:
(3)第三步,加载数据表
加载数据表,就是用的StarForce项目的拓展函数:
csharp
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFramework.DataTable;
using System;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace StarForce
{
public static class DataTableExtension
{
private const string DataRowClassPrefixName = "StarForce.DR";
internal static readonly char[] DataSplitSeparators = new char[] { '\t' };
internal static readonly char[] DataTrimSeparators = new char[] { '\"' };
public static void LoadDataTable(this DataTableComponent dataTableComponent, string dataTableName, string dataTableAssetName, object userData)
{
if (string.IsNullOrEmpty(dataTableName))
{
Log.Warning("Data table name is invalid.");
return;
}
string[] splitedNames = dataTableName.Split('_');
if (splitedNames.Length > 2)
{
Log.Warning("Data table name is invalid.");
return;
}
string dataRowClassName = DataRowClassPrefixName + splitedNames[0];
Type dataRowType = Type.GetType(dataRowClassName);
if (dataRowType == null)
{
Log.Warning("Can not get data row type with class name '{0}'.", dataRowClassName);
return;
}
string name = splitedNames.Length > 1 ? splitedNames[1] : null;
DataTableBase dataTable = dataTableComponent.CreateDataTable(dataRowType, name);
dataTable.ReadData(dataTableAssetName, Constant.AssetPriority.DataTableAsset, userData);
}
public static Color32 ParseColor32(string value)
{
string[] splitedValue = value.Split(',');
return new Color32(byte.Parse(splitedValue[0]), byte.Parse(splitedValue[1]), byte.Parse(splitedValue[2]), byte.Parse(splitedValue[3]));
}
public static Color ParseColor(string value)
{
string[] splitedValue = value.Split(',');
return new Color(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]), float.Parse(splitedValue[2]), float.Parse(splitedValue[3]));
}
public static Quaternion ParseQuaternion(string value)
{
string[] splitedValue = value.Split(',');
return new Quaternion(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]), float.Parse(splitedValue[2]), float.Parse(splitedValue[3]));
}
public static Rect ParseRect(string value)
{
string[] splitedValue = value.Split(',');
return new Rect(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]), float.Parse(splitedValue[2]), float.Parse(splitedValue[3]));
}
public static Vector2 ParseVector2(string value)
{
string[] splitedValue = value.Split(',');
return new Vector2(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]));
}
public static Vector3 ParseVector3(string value)
{
string[] splitedValue = value.Split(',');
return new Vector3(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]), float.Parse(splitedValue[2]));
}
public static Vector4 ParseVector4(string value)
{
string[] splitedValue = value.Split(',');
return new Vector4(float.Parse(splitedValue[0]), float.Parse(splitedValue[1]), float.Parse(splitedValue[2]), float.Parse(splitedValue[3]));
}
}
}
StarForce
的拓展类DataTableExtension
中定义LoadDataTable(ddataTableName,dataTableAssetName,this)
函数通过名字获取资源路径并执行ReadData()
函数。
通过DataProvie<DataTableBase>
读取并解析数据:
csharp
namespace GameFramework.DataTable
{
private readonly DataProvider<DataTableBase> m_DataProvider;
public DataTableBase(string name)
{
//dataProvider的初始化
m_DataProvider = new DataProvider<DataTableBase>(this);
}
/// 数据表基类。
public abstract class DataTableBase : IDataProvider<DataTableBase>
{
public void ReadData(string dataTableAssetName, int priority, object userData)
{
m_DataProvider.ReadData(dataTableAssetName, priority, userData);
}
}
}
通过m_ResourceManager
来加载数据,然后通过m_DataProviderHelper
加载数据:
csharp
/// 配置表属于AssetOnFileSystem,调用ResourceManager资源管理器,执行读取数据。
public void ReadData(string dataAssetName, int priority, object userData)
{
HasAssetResult result = m_ResourceManager.HasAsset(dataAssetName);
switch (result)
{
case HasAssetResult.AssetOnFileSystem:
m_ResourceManager.LoadAsset(dataAssetName, priority, m_LoadAssetCallbacks, userData);
break;
default:
throw new GameFrameworkException(Utility.Text.Format("Data asset '{0}' is '{1}'.", dataAssetName, result.ToString()));
}
}
///加载成功的回调,使用数据辅助器进行读取并解析数据
private void LoadAssetSuccessCallback(string dataAssetName, object dataAsset, float duration, object userData)
{
if (!m_DataProviderHelper.ReadData(m_Owner, dataAssetName, dataAsset, userData))
{
throw GameFrameworkException
}
}
(4)第四步,使用数据表
使用数据辅助类来读取数据,读取出来txt/bytes流,最后通过数据辅助类来解析数据:
csharp
public class DefaultDataTableHelper : DataTableHelperBase
{
/// 读取数据表。
public override bool ReadData(DataTableBase dataTable, string dataTableAssetName, object dataTableAsset, object userData)
{
TextAsset dataTableTextAsset = dataTableAsset as TextAsset;
if (dataTableTextAsset != null)
{
return dataTable.ParseData(dataTableTextAsset.text, userData);
}
return false;
}
/// 解析数据表。
public override bool ParseData(DataTableBase dataTable, string dataTableString, object userData)
{
int position = 0;
string dataRowString = null;
while ((dataRowString = dataTableString.ReadLine(ref position)) != null)
{
if (dataRowString[0] == '#') continue;
dataTable.AddDataRow(dataRowString, userData)
}
return true;
}
}
每个DR类都有ParseDataRow()
函数来解析自己的数据格式,读取完毕,得到一个数据实体对象,保存在数据集合中。
三、后记
如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。
你的点赞就是对博主的支持,有问题记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
专栏 | 方向 | 简介 |
---|---|---|
Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |