各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://blog.csdn.net/qinyuanpei。今天我们来聊聊常用办公软件Excel和游戏开发那不为人知的秘密。今天的内容将涉及到Excel在游戏开发中的应用以及如何利用程序解析Excel中的数据。

  作为常用的办公软件的Excel相信大家都不陌生啦。可是如果我们认为Excel只是办公软件的话,那么这就不只是天真而是Out了。事实上Excel和游戏开发有着密切的联系,不知道大家还记不记得那款利用Excel开发出来的三国杀,这可能是Excel第一次以游戏开发的身份出现在大家面前吧。我们知道在游戏开发领域有一种工作叫做策划,就像在软件开发领域有一种工作叫做产品经理一样。而在诸多的策划工作中,数值策划是一个可以直接影响游戏进程的工作,因为数值策划体现了一个游戏在整体数值上的平衡,设计者需要维护好这样一个平衡,确保游戏外的玩家和游戏里的敌人面对的是同一个公平的虚拟世界。

  例如《仙剑奇侠传四》这款游戏中,韩菱纱在游戏后期的速度可以说是完全打破了游戏的平衡性,因为韩菱纱本身的速度就比较快,再加上仙风云体术的加速效果完全对玄霄产生了戏剧性压制,导致在游戏结尾的Boss战中经常是韩菱纱出手N次后才挨到玄霄出手,我们知道韩菱纱的乾坤一掷每次消耗气15,可是因为韩菱纱的速度足够快,所以韩菱纱完全可以通过普通物理攻击快速地积满气进而施展乾坤一掷,这就是游戏的平衡性被打破了呀,更不要说这部游戏里最为经典的千方残光剑Bug了,这同样是游戏平衡性的问题,归根到底是紫英的这个技能在配置数据时出现了错误,这充分说明数据的正确合理与否是会对游戏产生重要影响的。
慕容紫英千方残光剑
韩菱纱乾坤一掷
尽管我们可以使用Xml、Json、ini、数据库等存储形式来存储这些数据,可是毫无疑问的是,Excel是Window平台上最好的数据处理软件,因此数值策划更倾向于使用Excel来设计游戏中的数据,面对如此重要的数值策划工作,我们自然希望在解析Excel文件时不会出现错误,可是我们总不能指望着策划把Excel数据转换成我们能处理的数据类型吧,因此就有了博主今天的这篇文章,所以在今天的文章中我们主要的内容就是如何通过程序来解析Excel文件。

###1、项目需求
  最近博主一个朋友向我抱怨,说手头上有好几百个Excel工作表要处理,大概几十万条数据吧。原因是当时公司分配任务时交待不清,等到了向公司交接数据的时候,朋友忽然发现这些Excel文件的表格格式和公司规定的不一样啊。这可急坏了博主的这位朋友,博主的朋友只好不断地的复制、黏贴,因为这些数据是分布在不同的数据表里,朋友整天都忙得焦头烂额,可是即使这样效率还是得不到保证啊,朋友最后找到了博主这里,问我能不能编写程序帮他解决这个问题。因为平时经常与技术圈子里的朋友聊天,所以在博主印象里Excel的解析在游戏开发中还是较为常见的,而且博主知道对于微软的Office办公软件是可以通过VBA编程来实现某些功能的,可是因为博主一直在用国产的WPS,所以对于Excel的解析基本上是停留在一个概念性的认识上,可是朋友的忙不能不帮不是,所以博主决定借着这个机会好好研究下Excel文件的解析。

###2、解决方案
  因为博主在之前并没有过解析Excel文件的经历,所以博主就到Github上淘了些开源项目。和很多人爱逛天猫、淘宝的经历类似,如果你发现有一个人经常喜欢到Github上晃荡、喜欢关注技术类的博客或者资讯、经常再看PDF版的技术文档或书籍,请千万不要怀疑,这个人绝对是程序员。哈哈,好了,玩笑就此打住啊。经过博主对这些开源项目的简单分析和整理,目前,对Excel文件解析的解决方案主要有以下三种:

####1、Microsoft.Office.Interop.Excel
  第一种解决方案是基于微软提供的Office API,这组API以COM组件的形式给出,我们可以通过调用该API实现对Excel文件的解析。使用这组API非常简单,博主稍后会为大家给出一个示例代码。微软的Office API特点是使用起来方便,可以使用C#、Visual Basic等语言进行相关开发。可是这种解决方案的的缺点同样很明显,因为COM组件主要依赖于系统,因此使用COM组件需要在系统中注册,这将对代码的可移植性产生影响,而且受制于COM技术,这种解决方案只能运行在Windows平台上,无法实现跨平台,加之解析速度较慢,因此这种方案通常只适合在解析速度要求不高,运行环境为Windows平台的应用场景。

####2、ExcelReader
  第二种解决方案得益于OpenOffice标准,OpenOffice标准可以让我们使用一种标准来解析和处理Excel文件而无需关注Excel文件是来自微软的Misrosoft Office、金山的WPS还是其它的办公软件。如果说第一种解决方案是Windows平台上解析Excel文件的选择之一,那么ExcelRead就是跨平台目标下解析Excel文件的首选方案。尤其像Unity3D这样的跨平台解决方案下,选择一个跨平台的类库或者组件能够保证我们的游戏在各种平台下稳定地运行,所以ExcelRead是博主向大家推荐的一个跨平台的Excel解析方案。

####3、FastExcel
  第三种解决方案FastExcel是博主在解决博主的这位朋友的问题时所采取的方案。FastExcel是一个在开源世界里比较著名的Excel读写的类库,因此使用这个类库可以得到较为广泛的社区支持,而且在FastExcel这个项目的源代码中,作者为我们提供了使用FastExel进行Excel解析的相关示例,具有较高的参考价值,基本上可以在这个示例的基础上写出可以运行的代码。根据示例代码的运行结果使用FastExcel单独读写100000行数据基本上维持在3~4秒,读写速度还是蛮快的。不过FastExcel使用的是迭代器和Linq to Xml来读取Excel文件的,所以当数据表中存在空白单元格时,读写的时候会比较诡异,这一点希望大家注意。

###3、工程案例
  既然今天的主题是Unity3D游戏开发,所以无论我们在前面提出了什么样的解决方案,最后我们都要落实到游戏开发上,所以最后和大家分享的是一个Unity3D配合ExcelReader实现Excel解析的简单案例。为什么要选择ExcelReader呢?因为ExcelReader是一个跨平台的解决方案。好了,下面我们一起来学习这个案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using UnityEngine;
using System.Collections;
using System.IO;
using Excel;
using System.Data;


public class ExcelScripts : MonoBehaviour
{

void Start ()
{
FileStream m_Stream=File.Open(Application.dataPath +
"\\Excel\\UserLevel.xlsx",FileMode.Open,FileAccess.Read);
//使用OpenXml读取Excel文件
IExcelDataReader mExcelReader=ExcelReaderFactory.CreateOpenXmlReader(m_Stream);
//将Excel数据转化为DataSet
DataSet mResultSets=mExcelReader.AsDataSet();
//读取行数
int rowCount=mResultSets.Tables[0].Rows.Count;
//逐行读取,从第一行读以跳过表头
for(int i=1;i<rowCount;i++)
{
//将读取的Excel数据转化成数据实体
UserLevel mUser=new UserLevel();
mUser.Name=mResultSets.Tables[0].Rows[i][0].ToString();
mUser.Level=mResultSets.Tables[0].Rows[i][1].ToString();
mUser.Description=mResultSets.Tables[0].Rows[i][2].ToString();
mUser.Skill=mResultSets.Tables[0].Rows[i][3].ToString();
//输出Debug信息
Debug.Log(mUser.ToString());
//ADD:更多逻辑
}
}

//定义一个数据实体类UserLevel
private class UserLevel
{
private string m_Name;
public string Name
{
get { return m_Name;}
set { m_Name = value;}
}

private string m_Level;
public string Level
{
get { return m_Level;}
set { m_Level = value;}
}

private string m_Description;
public string Description
{
get { return m_Description;}
set { m_Description = value;}
}

private string m_Skill;
public string Skill
{
get { return m_Skill;}
set { m_Skill = value;}
}

public override string ToString()
{
return string.Format("Name={0}&Level={1}&Description={2}&Skill={3}",
m_Name,m_Level,m_Description,m_Skill);
}
}
}

  好了,这就是今天这篇文章的全部内容了,希望大家喜欢!