[FreedomAI]第二周——DataDriveSystem

关于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;
		}
	}
这样的好处是,允许指定自定义的数据类型,只要有对应的转化器。


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页