本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (一)

上个帖子主要是讲了如何读取Excel,本帖主要是讲述读取的Excel数据是如何序列化成二进制的,考虑到现在在手游中应用很广泛的序列化技术Google的ProtoBuf,所以本文也是按照ProtoBuf的方式来操作的。ProtoBuf是一个开源库,简单来说ProtoBuf就是一个能使序列化的数据变得更小的类库,当然这里指的更小是相对的。好了ProtBuf的东西就不在多说,以后会专门写一篇帖子的。本帖其实就相当于上帖中列出的“流程”中的2.将读取的数据进行序列化并写入文件。虽然这句话只有16个字,但是他的内容绝对不止16个字这么简单。

一开始计划的是这样的:1.先把Excel数据读出来。2.然后根据数据生成相应的类代码。3.然后再生成代码的实例并且通过反射(Reflection)把Excel中的数据填进去。4.最后用ProtBuf进行序列化。这听起来似乎很合理,不过在实现过程中还是遇到了几个棘手的问题。但是最重要的一个问题是,3.生成代码的实例并且通过反射(Reflection)把Excel中的数据填进去,中“生成实例”的过程。

在运行时生成的代码知识一串字符而已,想要生成字符串中的类的实例需要用到动态编译把代码编译后生成一个Assembly,然后用Assembly中的CreateInstance方法创建一个object类型的实例,这个实例就是字符串代码中的类的实例。但是,我们使用了ProtoBuf数据的格式来生成代码,所以工程中直接把ProtBuf的源码放进来而不是引入protobuf-net.dll,所以在动态编译设置编译参数来动态引入DLL文件的时候经常出错而导致,编译生成的Assembly是null。所以考虑到这些本文使用了一种折中的方案来跳过动态编译这个过程。

因为在Unity项目中我们关注的只是最终生成的二进制文件本身所以对于它是如何生成的对于Unity项目来说是没有任何影响的,只要保证数据的格式和生成代码的格式相对应一切就OK。基于此,我们把这个过程分成了两部分,1.生成代码。2.生成二进制文件(其实就是把编译的工作交给了编译器。。。)OK,话不多说了,帖代码吧!

1.生成代码

    public static void GenerateCode()
{
FileInfo info;
FileStream stream;
IExcelDataReader excelReader;
DataSet result;
string[] files = Directory.GetFiles(Application.dataPath + "/EasyUI/ExcelFiles", "*.xlsx", SearchOption.TopDirectoryOnly); string staticDataClassCode = ""; try
{
int priority1 = ;
string code;
foreach (string path in files)
{
info = new FileInfo(path);
stream = info.Open(FileMode.Open, FileAccess.Read);
excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
result = excelReader.AsDataSet();
int rowCount = result.Tables[].Rows.Count;
int colCount = result.Tables[].Columns.Count;
string className = result.Tables[].Rows[][].ToString(); staticDataClassCode += " [ProtoMember(" + priority1++ + ")]\n";
staticDataClassCode += " public List<" + className + "> " + className + "List = new List<" + className + ">();\n"; code = "";
code += "using System.Collections;\n";
code += "using System.Collections.Generic;\n";
code += "using ProtoBuf;\n";
code += "[ProtoContract]\n";
code += "public class " + className + "\n";
code += "{\n";
int priority2 = ;
for (int col = ; col < colCount; col++)
{
code += " [ProtoMember(" + priority2++ + ")]\n";
code += " public " + result.Tables[].Rows[][col].ToString() + " " + result.Tables[].Rows[][col].ToString() + ";\n";
}
code += " public " + className + "()\n";
code += " {}\n";
code += "}\n";
WriteClass(Application.dataPath + "/Script/Datas/" + className + ".cs", className, code); excelReader.Close();
stream.Close();
}
code = "";
code += "using System.Collections;\n";
code += "using System.Collections.Generic;\n";
code += "using ProtoBuf;\n";
code += "[ProtoContract]\n";
code += "public class StaticData\n";
code += "{\n";
code += staticDataClassCode;
code += " public StaticData(){}\n";
code += "}\n";
WriteClass(Application.dataPath + "/Script/Datas/StaticData.cs", "StaticData", code);
}
catch (IndexOutOfRangeException exp)
{
Debug.LogError(exp.StackTrace);
}
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
}

2..生成二进制文件

    public static void GenerateBinFile()
{
FileInfo info;
FileStream stream;
IExcelDataReader excelReader;
DataSet result;
string[] files = Directory.GetFiles(Application.dataPath + "/EasyUI/ExcelFiles", "*.xlsx", SearchOption.TopDirectoryOnly);
int row = , col = ;
string name = "", value = "", type = "";
StaticData staticData = new StaticData(); foreach (string path in files)
{
info = new FileInfo(path);
stream = info.Open(FileMode.Open, FileAccess.Read);
excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
result = excelReader.AsDataSet();
int rowCount = result.Tables[].Rows.Count;
int colCount = result.Tables[].Columns.Count; string className = result.Tables[].Rows[][].ToString();
FieldInfo field = staticData.GetType().GetField(className + "List");//获取类中的一个Field
object fieldValue = field.GetValue(staticData);//给这个实例中的Field的代表的属性赋值
IList list = fieldValue as IList; for (row = ; row < rowCount; row++)
{
Type tt = DataManager.Instance.GetType(className);
object obj = System.Activator.CreateInstance(tt); for (col = ; col < colCount; col++)
{
name = result.Tables[].Rows[][col].ToString();
value = result.Tables[].Rows[row][col].ToString();
type = result.Tables[].Rows[][col].ToString(); Debug.Log(className);
FieldInfo ifo = tt.GetField(name);
object cvalue = System.ComponentModel.TypeDescriptor.GetConverter(ifo.FieldType).ConvertFrom(value);
ifo.SetValue(obj, cvalue);
}
if (list != null)
list.Add(obj);
}
excelReader.Close();
stream.Close();
} using (FileStream fstream = File.Create(Application.dataPath + "/StaticDatas.bin"))
{
Serializer.Serialize(fstream, staticData);
}
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
}

说明一下DataManager.Instance.GetType(className);

Type.GetType(string)会默认从当前程序及搜索类型,而我的类文件是放到了Unity的Editor文件下,Unity会把这个文件识别为属于Editor程序集,但是生成的代码是放到了其他文件夹下Unity会把文件识别为另一个程序集,这个DataManager一定要放到和生成的类文件的相同程序集中。代码如下

using UnityEngine;
using System.Collections;
using System; public class DataManager { private static DataManager _instance;
public static DataManager Instance
{
get
{
if (_instance == null)
_instance = new DataManager();
return _instance;
}
}
public DataManager()
{
_instance = this;
}
public Type GetType(string name)
{
return Type.GetType(name);
}
}

将代码写入文件的方法

    private static void WriteClass(string path,string className,string code)
{
System.IO.File.WriteAllText(path, code, System.Text.UnicodeEncoding.UTF8);
}

把动态编译代码的方法也贴出来吧,方便以后用到

    private static Assembly compileCode(string fullClassName, string code)
{
//创建编译器
CSharpCodeProvider provider = new CSharpCodeProvider();
//设置编译参数
CompilerParameters paras = new CompilerParameters();
//paras.ReferencedAssemblies.Add("System.Collections.dll");
//paras.ReferencedAssemblies.Add("System.Collections.Generic.dll");
paras.ReferencedAssemblies.Add("System.dll");
paras.ReferencedAssemblies.Add("protobuf-net.dll");
//paras.OutputAssembly = "ScriptData.dll";
//编译成exe还是dll
paras.GenerateExecutable = false;
//是否写入内存,不写入内存就写入磁盘
paras.GenerateInMemory = true;
CompilerResults result = provider.CompileAssemblyFromSource(paras, code);//编译代码
Assembly as1 = result.CompiledAssembly; //获得编译后的程序集
return as1;
}

OK,文件已经生成了,经测试两个10K的Excel文件打包成一个StaticData.bin文件,1K!!!

不过一般游戏中的数据表无论是内容还是数量都远远大于这个数量。所以一般生成的bin文件还要进行压缩,或者用Unity打包,哦了,就先到这把剩下的内容下一帖继续。

http://m.oschina.net/blog/308267
http://dsqiu.iteye.com/blog/1887702
http://www.cnblogs.com/zfanlong1314/p/4197383.html
http://stackoverflow.com/questions/2522376/how-to-choose-between-protobuf-csharp-port-and-protobuf-net

本文固定连接:http://www.cnblogs.com/fly-100/p/4541211.html

C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二)的更多相关文章

  1. C# Unity游戏开发——Excel中的数据是如何到游戏中的 (三)

    本帖是延续的:C# Unity游戏开发——Excel中的数据是如何到游戏中的 (二) 前几天有点事情所以没有继续更新,今天我们接着说.上个帖子中我们看到已经把Excel数据生成了.bin的文件,不过其 ...

  2. C# Unity游戏开发——Excel中的数据是如何到游戏中的 (四)2018.4.3更新

    本帖是延续的:C# Unity游戏开发--Excel中的数据是如何到游戏中的 (三) 最近项目不算太忙,终于有时间更新博客了.关于数据处理这个主题前面的(一)(二)(三)基本上算是一个完整的静态数据处 ...

  3. 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!

    [COCOS2DX-LUA 脚本开发之一]在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途! 分类: [Cocos2dx Lua 脚本开发 ] 2012-04-1 ...

  4. excel中的数据粘贴不全到plsql中,excel 粘贴后空白,Excel复制粘贴内容不全

    http://zhidao.baidu.com/link?url=pHZQvfWJzI-lQjl4uP86q4GLcpYHu4o-fdjiYegJS0Cy5HEq5oz0YrUye3iHjmv5CJ3 ...

  5. [置顶] cocos2d-x 3.0游戏开发xcode5帅印博客教学 003.[HoldTail]游戏世界以及背景画面

    cocos2d-x 3.0游戏开发xcode5帅印博客教学 003.[HoldTail]游戏世界以及背景画面 写给大家的前言,在学习cocos2d-x的时候自己走了很多的弯路,也遇到了很多很多问题,不 ...

  6. hbase使用MapReduce操作4(实现将 HDFS 中的数据写入到 HBase 表中)

    实现将 HDFS 中的数据写入到 HBase 表中 Runner类 package com.yjsj.hbase_mr2; import com.yjsj.hbase_mr2.ReadFruitFro ...

  7. sql之将一个表中的数据注入另一个表中

    sql之将一个表中的数据注入另一个表中 需求:现有两张表t1,t2,现需要将t2的数据通过XZQHBM相同对应放入t1表中 t1: t2: 思路:left join 语句: select * from ...

  8. 使用spark将内存中的数据写入到hive表中

    使用spark将内存中的数据写入到hive表中 hive-site.xml <?xml version="1.0" encoding="UTF-8" st ...

  9. SQL语句的使用,SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中的数据 DELETE - 从数据库表中删除数据 INSERT INTO - 向数据库表中插入数据

    SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法. 但是 SQL 语言也包含用于更新. ...

随机推荐

  1. MySQL中文乱码修改

    一.从服务端进行修改 show variables like "%char%"; 然后可能显示如下信息,注意红色部分,不同的用户可能实际情况不同,但是需要保证除了 filesyst ...

  2. Init.rc分析(刘举奎)

    http://www.360doc.com/content/14/0926/20/13253385_412582822.shtml

  3. Swift中自定义打印方法

    // 1.获取打印所在的文件 let file = ( #file as NSString).lastPathComponent // 2.获取打印所在的方法 let funcName = #func ...

  4. SQL复习三(子查询)

    子查询 子查询就是嵌套查询,即select中包含这select,如果一条语句中存在着两个,或者两个以上的select,那么就是子查询语句了. 子查询出现的位置 where后,作为条件的一部分: fro ...

  5. mahout第一篇-----Mahout学习路线图

    Mahout学习路线图 前言 Mahout是Hadoop家族中与众不同的一个成员,是基于一个Hadoop的机器学习和数据挖掘的分布式计算框架.Mahout是一个跨学科产品,同时也是我认为Hadoop家 ...

  6. CI如何在子目录下可以设置默认控制器

    CI建立大型大型的应用程序,需要创建子文件夹在application/controllers下建立文件夹app1app1目录下有多个控制器,ca.php,cb.php我希望定义app1下的默认控制器, ...

  7. 路过Haxe

    刚才在看Nape的时候,看到Haxe的代码,意外的感觉到亲切. 因为之前写过as2代码,最近学习了python,所以对haxe看起来很亲切,于是路过一下写了个HelloWorld. 另外,估计很长时间 ...

  8. [DNS]ACL、also-notify、视图服务器及安全设置

    1. ACL :访问控制列表放在named.conf的头部,如果acl的内容太多,可以另创建一个文件,将acl放在该文件中,再在主配置文件named.conf用include 将文件加载进来(记得放在 ...

  9. lvs(+keepalived)、haproxy(+heartbeat)、nginx 负载均衡的比较分析

    目前使用比较多的就是标题中提到的这两者,其实lvs和haproxy都是实现的负载均衡的作用,keepalived和heartbeat都是提高高可用性的,避免单点故障.那么他们为什么这么搭配,而又有什么 ...

  10. (简单) HDU 5154 Harry and Magical Computer,图论。

    Description In reward of being yearly outstanding magic student, Harry gets a magical computer. When ...