因项目中有做与Unity对接相关的需求,完成后在此记录一下。主要记录如何引入、两端如何交互,以及碰到的问题解决。
先说一下原生工程如何引入,目前有两种引入方式
一、项目工程引入
当Unity开发完成后,他们会根据平台导出不同的项目工程文件,iOS就是xcodeproj;
首先,打开你的项目
将Unity工程添加进来workspace来
add完成之后,就会
至此第一步完成,开始编译Unity, Command + B
选中Data文件夹,右侧勾选UnityFramework
找到NativeCallProxy.h文件,改成Public
这里改成UnityFramwork,开始编译(可以在scheme中选择debug和release)
编译成功后,可以在Xcode的DerivedData文件夹下找到,因为此时Unity工程已经被引入到原生项目,所以编译后的文件会在原生项目中
然后就是要到原生项目中引入这个UnityFramework
在原生的项目 找到添加framework的地方
点击加号添加
如果在你的添加的framework中能找到,即成功;之后就可以编译原生项目;
二、编译framework引入
考虑到原生项目需要的就是UnirtyFramework,所以只需要把编译成功的Unityframework导入进来即可;
这种方式好处在于,不影响其他同事,其他人拉取项目,不需要考虑编译Unity工程。
同方式一一样,
点击Data文件夹时右侧勾选framework, 点击NativeCallProxy时右侧选择public ,
配置完成之后,选择Target是Unity-iPhone,然后进行编译
(此时需要选择签名账号,不选会编译报错,bundleID与账号可以与原生工程一样即可)
编译成功后,打开Product文件夹
找到他show in Finder,然后将framework直接拖入到原生项目里去,不过要在导入的framework中检查一下是否导入,然后编译即可。
引入Unity时常见问题就是找不到UnityFrmework,可以编译原生工程,编译后看DerivedData文件夹下是否有UnityFrmework,如果没有,说明没有引入正确。
三、代码部分
可以参考官方的demo : github.com/Unity-Techn...
这里我建了一个管理类统一处理: HCUnityManager
首选在main.m
中,保存argc ,argv
objectivec
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@(argc) forKey:@"argc"];
[userDefaults setObject:[NSString stringWithFormat:@"%p",argv] forKey:@"argv"];
[userDefaults synchronize];
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
在AppDelegate.m
中保存launchOptions
,除了didFinishLaunchingWithOptions
方法外,其他方法需要告诉UnityFramework
此时application的状态,否则会出现异常(如退到后台后,Unity页面无响应了等等),
css
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
[HCUnityManager shareInstance].launchOptions = launchOptions;
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
[[[[HCUnityManager shareInstance] unityFramework] appController] applicationWillResignActive: application];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[[[HCUnityManager shareInstance] unityFramework] appController] applicationDidEnterBackground: application];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[[[HCUnityManager shareInstance] unityFramework] appController] applicationWillEnterForeground: application];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[[[HCUnityManager shareInstance] unityFramework] appController] applicationDidBecomeActive: application];
}
- (void)applicationWillTerminate:(UIApplication *)application {
[[[[HCUnityManager shareInstance] unityFramework] appController] applicationWillTerminate: application];
}
在HCUnityManager.h中,包含之前保存的数据,以及可供展示的unityView
objectivec
#import <UnityFramework/UnityFramework.h>
#include <UnityFramework/NativeCallProxy.h>
@interface HCUnityManager : NSObject<UnityFrameworkListener,NativeCallsProtocol>
+ (HCUnityManager *)shareInstance;
@property (nonatomic, assign) int gArgc;
@property (nonatomic, assign) char **gArgv;
@property(nonatomic, strong) NSDictionary *launchOptions;
@property (nonatomic, strong) UnityFramework *unityFramework;
///用于展示Unity的View,可添加到自己需要展示页面的控制器上
@property (nonatomic, strong ) UIView *unityView;
///初始化
- (void)initUnity;
///是否已经初始化
- (BOOL)unityIsInitialized;
///发送消息给Unity
- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg;
在HCUnityManager.m中,做一些unity初始化操作,以及原生与Unity交互的方法
objectivec
#pragma mark - Unity
//初始化 Unity 加载
- (UnityFramework *)loadUnityFramework {
NSString* bundlePath = nil;
bundlePath = [[NSBundle mainBundle] bundlePath];
bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"];
NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
if ([bundle isLoaded] == false) {
[bundle load];
}
UnityFramework* ufw = [bundle.principalClass getInstance];
if (![ufw appController]) {
// unity is not initialized
[ufw setExecuteHeader: &_mh_execute_header];
}
return ufw;
}
// 判断Unity是否已经初始化
- (BOOL)unityIsInitialized {
return [self unityFramework] && [[self unityFramework] appController];
}
// 初始化Unity
- (void)initUnity {
// 判断Unity 是否已经初始化
if ([self unityIsInitialized]) return;
// 初始化Unity
self.unityFramework = [self loadUnityFramework];
[self.unityFramework setDataBundleId:"com.unity3d.framework"];
[self.unityFramework registerFrameworkListener:self];
//用于桥接使用
[NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
NSString *argvStr = [[NSUserDefaults standardUserDefaults] valueForKey:@"argv"];
char **argv;
sscanf([argvStr cStringUsingEncoding:NSUTF8StringEncoding], "%p",&argv);
int argc = [[[NSUserDefaults standardUserDefaults] valueForKey:@"argc"] intValue];
[self.unityFramework runEmbeddedWithArgc:argc argv:argv appLaunchOpts:self.launchOptions];
self.unityView = [[[self unityFramework] appController] rootView];
//隐藏 unity 自己的 window,否则会挡住 UIApplication 的 window
[[self unityFramework] appController].window.hidden = YES;
}
///展示Unity的view
- (void)showUnityView {
if (![self unityIsInitialized]){
NSLog(@"Unity 还未初始化");
}
[self.unityFramework showUnityWindow];
}
#pragma mark - UnityFrameworkListener
- (void)unityDidUnload:(NSNotification *)notification {
[[UIApplication sharedApplication].keyWindow makeKeyAndVisible];
[[self unityFramework] unregisterFrameworkListener: self];
self.unityFramework = nil;
}
- (void)unityDidQuit:(NSNotification *)notification {
NSLog(@"========== %s ============",__func__);
}
- (void)retryRegisterListener {
[self.unityFramework registerFrameworkListener:self];
}
///原生调用unity方法
- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg {
[self.unityFramework sendMessageToGOWithName:goName functionName:name message:msg];
}
//MARK: -- NativeCallsProtocol
- (void) showHostMainWindow:(NSString*)color {
if(![self unityIsInitialized]) {
NSLog(@"Unity is not initialized, Initialize Unity first");
} else {
[self.unityFramework showUnityWindow];
}
}
///获取token
- (char *)getToken {
// 获取之前保存的token
NSString *token = [[HCUserManager sharedInstance] token];
char *cString = [token cStringUsingEncoding:NSUTF8StringEncoding];
return cString;
}
总结一下就是:
unity调用原生的方法,统一通过NativeCallsProtocol
协议实现;
原生调用unity方法,即统一是[self.unityFramework sendMessageToGOWithName:goName functionName:name message:msg];
NativeCallsProtocol
协议里面通常用C编写,unity那边的C#可以调用的到
如果与unity有交互,需要在unity工程内增加代码后再编译引入;
在NativeCallsProtocol.h
中增加供unity调用的方法,showHostMainWindow
是Unity给的,仿照写即可
objectivec
#import <Foundation/Foundation.h>
// NativeCallsProtocol defines protocol with methods you want to be called from managed
@protocol NativeCallsProtocol
@required
- (void) showHostMainWindow:(NSString*)color;
// other methods
- (char *)getToken;
@end
在NativeCallProxy.mm
中加入方法的实现
arduino
extern "C" {
void showHostMainWindow(const char* color) {
return [api showHostMainWindow:[NSString stringWithUTF8String:color]];
}
char* getToken(){
return strdup([api getToken]);
}
}
在此补充一下,如果要返回字符串给unity,需要加上strdup
函数,否则会报内存问题;
添加方法后,因为HCUnityManager
是遵守了NativeCallsProtocol,在HCUnityManager
中具体实现即可;
四:存在问题
1.内存问题
当unity初始化完成,我们用自己的控制器加载unityView时,内存会暴增,目前我的是300M;
优化点:UnityFramework 提供了unloadApplication
,quitApplication
,pause
方法
调用unloadApplication
,会使内存减少50M左右,还是未能完全回收;重新进入页面时,需要重新init;
调用quitApplication
,会使应用直接退出,不建议使用;
调用pause
,仅暂停unity页面,无法继续动画,但内存未减少;
2.安装包大小问题,
目前我的增加200M左右,这里只能靠unity开发同事优化;
其他问题待发现后再续写吧。