UE:UPL与JNI调用的最佳实践

When developing Android with UE4, it is sometimes necessary to obtain platform-related information or perform platform-related operations. In such cases, it is essential to add Java code in the codebase and call it from C++. Some requirements also necessitate receiving events from the Java side in the game, requiring handling the Java calling C++ process.

This article mainly involves the following sections:

  • Adding Java code to UE projects
  • Java function signature rules
  • Java calling C++ functions
  • C++ calling Java functions

How to utilize UE’s UPL features, Java’s signature rules, and implement JNI calls in UE will be detailed in the article.

Read more »

Hook is a mechanism that allows you to meet your requirements by intercepting and hooking into certain events. Unlike traditional low-level hooks, this article mainly introduces how to use similar hooking mechanisms in UE to achieve business needs.

Some requirements necessitate the global modification of all objects of a specific class, such as playing a uniform sound effect for a certain type of Button in the UI. If every control needs to listen to its OnClicked and then play the sound, it leads to a lot of redundant operations. So, I want to find a method that can globally listen to the click events of all UButtons and handle them collectively. Alternatively, if I want to control an attribute that is invisible in blueprints, modifying the engine’s code for simple needs seems counterproductive.

These requirements can be achieved through UE’s reflection mechanism. This article provides a simple implementation analysis.

Read more »

Error of Not Importing Provision

Other errors during compilation:

1
2
3
4
5
6
7
8
9
10
11
****** [6/11] Compile UE4Game IOS

Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Tag-Compile UnrealHeaderTool Mac.xml
Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Update Version Files\Tag-Update Version Files.xml
Reading shared manifest from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Manifest.xml
Running: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\UnrealBuildTool.exe UE4Game IOS Development -NoUBTMakefiles -nobuilduht -precompile -allmodules -nolink -nodebuginfo -Manifest=C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Build\Manifest.xml -NoHotReload -log="C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UE4Game-IOS-Development.txt"
[Remote] Using remote server 'xx.xx.xx.xxx' on port 22 (user 'buildmachine')
[Remote] Using private key at C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Build\NotForLicensees\SSHKeys\10.75.27.129\buildmachine\RemoteToolChainPrivate.key
[Remote] Using base directory '/Users/buildmachine/UE4/Builds/lipengzha-PC2'
ERROR: Unable to find mobile provision for UE4Game. See log for more information.
Took 4.0300959s to run UnrealBuildTool.exe, ExitCode=6

This is because the provision was not configured during the engine compilation, and it needs to be set in Engine/Config/BaseEngine.ini.

Here you can only specify the name of the provision file. The code in UEBuildIOS.cs will look for it under the path C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribtion)
{
// ...
if(!string.IsNullOrEmpty(MobileProvision))
{
DirectoryReference MobileProvisionDir;
if(BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
{
MobileProvisionDir = DirectoryReference.Combine(new DirectoryReference(Environment.GetEnvironmentVariable("HOME")), "Library", "MobileDevice", "Provisioning Profiles");
}
else
{
MobileProvisionDir = DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.LocalApplicationData), "Apple Computer", "MobileDevice", "Provisioning Profiles");
}

FileReference PossibleMobileProvisionFile = FileReference.Combine(MobileProvisionDir, MobileProvision);
if(FileReference.Exists(PossibleMobileProvisionFile))
{
MobileProvisionFile = PossibleMobileProvisionFile;
}
}
// ...
}

This process can also be imported through the engine, which requires not just importing the provision but also importing the certificate (which can also be imported via iPhonePackager):

If not importing, the following error will occur:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
****** [7/12] Compile UE4Game IOS

Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Tag-Compile UnrealHeaderTool Mac.xml
Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Update Version Files\Tag-Update Version Files.xml
Reading shared manifest from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Manifest.xml
Running: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\UnrealBuildTool.exe UE4Game IOS Development -NoUBTMakefiles -nobuilduht -precompile -allmodules -nolink -nodebuginfo -Manifest=C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Build\Manifest.xml -NoHotReload -log="C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UE4Game-IOS-Development.txt"
[Remote] Using remote server '192.168.1.123' on port 22 (user 'buildmachine')
[Remote] Using private key at C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Build\NotForLicensees\SSHKeys\192.168.1.123\buildmachine\RemoteToolChainPrivate.key
[Remote] Using base directory '/Users/buildmachine/UE4/Builds/lipengzha-PC3'
[Remote] Uploading C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UE4Game\IOS\Development\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision
[Remote] Exporting certificate for C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision...
Executing iPhonePackager ExportCertificate C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Source -provisionfile C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision -outputcertificate C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UE4Game\IOS\Development\Certificate.p12
CWD: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\IOS
Initial Dir: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Source
Env CWD: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\IOS
BranchPath = lipengzha-PC3/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Binaries --- GameBranchPath = lipengzha-PC3/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Binaries

----------
Executing command 'ExportCertificate' '' ...
Looking for a certificate that matches the application identifier '9TV4ZYSS4J.com.xxxxx.xxxx.xx'
.. Provision entry SN '61B440405D86B84D' matched 0 installed certificate(s)
.. Failed to find a valid certificate that was in date
IPP ERROR: Failed to find a valid certificate

ERROR: IphonePackager failed.
Took 2.9281688s to run UnrealBuildTool.exe, ExitCode=6

After resolving the issues above, the BuildGraph process can be executed normally, exporting the binary engine compiled according to the engine code, and upon startup, it can be seen that packaging for Windows/Android/IOS is available.

## Error Troubleshooting

No certificate for team xxxx matching

This issue is likely caused by a mismatch between the configured certificate in the project and the one in the system (or a single mobileProvision corresponding to multiple certificates). After performing the following steps, a machine restart is needed for the changes to take effect.

If you encounter the following error message:

1
Code Signing Error: No certificate for team '9TV4ZYSS4J' matching 'iPhone Developer: Created via API (JDPXHYVWYZ)' found: Select a different signing certificate for CODE_SIGN_IDENTITY, a team that matches your selected certificate, or switch to automatic provisioning.

Solution:

  1. Clean up the extra mobileprovision files in ~/Library/MobileDevice/Provisioning Profiles on Mac.
  2. Clear any expired developer certificates in the Mac keychain.
  3. Reimport the mobileprovision and certificates.

Note: The name of the imported mobileprovision file must match the MobileProvision specified in BaseEngine.ini.

errSecInternalComponent error

Troubleshooting steps:

Execute the codesign command directly on the machine’s bash to see if a password prompt appears. If it does, it indicates that the keychain is locked.
Signing command:

1
codesign --force --sign 36126F8C735011DCC435309E03BC0B85C28CE7CB --verbose --preserve-metadata=identifier,entitlements,flags --timestamp=none /Users/buildmachine/UE4/Builds/Client/Binaries/IOS/Payload/FGame.app

Method One

This is due to the lack of permission to access the keychain when calling /usr/bin/codesign via ssh. You can use the following command to unlock in ssh:

1
security unlock-keychain -p password login.keychain

During UE remote builds, you can run this command to unlock the keychain in the current ssh environment, allowing subsequent signing to execute normally. Modify the Engine\Build\BatchFiles\Mac\Build.sh file to include the following content before calling UBT for compilation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh

cd "`dirname "$0"`/../../../.."

# Setup Mono
source Engine/Build/BatchFiles/Mac/SetupMono.sh Engine/Build/BatchFiles/Mac

if [ "$4" == "-buildscw" ] || [ "$5" == "-buildscw" ]; then
echo Building ShaderCompileWorker...
mono Engine/Binaries/DotNET/UnrealBuildTool.exe ShaderCompileWorker Mac Development
fi
echo unlock mac keychain...
security unlock-keychain -p password login.keychain
echo Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"
mono Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"

ExitCode=$?
if [ $ExitCode -eq 254 ] || [ $ExitCode -eq 255 ] || [ $ExitCode -eq 2 ]; then
exit 0
else
exit $ExitCode
fi

As the Build.sh will be passed to Mac via RSync during compilation, the following log can be seen:

1
2
3
4
5
6
7
8
9
10
11
[Remote] Executing build
Running bundled mono, version: Mono JIT compiler version 5.16.0.220 (2018-06/bb3ae37d71a Fri Nov 16 17:12:11 EST 2018)
unlock mac keychain...
Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe UnrealHeaderTool Mac Development -SkipRulesCompile -XmlConfigCache=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Intermediate/Build/XmlConfigCache.bin -precompile -allmodules -Log=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Programs/AutomationTool/Saved/Logs/UBT-UnrealHeaderTool-Mac-Development_Remote.txt -Manifest=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Intermediate/Remote/UnrealHeaderTool/Mac/Development/Manifest.xml
Target is up to date
Deploying UnrealHeaderTool Mac Development...
Deploying now!
Total execution time: 1.01 seconds
[Remote] Downloading C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UnrealHeaderTool\Mac\Development\Manifest.xml
[Remote] Downloading build products
receiving file list ... done

This way, the keychain will be unlocked with every build, avoiding signing errors caused by lack of access to codesign during ssh connection.

Note: It is also necessary to check whether the value of SigningCertificate in BaseEngine.ini has been specified.

Method Two

If the intermediate developer certificate from Apple in the system is expired, it will also lead to this issue. The solution is to import the new Apple developer root certificate:

Delete the Apple Worldwide Developer Relations Certification Authority from the keychain, and then re-download and import a new one from the link above.

Method Three

Check in the keychain if the certificate was imported under Login or System. If it is in Login, delete it and re-import it to System.

Invalid trust settings

If you see the following error in the log:

1
2
Code Signing Error: Invalid trust settings. Restore system default trust settings for certificate "iPhone Developer: Created via API (JDPXHYVWYZ)" in order to sign code with it.
Code Signing Error: Code signing is required for product type 'Application' in SDK 'iOS 13.6'

This is due to a change in trust settings for the certificate in the Mac keychain. Change it back to Use System Defaults to resolve the issue.

unable to build chain to self-signed root for signer

Check if the certificate is imported under Login or System in the keychain. If it is in Login, delete it, and re-import it into System.

Copying Custom Directories

When building the binary engine, by default it is consistent with the installation from EpicLauncher, but if you modify the engine and want to package some new directories, modify the InstalledEngineFilters.xml file by adding directories under CopyEditorFilter:

InstalledEngineFilters.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!-- Define Editor Filters -->
<Property Name="CopyEditorFilter">
<!-- This assembly is normally embedded into the UBT executable, but it can technically be rebuilt from an installed build -->
Engine/Binaries/ThirdParty/Newtonsoft/...
Engine/Binaries/ThirdParty/VisualStudio/...

<!-- In-editor documentation -->
Engine/Documentation/Source/Shared/...
Engine/Documentation/Extras/...

<!-- Content folders -->
Engine/Content/...
<!-- Shaders folders -->
Engine/Shaders/...
<!-- Source code -->
Engine/Source/UE4Game.Target.cs
Engine/Source/UE4Editor.Target.cs

<!-- Starter content -->
Samples/StarterContent/Content/...
Samples/MobileStarterContent/Content/...

<!-- Templates -->
Templates/TemplateCategories.ini
Templates/FP_FirstPerson/...
Templates/FP_FirstPersonBP/...
Templates/Media/...
Templates/TP_Blank/...
Templates/TP_BlankBP/...
Templates/TP_FirstPerson/...
Templates/TP_FirstPersonBP/...
Templates/TP_Flying/...
Templates/TP_FlyingBP/...
Templates/TP_HandheldARBP/...
Templates/TP_ProductConfigBP/...
Templates/TP_Rolling/...
Templates/TP_RollingBP/...
Templates/TP_SideScroller/...
Templates/TP_SideScrollerBP/...
Templates/TP_ThirdPerson/...
Templates/TP_ThirdPersonBP/...
Templates/TP_TopDown/...
Templates/TP_TopDownBP/...
Templates/TP_TwinStick/...
Templates/TP_TwinStickBP/...
Templates/TP_Vehicle/...
Templates/TP_VehicleBP/...
Templates/TP_Puzzle/...
Templates/TP_PuzzleBP/...
Templates/TP_2DSideScroller/...
Templates/TP_2DSideScrollerBP/...
Templates/TP_VehicleAdv/...
Templates/TP_VehicleAdvBP/...
Templates/TP_VirtualRealityBP/...

<!-- Enterprise Templates -->
Templates/TP_AEC_BlankBP/...
Templates/TP_AEC_ArchvisBP/...
Templates/TP_PhotoStudioBP/...

Templates/TP_ME_BlankBP/...
Templates/TP_ME_VProdBP/...

Templates/TP_CollaborativeBP/...

<!-- Shared template resources -->
Templates/TemplateResources/...

<!-- Build files -->
Engine/Build/Build.version
Engine/Build/Target.cs.template
</Property>

Modify according to your needs.

Incremental Build for Binary Engine

To avoid recompiling the Engine module every time, which can lead to a large number of dependent modules in the engine being recompiled, modify the Engine.Build.cs:

1
2
3
4
5
6
PrivateIncludePathModuleNames.AddRange(
new string[] {
"MessagingRpc",
"PortalRpc",
"PortalServices",
}

Change it to:

1
2
3
4
5
6
PublicDependencyModuleNames.AddRange(
new string[] {
"MessagingRpc",
"PortalRpc",
"PortalServices",
}

Relevant discussions on UDN: Make Installed Build Win64中 Compile UE4Game Win64 特别慢的问题

Conclusion

This article mainly introduces the workflow of BuildGraph, optimization of build speed, and support for Android/IOS packaging.

It is important to note that the command used at the beginning of the article:

1
RunUAT.bat BuildGraph -target="Make Installed Build Win64" -script=Engine/Build/InstalledEngineBuild.xml -set:WithMac=false -set:WithAndroid=false -set:WithIOS=false -set:WithTVOS=false -set:WithLinux=false -set:WithHTML5=false -set:WithSwitch=false -WithDDC=false -set:WithWin32=false -set:WithLumin=false -set:WithPS4=false -set:WithXboxOne=false -set:WithHoloLens=false -set:GameConfigurations=Development

The GameConfigurations was only set to Development, which means the compiled engine can only package Development. If you want to support Shipping packaging, you need to specify in BuildGraph: -set:GameConfigurations=Development;Shipping, but this will increase the build time since both Development and Shipping need to be compiled separately, adding some additional compilation time overhead across all supported platforms. However, this is necessary and can be selectively turned on during daily development to save build time.

UE工具集:我的开源项目介绍

工欲善其事必先利其器,本文主要介绍我在使用 UE 的过程中开发的一些开源的工具和插件,能够方便地在项目中使用,提高开发效率。之前简单罗列在 资源 页面里,今天做一个详细的整理,对各个工具、插件做一些介绍。

Read more »

如何构建自己的知识体系?

Learning is like rowing upstream; not to advance is to drop back. This is especially true in the field of CS, where new technologies and frameworks are developing rapidly, and the technical iteration on the business level is very fast. You may become familiar with a technology only to see it quickly become obsolete. Therefore, only by continuously engaging with, understanding, and learning new knowledge and skills can one continually expand their programming career.
This article is a reflection on the technical accumulation I’ve made over the past few years while blogging. Originally intended as a record of the notes revision, I now feel it’s appropriate to separate it into a standalone article. I rarely write reflective pieces, thinking that methodologies are too abstract, but if one has the self-discipline to turn theoretical guidance into action, methodologies are indeed necessary. I hope I can accomplish this as well.

Read more »

UE集成WWise:概念与代码分析

WWise is Audiokinetic’s cross-platform audio engine that interacts well with game engines, allowing audio designers to handle audio solely within WWise. This separates game logic from audio production and management, providing events and parameters for use in game engines, achieving decoupling with business logic and precision control over audio.

This article mainly introduces the integration of WWise with UE4, remote building, resource analysis, documentation collection, the control interaction between WWise and UE, and code analysis of generated Banks.

Read more »

UE导入图集:TexturePacker

When developing games, a large number of image resources are used, and the purpose of using texture atlases is to reduce DrawCall and improve performance. In UE, there is no packaging tool for texture atlases, and a popular solution is to use the third-party texture packing tool TexturePacker. The new version of TexturePacker supports direct export of UE4 Sprites and can be imported directly into the engine. In earlier versions, it was possible to import data via a Json Array using VaTexAtlas, but compared to the direct export of UE Sprites by TexturePacker, VaTexAtlas does not have an advantage. UE Sprites can be previewed directly, while VaTexAtlas cannot. Given that official support is already available, it is not recommended to use VaTexAtlas as a method for importing texture atlases into UE.

This article mainly focuses on documenting the analysis of TexturePacker atlas generation files, importing into UE, introducing options, and recording the issues encountered while using it in UE.

Read more »

UE开发笔记:Mac/iOS篇

The main content of this article introduces the deployment of the UE development environment on Mac, configuration of iOS remote packaging, the application of UPL on iOS (intervening in the ipa packaging process), tools and development skills, as well as analysis of related engine code, documenting some pitfalls encountered in the project. It is primarily compiled from my previous notes, and related Mac and iOS content will also be updated in this article in the future.

Read more »

UE热更新:需求分析与方案设计

Game hot updates are a way for players to obtain the latest game content without reinstalling the game. They are widely used in network games on both PC and mobile platforms, as quick adjustments, bug fixes, and content updates are often needed after a game goes live. If even the smallest change requires players to update the app through the App Store, or manually download and install it from a website, and given that different platforms have inconsistent review rules and feedback times, operations could become chaotic.

While there may be relatively mature solutions for hot updates in other engines, comprehensive discussions on implementing hot updates in UE4 are not commonly found. Fortunately, I have previously analyzed and implemented UE4’s hot update mechanism and plan to write a couple of articles to document my thoughts and implementation plans, along with a runnable demo, which I hope will be of help to those who need it.

To conveniently collect and manage common questions and solutions regarding hot updates and HotPatcher, I’ve created a new article to document and organize them: UE4 Hot Update: Questions & Answers. If you encounter issues, please check this FAQ page first.

Read more »