关于AI系统中涉及到的大量的数据配置,手动在Unity中进行填写是非常痛苦的一件事,所以我们想到了Excel表,我们大致想要完成这样一件事,每一行填好AI的标签或名字,填好下面的组件,组件下变量的名字,变量的数据,Excel表能完成对其的自动配置,一键配置。
这其中需要用到C#中很重要的特性,反射。简单来说,反射支持你在代码中很多运行时动态绑定的特性,比如编译时不知道具体的类型,不知道具体哪个变量,这些都在RT是才指定,也是可以的。注意到了吗?这正符合我们的想法!我们从Excel读入的数据也是string,而相当于要使用这些string动态在RT时找到类型、变量进行数据的指定!
如何加载Excel:
static STATECODE LoadExcel(string s)
{
FileStream stream = File.Open(Application.dataPath + "/"+s, FileMode.Open, FileAccess.Read);
if (stream == null)
return STATECODE.STATE_PATH_ERROR;
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
DataSet result = excelReader.AsDataSet();
int columns = result.Tables[0].Columns.Count;
int rows = result.Tables[0].Rows.Count;
// Debug.Log (columns);
if (columns < 6)
return STATECODE.STATE_FORMAT_ERROR;
InitArray (rows);
for(int i = 0; i< rows; i++)
{
KeyName [i] = result.Tables [0].Rows [i] [0].ToString();
MatchMode [i] = result.Tables [0].Rows [i] [1].ToString ();
ClassName [i] = result.Tables [0].Rows [i] [2].ToString ();
PropertyName [i] = result.Tables [0].Rows [i] [3].ToString ();
DataType [i] = result.Tables [0].Rows [i] [4].ToString ();
DataValue [i] = result.Tables [0].Rows [i] [5].ToString ();
}
return STATECODE.STATE_SUCCEED;
}
如何利用反射:
// tagName or GameObjectName
static string[] KeyName;
// Find GameObject by name or tag?
static string[] MatchMode;
// class name
static string[] ClassName;
// the property name of this class
static string[] PropertyName;
// data type
static string[] DataType;
// data value
static string[] DataValue;
// dafault path
static string ExcelName = "GameData.xls";
public enum STATECODE
{
STATE_SUCCEED = 0,
STATE_PATH_ERROR = -1,
STATE_FORMAT_ERROR = -2,
STATE_NOGAMEOBJECT_ERROR = -3,
STATE_NOCLASS_ERROR = -4,
STATE_NOPROPERTY_ERROR = -5,
STATE_VALUETRANSFER_ERROR = -6
};
[MenuItem("DataDrive/Load")]
static void Load()
{
STATECODE statecode = 0;
statecode = LoadExcel (ExcelName);
if (statecode != STATECODE.STATE_SUCCEED)
{
LogError (statecode);
return;
}
for(int i=0;i<KeyName.Length;i++)
{
GameObject[] tempResult=null;
statecode = FindGameObject (ref tempResult,KeyName[i],MatchMode[i]);
if (statecode != STATECODE.STATE_SUCCEED)
{
LogError (statecode);
return;
}
for (int j = 0; j < tempResult.Length; j++)
{
MonoBehaviour[] monothis = tempResult [j].GetComponents<MonoBehaviour> ();
for(int k=0;k<monothis.Length;k++)
{
if (monothis [k].GetType ().ToString () == ClassName [i])
{
FieldInfo fInfo = monothis [k].GetType ().GetField (PropertyName[i]);
if (fInfo == null)
{
LogError (STATECODE.STATE_NOPROPERTY_ERROR);
return;
}
object tempTargetResult = null;
statecode = DataTransfer (DataType[i],DataValue[i],ref tempTargetResult);
if (statecode != STATECODE.STATE_SUCCEED)
{
LogError (statecode);
return;
}
fInfo.SetValue (monothis[k],tempTargetResult);
break;
}
if (k == monothis.Length - 1)
{
LogError (STATECODE.STATE_NOCLASS_ERROR);
return;
}
}
}
}
}
static STATECODE DataTransfer(string type,string data,ref object result)
{
Type _Type = typeof(DataDriveTransferer);
MethodInfo MI = _Type.GetMethod (type);
object[] para = new object[1];
para [0] = data;
result = (object)MI.Invoke (null,para);
if (result == null)
return STATECODE.STATE_VALUETRANSFER_ERROR;
else
return STATECODE.STATE_SUCCEED;
}
static STATECODE FindGameObject(ref GameObject[] result,string key,string type)
{
if (type == "name")
{
GameObject temp = GameObject.Find (key);
if (temp!=null)
{
result = new GameObject[1];
result [0] = temp;
return STATECODE.STATE_SUCCEED;
}
return STATECODE.STATE_NOGAMEOBJECT_ERROR;
}
else
{
result = GameObject.FindGameObjectsWithTag (key);
if (result.Length != 0)
return STATECODE.STATE_SUCCEED;
return STATECODE.STATE_NOGAMEOBJECT_ERROR;
}
}
再来看看Excel表:
对第E行做一个说明,E行是一个方法名(通过方法名抓取方法),方法用于将string转化成对应了的类型,可自己在代码中扩展这些方法,只要符合返回类型和参数。
代码中在:
public class DataDriveTransferer
{
public static object string2int(string s)
{
int i = int.Parse (s);
return (object)i;
}
public static object string2float(string s)
{
float f = float.Parse (s);
return (object)f;
}
public static object string2string(string s)
{
return (object)s;
}
public static object string2char(string s)
{
char c = char.Parse (s);
return (object)c;
}
public static object string2double(string s)
{
double d = double.Parse (s);
return (object)d;
}
public static object string2bool(string s)
{
bool b = bool.Parse (s);
return (object)b;
}
}
这样的好处是,允许指定自定义的数据类型,只要有对应的转化器。