今天在做编辑器中一个功能时,碰到一个这样的需求,Unity3D Editor脚本中会通过System.Diagnostics.Process这个类来调用外部的系统命令来执行一个操作,其实就是调用系统的Python命令执行一个Python脚本,来将Excel文件转换成文本数据。
[MenuItem ("LightHonor/Config/ImportExcel")] static void ConvertXlsxToTxtConfig () { string txt2xlsxPythonFilePath = Directory.GetParent (Application.dataPath) + Path.DirectorySeparatorChar.ToString () + "Docs" + Path.DirectorySeparatorChar.ToString () + "Tools" + Path.DirectorySeparatorChar.ToString () + "convert.py"; if (Application.platform == RuntimePlatform.OSXEditor) { System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo ("/usr/bin/python", txt2xlsxPythonFilePath); System.Diagnostics.Process convertProcess = new System.Diagnostics.Process (); convertProcess.StartInfo = startInfo; convertProcess.EnableRaisingEvents = true; convertProcess.Exited += new System.EventHandler (ConvertProcessExited); convertProcess.Start (); } else if (Application.platform == RuntimePlatform.WindowsEditor) { System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo (@"C:\Python27\python.exe", txt2xlsxPythonFilePath); System.Diagnostics.Process convertProcess = new System.Diagnostics.Process (); convertProcess.StartInfo = startInfo; convertProcess.EnableRaisingEvents = true; convertProcess.Exited += new System.EventHandler (ConvertProcessExited); convertProcess.Start (); } } private static void ConvertProcessExited (object sender, System.EventArgs e) { Debug.Log ("Convert Excel Xlsx Config To Text File Done!"); }
但是这样执行完ImportExcel菜单项之后,可能会出现我们通过外部的脚本修改了Unity3D需要使用到的文本文件,但是Unity3D未能及时更新AssetDatabase,导致Unity3D中读取到的文本文件还是执行ImportConfig菜单项之前的数据。那么我们能否直接在这个外部程序执行完毕的回调中执行AssetDatabase.Refresh()方法呢?不行,因为这些函数都只能在主线程里头调用,而这个回调函数是在启动外部程序的线程中执行的,所以在ConvertProcessExited()方法中访问AssetDatabase对象会抛出异常。这个时候我们可以通过EditorApplication的update委托来配合Coroutine机制来完成我们想要的功能。
关于EditorApplication如何支持Coroutine,这个是在GitHub上找到了一个很棒的参考,地址在这儿,链接页面中是一个完整的独立的EditorCoroutine的实现,借助于这个漂亮的工具,我们修改了代码,然后就能达成我们的需求了,在执行完外部转化的Python程序之后,及时通过AssetDatabase刷新Unity3D中需要使用到的文本文件,代码如下:
private static bool sConvertDone = false; [MenuItem ("LightHonor/Config/ImportExcel")] static void ConvertXlsxToTxtConfig () { string txt2xlsxPythonFilePath = Directory.GetParent (Application.dataPath) + Path.DirectorySeparatorChar.ToString () + "Docs" + Path.DirectorySeparatorChar.ToString () + "Tools" + Path.DirectorySeparatorChar.ToString () + "convert.py"; if (Application.platform == RuntimePlatform.OSXEditor) { System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo ("/usr/bin/python", txt2xlsxPythonFilePath); System.Diagnostics.Process convertProcess = new System.Diagnostics.Process (); convertProcess.StartInfo = startInfo; convertProcess.EnableRaisingEvents = true; convertProcess.Exited += new System.EventHandler (ConvertProcessExited); convertProcess.Start (); } else if (Application.platform == RuntimePlatform.WindowsEditor) { System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo (@"C:\Python27\python.exe", txt2xlsxPythonFilePath); System.Diagnostics.Process convertProcess = new System.Diagnostics.Process (); convertProcess.StartInfo = startInfo; convertProcess.EnableRaisingEvents = true; convertProcess.Exited += new System.EventHandler (ConvertProcessExited); convertProcess.Start (); } sConvertDone = false; EditorCoroutine.Start (RefreshAssetDatabase ()); } static IEnumerator RefreshAssetDatabase () { while (!sConvertDone) { yield return 0; } AssetDatabase.Refresh (); Debug.Log ("Refresh Asset Database Done"); } private static void ConvertProcessExited (object sender, System.EventArgs e) { Debug.Log ("Convert Excel Xlsx Config To Text File Done!"); sConvertDone = true; }