Unity一鍵打渠道包
阿新 • • 發佈:2018-11-19
忙了好一陣接入安卓sdk,也算積累了一些經驗,在這裡總結一下,如有不足,歡迎大大指出;
專案接了大大小小十多個sdk,我的想法是在android studio打好aar包,和資原始檔還有AndroidManifest.xml儲存成一個壓縮包,用的時候解壓縮到專案的Plugins/Android目錄下,設定好需要的引數,一鍵出包或者出多個渠道包
接下來看程式碼把:
還有如何解壓縮:using UnityEngine; using System.Collections; using UnityEditor; using System.IO; using System; using System.Collections.Generic; using UnityEditor.Callbacks; public class BuildWindow : EditorWindow { enum Build_Asset { UseSign, OutPath, SignPath, keyaliasName, } //以下為各個SDK的壓縮包名,改動可能導致打包失敗,勿動!!! private const string XiaoMi = "AndroidXiaoMi.rar"; private const string MeiZu = "AndroidMeiZu.rar"; private const string OPPO = "AndroidOppo.rar"; private const string HuaWei = "AndroidHuaWei.rar"; private const string JinLi = "AndroidJinLi.rar"; private const string VIVO = "AndroidVIVO.rar"; private const string JinLiYouXi = "AndroidJinLiYouXi.rar"; private const string QIHOO360 = "AndroidQIHOO360.rar"; private const string QuickSDK = "AndroidQuickSDK.rar"; private const string M4399 = "AndroidM4399.rar"; private const string PapaWuFan = "AndroidPapa.rar"; private const string Samsung = "AndroidSamsung.rar"; private const string Lehaihai = "AndroidLehaihai.rar"; private const string Nubiya = "AndroidNubiya.rar"; private const string Yingyongbao = "AndroidYingyongbao.rar"; //以下為各個SDK的包名,改動可能導致打包失敗,勿動!!! private const string XiaoMi_Identifier = "com.magicbirds.master.sf.mi"; private const string MeiZu_Identifier = "com.magicbirds.fighter.mz"; private const string OPPO_Identifier = "com.magicbirds.fighters.nearme.gamecenter"; private const string Huawei_Identifier = "com.magicbirds.master.huawei"; private const string JinLi_Identifier = "com.magicbirds.fighters.jinli"; private const string JinLiYouXi_Identifier = "com.magicbirds.fighters.jinli"; private const string VIVO_Identifier = "com.magicbirds.fighter.vivo"; private const string QIHOO360_Identifier = "com.magicbirds.master.sll"; private const string QuickSDK_Identifier = "com.qk.test"; private const string M4399_Identifier = "com.magicbirds.master.m4399"; private const string PapaWuFan_Identifier = "com.magicbirds.master.papa"; private const string Samsung_Identifier = "com.magicbirds.master.samsung"; private const string Lehaihai_Identifier = "com.magicbirds.master.lhh"; private const string Nubiya_Identifier = "com.magicbirds.master.nubia"; private const string Yingyongbao_Identifier = "com.magicbirds.fighters.nearme.gamecenter"; private void AddZipNameAndIdentifier() { ZipNameDict.Add(SDK_TYPE.XiaoMi, XiaoMi); IdentifierDict.Add(SDK_TYPE.XiaoMi, XiaoMi_Identifier); ZipNameDict.Add(SDK_TYPE.MeiZu, MeiZu); IdentifierDict.Add(SDK_TYPE.MeiZu, MeiZu_Identifier); ZipNameDict.Add(SDK_TYPE.Oppo, OPPO); IdentifierDict.Add(SDK_TYPE.Oppo, OPPO_Identifier); ZipNameDict.Add(SDK_TYPE.Huawei, HuaWei); IdentifierDict.Add(SDK_TYPE.Huawei, Huawei_Identifier); ZipNameDict.Add(SDK_TYPE.JinLiAibei, JinLi); IdentifierDict.Add(SDK_TYPE.JinLiAibei, JinLi_Identifier); ZipNameDict.Add(SDK_TYPE.VIVO, VIVO); IdentifierDict.Add(SDK_TYPE.VIVO, VIVO_Identifier); ZipNameDict.Add(SDK_TYPE.JinLiYouXi, JinLiYouXi); IdentifierDict.Add(SDK_TYPE.JinLiYouXi, JinLiYouXi_Identifier); ZipNameDict.Add(SDK_TYPE.QIHO360, QIHOO360); IdentifierDict.Add(SDK_TYPE.QIHO360, QIHOO360_Identifier); ZipNameDict.Add(SDK_TYPE.QuickSDK, QuickSDK); IdentifierDict.Add(SDK_TYPE.QuickSDK, QuickSDK_Identifier); ZipNameDict.Add(SDK_TYPE.M4399, M4399); IdentifierDict.Add(SDK_TYPE.M4399, M4399_Identifier); ZipNameDict.Add(SDK_TYPE.PapaWuFan, PapaWuFan); IdentifierDict.Add(SDK_TYPE.PapaWuFan, PapaWuFan_Identifier); ZipNameDict.Add(SDK_TYPE.SamsungAibei, Samsung); IdentifierDict.Add(SDK_TYPE.SamsungAibei, Samsung_Identifier); ZipNameDict.Add(SDK_TYPE.Lehaihai, Lehaihai); IdentifierDict.Add(SDK_TYPE.Lehaihai, Lehaihai_Identifier); ZipNameDict.Add(SDK_TYPE.Nubiya, Nubiya); IdentifierDict.Add(SDK_TYPE.Nubiya, Nubiya_Identifier); ZipNameDict.Add(SDK_TYPE.Yingyongbao, Yingyongbao); IdentifierDict.Add(SDK_TYPE.Yingyongbao, Yingyongbao_Identifier); } public static BuildWindow instance = null; public bool IsUnzipFile = false; private SDK_TYPE SDK_State = SDK_TYPE.None; private Dictionary<SDK_TYPE, string> ZipNameDict = new Dictionary<SDK_TYPE, string>(); private Dictionary<SDK_TYPE, string> IdentifierDict = new Dictionary<SDK_TYPE, string>(); //顯示的行列 private int row = 0; private const int Line = 4; private int TypeMax = 0; public bool UseSign = false; public string OutPath = ""; public string SignPath = ""; public string keyaliasName = ""; GUIStyle fontStyle; GUIStyle SVStyle; private bool BuildAll = false; [MenuItem("Window/Build Android Player Setting")] public static void Init() { //彈出視窗 EditorWindow.GetWindow(typeof(BuildWindow)); } private static void InitData() { if (instance == null) { Debug.LogError("instance is null"); return; } instance.UseSign = EditorPrefs.GetBool(Build_Asset.UseSign.ToString(), instance.UseSign); instance.OutPath = EditorPrefs.GetString(Build_Asset.OutPath.ToString(), instance.OutPath); instance.SignPath = EditorPrefs.GetString(Build_Asset.SignPath.ToString(), instance.SignPath); instance.keyaliasName = EditorPrefs.GetString(Build_Asset.keyaliasName.ToString(), instance.keyaliasName); } BuildWindow() { instance = this; InitData(); this.titleContent = new GUIContent("打包設定"); fontStyle = new GUIStyle(); fontStyle.normal.background = null; //設定背景填充 fontStyle.normal.textColor = new Color(1, 0, 0); //設定字型顏色 fontStyle.fontSize = 20; //字型大小 fontStyle.alignment = TextAnchor.MiddleCenter; AddZipNameAndIdentifier(); SetRows(); } private void SetRows() { TypeMax = (int)SDK_TYPE.Max; row = TypeMax / 3; } Vector2 SVpos = new Vector2(0, 0); void OnGUI() { EditorGUILayout.BeginVertical(); ////繪製標題 GUILayout.Space(10); fontStyle.fontSize = 24; //字型大小 fontStyle.alignment = TextAnchor.MiddleCenter;//居中 GUILayout.Label("打包設定", fontStyle); //繪製文字 應用名字 GUILayout.Space(15); PlayerSettings.productName = EditorGUILayout.TextField("ProductName", PlayerSettings.productName); GUILayout.Space(15); PlayerSettings.bundleVersion = EditorGUILayout.TextField("Version", PlayerSettings.bundleVersion); GUILayout.Space(15); PlayerSettings.Android.bundleVersionCode = int.Parse(EditorGUILayout.TextField("BundleVersionCode", PlayerSettings.Android.bundleVersionCode.ToString())); //繪製包名 GUILayout.Space(20); fontStyle.fontSize = 14; //字型大小 fontStyle.alignment = TextAnchor.UpperLeft;//居中 fontStyle.normal.textColor = Color.white; GUILayout.Label("Bundle Identifier: " + PlayerSettings.bundleIdentifier, fontStyle); GUILayout.Space(20); fontStyle.fontSize = 14; //字型大小 fontStyle.alignment = TextAnchor.UpperLeft;//居中 GUILayout.Label("Min Sdk Version: " + PlayerSettings.Android.minSdkVersion.ToString(), fontStyle); GUILayout.Space(20); GUILayout.Label("Save Path", fontStyle); EditorGUILayout.BeginHorizontal(); EditorGUILayout.TextField(OutPath, GUILayout.ExpandWidth(false)); if (GUILayout.Button("Browse", GUILayout.ExpandWidth(false))) { if (string.IsNullOrEmpty(OutPath)) OutPath = EditorUtility.SaveFolderPanel("Path to Save Images", @"C:\Users\mlxn\Desktop", @"C:\Users\mlxn\Desktop"); //開啟儲存資料夾面板 else OutPath = EditorUtility.SaveFolderPanel("Path to Save Images", OutPath, OutPath); //開啟儲存資料夾面板 EditorPrefs.SetString(Build_Asset.OutPath.ToString(), OutPath); } EditorGUILayout.EndHorizontal(); //是否開啟簽名 UseSign = EditorGUILayout.Toggle("Use Sign", UseSign); if (UseSign) { //開關點開 GUILayout.Label("Sign Path", fontStyle); EditorGUILayout.BeginHorizontal(); if (string.IsNullOrEmpty(SignPath)) { EditorGUILayout.TextField(PlayerSettings.Android.keystoreName, GUILayout.ExpandWidth(false)); } else { PlayerSettings.Android.keystoreName = SignPath; PlayerSettings.Android.keyaliasName = keyaliasName; EditorGUILayout.TextField(PlayerSettings.Android.keystoreName, GUILayout.ExpandWidth(false)); } if (GUILayout.Button("Browse", GUILayout.ExpandWidth(false))) { SignPath = EditorUtility.OpenFilePanel("Path to Save Images", @"C:\Users\mlxn\Desktop", "keystore"); //開啟儲存資料夾面板 string[] temp = SignPath.Split('/'); keyaliasName = temp[temp.Length - 1]; PlayerSettings.Android.keystoreName = SignPath; PlayerSettings.Android.keyaliasName = keyaliasName; } EditorPrefs.SetString(Build_Asset.SignPath.ToString(), SignPath); PlayerSettings.Android.keystorePass = EditorGUILayout.PasswordField("keystorePass:", PlayerSettings.Android.keystorePass); PlayerSettings.Android.keyaliasPass = EditorGUILayout.PasswordField("keystorePass:", PlayerSettings.Android.keyaliasPass); EditorGUILayout.EndHorizontal(); } else { PlayerSettings.Android.keystoreName = null; PlayerSettings.Android.keystorePass = null; PlayerSettings.Android.keyaliasName = null; PlayerSettings.Android.keyaliasPass = null; } EditorPrefs.SetBool(Build_Asset.UseSign.ToString(), UseSign); GUILayout.Space(12); EditorGUILayout.LabelField("請選擇要打包的渠道:", fontStyle);//可選格式如粗體 EditorGUILayout.Space(); SVpos = EditorGUILayout.BeginScrollView(SVpos); for (int i = 0; i < row; i++) { EditorGUILayout.BeginHorizontal(); for (int j = i * Line; j < (i + 1) * Line; j++) { if (j >= TypeMax) continue; if (GUILayout.Button(((SDK_TYPE)j).ToString(), GUILayout.MaxWidth(160), GUILayout.MinHeight(60), GUILayout.ExpandWidth(false))) { SetSDKZip((SDK_TYPE)j); } } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); GUILayout.Space(12); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("開始打包(單個)", GUILayout.MaxWidth(160), GUILayout.MinHeight(60), GUILayout.ExpandWidth(false))) { OnBuildClick(); } if (GUILayout.Button("開始打包(All)", GUILayout.MaxWidth(160), GUILayout.MinHeight(60), GUILayout.ExpandWidth(false))) { OnBuildAllClick(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } private Queue<SDK_TYPE> BuildList; private void OnBuildAllClick() { if (!CheckIsFinish(true)) return; BuildList = new Queue<SDK_TYPE>(); int max = (int)SDK_TYPE.Max; int otherOne = (int)SDK_TYPE.WhiteBag; int otherTwo = (int)SDK_TYPE.QuickSDK; for (int i = 0; i < max; i++) { if (i != otherOne && i != otherTwo) BuildList.Enqueue((SDK_TYPE)i); } BuildAll = true; StartBuildAll(); } private void StartBuildAll() { if (BuildList.Count == 0) { BuildAll = false; EditorUtility.DisplayDialog("提示", "all apk build ok!", "確定"); } else { SetSDKZip(BuildList.Dequeue()); } } public void StartBuildAllOne() { string NowPath = OutPath + "/" + "fighters_" + DateTime.Now.ToString("yyyyMMdd") + SDK_State.ToString() + ".apk"; if (File.Exists(NowPath)) { File.Delete(NowPath); BuildPipeline.BuildPlayer(GetBuildScenes(), NowPath, BuildTarget.Android, BuildOptions.None); } else { BuildPipeline.BuildPlayer(GetBuildScenes(), NowPath, BuildTarget.Android, BuildOptions.None); } } [PostProcessBuild] public static void OnPostProcessBuild(BuildTarget target, string path) { if (path.Contains(instance.SDK_State.ToString()) && instance.BuildAll) { instance.StartBuildAll(); } } private void OnBuildClick() { if (!CheckIsFinish()) return; string NowPath = OutPath + "/" + "fighters_" + DateTime.Now.ToString("yyyyMMdd") + SDK_State.ToString() + ".apk"; if (File.Exists(NowPath)) { if (UnityEditor.EditorUtility.DisplayDialog("提示", "將替換路徑下的同名檔案!", "確定", "取消")) { File.Delete(NowPath); BuildPipeline.BuildPlayer(GetBuildScenes(), NowPath, BuildTarget.Android, BuildOptions.None); } else { Debug.LogError("操作取消"); } } else { BuildPipeline.BuildPlayer(GetBuildScenes(), NowPath, BuildTarget.Android, BuildOptions.None); } } private bool CheckIsFinish(bool isbuildAll=false ) { if (string.IsNullOrEmpty(OutPath)) { EditorUtility.DisplayDialog("提示", "請選擇儲存路徑!", "確定"); return false; } if (UseSign && string.IsNullOrEmpty(SignPath)) { EditorUtility.DisplayDialog("提示", "請選擇簽名檔案路徑!", "確定"); return false; } if (UseSign && (string.IsNullOrEmpty(PlayerSettings.Android.keystorePass) || string.IsNullOrEmpty(PlayerSettings.Android.keyaliasPass))) { EditorUtility.DisplayDialog("提示", "請輸入簽名檔案密碼!", "確定"); return false; } if (SDK_State == SDK_TYPE.None && !isbuildAll) { EditorUtility.DisplayDialog("提示", "請選擇要打的渠道包!", "確定"); return false; } if (!ClientMgr.IsOpenChannel && SDK_State != SDK_TYPE.WhiteBag && SDK_State != SDK_TYPE.None) { EditorUtility.DisplayDialog("提示", "請將渠道開關開啟!", "確定"); return false; } if (ClientMgr.IsOpenChannel && SDK_State == SDK_TYPE.WhiteBag) { EditorUtility.DisplayDialog("提示", "請關閉渠道開關!", "確定"); return false; } if (SDK_State == SDK_TYPE.QuickSDK && (!ClientMgr.IsOpenChannel || !ClientMgr.IsQuickSDK)) { EditorUtility.DisplayDialog("提示", "請開啟渠道開關和QuickSDK開關!", "確定"); return false; } if (SDK_State != SDK_TYPE.QuickSDK && ClientMgr.IsQuickSDK) { EditorUtility.DisplayDialog("提示", "請關閉QuickSDK開關!", "確定"); return false; } return true; } static string[] GetBuildScenes() { List<string> pathList = new List<string>(); foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) { if (scene.enabled) { pathList.Add(scene.path); } } return pathList.ToArray(); } void SetSDKZip(SDK_TYPE type) { if (BuildAll) { SDK_State = type; SetAssets(type); } else { if (IsUnzipFile) return; IsUnzipFile = true; SDK_State = type; SetAssets(type); } } private static void SetAssets(SDK_TYPE type) { SetSdkVersion(type); string Name = ""; if (type == SDK_TYPE.WhiteBag) { DeleteFolder(PathUtil.PathAndroid); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("提示", "刪除完成!", "確定"); } else { Name = instance.ZipNameDict.ContainsKey(type) ? instance.ZipNameDict[type] : string.Empty; PlayerSettings.bundleIdentifier = instance.IdentifierDict.ContainsKey(type) ? instance.IdentifierDict[type] : string.Empty; RefreshInfo(Name); //SetAndroidIcon(type); } } private static void SetAndroidIcon(SDK_TYPE type) { Texture2D tx =null; tx = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ArtWork/app_icon"); Texture2D[] txs = { tx, tx, tx, tx, tx, tx, tx, }; PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, txs); switch (type) { case SDK_TYPE.QIHO360: tx = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ArtWork/app_icon_360"); Texture2D[] txs360 = { tx, tx, tx, tx, tx, tx, tx, }; PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, txs360); break; case SDK_TYPE.M4399: tx = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ArtWork/app_icon_4399"); Texture2D[] txs4399 = { tx, tx, tx, tx, tx, tx, tx, }; PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, txs4399); break; case SDK_TYPE.PapaWuFan: tx = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ArtWork/app_icon_wufan"); Texture2D[] txsPapa = { tx, tx, tx, tx, tx, tx, tx, }; PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, txsPapa); break; case SDK_TYPE.Lehaihai: tx = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ArtWork/app_icon_lehaihai"); Texture2D[] txsLehaihai = { tx, tx, tx, tx, tx, tx, tx, }; PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, txsLehaihai); break; } } private static void SetSdkVersion(SDK_TYPE type) { PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel16; if (type == SDK_TYPE.QIHO360) PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel17; } private static void RefreshInfo(string Name) { DeleteFolder(PathUtil.PathAndroid); mZip.UnzipFile(Name,instance.BuildAll); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } private static void DeleteFolder(string dir) { foreach (string d in Directory.GetFileSystemEntries(dir)) { if (File.Exists(d)) { FileInfo fi = new FileInfo(d); if (fi.Attributes.ToString().IndexOf("ReadOnly") != -1) fi.Attributes = FileAttributes.Normal; File.Delete(d); } else { DirectoryInfo d1 = new DirectoryInfo(d); if (d1.GetFiles().Length != 0) { DeleteFolder(d1.FullName);////遞迴刪除子資料夾 } Directory.Delete(d); } } } } public enum SDK_TYPE { None = -1, WhiteBag, XiaoMi,//3 MeiZu,//21 Oppo,//14 Huawei,//10 JinLiAibei,//12 VIVO,//13 JinLiYouXi,//31 QIHO360,//8 QuickSDK,//9999 M4399,//32 PapaWuFan,//33 SamsungAibei,//34 Lehaihai,//35 Nubiya,//36 Yingyongbao,//37 Max, }
每次只要手動新增包名和壓縮包名字,還有渠道列舉值,就可以啦using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Diagnostics; using System.IO; public static class mZip { //壓縮檔案 public static void Button1_Click() { ProcessStartInfo startinfo = new ProcessStartInfo(); ; Process process = new Process(); string rarName = "1.rar"; //壓縮後文件名 string path = @"C:\images"; //待壓縮打包資料夾 string rarPath = @"C:\zip"; //壓縮後存放資料夾 string rarexe = @"c:\Program Files\WinRAR\WinRAR.exe"; //WinRAR安裝位置 try { //壓縮命令,相當於在要壓縮的資料夾(path)上點右鍵->WinRAR->新增到壓縮檔案->輸入壓縮檔名(rarName) string cmd = string.Format("a {0} {1} -r", rarName, path); startinfo.FileName = rarexe; startinfo.Arguments = cmd; //設定命令引數 startinfo.WindowStyle = ProcessWindowStyle.Hidden; //隱藏 WinRAR 視窗 startinfo.WorkingDirectory = rarPath; process.StartInfo = startinfo; process.Start(); process.WaitForExit(); //無限期等待程序 winrar.exe 退出 if (process.HasExited) { UnityEditor.EditorUtility.DisplayDialog("提示", "壓縮成功!", "確定"); } } catch (Exception ex) { UnityEngine.Debug.LogError(ex.Message); UnityEditor.EditorUtility.DisplayDialog("提示", ex.Message, "確定"); } finally { process.Dispose(); process.Close(); } } //解壓檔案 public static void UnzipFile(string NameRar,bool IsAll=false) { if (string.IsNullOrEmpty(NameRar)) { UnityEngine.Debug.LogError("NameRar為空,請檢查是否寫了壓縮包名"); BuildWindow.instance.IsUnzipFile = false; return; } string path = PathUtil.PathAndroid; //檔案解壓路徑(絕對) string rarPath = PathUtil.RarPath; //將要解壓縮的 .rar 檔案的存放目錄(絕對路徑) string rarName = NameRar; //將要解壓縮的 .rar 檔名(包括字尾) string rarexe = PathUtil.Rrarexe;// @"c:\Program Files\WinRAR\WinRAR.exe"; //WinRAR安裝位置 if (!File.Exists(rarexe)) { UnityEditor.EditorUtility.DisplayDialog("提示", "未檢測到解壓縮工具,請確定在"+ rarexe + "安裝WinRAR!", "確定"); return; } ProcessStartInfo startinfo = new ProcessStartInfo(); ; Process process = new Process(); try { //解壓縮命令,相當於在要壓縮檔案(rarName)上點右鍵->WinRAR->解壓到當前資料夾 string cmd = string.Format("x {0} {1} -y", rarName, path); startinfo.FileName = rarexe; startinfo.Arguments = cmd; //設定命令引數 startinfo.WindowStyle = ProcessWindowStyle.Hidden; //隱藏 WinRAR 視窗 startinfo.WorkingDirectory = rarPath; process.StartInfo = startinfo; process.Start(); process.WaitForExit(); //無限期等待程序 winrar.exe 退出 if (process.HasExited) { if (!IsAll) { if (UnityEditor.EditorUtility.DisplayDialog("提示", NameRar + "解壓縮成功!", "確定")) { if (BuildWindow.instance != null) { BuildWindow.instance.IsUnzipFile = false; } } } else { if (BuildWindow.instance != null) { BuildWindow.instance.StartBuildAllOne(); } } } } catch (Exception ex) { UnityEngine.Debug.LogError(ex.Message); UnityEditor.EditorUtility.DisplayDialog("提示", ex.Message, "確定"); } finally { process.Dispose(); process.Close(); } } }