UE4 IOS互相调用
上篇文章讲了UE4安卓互相调用,这篇来看看IOS原生代码和UE4的关系。
plist
上篇文章提到了一个概念:UPL,在IOS代码编写里同样有使用UPL的地方,但对于IOS目前我所见到只是用于更改plist(UE 4.25.1 默认打包会产生下面这样一个 plist 文件info.plist,在一些特殊的需求中,需要往这个 plist 中添加元素或者修改以及删除。)
ios 的 ipa 包中都会有 plist 文件,可以用来配置 app 的一些属性,apple 的开发者文档里对每个支持的 key 有详细的描述:iOS Keys
在 UE 的项目设置中,可以给 plist 添加元素,在 Project Settings
-Platform
-iOS
-Additional Plist data
中可以填入一个字符串,它会被插入到 plist 文件中:
<key>AdditionalElementAAA</key>\n<string>this key is a test element.</string>
中间的 \n
是格式化代码,用于另起一行。
如果想要修改或者删除 plist 的元素,需要通过 UPL 来写逻辑(当然也可以使用 UPL 来添加元素,建议使用这种做法)。
<?xml version="1.0" encoding="utf-8"?>
<root>
<init>
<log text="UPL Exalpme adding element to plist..."/>
</init>
<trace enable="true"/>
<iosPListUpdates>
<addElements tag="dict" once="true">
<key>AdditionalElementAAA</key>
<string>this key is a test element.</string>
</addElements>
</iosPListUpdates>
</root>
上面是用来添加元素的,上面的内容和直接写到 Additional Plist data
是一样的。
遍历 plist 中的 key:
<?xml version="1.0" encoding="utf-8"?>
<root>
<init>
<log text="UPL Exalpme..."/>
</init>
<trace enable="true"/>
<iosPListUpdates>
<loopElements tag="dict">
<loopElements tag="$">
<setStringFromTag result="TagName" tag="$"/>
<setBoolIsEqual result="bIsKey" arg1="$S(TagName)" arg2="key"/>
<if condition="bIsKey">
<true>
<log text="$S(TagName):$S(TagValue)"/>
</true>
</if>
</loopElements>
</loopElements>
</iosPListUpdates>
</root>
注意:当前元素以 tag = "$"
方式引用。
两种调用方式
Framework
通过给项目添加ios的framework库来添加ios功能
这块我没有实践过,引用他人的案例来讲解
https://blog.csdn.net/shenjie12345678/article/details/111812429
Build.cs文件
if (Target.Platform == UnrealTargetPlatform.IOS){
PublicAdditionalFrameworks.Add(
new Framework(
"TestLoginSDK",
"../ThirdParty/iosframeworks/iosextend.embeddedframework.zip"
)
);
}
IOS 上的 Framework
有点类似于静态链接库的意思,相当于把.a
+.h
+ 资源打包到一块的集合体。更具体的区别描述请看:iOS 库 .a 与.framework 区别
在 UE 中以集成 IOS 上操作 Keycahin 的 SSKeychain
为例,在 Module 的 build.cs
中使用 PublicAdditionalFrameworks
来添加:
PublicAdditionalFrameworks.Add(
new Framework(
"SSKeychain",
"ThirdParty/IOS/SSKeychain.embeddedframework.zip",
"SSKeychain.framework/SSKeychain.bundle"
)
);
构造 Framework 的第一个参数是名字,第二个是 framework 的路径(相对于 Module),第三个则是解压之后的 Framework 的 bundle
路径(如果 framework 没有 bundle 则可以忽略这个参数,而且就算有 bundle,但是不写这第三个参数貌似也没什么问题)。
这种方法就是一开始将所有的IOS平台有关的代码封成库,然后在UE中的CPP函数中用一句OC的调用。
注意点:
其中需要注意的是:
-
在调用 SDK 接口的时候需要切换到主线程上,不然 App 会 Crash;
-
dispatch_async(dispatch_get_main_queue(), ^{ //your code });
-
-
在使用 OC 代码的时候需要用#if PLATFORM_IOS #endif 进行包裹;
优缺点
优点:部分代码可以不开源,成熟的模块可以拿来就用!便于进行分模块管理等等。如果有写ios的同学,直接让他给你整一个你只需要调用一下就好。
缺点:和UE的交互有限,复杂的交互需求写起来比较麻烦。
OC混编
先不提OC的语法各种对CPP程序员反人类,OC混编的优点还是很棒的,其实你可能发现了,上面一种方法里的调用framework库的那一句代码就是oc代码,其实这就是混编了。不过这里的混编更为复杂,指将原先的ios的代码的类Interface、implementation直接写在cpp里。
oc、c、 c++同根同源,他们的基础数据类型天然就能互相调用。
使用OC混编其实可以直接引用ios的库文件,比如
#import <UIKit/UIKit.h>
很多sdk就是这么干的,这样代码可以大部分都放在一起,开发起来很方便,调试一下,想改oc就改oc,想改cpp就改cpp
而且因为身在UE的工程环境下,oc的函数体里可以直接使用UE提供的一些类,当然还是要注意线程问题使用AsyncTask
不过混编的时候执行顺序有时候让人比较困惑,比如我这边先写了NSLog,后写了UE_LOG,在日志中UE_LOG有可能先于NSLog执行,这块还是分开一点比较好,不要太过自由。
优缺点
很多的优点,上面提到了的都是优点,我在oc的函数体里直接excute UE作用域绑定的委托,传递数据,改ue代码和oc代码,xcode build->install->running都很快。
缺点其实也比较明显,写小规模的代码还好,在很大量平台代码的情况下,要在没有代码提示的环境下写逻辑难度还是很大的,况且很多ios平台有一些成熟的代码的解决方案,直接以一个framework库提供更好
扩展
杰少这篇文章讲了UE4的委托和IOS的委托的交互
https://blog.csdn.net/shenjie12345678/article/details/112168356#comments_15713973
平台代码技巧
小技巧:跨平台的代码可以封在不同的同名cpp文件中(分平台名字,编辑器会识别到对应的平台),这样在不同的平台编译的时候只会取对应文件夹下的cpp文件,不会产生重定义
采坑
提示xml is missing
字符串IosPlugin 能找到xml文件,IOSPlugin找不到xml,奇怪的拼写检查。
AdditionalPropertiesForReceipt.Add(new ReceiptProperty("IosPlugin",System.IO.Path.Combine(PluginPath,"IOS/PlatformCommon_APL_IOS.xml")));
IOS截图
截图的传递到ue4的思路很简单,把图片数据转化成字节数组,然后参数传到UE4后,通过这个buffer再解析出图片
使用UIImagePNGRepresentation传到UE4后转换成Texture2D发现显示失败,是一个透明图
定位后发现ios的截图默认为png,64位深,UE4对应处理图片的ImportBufferAsTexture2D接口在处理png64的时候不报错但是无法正确处理。
使用UIImageJPEGRepresentation在IOS层把图片转成jpg解决问题。