游戏开发进阶教你Unity通过Jenkins实现自动化打包,打包这种事情就交给策划了(保姆级教程 | 命令行打包 | 自动构建)
Posted 林新发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发进阶教你Unity通过Jenkins实现自动化打包,打包这种事情就交给策划了(保姆级教程 | 命令行打包 | 自动构建)相关的知识,希望对你有一定的参考价值。
文章目录
一、前言
嗨,大家好,我是新发。
前几天我写了一篇文章,【游戏开发进阶】教你自制离线Maven仓库,实现Unity离线环境使用Gradle打包(Unity | Android | 谷歌 | Gradle),里面我提到了Unity
使用Jenkins
实现自动化打包,
不过那篇文章中我只是一笔带过,没有细说具体操作流程。今天,我就专门写一篇关于Unity
通过Jenkins
实现自动化打包的教程吧~
特别说明:
我的电脑系统环境是Windows 10
,所以下面的操作环境都是在Windows 10
系统下的。
二、Jenkins简介
相信很多人都知道Jenkins
,不过为了照顾萌新,我这里还是简单说下Jenkins
是什么。
Jenkins
官网:https://www.jenkins.io/
Jenkins
是一个开源软件项目,是基于Java
开发的一个持续集成工具(CI
),具有友好的操作界面,主要用于持续、自动的构建/测试软件项目、监控外部任务的运行。它可以在Tomcat
等流行的servlet
容器中运行,也可独立运行。通常与版本管理工具(SCM
)、构建工具结合使用。常用的版本控制工具有SVN
、GIT
,构建工具有Maven
、Ant
、Gradle
。
注:
什么是集成?
代码由编译、发布和测试、直到上线的一个过程。
什么是持续集成?
高效的、持续性的不断迭代代码的集成工作。
这样讲好像也不是很直观,没关系,它就是一个工具,我们学会使用它就好,下面我来一步步教大家如何使用Jenkins
。
三、Jenkins的下载与安装
1、JDK下载与安装
因为Jenkins
是基于Java
开发的,要运行Jenkins
需要Java
环境,即JDK
,所以我们需要先安装下JDK
。
JDK
下载:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
根据你的系统环境选择对应的JDK
下载,
下载下来后双击即可执行安装,安装过程没什么,这里就不啰嗦了。
安装完毕后,配置一下JDK
的 环境变量。
最后在命令行中输入java -version
,如果能正常输出版本号,则说明JDK
环境弄好了。
2、Jenkins下载
进入Jenkins
官网:https://www.jenkins.io/
点击Download
,
根据你的系统和环境选择对应的安装包,因为我是Windows
系统,所以我下载Windows
版的安装包,
下载下来是一个msi
文件,
3、Jenkins安装
双击jenkins.msi
,执行安装,设置一下安装路径,
选择Run service as LocalSystem
(即使用本地系统账号)
设置端口号,比如我设置为8075
,然后点击Test Port
按钮测试一下端口有没有被占用,
确认端口没被占用后,点击Next
,
设置JDK
所在的路径,
继续Next
,
点击Install
开始安装,
注意,安装过程中可能会弹出360
提醒,选择允许即可。
完整完毕,
4、Jenkins初始化
上面安装完毕后会自动启动Jenkins
服务,我们可以在任务管理器中看到一个Java
的进程,它就是Jenkins
的服务进程。
我们在浏览器中访问 http://localhost:8075
,此时会显示需要解锁Jenkins
,如下
我们找到这个initialAdminPassword
文件,使用文本编辑器打开它,
可以看到里面是一串密码,我们复制它,
回到浏览器页面中,在管理员密码栏中粘贴刚刚的密码,然后点击继续,
接下来是插件安装界面,因为Jenkins
插件的下载需要翻墙,所以如果你可以科学上网,则点击安装推荐的插件
,当然也可以先不安装插件,后续有需要再安装对应的插件即可,
如果是离线环境(比如内网环境),则点击跳过插件安装(下文我会教如何在离线环境下安装插件),
接着创建管理员账号,
完成,进入Jenkins
主页,
四、Jenkins的基本操作
1、关闭Jenkins
1.1、方式一:暴力杀进程(不推荐)
上面我们说到,在任务管理器中可以看到一个Java
进程,它就是Jenkins
的服务进程,
如果你直接暴力杀掉这个Java
进程,那么Jenkins
也就关闭了,不过不建议这么做。
1.2、方式二:以管理员身份执行 net stop jenkins
以管理员身份运行命令net stop jenkins
,如下(我是使用管理员身份运行PowerShell
来执行命令的)
注意,如果你不是以管理员身份执行上面的命令,则会提示
发生系统错误 5
如下(普通账号权限下通过cmd
执行命令)
如何以管理员身份运行cmd?
进入cmd
所在目录:
C:\\Users\\linxinfa\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\System Tools
右键命令提示符
,点击以管理员身份运行
即可,
如果觉得麻烦的话,也可以直接在系统的开始菜单那里直接以管理员身份运行PowerShell
,
1.3、方式三:通过jenkins.exe来关闭,jenkins stop
进入Jenkins
的安装目录,如下,
在地址栏输入cmd
,然后执行jenkins stop
,如下,与上面的效果是一样的,
2、启动Jenkins
2.1、方式一:以管理员身份执行 net start jenkins
以管理员身份执行命令net start jenkins
,如下
2.2、方式二:通过jenkins.exe来启动,jenkins start
进入Jenkins
的安装目录,
执行命令jenkins start
,如下
如果想重启Jenkins
,则执行jenkins restart
,如下
3、修改端口号
先关闭Jenkins
,进入Jenkins
的安装目录,可以看到里面有一个jenkins.xml
,使用文本编辑器打开它,
把--httpPort
的端口改为别的,比如我改成8076
,
重新启动Jenkins
服务,在浏览器中使用新的端口进行测试,能够正常访问则说明端口修改成功了。
4、新建账号
Jenkins
可能需要多人登录,我们可以新建一些账号供其他人登录。
在Jenkins
主页的左侧栏中点击Manage Jenkins
,
接着点击Manager Users
,
然后点击Create User
,
输入要创建的新账号的账号密码,点击创建即可,
创建成功,可以看到多了一个账号了,
我们可以退出当前账号,使用这个新账号登录,
登录成功,
5、修改密码
点击账号的齿轮按钮,
修改Password
,点击Save
即可,
6、安装插件
6.1、方式一:通过Manage Plugins安装(需要科学上网)
在Manage Jenkins
页面中,点击Manage Plugins
,
搜索需要的插件名称进行安装即可(需要能科学上网才行)
6.2、方式二:CLI命令行安装(需要科学上网)
Jenkins CLI
就是Jenkins
的命令行工具,类似于MacOS
的终端。
我们可以在Jenkins
的Manage Jenkins
页面中看到Jenkins CLI
,点击进入,
点击jenkins-cli.jar
,
把下载下来的jenkins-cli.jar
放到Jenkins
安装目录中,
接着我们就可以通过命令来操作Jenkins
了,具体命令参数可以看Jenkins CLI
页面,
我们可以看到安装插件的参数是install-plugin
,
点击去可以看到具体的使用方法,
我们进入jenkins-cli.jar
所在的目录,通过下面的命令即可安装插件,(注意端口根据你的Jenkins
的实际端口号而定)
java -jar jenkins-cli.jar -s http://localhost:8075/ 插件名
如果不清楚插件名可以上Jenkins
的插件官网查看:https://plugins.jenkins.io/
以Maven Integration
插件为例,搜索Maven Integration
,点击搜索到的插件,
点击Releases
页面,即可看到,插件名就是maven-plugin:3.12
,
对应的插件安装命令就是:
java -jar jenkins-cli.jar -s http://localhost:8075/ maven-plugin:3.12
注意,你可能会提示
ERROR: anonymous is missing the Overall/Read permission
我们需要在Configure Global Security
中勾选项目矩阵授权策略
,给Anonymous
添加Administer
权限即可。
6.3、方式三:离线环境安装插件
上面两种方式都需要联网,而我们有可能需要把Jenkins
部署在离线环境的电脑上(比如内网),这个时候就只能通过离线安装的方式了。
这个时候,我们需要先在有网络(能科学上网)的电脑上下载安装插件。
安装好的插件可以在这个目录中找到:
C:\\Windows\\System32\\config\\systemprofile\\AppData\\Local\\Jenkins\\.jenkins\\plugins
将其拷贝到内网机的相同路径中,然后重启Jenkins
即可。
7、创建并执行任务:Hello World
我以创建一个HelloWorld
任务为例来演示一下。
点击New Item
,
输入任务名,比如HelloWorld
,点击Freesytyle project
,点击OK
,
输入任务描述,
Build
选项选择Execute Windows batch command
(即批处理,也就是我们说的bat
)
然后在Command
中编写我们要执行的bat
命令,比如
echo "Hello World"
如下
最后点击保存,
这样我们的任务就创建成功了,我们可以点击Build Now
来执行这个任务,
按F5
刷新一下浏览器,可以看到任务执行的进度,
执行完后我们可以查看对应的日志,
从日志中我们可以看到我们输出的Hello World
,
8、执行带参数的任务
有时候我们需要创建带参数的任务。
我们勾选This project is parameterized
,然后点击Add Parameter
,可以看到它提供了多种类型的参数,
我以选择项
参数为例,
分别填写参数名、选项(每个选项一行)、描述,
编写bat
命令,
点击Build with Parameters
,然后设置好参数,
最后点击Build
,
执行完毕可以看到输出日志,结果正确,
9、执行python任务
我们看到任务Build
中并没有Phython
的选项,
但我们又想要让Jenkins
可以执行Python
,怎么办呢?很简单,在bat
中call python
就好啦,
其中python
代码如下:
print("Hello, I am python")
最后执行任务,输出日志如下,结果正确,
10、周期性触发执行任务
有时候我们需要周期性地执行任务,比如每天8
点触发一次执行任务,或者每隔30
分钟触发一次执行任务。
在Build Triggers
(触发器)中勾选Build periodically
,
然后在Schedule
中编写规则。
格式:
MINUTE HOUR DOM MONTH DOW
字段 | 说明 | 取值范围 |
---|---|---|
MINUTE | 分钟 | 0~59 |
HOUR | 小时 | 0~23 |
DOM | 一个月中的第几天 | 1~31 |
MONTH | 月 | 1~12 |
DOW | 星期 | 0~7(0和7代表的都是周日) |
语法:
*
:匹配范围内所有值,例:* * * * *
M-N
:匹配M~N
范围内所有值,例:10-30 * * * *
M-N/X
:在指定M~N
范围内每隔X
构建一次,例:10-30/5 * * * *
*/X
:整个有效区间内每隔X
构建一次,例:*/30 * * * *
A,B,...,Z
:匹配多个值,例:10,20,30 * * * *
关于符号H:
为了在系统中生成定时任务,符号H
(代表Hash
,后面用散列
代替)应该用在可能用到的地方,例如:为十几个日常任务配置0 0 * * *
将会在午夜产生较大峰值。相比之下,配置H H * * *
仍将每天一次执行每个任务,不是都在同一时刻,可以更好的使用有限资源。
符号H
可用于范围,例如,H H(0-7) * * *
代表凌晨0:00
到 上午7:59
一段时间。
符号H
在一定范围内可被认为是一个随机值,但实际上它是任务名称的一个散列而不是随机函数。
案例:
每30分钟
构建一次
H/30 * * * *
每2小时
构建一次
H H/2 * * *
每天早上8点
构建一次
0 8 * * *
每天的8点
,12点
,22点
,一天构建3
次
0 8,12,22 * * *
每前半小时中每隔10分钟
构建一次
H(0-29)/10 * * * *
每个工作日从早上9点45分
开始到下午4点45分
结束这段时间内每间隔2小时
的45分钟
那一刻构建一次
45 9-16/2 * * 1-5
每月(除了12月
)从1号
到15号
这段时间内某刻构建一次
H H 1,15 1-11 *
好了,案例就列举这么多了。
现在,为了演示,我设置为每隔1
分钟执行一次,
命令如下,
可以看到它每分钟就触发执行一次任务,
五、实战:Unity + Jenkins
下面我演示一下通过Jenkins
来调用Unity
打包android
的APK
。
我先画个流程图,方便大家理解:
现在,我们开始吧。
1、Unity Demo工程
1.1、创建Demo工程
创建一个Unity
工程,
简单弄点东西,
1.2、切换Android平台
点击 File / Build Settings
菜单,切换成Android
平台,
1.3、设置JDK、Android SDK、Gradle
点击Edit / Preferences
,在External Tools
中设置好JDK
、Android SDK
、Gradle
,
1.4、设置包名
在Player Settings
中设置一下包名,比如com.linxinfa.test
,
1.5、测试打包
添加要打包的场景,手动点击Build
,测试一下是否能正常打出APK
,
可以正常打出APK
,说明打包环境设置都正确,
2、编写Editor打包工具
2.1、Editor打包工具代码
新建一个Editor
文件夹,
在Editor
文件夹中新建一个BuildTools
脚本,
BuildTools.cs
脚本代码如下:
using UnityEngine;
using UnityEditor;
public class BuildTools
{
[MenuItem("Build/Build APK")]
public static void BuildApk()
{
BuildPlayerOptions opt = new BuildPlayerOptions();
opt.scenes = new string[] { "Assets/Scenes/SampleScene.unity" };
opt.locationPathName = Application.dataPath + "/../Bin/test.apk";
opt.target = BuildTarget.Android;
opt.options = BuildOptions.None;
BuildPipeline.BuildPlayer(opt);
Debug.Log("Build App Done!");
}
}
2.2、执行Editor打包工具菜单
点击菜单Build / Build Apk
,
可以正常打出APK
,
3、命令行调用Unity静态函数:打包函数
3.1、Unity命令行模式
Unity
提供了命令行模式给开发者,我们可以写bat
脚本来调用Unity
中的静态函数,比如我们的打包函数。
格式:
Unity程序 -参数 -projectPath 工程地址 -executeMethod 静态函数
例:
"D:\\software\\Unity\\2021.1.7f1c1\\Editor\\Unity.exe" ^
-quit ^
-batchmode ^
-projectPath "E:\\UnityProject\\UnityDemo" ^
-executeMethod BuildTools.BuildApk ^
-logFile "E:\\UnityProject\\UnityDemo\\output.log"
注:为了阅读方便,命令我写成多行,在
bat
中连接多行的符号是^
我们可以在Unity
官方手册看到具体的命令参数说明:https://docs.unity3d.com/Manual/CommandLineArguments.html
3.2、命令参数解释
-batchmode
在 批处理模式下运行Unity
,它不会弹出窗口。当脚本代码在执行过程中发生异常或其他操作失败时Unity
将立即退出,并返回代码为1
。
-quit
命令执行完毕后将退出Unity
编辑器。请注意,这可能会导致错误消息被隐藏(但他们将显示在Editor.log
文件)
-buildWindowsPlayer <pathname>
构建一个32位
的Windows
平台的exe
(例如:-buildWindowsPlayer path/to/your/build.exe
)
-buildWindows64Player <pathname>
构建一个64位
的Windows
平台的exe
(例如:-buildWindows64Player path/to/your/build.exe
)
-importPackage <pathname>
导入一个的package
,不会显示导入对话框
-createProject <pathname>
根据提供的路径建立一个空项目
-projectPath <pathname>
打开指定路径的项目
-logFile <pathname>
指定输出的日志文件
-nographics
当运行在批处理模式,不会初始化显卡设备,不需要GPU
参与;但如果你需要执行光照烘焙等操作,则不能使用这个参数,因为它需要GPU
运算。
-executeMethod <ClassName.MethodName>
在Unity
启动的同时会执行静态方法。也就是说,使用executeMethod
我们需要在编辑文件夹有一个脚本并且类里有一个静态函数。
-single-instance
在同一时间只允许一个游戏实例运行。如果另一个实例已在运行,然后再次通过-single-instance
启动它的话会调节到现有的这个实例。
-nolog
不产生输出日志。 通常output_log.txt
被写在游戏输出目录下的*_Data
文件夹中。
3.3、批处理脚本
我们知道,一个Unity
工程只能打开一个实例,所以如果我们已经手动用Unity
打开了工程,此时执行下面这个命令是会报错的,
"D:\\software\\Unity\\2021.1.7f1c1\\Editor\\Unity.exe" ^
-quit ^
-batchmode ^
-projectPath "E:\\UnityProject\\UnityDemo" ^
-executeMethod BuildTools.BuildApk ^
报错如下:
Aborting batchmode due to fatal error:
It looks like another Unity instance is running with this project open.
Multiple Unity instances cannot open the same project.
我们需要先判断Unity
是否在运行中,如果是,则先将旧的Unity
实例进程杀掉,对应的bat
代码如下:
::判断Unity是否运行中
TASKLIST /V /S localhost /U %username%>tmp_process_list.txt
TYPE tmp_process_list.txt |FIND "Unity.exe"
IF ERRORLEVEL 0 (GOTO UNITY_IS_RUNNING)
ELSE (GOTO START_UNITY)
:UNITY_IS_RUNNING
::杀掉Unity
TASKKILL /F /IM Unity.exe
::停1秒
PING 127.0.0.1 -n 1 >NUL
GOTO START_UNITY
:START_UNITY
:: 此处执行Unity打包
另外,我们想要在执行打包时传入一些参数,比如APP名字
、版本号
等,可以在命令中加上,格式可以自定义,我们只需在后面的C#
代码中进行相应的解析即可,例:
--productName:%1 --version:%2
其中%1
表示参数1
,%2
表示参数2
,
完整命令如下:
"D:\\software\\Unity\\2021.1.7f1c1\\Editor\\Unity.exe" ^
-quit ^
-batchmode ^
-projectPath "E:\\UnityProject\\UnityDemo" ^
-executeMethod BuildTools.BuildApk ^
--productName:%1 ^
--version:%2
整合上面的Unity
进程判断,最终完整的bat
代码如下:
::判断Unity是否运行中
TASKLIST /V /S localhost /U %username%>tmp_process_list.txt
TYPE tmp_process_list.txt |FIND "Unity.exe"
IF ERRORLEVEL 0 (GOTO UNITY_IS_RUNNING)
ELSE (GOTO START_UNITY)
:UNITY_IS_RUNNING
::杀掉Unity
TASKKILL /F /IM Unity.exe
::停1秒
PING 127.0.0.1 -n 1 >NUL
GOTO START_UNITY
:START_UNITY
:: 此处执行Unity打包
"D:\\software\\Unity\\2021.1.7f1c1\\Editor\\Unity.exe" ^
-quit ^
-batchmode ^
-projectPath "E:\\UnityProject\\UnityDemo" ^
-executeMethod BuildTools.BuildApk ^
-logFile "E:\\UnityProject\\UnityDemo\\output.log" ^
--productName:%1 ^
--version:%2
将上面的bat
代码保存为build_app.bat
,我们通过命令行去执行这个build_app.bat
,如下:
可以看到此时能打出APK
,
在输出的日志文件中我们也可以看到我们Debug.Log
输出的日志,
3.4、Unity打包工具接收命令行参数
虽然我们上面的bat
脚本传递了包名和版本号两个参数,但是我们在Unity
的打包工具中并没有对这两个参数进行解析,现在,我们补上解析参数的逻辑吧。
// 解析命令行参数
string[] args = System.Environment.GetCommandLineArgs();
foreach (var s in args)
{
if (s.Contains("--productName:"))
{
string productName= s.Split(':')[1];
// 设置app名字
PlayerSettings.productName = productName;
}
if (s.Contains("--version:"))
{
string version = s.Split(':')[1];
// 设置版本号
PlayerSettings.bundleVersion = version;
}
}
打包工具完整代码如下:
// BuildTools.cs
using UnityEngine;
using UnityEditor;
public class BuildTools
{
[MenuItem("Build/Build APK")]
public static void BuildApk()
{
// 解析命令行参数
string[] args = System.Environment.GetCommandLineArgs();
foreach (var s in args)
{
if (s.Contains("--productName:"))
{
string productName= s.Split(':')[1];
// 设置app名字
PlayerSettings.productName = productName;
}
if (s.Contains("--version:"))
以上是关于游戏开发进阶教你Unity通过Jenkins实现自动化打包,打包这种事情就交给策划了(保姆级教程 | 命令行打包 | 自动构建)的主要内容,如果未能解决你的问题,请参考以下文章
游戏开发进阶教你自制离线Maven仓库,实现Unity离线环境使用Gradle打包(Unity | Android | 谷歌 | Gradle)
游戏开发进阶教你自制离线Maven仓库,实现Unity离线环境使用Gradle打包(Unity | Android | 谷歌 | Gradle)
游戏开发进阶篇教你在Windows平台编译tolua runtime的各个平台库(Unity | 热更新 | tolua | 交叉编译)
游戏开发进阶篇教你在Windows平台编译tolua runtime的各个平台库(Unity | 热更新 | tolua | 交叉编译)