【狂雲歌之unity_vr】unity專案持續整合dailybuild以及多平臺打包管理
【狂雲歌之unity_vr】unity專案持續整合dailybuild以及多平臺打包管理
前言
持續整合的意義就不多說了。unity通常打包一般就直接build&run,但是在實際專案中,往往直接在伺服器build包,所以命令列打包必不可少,這裡一方面分享unity打包做持續整合,一方面分享使用unity管理多平臺打包,例如一個vrapp需要支援gear版本,支援小米版本,支援cardboard版本等等~懂的人就知道這裡具有一定的管理維護成本。
我們做vr相關的app,需要支援gear、cardboard、小米、vivo、大朋、暴風、Idealens、pico、nibiru、酷開等一大堆平臺,曾經還有lg和htcvive、oculus平臺,未來還會有更多的平臺,所以關於unity專案的多平臺管理是很重要的,在這方面我們也在探索,積累了一點經驗。這裡介紹的主要是基於unity中c#寫的打包和多平臺管理,如果將其中一部分功能使用python和其他配置檔案來實現也是可以的,只是用c#直接做會方便許多。
jenkins
https://jenkins.io/index.html
jenkins不用多說,懂的人都瞭解是幹什麼的,來源於hudson,可以比較容易的搭起一個持續整合伺服器,支援svn和git等版本管理。支援bash,所以可以用bash、python等大部分指令碼來寫打包指令碼和前後的處理。
unity命令列打包
unity如何進行命令列打包呢,其實unity是支援以命令列方式啟動的,但是需要關閉editor支援執行命令,如下:
${unity可執行檔案路徑} -projectPath ${專案路徑}
-executeMethod CloudBuild.PerformBuildAndroidCloudAlphaRelease
-batchmode -quit -logFile ${放log的路徑}
-ForceExitEditor
整體比較容易理解,其中CloudBuild.PerformBuildAndroidCloudAlphaRelease
是一個類的靜態方法,然後在這個方法中寫打包相關邏輯即可。
csharp
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android);
string[] scenes = { "Assets/Scenes/Init.unity", "Assets/Scenes/Main.unity" };
BuildPipeline.BuildPlayer(scenes, buildpath, BuildTarget.Android, BuildOptions.None);
核心邏輯就這麼多,就會開始打一個android包並且生成到buildpath下面
多平臺打包管理
準備工作
對unity外掛不熟悉的可以看下 開發unity外掛——一次搞定unity編輯器常用功能
我這裡準備了一個全域性的配置檔案,當然這個可以使用外部配置檔案來管理配置,是一個道理的。這裡定義了兩個平臺版本一個是alpha一個是beta,使用巨集來區分不同平臺的版本號。
```csharp using UnityEngine; using System.Collections;
public class GlobalConfig {
if CLOUD_ALPHA
public const int ClientVersionCode = 1;
public const string ClientVersion = "1.0";
elif CLOUD_BETA
public const int ClientVersionCode = 2;
public const string ClientVersion = "1.1";
endif
} ```
檔案目錄組織大概如下,其中CloudBuild是編輯器工具,包含選單項和打包功能,兩個平臺分別依賴不同的so檔案和manifest檔案。
製作了一個menu,主要包含的功能是可以在alpha和beta平臺之間切換,可以打alpha平臺的apk包,當然想打beta平臺的包只需要簡單修改。
manifest管理
寫個簡單的指令碼進行manifest替換就好,對於alpha平臺和beta平臺各有自己的manifest檔案,在切換平臺的時候將對應的manifest複製替換。
/// <summary>
/// 使用相應的androidmanifest
/// </summary>
static void UseAndroidManifest(string filename)
{
string src_filename = string.Format("AndroidManifest-{0}.xml", filename);
string dst_filename = "AndroidManifest.xml";
string path = Application.dataPath + "/Plugins/Android/";
File.Copy(path + src_filename, path + dst_filename, true);
AssetDatabase.Refresh();//因為修改了manifest檔案,所以重新整理unity的assets
}
依賴包管理
為什麼要做依賴包管理呢?因為在使用不同平臺sdk的時候,可能會引入很多sdk,每個sdk裡包含自己的so、jar、aar包等,如果什麼都不管理,直接打包的話,那麼這些依賴的檔案都會打進所有的apk包,簡單來說就會增加包的體積,更嚴重的情況下,這些不同平臺sdk裡的依賴庫可能還會有衝突,如果打進同一個apk包,後果不堪設想~
做依賴包管理主要依賴unity自己的assetimport管理如下圖,那麼只要在需要的時候勾選不需要的時候取消勾選就好了,我們要做的就是用程式碼來自動實現這個功能。
先準備好各個平臺的依賴包路徑
``` static string[] Plugins_Alpha = new string[] { "Assets/Plugins/Android/libs/armeabi-v7a/alpha.so", };
static string[] Plugins_Beta = new string[] { "Assets/Plugins/Android/libs/armeabi-v7a/beta.so", }; ```
然後在切換不同平臺的時候對這些依賴包的import做處理,這塊Asset屬於plugin,所以使用pluginimporter來管理勾選的問題。
``` static void ChangePluginToAlpha() { SetEnablePluginImport(Plugins_Alpha, true); SetEnablePluginImport(Plugins_Beta, false); }
static void ChangePluginToBeta() { SetEnablePluginImport(Plugins_Alpha, false); SetEnablePluginImport(Plugins_Beta, true); }
static void SetEnablePluginImport(string[] plugins, bool enable = true) { foreach(var path in plugins) { PluginImporter vrlib = AssetImporter.GetAtPath(path) as PluginImporter; vrlib.SetCompatibleWithPlatform(BuildTarget.Android, enable); } } ```
非常簡單一看就可以懂,然後試一下就明白了。
平臺切換
平臺切換功能主要是在editor裡除錯各個平臺功能的時候使用的選單項,功能也很簡單,就做了下面幾件事情 - 切平臺和巨集定義 - 切playersetting引數 - 切buildscene配置 - 切manifest和依賴包 - 儲存及開啟對應平臺的場景(如果場景不是複用的)
程式碼示例如下,因為我們做vr相關的app,所以在gear平臺時vrsupport為true,其他平臺時為false,如果不同平臺的scene不一樣,那麼在最後問使用者是否儲存當前場景,然後開啟對應平臺的場景。
/// <summary>
/// 切換alpha平臺
/// </summary>
[UnityEditor.MenuItem("CloudBuild/SwitchToAlpha", priority = 50)]
static void SwitchToAlpha()
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, DEFINES_ALPHA);
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, DEFINES_ALPHA);
PlayerSettings.virtualRealitySupported = true;
EditorBuildSettings.scenes = new EditorBuildSettingsScene[] {
new EditorBuildSettingsScene("Assets/Scenes/Init.unity", true),
new EditorBuildSettingsScene("Assets/Scenes/Main.unity", true)
};//場景
UseAndroidManifest("Alpha");
ChangePluginToAlpha();
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
EditorSceneManager.OpenScene("Assets/Scenes/Main.unity");
}
打包
那麼最後打包指令碼如下,粗看資訊量可能比較大,實際只做了幾件事情 - 準備好要打包的scene - 將當前editor的狀態儲存一下,以便打完包恢復,這是為了開發使用方便而已,否則在開發機上打個包就發現editor的很多屬性變了有時很尷尬 - 處理android的簽名問題 - 打包 - 最後如果是windows,一般是開發機,直接開啟build好的apk所在資料夾,方便使用
``` /// /// [UnityEditor.MenuItem("CloudBuild/CloudAlpha-Release")] static void PerformBuildAndroidCloudAlphaRelease() { EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android); string[] scenes = { "Assets/Scenes/Init.unity", "Assets/Scenes/Main.unity" }; string path = GetBuildPathAndroid(); if (scenes == null || scenes.Length == 0 || path == null) { Debug.LogError("error scene is null"); return; } string tempid = PlayerSettings.bundleIdentifier; string name = PlayerSettings.productName; bool virtualRealitySupported = PlayerSettings.virtualRealitySupported; PlayerSettings.virtualRealitySupported = true; PlayerSettings.bundleIdentifier = "net.itsong.vralpha"; PlayerSettings.productName = PRODUCT_NAME; PlayerSettings.Android.keystoreName = ""; PlayerSettings.Android.keyaliasName = ""; PlayerSettings.Android.keyaliasPass = ""; PlayerSettings.Android.keystorePass = ""; PlayerSettings.Android.bundleVersionCode = GlobalConfig.ClientVersionCode; PlayerSettings.bundleVersion = GlobalConfig.ClientVersion; string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android); UseAndroidManifest("Alpha"); ChangePluginToAlpha(); PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, DEFINES_ALPHA + DEFINES_RELEASE); string buildpath = path + string.Format("cloudvr_alpha_release_{0}.apk", DateTime.Now.ToString("MMddHHmm", DateTimeFormatInfo.InvariantInfo)); BuildPipeline.BuildPlayer(scenes, buildpath, BuildTarget.Android, BuildOptions.None); PlayerSettings.virtualRealitySupported = virtualRealitySupported; PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, defines); PlayerSettings.bundleIdentifier = tempid; PlayerSettings.productName = name; PlayerSettings.Android.keystoreName = ""; PlayerSettings.Android.keyaliasName = ""; PlayerSettings.Android.keyaliasPass = ""; PlayerSettings.Android.keystorePass = ""; string dir = path.Replace('/', '\');
if UNITY_EDITOR_WIN
System.Diagnostics.Process.Start("explorer.exe", "\"" + dir + "\"");
endif
} ```
完整的CloudBuild檔案如下:
``` using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System; using System.Globalization; using UnityEditor.SceneManagement; /// /// partial class CloudBuild { const string PRODUCT_NAME = "狂雲歌VR"; const string DEFINES_ALPHA = "CROSS_PLATFORM_INPUT;MOBILE_INPUT;CLOUD_ALPHA;"; const string DEFINES_BETA = "CROSS_PLATFORM_INPUT;MOBILE_INPUT;CLOUD_BETA;"; const string DEFINES_RELEASE = "CLOUD_RELEASE";
static string[] Plugins_Alpha = new string[] {
"Assets/Plugins/Android/libs/armeabi-v7a/alpha.so",
};
static string[] Plugins_Beta = new string[] {
"Assets/Plugins/Android/libs/armeabi-v7a/beta.so",
};
// Build the Android APK and place into main project folder
static string GetBuildPathAndroid()
{
string dirPath = Application.dataPath + "/../build/android/";
if (!System.IO.Directory.Exists(dirPath))
{
System.IO.Directory.CreateDirectory(dirPath);
}
return dirPath;
}
/// <summary>
/// </summary>
[UnityEditor.MenuItem("CloudBuild/CloudAlpha-Release")]
static void PerformBuildAndroidCloudAlphaRelease()
{
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android);
string[] scenes = { "Assets/Scenes/Init.unity", "Assets/Scenes/Main.unity" };
string path = GetBuildPathAndroid();
if (scenes == null || scenes.Length == 0 || path == null)
{
Debug.LogError("error scene is null");
return;
}
string tempid = PlayerSettings.bundleIdentifier;
string name = PlayerSettings.productName;
bool virtualRealitySupported = PlayerSettings.virtualRealitySupported;
PlayerSettings.virtualRealitySupported = true;
PlayerSettings.bundleIdentifier = "net.itsong.vralpha";
PlayerSettings.productName = PRODUCT_NAME;
PlayerSettings.Android.keystoreName = "";
PlayerSettings.Android.keyaliasName = "";
PlayerSettings.Android.keyaliasPass = "";
PlayerSettings.Android.keystorePass = "";
PlayerSettings.Android.bundleVersionCode = GlobalConfig.ClientVersionCode;
PlayerSettings.bundleVersion = GlobalConfig.ClientVersion;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
UseAndroidManifest("Alpha");
ChangePluginToAlpha();
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, DEFINES_ALPHA + DEFINES_RELEASE);
string buildpath = path + string.Format("cloudvr_alpha_release_{0}.apk", DateTime.Now.ToString("MMddHHmm", DateTimeFormatInfo.InvariantInfo));
BuildPipeline.BuildPlayer(scenes, buildpath, BuildTarget.Android, BuildOptions.None);
PlayerSettings.virtualRealitySupported = virtualRealitySupported;
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, defines);
PlayerSettings.bundleIdentifier = tempid;
PlayerSettings.productName = name;
PlayerSettings.Android.keystoreName = "";
PlayerSettings.Android.keyaliasName = "";
PlayerSettings.Android.keyaliasPass = "";
PlayerSettings.Android.keystorePass = "";
string dir = path.Replace('/', '\\');
if UNITY_EDITOR_WIN
System.Diagnostics.Process.Start("explorer.exe", "\"" + dir + "\"");
endif
}
/// <summary>
/// 切換alpha平臺
/// </summary>
[UnityEditor.MenuItem("CloudBuild/SwitchToAlpha", priority = 50)]
static void SwitchToAlpha()
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, DEFINES_ALPHA);
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, DEFINES_ALPHA);
PlayerSettings.virtualRealitySupported = true;
EditorBuildSettings.scenes = new EditorBuildSettingsScene[] {
new EditorBuildSettingsScene("Assets/Scenes/Init.unity", true),
new EditorBuildSettingsScene("Assets/Scenes/Main.unity", true)
};//場景
UseAndroidManifest("Alpha");
ChangePluginToAlpha();
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
EditorSceneManager.OpenScene("Assets/Scenes/Main.unity");
}
/// <summary>
/// 切換beta平臺
/// </summary>
[UnityEditor.MenuItem("CloudBuild/SwitchToBeta", priority = 50)]
static void SwitchToBeta()
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, DEFINES_BETA);
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, DEFINES_BETA);
PlayerSettings.virtualRealitySupported = true;
EditorBuildSettings.scenes = new EditorBuildSettingsScene[] {
new EditorBuildSettingsScene("Assets/Scenes/Init.unity", true),
new EditorBuildSettingsScene("Assets/Scenes/Main.unity", true)
};//場景
UseAndroidManifest("Beta");
ChangePluginToBeta();
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
EditorSceneManager.OpenScene("Assets/Scenes/Main.unity");
}
/// <summary>
/// 使用相應的androidmanifest
/// </summary>
static void UseAndroidManifest(string filename)
{
string src_filename = string.Format("AndroidManifest-{0}.xml", filename);
string dst_filename = "AndroidManifest.xml";
string path = Application.dataPath + "/Plugins/Android/";
File.Copy(path + src_filename, path + dst_filename, true);
PlayerSettings.Android.bundleVersionCode = GlobalConfig.ClientVersionCode;
PlayerSettings.bundleVersion = GlobalConfig.ClientVersion;
AssetDatabase.Refresh();
}
static void ChangePluginToAlpha()
{
SetEnablePluginImport(Plugins_Alpha, true);
SetEnablePluginImport(Plugins_Beta, false);
}
static void ChangePluginToBeta()
{
SetEnablePluginImport(Plugins_Alpha, false);
SetEnablePluginImport(Plugins_Beta, true);
}
static void SetEnablePluginImport(string[] plugins, bool enable = true)
{
foreach(var path in plugins)
{
PluginImporter vrlib = AssetImporter.GetAtPath(path) as PluginImporter;
vrlib.SetCompatibleWithPlatform(BuildTarget.Android, enable);
}
}
}
```
後續
這裡沒寫build ios ipa包的過程,ios的build過程會稍微長一些,要先build好xcode project然後再通過xcode的命令列去打包,所以前半部分與android是可以複用的,只要稍加修改就可以支援ios的build。另外我們現在做vr相關的app,大部分都是android版本,所以apk的管理比較實用。
VR開發或者unity相關交流可以郵件madcloudsong@qq.com 轉載請註明原文連結 http://blog.csdn.net/madcloudsong/article/details/54603548
相關文章
- 【狂雲歌之unity_vr】unity裡獲取text中文字寬度並截斷省略UnityVR
- Unity打包安卓專案問題彙總(持續更新)Unity安卓
- iOS專案的持續整合與管理iOS
- Jenkins搭建持續打包平臺Jenkins
- .net持續整合sonarqube篇之專案管理與使用者管理專案管理
- ET·ci —持續整合驗證平臺
- 持續整合持續部署持續交付_持續整合與持續部署之間的真正區別
- Jenkins 持續整合 Android 專案JenkinsAndroid
- Java專案持續整合檢查項Java
- 使用Hudson持續整合Android專案Android
- 自動化專案Jenkins持續整合Jenkins
- Practice - iOS 專案持續整合實踐(一)iOS
- Practice – iOS 專案持續整合實踐(一)iOS
- 使用Jenkins可持續整合maven專案JenkinsMaven
- 持續整合平臺 01 jenkins 入門介紹Jenkins
- 淺談持續整合的理解以及實現持續整合,需要做什麼?
- 釋出 Spinnaker 1.0:持續的雲交付平臺
- 易趨攜手中科聞歌,聯合搭建專案管理平臺專案管理
- 使用 Xcode Server 持續整合 & 打包測試XCodeServer
- (jenkins)hudson平臺搭建android專案持續化整合以及基於NativeDriver的UI自動化測試環境JenkinsAndroidUI
- LevOJ平臺 - 持續更新
- 關於持續整合打包平臺的Jenkins配置和構建指令碼實現細節Jenkins指令碼
- 前端er,Jenkins持續化整合Webpack專案前端JenkinsWeb
- Framework專案持續整合(jenkins)及集合SonarQubeFrameworkJenkins
- vue + webpack 前端專案持續釋出整合部署VueWeb前端
- 談談持續整合,持續交付,持續部署之間的區別
- 【雲管平臺】多雲混合雲管理平臺用哪個好?
- 前端專案基於GitLab-CI的持續整合/持續部署(CI/CD)前端Gitlab
- 持續整合、持續部署、持續交付、持續釋出
- Unity平臺 | 快速整合華為AGC雲資料庫服務UnityGC資料庫
- 傳統企業如何打造統一的持續整合平臺
- 搭建持續整合介面測試平臺(Jenkins+Ant+Jmeter)JenkinsJMeter
- 持續整合、持續交付、持續部署簡介
- 整合持續整合工具
- Shiro多專案許可權集中管理平臺
- 你的專案管理有持續改進嗎?專案管理
- 經緯恆潤汽車軟體持續整合平臺全新發布
- Jenkins+Ant+Jmeter搭建持續整合的介面測試平臺JenkinsJMeter