自学内容网 自学内容网

【Framework系列】UnityEditor调用外部程序详解

需求介绍

        之前Framework系列有介绍过导表配置工具,感兴趣的小伙伴可以看一看之前的文章《【Framework系列】Excel转Json,配置表、导表工具介绍》。由于导表工具和Unity是两个工程,导表工具不在Unity工程之内,所以在配置生成完成之后需要将配置复制到Unity工程内。为了导表方便,也为了避免配表手动复制过程中出错。我们则需要在Unity中实现一个能自动调用导表工具,并在配置生成完成之后将配置复制到Unity工程内的功能。

        

功能介绍

        

        上图是UnityEditor实现的导表调用工具,主要分为两个部分,第一部分是选择导表工具目录,第二部分则是配置导出并复制到Unity工程下。

目录设置
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using Framework.Define;
using Framework.Manager;

public class ConfigGenerateEditor : EditorWindow
{
    /// <summary>配置生成目录路径(即导表工具所在路径)</summary>
    private static string mConfigGenerateFolderPath = string.Empty;

    [MenuItem("FrameworkEditor/ConfigGenerate")]
    public static void ConfigGenerate()
    {
        mConfigGenerateFolderPath = PlayerPrefs.GetString("ConfigGenerateFolderPath");
        EditorWindow.GetWindowWithRect<ConfigGenerateEditor>(new Rect(0, 0, 500, 100), false, "ConfigGenerate");
    }

    private void OnValidate()
    {
        mConfigGenerateFolderPath = PlayerPrefs.GetString("ConfigGenerateFolderPath");
    }

    private void OnGUI()
    {
        GUIConfigFolder();
    }

    private void GUIConfigFolder()
    {
        GUILayout.Label("配置目录:" + mConfigGenerateFolderPath);
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("选择目录"))
        {
            string path = EditorUtility.OpenFolderPanel("选择目录", mConfigGenerateFolderPath, "");
            if (!string.IsNullOrEmpty(path))
            {
                mConfigGenerateFolderPath = path + "/";
                ManagerCollection.DataManager.SetPlayerPrefs<string>(FrameworkDefine.ConfigGenerateFolderPathKey, mConfigGenerateFolderPath);
            }
        }

        if (GUILayout.Button("打开目录"))
        {
            if (Directory.Exists(mConfigGenerateFolderPath))
                EditorUtility.RevealInFinder(mConfigGenerateFolderPath);
            else
                Debug.LogWarning("目录不存在");
        }

        if (GUILayout.Button("输出目录路径"))
        {
            Debug.Log("配置目录路径:" + mConfigGenerateFolderPath);
        }
        GUILayout.EndHorizontal();
    }
}

        第一部分是目录设置,这部分比较简单,通过调用 EditorUtility.OpenFolderPanel 接口,Unity会弹出一个目录选择的窗口,选择确定后接口会返回选择目录的完整路径。这里我们使用PlayerPrefs进行了永久保存,当我们下次打开工程时依然可以获取到导表目录路径。这里有一个小细节需要说明一下,当使用 EditorUtility.RevealInFinder 接口打开目录时,由于返回路径问题会打开选择目录的上一级目录,为正确打开目录需要在返回路径后多增加 "/" 斜杠。

配置导出
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using Framework.Define;
using Framework.Manager;
using System.Diagnostics;

using Debug = UnityEngine.Debug;


public class ConfigGenerateEditor : EditorWindow
{
    /// <summary>配置生成目录路径(即导表工具所在路径)</summary>
    private static string mConfigGenerateFolderPath = string.Empty;
    /// <summary>配置生成客户端目录名称</summary>
    private static string mConfigGenerateClientFolderName = "Client";
    /// <summary>配置生成Exe名称</summary>
    private static string mConfigGenerateExeName = "ExcelToJson.exe";

    [MenuItem("FrameworkEditor/ConfigGenerate")]
    public static void ConfigGenerate()
    {
        mConfigGenerateFolderPath = ManagerCollection.DataManager.GetPlayerPrefs<string>(FrameworkDefine.ConfigGenerateFolderPathKey);
        EditorWindow.GetWindowWithRect<ConfigGenerateEditor>(new Rect(0, 0, 500, 100), false, "ConfigGenerate");
    }

    private void OnGUI()
    {
        GUIConfigGenerate();
    }

    private void GUIConfigGenerate()
    {
        GUILayout.Label("配置导出:");
        if (GUILayout.Button("配置导出"))
        {
            if (!Directory.Exists(mConfigGenerateFolderPath))
            {
                Debug.LogWarning("目录不存在");
                return;
            }

            string exePath = mConfigGenerateFolderPath + mConfigGenerateExeName;
            if (!File.Exists(exePath))
            {
                Debug.LogWarning("导表程序不存在,路径:" + exePath);
                return;
            }

            ProcessStartInfo processStartInfo = new ProcessStartInfo();
            processStartInfo.FileName = exePath;
            processStartInfo.WorkingDirectory = mConfigGenerateFolderPath;
            processStartInfo.UseShellExecute = false;
            processStartInfo.RedirectStandardOutput = true;

            using (Process process = Process.Start(processStartInfo))
            {
                // 等待进程结束
                process.WaitForExit();

                string result = process.StandardOutput.ReadToEnd();
                if (result.Contains("配表导出成功"))
                {
                    Debug.Log(result);
                    CopyConfigToFramework();
                }
                else
                {
                    Debug.LogWarning(result);
                }
            }
        }
    }

    private void CopyConfigToFramework()
    {
        string sourcePath = string.Format("{0}{1}", mConfigGenerateFolderPath, mConfigGenerateClientFolderName);
        if (!Directory.Exists(sourcePath))
        {
            Debug.LogWarning("生成配置目录不存在,路径为:" + sourcePath);
            return;
        }

        string destPath = string.Format("{0}/{1}", Application.dataPath, FrameworkDefine.ConfigRootDirectoryPath);
        if (Directory.Exists(destPath))
        {
            string[] files = Directory.GetFiles(destPath);
            if (files.Length > 0)
                Directory.Delete(destPath, true);
        }
        else
        {
            Directory.CreateDirectory(destPath);
        }

        CopyDirectory(sourcePath, destPath);

        AssetDatabase.Refresh();
    }

    private void CopyDirectory(string pSourcePath, string pDestPath)
    {
        Debug.Log(string.Format("srcDirPath:{0} \n destDirPath:{1}", pSourcePath, pDestPath));

        if (!Directory.Exists(pDestPath))
            Directory.CreateDirectory(pDestPath);

        string[] directories = Directory.GetDirectories(pSourcePath);
        foreach (string sourcePath in directories)
        {
            string destPath = sourcePath.Replace(pSourcePath, pDestPath);
            CopyDirectory(sourcePath, destPath);
        }

        string[] files = Directory.GetFiles(pSourcePath);
        foreach (string file in files)
        {
            string destFile = file.Replace(pSourcePath, pDestPath);
            File.Copy(file, destFile, true);
        }
    }
}

        第二部分是配置导出,配置导出分为两步,第一步是调用外部导表程序,第二步是在导表结束之后将配置文件复制到Unity工程内。

        Unity外部调用导表程序需要用到ProcessStartInfo和Process两个类,使用方法可以参考示例代码或官方文档。这里需要提醒注意的是processStartInfo.WorkingDirectory属性,WorkingDirectory默认为空,虽然不进行设置依然可以调用到外部程序,但工作目录会默认为Unity工程所在目录,也就是Assets所在目录。外部程序在获取相对路径时就会出现路径错误,要确保外部程序的相对路径正确,则需要设置WorkingDirectory属性,将WorkingDirectory设置为外部程序所在位置。

        process.WaitForExit用于等待外部程序调用结束,程序结束后会执行之后的代码。通过process.StandardOutput.ReadToEnd方法则可以获取到程序输出内容。如果配置生成成功,则可以通过文件操作,将配置文件复制到Unity工程的指定目录。

官方文档链接

EditorUtility文档链接:https://docs.unity3d.com/cn/2022.2/ScriptReference/EditorUtility.html

ProcessStartInfo文档链接:https://learn.microsoft.com/zh-cn/dotnet/api/system.diagnostics.processstartinfo?view=net-9.0

Process文档链接:Process 类 (System.Diagnostics) | Microsoft Learn


原文地址:https://blog.csdn.net/huoyixian/article/details/143798926

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!