在使用UE开发的过程中,指定命令行参数(Command Line)是一个经常会用到的功能,可以方便地控制部分流程、开关等。UE也是大量地使用了Comandline来控制引擎的行为,官方文档:Command-Line Arguments,不同的模块都可以从Commandline中读取或检查参数,实现自定义的命令行参数功能。
但是,对于移动端Android平台,并不能很方便地指定启动参数,需要编辑ue4commandline.txt
文件,非常繁琐。
基于这种痛点需求,我利用元旦的假期开发一个UE的Android的插件,可以让Android的程序像PC一样方便启动和指定命令行参数,并且不需要修改引擎,启用插件后打包Apk即可使用,开源在Github上:hxhb/AppCmderUE。
本篇文章分析了UE在Androdid端Commandline的读取规则,并介绍了AppCmderUE插件的实现原理与使用方法。
在Windows和Mac这种桌面平台上,只需要用CMD或者bash之类的脚本环境中指定程序路径、以及参数即可:
1 | UE4Editor.exe D:\UnrealProject\BlankGame.uproject -game |
它们都会传递到默认程序入口main
函数中:
1 | int main(int argc, char** argv); |
然后再通过FCommandLine::Set
设置到引擎,供运行时使用。
之前介绍也过,使用Unreal Insights也是需要通过给游戏指定-tracehost
和-trace=cpu
来控制游戏Profiling的数据种类和传递到的机器IP。
但是,对于使用UE开发的游戏,并没有很方便的方法来为移动端程序指定启动参数。UE默认提供了一个机制,可以编辑一个ue4commandline.txt文件,将命令行参数填入其中,当引擎启动时会去加载这个文件,并将其追加到引擎的FCommandline之中。
此种方式我在之前的wiki中有具体的介绍:移动端指定启动参数。
这种方式不够优雅,也存在一些问题:
- ue4commandline.txt默认不存在,需要手动创建;
- 每次修改参数都需要访问设备上的文件,修改、保存;
- 不想使用启动参数时还需要手动删除;
- 在Android11上,收紧了权限控制,无法在PC上访问数据目录;
既然发现了这些痛点,就尝试开了个新的项目来解决。为了避免重复造轮子以及看引擎中有哪些可以利用到的流程,查阅代码后发现UE中除了ue4commandline.txt,还有两种方式可以添加命令行参数。
AndroidConfigRuleSystem
- 通过AndroidConfigRuleSystem,安卓配置规则系统,它在GameActivity.java.template#L1579中被读取,并通过JNI调用(Java_com_epicgames_ue4_GameActivity_nativeSetConfigRulesVariables)传递给引擎C++侧,最终在
InitCommandLine
中获取,并追加到引擎的命令行参数。
1 | if (FString* ConfigRulesCmdLineAppend = FAndroidMisc::GetConfigRulesVariable(TEXT("cmdline"))) |
但引擎中也不能够对他进行动态设置,只能在打包时设置,然后在运行时读取。
adb shell setprop
- setprop,通过adb设置一个名为
debug.ue4.commandline
的prop,将参数填入其中,会被追加到启动参数里。在UE4.26引擎中才添加了支持,所以在UE4.25及之前的引擎中无法使用。
1 | // Name of the UE4 commandline append setprop |
使用方法:
1 | adb shell setprop debug.ue4.commandline '-test123' |
如果开启了启动时的LaunchImage:
1 | bool bShowLaunchImage = false; |
则启动的Activity则就从com.epicgames.ue4.GameActivity
变成了com.epicgames.ue4.SplashActivity
。
但是这种方式也有缺点:
- 需要执行两条命令
- 当
setprop
之后,在下次重启之前,该属性一直存在 - 4.25及之前的引擎无法使用
AppCmderUE
我基于UPL和JNI调用,实现了一个结合优点的方案。只需要将插件放入工程启用打包即可,无需任何配置。
先看最终的效果,启动命令:
1 | append_cmd.bat com.imzlp.gworld "-test123" "-test456" "-test789" |
UE Log:
1 | Final commandline: ../../../Blank426/Blank426.uproject -test123 -test456 -test789 |
实现原理:
- Java端通过Intent接受
-e
的参数 - 在OnCreate中通过JNI调用与引擎层交互,将Intent的参数添加到AndroidConfigRuleSystem中
- 利用UE自身的AndroidConfigRuleSystem模式支持,让引擎自动添加至FCommandLine中
代码并不多,但是利用了Java侧GameActivity和引擎的UPL、JNI等执行流程组合起来,才实现了无需修改引擎即可实现所有的功能。
append_cmd.bat
是我封装了adb命令的一个脚本,用于方便使用:
1 | @echo off |
该命令需要传递两个参数:
- UE打包APK的包名,如
com.imzlp.gworld
; - 参数列表,若有多个参数,则使用空格分割。每个参数都需要使用双引号(
""
)包裹,当脚本转发给adb的时候append_cmd.bat
会将参数修改为以/.,;/
分割,为了避免干扰传递参数中包含这些符号;
如:
1 | append_cmd.bat com.imzlp.gworld "-tracehost=127.0.0.1" "-trace=cpu" |
则真实的adb命令为:
1 | adb shell am start -a android.intent.action.MAIN -n com.imzlp.gworld/com.epicgames.ue4.GameActivity --es cmdline "-tracehost=127.0.0.1"/.,;/"-trace=cpu" |
在引擎端会自动解析,把他们再按照原来的格式追加到FCommandLine中。上面的命令执行后,可以直接在PC上看到Profiling数据:
再例如,在Android11系统上时,因为系统权限收紧,没有root的系统无法再直接访问UE的沙盒路径(Android/data/com.xxx.xxxx
)下的文件。
如果游戏的数据路径在沙盒目录下,就无法方便地取出设备中的log文件,利用AppCmderUE插件可以方便地指定UE的log到其他路径,而无需对工程做任何修改。
1 | append_cmd.bat com.imzlp.GWorld "-abslog=/storage/emulated/0/GWorld.log" |
所以,当使用append_cmd.bat
脚本执行,就会自动拉起手机上的App启动,并追加了参数,实现了与在PC上类似的功能。
本方案只适用于Android,对于IOS的类似方案,有时间再具体研究。
ADB over Wi-Fi
除了使用USB连接Android设备外,adb还支持wifi模式,基于本文的hxhb/AppCmderUE项目和ADB over WiFi结合,可以非常方便地远程Profiling。
- USB连接设备
- 让设备的
adbd
守护进程监听5555端口,等待PC连接
1 | adb tcpip 5555 |
- 查看设备IP,获取
wlan0
的IP地址
1 | adb shell ifconfig |
断开设备的USB连接
通过Adb远程连接设备
1 | adb connect DEVICE_IP:5555 |
- 通过adb devices即可看到远程连接的设备
当远程adb环境OK之后,就可以使用append_cmd.bat
远程启动并Profiling UE项目:
1 | append_cmd.bat com.imzlp.gworld "-tracehost=192.168.31.175" "-trace=cpu" |
在PC上的Unreal Insights同样可以捕获Profiling数据:
结语
基于hxhb/AppCmderUE项目可以非常方便地给UE开发的APP传递启动参数,对于开发调试和性能分析来说都是极大提升效率的。