请选择 进入手机版 | 继续访问电脑版

石家庄老站长

点击联系客服
客服QQ: 客服微信:
 找回密码
 立即注册
查看: 5|回复: 0

[游戏开发高级]Unity教我通过Jenkins自动包装 这种工作交给了企划(保姆级教程|命令行包装|自动构建)

[复制链接]

1

主题

1

帖子

-7

积分

限制会员

积分
-7
发表于 2021-7-21 15:22:05 | 显示全部楼层 |阅读模式
文章目录

一、前言2、詹金斯简介3、詹金斯的下载和安装1、JDK的下载和安装2、詹金斯的下载3、詹金斯的安装4、詹金斯的初始化4、詹金斯的基本工作1、詹金斯的关闭1.1方法2:以管理员身份运行net  stop  jenkins1.3、方法3: 方法2:安装CLI命令行(需要科学、互联网)6.3运行Python作业10,定期运行作业5,实践:Unity  Jenkins1,Unity  Demo项目1.1,Demo项目编写1.2,Android平台转换1.3,JDK

一、前言

大家好,我是新的。

前几天我写了一篇文章[游戏开发高级]自己制作离线Mayben仓库,实现Unity离线环境,使用Gradle套餐(Unity  | Android  | Google  | Gradle),其中提到Unity使用Jenkins自动包装。
/img-blog.csdnimg.cn/20210716185404460.png?x-oss-process=/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpbnhpbmZh,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述" />
不过那篇文章中我只是一笔带过,没有细说具体操作流程。今天,我就专门写一篇关于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~59HOUR小时0~23DOM一个月中的第几天1~31MONTH月1~12DOW星期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
构建一个32位的Windows平台的exe(例如:-buildWindowsPlayer path/to/your/build.exe)

-buildWindows64Player
构建一个64位的Windows平台的exe(例如:-buildWindows64Player path/to/your/build.exe)

-importPackage
导入一个的package,不会显示导入对话框

-createProject
根据提供的路径建立一个空项目

-projectPath
打开指定路径的项目

-logFile
指定输出的日志文件

-nographics
当运行在批处理模式,不会初始化显卡设备,不需要GPU参与;但如果你需要执行光照烘焙等操作,则不能使用这个参数,因为它需要GPU运算。

-executeMethod
在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:"))
            {
                string version = s.Split(':')[1];
                // 设置版本号
                PlayerSettings.bundleVersion = version;
            }
        }
      
        // 执行打包
        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!");
    }
}

重新执行命令:


然后我们安装一下APK,看看APP名字是不是哈哈哈,


在应用信息里可以看到版本号也是我们命令行中设置的1.2.0.0,



4、Jenkins调用bat脚本
我们回到Jenkins页面中,创建一个带参数的任务,
appName参数:



version参数:


命令行:
E:\UnityProject\UnityDemo\bat\build_app.bat %appName% %version%,如下:


执行Jenkins任务,如下:


等等运行结果:


执行完毕,我们看下输出的日志,


可以看到我们的bat脚本被正确执行了,参数也传递正确,


APK也可以正常生成,


安装到模拟器上,可以看到名字正确,


版本号也正确,


流程走通了,剩下的就是根据自己的需求进行扩展啦,比如打包前先执行一下svn更新之类的,需要额外参数,就在Jenkins中添加,传递到bat脚本中,再传递到Unity中,最后根据参数进行打包。

5、拓展:python加强版脚本
我个人其实不是特别喜欢写bat脚本,我更喜欢写pytho,于是,我就写了个python版的脚本,脚本中我加了监控Unity日志输出的逻辑,方便进行一些判断,画个图:


python完整代码如下:

import os
import sys
import time

# 设置你本地的Unity安装目录
unity_exe = 'D:/software/Unity/2021.1.7f1c1/Editor/Unity.exe'
# unity工程目录,当前脚本放在unity工程根目录中
project_path = 'E:/UnityProject/UnityDemo'
# 日志
log_file = os.getcwd() + '/unity_log.log'

static_func = 'BuildTools.BuildApk'

# 杀掉unity进程
def kill_unity():
    os.system('taskkill /IM Unity.exe /F')

def clear_log():
    if os.path.exists(log_file):
        os.remove(log_file)

# 调用unity中我们封装的静态函数
def call_unity_static_func(func):
    kill_unity()
    time.sleep(1)
    clear_log()
    time.sleep(1)
    cmd = 'start %s -quit -batchmode -projectPath %s -logFile %s -executeMethod %s --productName:%s --version:%s'%(unity_exe,project_path,log_file,func, sys.argv[1], sys.argv[2])
    print('run cmd:  ' + cmd)
    os.system(cmd)

   

# 实时监测unity的log, 参数target_log是我们要监测的目标log, 如果检测到了, 则跳出while循环   
def monitor_unity_log(target_log):
    pos = 0
    while True:
        if os.path.exists(log_file):
            break
        else:
            time.sleep(0.1)
    while True:
        fd = open(log_file, 'r', encoding='utf-8')
        if 0 != pos:
            fd.seek(pos, 0)
        while True:
            line = fd.readline()
            pos = pos + len(line)
            if target_log in line:
                print(u'监测到unity输出了目标log: ' + target_log)
                fd.close()
                return
            if line.strip():
                print(line)
            else:
                break
        fd.close()

if __name__ == '__main__':
    call_unity_static_func(static_func)
    monitor_unity_log('Build App Done!')
    print('done')

我们把脚本保存为build_app.py,


把Jenkins中的命令改为执行python脚本:
call python E:\UnityProject\UnityDemo\bat\build_app.py %appName% %version%
如下:


执行一下任务,


耐心等待执行结果,


执行完毕可以看到监控到的日志,


我们在python中输出的日志都可以在Jenkins的Console Output中看到,


APK顺利生成,Very Good,完美~



六、完毕
好了,就写这么多吧,我是新发,喜欢我的可以点赞、关注,有任何技术上的疑问欢迎评论或留言~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|无图版|手机版|小黑屋|石家庄@IT精英团

GMT+8, 2021-8-5 13:39 , Processed in 0.202800 second(s), 27 queries .

Powered by Discuz! X3.4

© 2001-2021 Comsenz Inc.

快速回复 返回顶部 返回列表