[FreedomAI] Week 2 - DataDriveSystem

Regarding the large amount of data configuration involved in the AI ​​system, it is very painful to fill in manually in Unity, so we thought of the Excel table, we roughly want to complete such a thing, fill in the AI ​​label in each row Or name, fill in the following components, the name of the variable under the component, the data of the variable, the Excel table can complete its automatic configuration, one-click configuration.

This requires the use of a very important feature in C#, reflection. In short, reflection supports many features of dynamic binding at runtime in your code. For example, you don't know the specific type at compile time, and you don't know which variable is specific. These are all specified in RT, which is also possible. Did you notice? This is exactly what we thought! The data we read from Excel is also a string, which is equivalent to using these strings to dynamically find types and variables in RT for data specification!

How to load 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;
		}

How to use reflection:

// 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];
			for [0] = date;
			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;
			}
		}

Let's take a look at the Excel table again:


Make an explanation for line E. Line E is a method name (the method is captured by the method name), and the method is used to convert the string into the corresponding type. You can extend these methods in the code yourself, as long as the return type and parameters are met .

In the code:

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;
		}
	}
The advantage of this is that it allows to specify custom data types, as long as there is a corresponding converter.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325872105&siteId=291194637