iOS集成Unity
近年来iOS和Unity发展都非常迅速,而iOS和Unity都有各自擅长的领域,iOS在传统应用开发的中坚力量,而Unity在游戏行业发展强劲,尤其是三维领域。作为可视化项目,我们既需要传统项目的功能模块,又需要三维场景的渲染效果,所以我们今天要把这两个领域的强者融合在一起,在一个项目中发挥各自所长。iOS集成Unity在现实中应用较少,几乎没有完整流程的资料,且其中的坑数不胜数,所以把这些内容整理出来提供给大家。
下面详细介绍下打包流程。
1.打包Unity并导入
Unity中打包iOS包
首先在Unity编辑器打开UnityProject项目,选择Menu -> Window -> Package Manager,因为2.0.8版本不兼容使用Unity作为库,所以要移除Ads资源包,或更新Ads资源包到v 3.*版本。
选择Menu -> Edit -> Player Settings -> Player -> iOS设置标签页 -> Identification Section,设置有效的Bundle Identification和Signing Team ID,以避免后续步骤出现Xcode签名问题。
打开Menu -> File -> Builds Settings,在此选择并切换平台为iOS。将UnityProject项目构建到iosBuild文件夹。
一般Unity导出的iOS工程是可以直接运行的,但是我们既然要集成Unity,只是需要其中的三个文件,分别是Data、Classes、Libraries。
接下来就是修改导出包的文件。
1.文件:main.mm
复制替换:
`int main_unity_default(int argc, char* argv[])
{
@autoreleasepool
{
UnityInitTrampoline();
// UnityParseCommandLine(argc, argv); //Unity 5.3+
UnityInitRuntime(argc, argv); //Unity 5.6+,5.4和5.5用哪个我没试过,可以根据报错情况选择。
RegisterMonoModules();
NSLog(@"-> registered mono modules %p\n", &constsection);
RegisterFeatures();
// iOS terminates open sockets when an application enters background mode.
// The next write to any of such socket causes SIGPIPE signal being raised,
// even if the request has been done from scripting side. This disables the
// signal and allows Mono to throw a proper C# exception.
std::signal(SIGPIPE, SIG_IGN);
//UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
// UIApplicationMain(argc, argv, nil, NSStringFromClass([UnitySubAppDelegate class]));
UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
}
return 0;
}`
2.文件:UnityAppController.h
导入文件:#import <UIKit/UIKit.h>
复制替换:
`NS_INLINE UnityAppController* GetAppController()
{
NSObject<UIApplicationDelegate>* delegate = [UIApplication sharedApplication].delegate;
UnityAppController* currentUnityController = (UnityAppController *)[delegate valueForKey:@"currentUnityController"];
return currentUnityController;
}`
3.文件:SplashScreen.mm
复制替换:
`void ShowSplashScreen(UIWindow* window)
{
_controller = [[SplashScreenController alloc] init];
[_controller create: window];
}`
4.文件:DeviceSettings.mm
代码行数:268
复制替换:
return deviceUnknown;
5.文件:CrashReporter.mm
代码行数:81
复制替换:// InitCrashReporter();
导入
为了方便管理,我们可以在我们iOS工程主目录下创建一个文件夹,专门来存放Unity文件,方便管理和维护。比如我们起名为unity。Classes和Libraries以create groups的方式导入进来,而Data文件以Create folder references的形式导入进来。
2.集成环境
添加框架
由于Unity项目依赖了一些框架,我们需要将Unity所有依赖的框架都导入进来,否则Unity无法运行。
配置config文件
1.现在里面用了一些三方库,比如 objectMapper,JsonKit,所以要引用三方库。分两种,使用cocoapods集成,不适用cocoapods集成。
2.Unity.xcconfig文件,这个文件已经编辑好,但是不同机器的文件路径、不同的unity版本会有差异。注意这里我们的Unity使用的是2019版的。
`// Unity.xcconfig
// 对应Build Settings
//User-Defined
GCC_THUMB_SUPPORT = NO;
GCC_USE_INDIRECT_FUNCTION_CALLS = NO
UNITY_RUNTIME_VERSION = 2019.1.4f1
UNITY_SCRIPTING_BACKEND = il2cpp
UNITY_IOS_EXPORT_PATH = $(PROJECT_DIR)/unity_ios;//后面的Search Paths要用
//Apple Clang - Language
GCC_PREFIX_HEADER = $(PROJECT_DIR)/unity_ios/Classes/Prefix.pch
//Swift Compiler - General
SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/RayData Mobile/RayDataEngine/objc/UnityBridge.h
//Build Options
ENABLE_BITCODE = NO
//Apple Clang - Custom Compiler Flags
OTHER_CFLAGS = $(inherited) -DINIT_SCRIPTING_BACKEND=1 -fno-strict-overflow -DNET_4_0 -DRUNTIME_IL2CPP=1 -DIL2CPP_MONO_DEBUGGER=1
//Linking
OTHER_LDFLAGS = -weak_framework CoreMotion -weak-lSystem -weak_framework GameKit -weak_framework iAd -framework CoreGraphics -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework SystemConfiguration -framework CoreLocation -framework MediaPlayer -framework CFNetwork -framework AudioToolbox -framework OpenAL -framework QuartzCore -framework OpenGLES -framework UIKit -framework Foundation -liconv.2 -liPhone-lib
//Search Paths
HEADER_SEARCH_PATHS = $(inherited) $(UNITY_IOS_EXPORT_PATH) $(UNITY_IOS_EXPORT_PATH)/Classes $(UNITY_IOS_EXPORT_PATH)/Classes/Native $(UNITY_IOS_EXPORT_PATH)/Classes/UI $(UNITY_IOS_EXPORT_PATH)/Libraries $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include $(UNITY_IOS_EXPORT_PATH)/Libraries/bdwgc/include;
LIBRARY_SEARCH_PATHS = $(inherited) $(UNITY_IOS_EXPORT_PATH) $(UNITY_IOS_EXPORT_PATH)/Libraries $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include;
// c 目测没有替换配置里的内容
CLANG_CXX_LANGUAGE_STANDARD = compiler-default;
CLANG_CXX_LIBRARY = libc++;
CLANG_WARN_BOOL_CONVERSION = NO;
CLANG_WARN_CONSTANT_CONVERSION = NO;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
CLANG_WARN_EMPTY_BODY = NO;
CLANG_WARN_ENUM_CONVERSION = NO;
CLANG_WARN_INT_CONVERSION = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES;
CLANG_WARN_UNREACHABLE_CODE = NO;
CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;
GCC_C_LANGUAGE_STANDARD = c11;
GCC_ENABLE_OBJC_EXCEPTIONS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_THUMB_SUPPORT = NO;
GCC_USE_INDIRECT_FUNCTION_CALLS = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64] = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = NO;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
GCC_WARN_UNUSED_FUNCTION = NO;
GCC_NO_COMMON_BLOCKS = NO;
CLANG_ENABLE_MODULES = NO;
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_EMPTY_BODY = NO;
CLANG_WARN_INFINITE_RECURSION = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
CLANG_WARN_UNREACHABLE_CODE = NO;
GCC_WARN_UNUSED_FUNCTION = NO;
CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;`
Apple Clang - Language - C++
Apple Clang - Custom Compiler Flags `
3.UnityBridge.h UnityUtils.h UnityUtils.mm文件已经提前配置好。也在包中
unity下的unity_ios文件加下Data文件夹应该是引用类型文件,蓝色文件夹,其他的都是黄色文件夹
4.配置xcconfig
如果集成了cocoapods
如果没有集成cocoapods
配置Build Settings
添加main.swift文件
import UIKit
custom_unity_init(CommandLine.argc, CommandLine.unsafeArgv)
UIApplicationMain(
CommandLine.argc,
UnsafeMutableRawPointer(CommandLine.unsafeArgv)
.bindMemory(
to: UnsafeMutablePointer<Int8>.**self**,
capacity: Int(CommandLine.argc)),
nil,
NSStringFromClass(AppDelegate.**self**)
修改AppDelegate.swift文件并做修改。
注释这个代码
//@UIApplicationMain
添加属性
@objc var currentUnityController: UnityAppController!
在 didFinishLaunchingWithOptions 方法中添加以下代码
window = UIWindow.init(frame: UIScreen.main.bounds)
currentUnityController = UnityAppController()
currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)
let vc = UIViewController.init()
window?.rootViewController = vc
window?.backgroundColor = UIColor.white
window?.makeKeyAndVisible()
在声明周期代理方法中添加以下方法
func applicationWillResignActive(**_** application: UIApplication) {
currentUnityController.applicationWillResignActive(application)
}
func applicationDidEnterBackground(**_** application: UIApplication) {
currentUnityController.applicationDidEnterBackground(application)
}
func applicationWillEnterForeground(**_** application: UIApplication) {
currentUnityController.applicationWillEnterForeground(application)
}
func applicationDidBecomeActive(**_** application: UIApplication) {
currentUnityController.applicationDidBecomeActive(application)
}
func applicationWillTerminate(**_** application: UIApplication) {
currentUnityController.applicationWillTerminate(application)
}
设置桥接文件路径
设置 Apple Clang - Language
设置Build Phases,搜索 dynamic后,删除。
配置桥接文件
因为我们用的是Swift语言,而Unity编译成iOS包中大部分是C语言。中间需要Objective-C语言作为中间转换。如果iOS调用Unity顺序为Swift->Objective-C->C;如果Unity调用iOS顺序为C->Objective-C->Swift。
这里需要一个Swift和Objective-C的桥接文件UnityBridge。
#define "useUnityInxxx"
#ifndef UnityBridge_h
#define UnityBridge_h
#if useUnityInRaydata
#import "UnityUtils.h"
#import "UnityAppController.h"
#import "UnityInterface.h"
#endif
#import "UnityTransform.h"
#endif /* UnityBridge_h */
3.iOS原生与Unity的交互与传值
创建Objective-C文件UnityTransform,用来做Objective和Unity中C语言的交互。
声明文件
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface UnityTransform : NSObject
@property(nonatomic,copy)NSString *objectName;
@property(nonatomic,copy)NSString *funcName;
@property(nonatomic,copy)NSString *json;
//快速赋值方法
- (**void**)setup:(NSString *)objectName andFname:(NSString *)fName andJsonStr:(NSString *)jsonStr;
//ios to unity
- (**void**)sendMessageToUnity;
#if defined(__cplusplus)
extern"C"
{
#endif
extern void UnitySendMessage(const char ,const char , const char );
#if defined(__cplusplus)
}
#endif
实现文件
`@implementation UnityTransform
static UnityTransform *transform=nil;
- (void)setup:(NSString *)objectName andFname:(NSString *)fName andJsonStr:(NSString *)jsonStr{
self.objectName = objectName;
self.funcName = fName;
self.json = jsonStr;
}
- (void)sendMessageToUnity{
const char *jsonStr = (char *)[self.json UTF8String];
const char *objectName = [self.objectName UTF8String];
const char *funcName = [self.funcName UTF8String];
UnitySendMessage(objectName, funcName, jsonStr);
}
//单例
+ (UnityTransform *) instance{
if (transform==nil)
{
transform=[[UnityTransform alloc]init];
transform.objectName = @"";
transform.funcName = @"";
}
return transform;
}`
创建一个Swift单利管理类,用来调用Unity方法及实现回调,UnityManager,这里举一个加载模型的例子
iOS调用unity函数
`
let transformTool = UnityTransform()
func loadModelIOS2Unity() {
transformTool.setup("APIManager", andFname: "loadModel", andJsonStr: model)
transformTool.sendMessageToUnity()
}
`
Unity调用iOS方法
UnityTransform文件中声明一个方法,
void _modelLoadSucess(void);
并实现
void _modelLoadSucess(void){
Unity.loadModelSucess_initDone();
}
UnityManager管理类中写被调用的方法,传递给使用的类。
`var loadModelSuccessCallBack: NullBlock? //完成了模型加载回调
@objc func loadModelSucess_initDone() {
Unity.loadModelSuccessCallBack()
}
`