iOS集成Unity - ShenYj/ShenYj.github.io GitHub Wiki
-
xcode 15.3(15C500b)
-
Swift 5.9
-
项目结构 【覆盖大部分场景,如果没有使用 Cocoapods 或没有用 workspace 应该更简单一些】
. ├── Gemfile ├── Gemfile.lock ├── README.en.md ├── README.md ├── Tour 原工程 | ├── Configurations 各个环境下 xcconfig 配置 | ├── Podfile | ├── Podfile.lock | ├── Pods Pods | ├── R.generated.swift | ├── Target 多 Target 资源 | ├── Tour 原项目通用部分资源、代码 | ├── Tour.xcodeproj 原 项目主 project | └── Tour.xcworkspace 原 workspace 文件 ├── UnityProject 新建的文件夹,用来存放unity project ├── build build 路径 └── scripts 项目使用的一些脚本
这个结构说简单,不简单,说复杂也不复杂,应该能满足大部分的场景需要
- 有多环境的使用:workspace、多project、多个target、多个scheme并配有多个xcconfig、多证书支持
-
原来只有一个主project + pod project,所以在同目录下存放不觉得有问题,现在想更清晰一些,让每个project都有独立的文件夹,并且将 xcworkspace 文件提出去(和各个project文件夹同级)
sudo gem install cocoapods-deintegrate cocoapods-clean cd ...Podfile路径... pod deintegrate pod clean
-
移除 Cocoapods 后,为了区别名称上的混淆,我给原 project(主项目project)新建了一个 workspace,并起了个不同的名字,存放在了上级目录(和UnityProject、Tour文件夹同级)
-
改造后的目录结构 (省略非关键部分)
. ├── TourWorkspace.xcworkspace workspace 文件 ├── Pods Pods ├── UnityProject Unity导出的项目 ├── SwiftProject 原项目 └── ...
-
-
在忽略文件中添加 UnityProject 目录
因为这家伙太大了,几个
.a
的库也都超了github的单个文件上限,所以决定不上传到仓库
-
利用 Unity Hub build 导出 Xcode project
-
我是直接将 project 文件夹内文件全部复制到工程下指定的一个路径内,为了清晰,我还删除了单元测试部分的配置
-
打开原项目,并保持project 都为未选中状态,否则在 Project navigator 区域点击添加会加入到当前 project 下,而非想要的并列的结构; 还可以直接右键显示workspace文件包内容, 去直接改文本配置
<?xml version="1.0" encoding="UTF-8"?> <Workspace version = "1.0"> <FileRef location = "group:UnityProject/Unity-iPhone.xcodeproj"> </FileRef> <FileRef location = "group:Tour/Tour.xcodeproj"> </FileRef> <FileRef location = "group:Pods/Pods.xcodeproj"> </FileRef> </Workspace>
因为我是先复制过去的,所以在添加 project 的时候,
Copy Items into destination group's folder(if need)
就再勾选, Forders 选择第一项Create groups for any added folders
-
在 unity 工程下确保 data 文件夹的 Target Membership 下勾选了 UnityFramework,然后确保 UnityFramework 能够编译成功
-
在主 project的 target 下添加 UnityFramework
默认导出的 UnityFramework 是动态库,所以就用 Embed Frameworks 的方式
- 如果项目没有使用自定义的xcconfig,那么集成就到此结束了,但我使用了自定义的xcconfig,导致原项目和unity项目的
TARGET_BUILD_DIR
不同,这导致原项目在链接 UnityFramework的时候,找不到而失败
-
同样先用 Unity Hub 导出 Xcode project
-
将项目文件复制到
UnityProject
目录中. ├── TourWorkspace.xcworkspace workspace 文件 ├── Pods Pods ├── UnityProject Unity导出的项目文件 ├── SwiftProject 原项目 └── ...
-
原项目我不动了, 给 Unity Project 添加一个podspec,作为本地库
为了图快,我先利用工程直接编译出 framework 验证可行性, 至于其他的放在以后优化
在当前根目录创建了一个新文件夹
UnityFramework
, 添加到忽略文件, 在这里创建 podspec 文件并复制 framework至此,创建VenderedLibraries
目录,复制依赖的.a
静态库-
目录结构
. ├── LICENSE ├── UnityFramework.framework │ ├── Data │ ├── Headers │ ├── Info.plist │ ├── Modules │ └── UnityFramework ├── UnityFramework.framework.dSYM │ ├── UnityFramework.podspec ├── VenderedLibraries │ ├── libGameAssembly.a │ └── libil2cpp.a └── pod_lib_lint.sh
-
关键的配置如下:
s.vendored_frameworks = ['UnityFramework.framework'] s.frameworks = 'AudioToolbox', 'AVFoundation', 'CFNetwork', 'CoreGraphics', 'CoreMedia', 'CoreMotion', 'CoreVideo', 'Foundation', 'OpenAL', 'QuartzCore', 'SystemConfiguration', 'UIKit' s.libraries = 'iconv.2' s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'VALID_ARCHS[sdk=iphonesimulator*]' => '' }
-
-
主项目直接在 Podfile 中本地依赖
def libs_unity_project_framework pod 'UnityFramework', :path => 'UnityFramework' end
这种方式我原项目几乎不产生影响,唯一要考虑的就是在使用 UnityFramework 的时候,要区分指令集,比如模拟器肯定是不能用了,这可以通过内置的预编译指令处理
因为是 Pod 统一管理的依赖关系,会帮我自动copy 到最终的目录内, 不需要我自己处理不同 Project 下,由于使用了自定义 xcconfig 而产生的路径不同的问题
Unity 与 iOS 的交互方式是基于 C、C++ 的,如果 Unity 需要调用 iOS 原生方法,暴露给 Unity 的接口一定要使用 C 的声明方式
使用
extern "C"
的目的就是为了防止编译器对高级语言的 name mangling处理导致符号找不到,按照 C 的方式去编译查找符号
关于交互的实现细节可以参考以下视频教程
至于交互的文件未必一定要放在 Unity 的工程目录内
Unity 通过 IMPL_APP_CONTROLLER_SUBCLASS
知道要使用我们定制的 Controller
#import "TourUnityAppController.h"
@interface TourUnityAppController ()
@end
@implementation TourUnityAppController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application: application didFinishLaunchingWithOptions: launchOptions];
NSLog(@" +++ TourUnityAppController +++");
return YES;
}
@end
IMPL_APP_CONTROLLER_SUBCLASS(TourUnityAppController)
现在 Swift 创建的工程默认早已没有 main 这个入口了, 而是使用 @main 这个attribute
@main 是一个通用的attribute, 在 iOS 下自动执行 @UIApplicationMain, 在 macOS 下执行 @NSApplicationMain
为什么提到入口,这与 Unity 集成使用的两个场景有关系
- 直接作为主体,也就是Xcode project 只是一个壳,提供一个容器
- 作为业务的一部分,在某些场景下展示 Unity,大部分仍是原生业务
由于我的场景属于第二部分,所以在前面的工程改造处并未提及工程入口的事儿,如果你是第一种,可能会需要以下操作(也未必一定,因为你也可以在AppDelegate 中设置入口作为主窗口使用) 所以我先去掉了@main,手动创建了一个 main.m
#import "SwiftProject-Swift.h"
#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
NSString * delegateName;
@autoreleasepool {
delegateName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, delegateName);
}
成功编译了 UnityFramework 并在主工程中 embeded in 后,主工程运行立即 crash,并定位在底层c++的一些函数上,如 DarwinApi::Baselib_SystemSemaphore...
之类,可以将主程的 scheme 中 Thread Performance Checker
去掉
ld: framework not found UnityFramework
clang: error: linker command failed with exit code 1 (use -v to see invocation)
这段链接错误使用过三方库的不会陌生,具体可能原因就不说了,通常情况下在集成 UnityFramework 的时候可能通过上面的配置就完成了,但是我的项目结构存在多环境配置,使用了多target+xcconfig+scheme 的组合,这个原因主要与xcconfig 有关
由于我使用了自定义的 xcconfig 配置,使导出的路径发生了变化,所以在链接时没有找到库文件
- 如果在自定义多个xcconfig的情况下,仍想要将 Unity Project 加入到 workspace 中实现动态库的依赖关系,在最终链接阶段的路径处理
- 本地 Pod 集成,UnityFramework 的指令集合成或优化
- 现在 UnityFramework 处于未被版本控制的状态,本地存储,如何进行版本控制
- 如果想要维护 Unity 源码项目, 依赖库等单个资源过大需要在 git 仓库外额外存储