| | |
| | | EditorGUILayout.Space();
|
| | | GUILayout.BeginHorizontal();
|
| | | var newPath = EditorGUILayout.TextField("Output Path", m_UserData.m_OutputPath);
|
| | | if ((newPath != m_UserData.m_OutputPath) &&
|
| | | (newPath != string.Empty))
|
| | | if (newPath != m_UserData.m_OutputPath)
|
| | | {
|
| | | m_UserData.m_UseDefaultPath = false;
|
| | | m_UserData.m_OutputPath = newPath;
|
| | |
| | | private void ExecuteBuildAll()
|
| | | {
|
| | | // 使用 YooAsset 全量打包所有 Package(含 HybridCLR)
|
| | | if (!YooAssetBuildTool.BuildAllPackagesCore(incremental: false))
|
| | | if (!YooAssetBuildTool.BuildAllPackagesCore(incremental: false, m_UserData.m_OutputPath))
|
| | | {
|
| | | Debug.LogError("[AssetBundleBuildTab] YooAsset 全量打包失败!");
|
| | | return;
|
| | |
| | | {
|
| | | Debug.LogError("Start to build APK, this may take a while...");
|
| | | packageIndex++;
|
| | | var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
| | | var outputPath = GetResolvedOutputPath();
|
| | | ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, false, false);
|
| | | Debug.LogError("Finished building APK.");
|
| | | }
|
| | |
| | | private void ExecuteBuildClientPackageDevelopApk()
|
| | | {
|
| | | packageIndex++;
|
| | | var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
| | | var outputPath = GetResolvedOutputPath();
|
| | | ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, true, false);
|
| | | }
|
| | |
|
| | |
| | | return;
|
| | | }
|
| | | packageIndex++;
|
| | | var assetBundlePath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
| | | var assetBundlePath = GetResolvedOutputPath();
|
| | | ClientPackage.ExportAndroidProject(publishers, packageIndex, false, AndroidExportPath, assetBundlePath, ExportForAppBundle);
|
| | | }
|
| | |
|
| | |
| | | return;
|
| | | }
|
| | | packageIndex++;
|
| | | var assetBundlePath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
| | | var assetBundlePath = GetResolvedOutputPath();
|
| | | ClientPackage.ExportAndroidProject(publishers, packageIndex, true, AndroidExportPath, assetBundlePath, ExportForAppBundle);
|
| | | }
|
| | |
|
| | | private string GetResolvedOutputPath()
|
| | | {
|
| | | var projectRoot = Directory.GetParent(Application.dataPath).FullName;
|
| | | if (string.IsNullOrWhiteSpace(m_UserData.m_OutputPath))
|
| | | {
|
| | | return projectRoot;
|
| | | }
|
| | |
|
| | | if (Path.IsPathRooted(m_UserData.m_OutputPath))
|
| | | {
|
| | | return Path.GetFullPath(m_UserData.m_OutputPath);
|
| | | }
|
| | |
|
| | | return Path.GetFullPath(Path.Combine(projectRoot, m_UserData.m_OutputPath));
|
| | | }
|
| | |
|
| | | private void ExecuteBuildClientPackageStandalone()
|
| | | {
|
| | | packageIndex++;
|
| | |
| | | for (int i = 0; i < smallPackages.Count; i++)
|
| | | {
|
| | | #if UNITY_ANDROID
|
| | | BuildApk(_sdkPath, _output, smallPackages[i], _buildIndex, _development);
|
| | | BuildApk(_sdkPath, _output, smallPackages[i], _buildIndex, _development, _assetBundlePath);
|
| | | #elif UNITY_IOS
|
| | | BuildIpa(_sdkPath, smallPackages[i], _buildIndex, _replace);
|
| | | #endif
|
| | |
| | | for (int i = 0; i < halfPackages.Count; i++)
|
| | | {
|
| | | #if UNITY_ANDROID
|
| | | BuildApk(_sdkPath, _output, halfPackages[i], _buildIndex, _development);
|
| | | BuildApk(_sdkPath, _output, halfPackages[i], _buildIndex, _development, _assetBundlePath);
|
| | | #elif UNITY_IOS
|
| | | BuildIpa(_sdkPath, halfPackages[i], _buildIndex, _replace);
|
| | | #endif
|
| | |
| | | for (int i = 0; i < bigPackages.Count; i++)
|
| | | {
|
| | | #if UNITY_ANDROID
|
| | | BuildApk(_sdkPath, _output, bigPackages[i], _buildIndex, _development);
|
| | | BuildApk(_sdkPath, _output, bigPackages[i], _buildIndex, _development, _assetBundlePath);
|
| | | #elif UNITY_IOS
|
| | | BuildIpa(_sdkPath, bigPackages[i], _buildIndex, _replace);
|
| | | #endif
|
| | |
| | |
|
| | | }
|
| | |
|
| | | public static void BuildApk(string _sdkPath, string _output, string _publisher, int _buildIndex, bool _development)
|
| | | public static void BuildApk(string _sdkPath, string _output, string _publisher, int _buildIndex, bool _development, string _streamingAssetsOutputPath = null)
|
| | | {
|
| | | PreBuild(_publisher, _buildIndex);
|
| | | var auditTimeFile = StringUtility.Concat(Application.dataPath, Path.DirectorySeparatorChar.ToString(), "Resources/Config/AuditTime.txt");
|
| | |
| | | AssetDatabase.SaveAssets();
|
| | | AssetDatabase.Refresh();
|
| | |
|
| | | if (_streamingAssetsOutputPath != null && !YooAssetBuildTool.CopyStreamingAssetsToOutputPath(_streamingAssetsOutputPath))
|
| | | {
|
| | | Debug.LogError("[ClientPackage] 拷贝 StreamingAssets 到资源输出目录失败,中止 APK 构建。TARGET=" + _streamingAssetsOutputPath);
|
| | | return;
|
| | | }
|
| | |
|
| | | // 确保不以 Gradle 工程模式输出(HybridCLR StripAOT 步骤会临时置为 true,若未还原则会输出文件夹而非 APK)
|
| | | EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
|
| | |
|
| | |
| | | // Debug.LogWarningFormat("AssetBundle路径为空或不存在: {0},导出工程将不包含游戏资源", _assetBundlePath);
|
| | | // }
|
| | |
|
| | | if (_assetBundlePath != null && !YooAssetBuildTool.CopyStreamingAssetsToOutputPath(_assetBundlePath))
|
| | | {
|
| | | Debug.LogError("[ClientPackage] 拷贝 StreamingAssets 到资源输出目录失败,中止 Android 工程导出。TARGET=" + _assetBundlePath);
|
| | | return;
|
| | | }
|
| | |
|
| | | // 关键设置:导出为Gradle工程而非直接打APK
|
| | | EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
|
| | | EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
|
| | |
| | | /// <summary> |
| | | /// 资源打包核心逻辑(不含确认对话框),返回是否全部成功。 |
| | | /// </summary> |
| | | public static bool BuildAllPackagesCore(bool incremental) |
| | | public static bool BuildAllPackagesCore(bool incremental, string streamingAssetsOutputPath = null) |
| | | { |
| | | CollectResBeforeUpdate.SyncCollectors(); |
| | | |
| | |
| | | success = CopyStartupConfigsToStreamingAssets(); |
| | | } |
| | | |
| | | if (success && streamingAssetsOutputPath != null) |
| | | { |
| | | success = CopyStreamingAssetsToOutputPath(streamingAssetsOutputPath); |
| | | } |
| | | |
| | | AssetDatabase.Refresh(); |
| | | return success; |
| | | } |
| | | |
| | | public static bool CopyStreamingAssetsToOutputPath(string outputPath) |
| | | { |
| | | bool sourceStartupConfigsCopied = false; |
| | | try |
| | | { |
| | | sourceStartupConfigsCopied = CopyStartupConfigsToStreamingAssets(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[YooAssetBuildTool] 补齐 StreamingAssets 启动配置异常,将继续拷贝现有 StreamingAssets。error={ex}"); |
| | | } |
| | | |
| | | if (!sourceStartupConfigsCopied) |
| | | { |
| | | Debug.LogWarning("[YooAssetBuildTool] 补齐 StreamingAssets 启动配置失败,将继续拷贝现有 StreamingAssets。"); |
| | | } |
| | | |
| | | string sourceRoot = System.IO.Path.Combine(Application.dataPath, "StreamingAssets"); |
| | | string destRoot = ResolveOutputDirectory(outputPath); |
| | | |
| | | if (!System.IO.Directory.Exists(sourceRoot)) |
| | | { |
| | | Debug.LogError($"[YooAssetBuildTool] StreamingAssets 源目录不存在: {sourceRoot}"); |
| | | return false; |
| | | } |
| | | |
| | | string sourceFullPath = NormalizeFullPath(sourceRoot); |
| | | string destFullPath = NormalizeFullPath(destRoot); |
| | | if (string.Equals(sourceFullPath, destFullPath, StringComparison.OrdinalIgnoreCase)) |
| | | { |
| | | Debug.LogWarning($"[YooAssetBuildTool] StreamingAssets 输出目录与源目录相同,跳过拷贝: {destFullPath}"); |
| | | return true; |
| | | } |
| | | |
| | | if (destFullPath.StartsWith(sourceFullPath + "/", StringComparison.OrdinalIgnoreCase)) |
| | | { |
| | | Debug.LogError($"[YooAssetBuildTool] StreamingAssets 输出目录不能位于源目录内部: {destFullPath}"); |
| | | return false; |
| | | } |
| | | |
| | | System.IO.Directory.CreateDirectory(destFullPath); |
| | | |
| | | int copiedCount = 0; |
| | | foreach (var sourceFile in System.IO.Directory.GetFiles(sourceFullPath, "*", System.IO.SearchOption.AllDirectories)) |
| | | { |
| | | if (string.Equals(System.IO.Path.GetExtension(sourceFile), ".meta", StringComparison.OrdinalIgnoreCase)) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | string relativePath = sourceFile.Substring(sourceFullPath.Length).TrimStart('\\', '/'); |
| | | string destFile = System.IO.Path.Combine(destFullPath, relativePath); |
| | | string destDir = System.IO.Path.GetDirectoryName(destFile); |
| | | if (!System.IO.Directory.Exists(destDir)) |
| | | { |
| | | System.IO.Directory.CreateDirectory(destDir); |
| | | } |
| | | |
| | | System.IO.File.Copy(sourceFile, destFile, true); |
| | | copiedCount++; |
| | | } |
| | | |
| | | bool configCopied = false; |
| | | try |
| | | { |
| | | configCopied = CopyStartupConfigsToYooDirectory(System.IO.Path.Combine(destFullPath, "yoo")); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[YooAssetBuildTool] 补齐输出目录启动配置异常,不阻塞 StreamingAssets 输出。error={ex}"); |
| | | } |
| | | |
| | | if (!configCopied) |
| | | { |
| | | Debug.LogWarning($"[YooAssetBuildTool] 补齐输出目录启动配置失败,请检查输出目录: {System.IO.Path.Combine(destFullPath, "yoo")}"); |
| | | } |
| | | |
| | | Debug.Log($"[YooAssetBuildTool] 已拷贝 StreamingAssets 到输出目录: {copiedCount} 个文件,from={sourceFullPath}, to={destFullPath}"); |
| | | return true; |
| | | } |
| | | |
| | | private static string ResolveOutputDirectory(string outputPath) |
| | | { |
| | | string projectRoot = System.IO.Directory.GetParent(Application.dataPath).FullName; |
| | | if (string.IsNullOrWhiteSpace(outputPath)) |
| | | { |
| | | return projectRoot; |
| | | } |
| | | |
| | | string normalizedPath = outputPath.Replace('\\', '/'); |
| | | if (System.IO.Path.IsPathRooted(normalizedPath)) |
| | | { |
| | | return System.IO.Path.GetFullPath(normalizedPath); |
| | | } |
| | | |
| | | return System.IO.Path.GetFullPath(System.IO.Path.Combine(projectRoot, normalizedPath)); |
| | | } |
| | | |
| | | private static string NormalizeFullPath(string path) |
| | | { |
| | | return System.IO.Path.GetFullPath(path).TrimEnd('\\', '/').Replace('\\', '/'); |
| | | } |
| | | |
| | | public static bool CopyStartupConfigsToStreamingAssets() |
| | | { |
| | | string sourceConfigDir = System.IO.Path.Combine(Application.dataPath, "ResourcesOut", "Config"); |
| | | string streamingAssetsDir = System.IO.Path.Combine(Application.dataPath, "StreamingAssets", "yoo"); |
| | | string streamingConfigDir = System.IO.Path.Combine(streamingAssetsDir, "config"); |
| | | return CopyStartupConfigsToYooDirectory(streamingAssetsDir); |
| | | } |
| | | |
| | | private static bool CopyStartupConfigsToYooDirectory(string targetYooDir) |
| | | { |
| | | string sourceConfigDir = System.IO.Path.Combine(Application.dataPath, "ResourcesOut", "Config"); |
| | | string yooDir = targetYooDir; |
| | | string streamingConfigDir = System.IO.Path.Combine(yooDir, "config"); |
| | | |
| | | if (!System.IO.Directory.Exists(sourceConfigDir)) |
| | | { |
| | |
| | | return false; |
| | | } |
| | | |
| | | System.IO.Directory.CreateDirectory(streamingAssetsDir); |
| | | System.IO.Directory.CreateDirectory(yooDir); |
| | | System.IO.Directory.CreateDirectory(streamingConfigDir); |
| | | |
| | | bool success = true; |
| | | string initialFunctionSrc = System.IO.Path.Combine(sourceConfigDir, "InitialFunction.txt"); |
| | | string initialFunctionDst = System.IO.Path.Combine(streamingAssetsDir, "InitialFunction.txt"); |
| | | string initialFunctionDst = System.IO.Path.Combine(yooDir, "InitialFunction.txt"); |
| | | if (System.IO.File.Exists(initialFunctionSrc)) |
| | | { |
| | | System.IO.File.Copy(initialFunctionSrc, initialFunctionDst, true); |
| | |
| | | Debug.LogWarning($"[YooAssetBuildTool] 未找到 OPConfig 配置: {sourceConfigDir}"); |
| | | } |
| | | |
| | | Debug.Log($"[YooAssetBuildTool] 已拷贝启动配置到 StreamingAssets: InitialFunction={(System.IO.File.Exists(initialFunctionDst) ? "ok" : "missing")}, OPConfig={opConfigCount}, 目录={streamingAssetsDir}"); |
| | | Debug.Log($"[YooAssetBuildTool] 已拷贝启动配置到 Yoo 目录: InitialFunction={(System.IO.File.Exists(initialFunctionDst) ? "ok" : "missing")}, OPConfig={opConfigCount}, 目录={yooDir}"); |
| | | return success; |
| | | } |
| | | |