iOS原生接入Unity

iOS原生接入Unity

因项目中有做与Unity对接相关的需求,完结后在此记载一下。主要记载怎么引进、两头怎么交互,以及碰到的问题解决。

先说一下原生工程怎么引进,现在有两种引进办法

一、项目工程引进

当Unity开发完结后,他们会依据渠道导出不同的项目工程文件,iOS便是xcodeproj;

首要,翻开你的项目

将Unity工程增加进来workspace来

iOS原生接入Unity

iOS原生接入Unity

add完结之后,就会

iOS原生接入Unity

至此第一步完结,开端编译Unity, Command + B

选中Data文件夹,右侧勾选UnityFramework

iOS原生接入Unity

找到NativeCallProxy.h文件,改成Public

iOS原生接入Unity

这儿改成UnityFramwork,开端编译(能够在scheme中挑选debug和release)

iOS原生接入Unity

编译成功后,能够在Xcode的DerivedData文件夹下找到,因为此时Unity工程现已被引进到原生项目,所以编译后的文件会在原生项目中

iOS原生接入Unity

然后便是要到原生项目中引进这个UnityFramework

在原生的项目 找到增加framework的当地

iOS原生接入Unity

点击加号增加

iOS原生接入Unity

假如在你的增加的framework中能找到,即成功;之后就能够编译原生项目;

二、编译framework引进

考虑到原生项目需求的便是UnirtyFramework,所以只需求把编译成功的Unityframework导入进来即可;

这种办法好处在于,不影响其他同事,其他人拉取项目,不需求考虑编译Unity工程。

同办法一一样,

点击Data文件夹时右侧勾选framework, 点击NativeCallProxy时右侧挑选public ,

装备完结之后,挑选Target是Unity-iPhone,然后进行编译

(此时需求挑选签名账号,不选会编译报错,bundleID与账号能够与原生工程一样即可)

编译成功后,翻开Product文件夹

iOS原生接入Unity

找到他show in Finder,然后将framework直接拖入到原生项目里去,不过要在导入的framework中检查一下是否导入,然后编译即可。

引进Unity经常见问题便是找不到UnityFrmework,能够编译原生工程,编译后看DerivedData文件夹下是否有UnityFrmework,假如没有,说明没有引进正确。

三、代码部分

能够参阅官方的demo : github.com/Unity-Techn…

这儿我建了一个管理类一致处理: HCUnityManager

首选在main.m中,保存argc ,argv

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页面无呼应了等等),

- (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

#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交互的办法

#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工程内增加代码后再编译引进;

iOS原生接入Unity

NativeCallsProtocol.h中增加供unity调用的办法,showHostMainWindow是Unity给的,仿照写即可

#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中参加办法的完成

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 提供了unloadApplicationquitApplicationpause办法

调用unloadApplication,会使内存削减50M左右,仍是未能完全回收;重新进入页面时,需求重新init;

调用quitApplication,会使使用直接退出,不建议运用;

调用pause,仅暂停unity页面,无法继续动画,但内存未削减;

2.安装包大小问题,

现在我的增加200M左右,这儿只能靠unity开发同事优化;

其他问题待发现后再续写吧。

以上若有过错,欢迎指正。转载请注明出处。