因项目中有做与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开发同事优化;
其他问题待发现后再续写吧。