iOS原生接入Unity

因项目中有做与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 提供了unloadApplicationquitApplicationpause方法

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

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

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

2.安装包大小问题,

目前我的增加200M左右,这里只能靠unity开发同事优化;

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

以上若有错误,欢迎指正。转载请注明出处。
相关推荐
古力德2 天前
Unity中造轮子:定时器
c#·unity3d
Mapmost4 天前
【数据融合实战手册·进阶篇】模型融合总出错?先看看这些“对齐”了没!
unity3d
北桥苏6 天前
如何在 Unity3D 导入 Spine 动画
unity3d
Thomas游戏开发7 天前
Unity3D状态管理器实现指南
前端框架·unity3d·游戏开发
土豆宝12 天前
Unity Visual Scripting(可视化脚本) 自定义节点 踩坑教程
unity3d
Thomas游戏开发13 天前
Unity3D光照层级与动态切换指南
前端框架·unity3d·游戏开发
Thomas游戏开发24 天前
Unity3D 崩溃分析工具的集成与优化
前端框架·unity3d·游戏开发
Thomas游戏开发1 个月前
Unity3D网格简化与LOD技术详解
前端框架·unity3d·游戏开发
Thomas_YXQ1 个月前
Unity3D 图形渲染(Graphics & Rendering)详解
开发语言·unity·图形渲染·unity3d·shader
Thomas游戏开发1 个月前
Unity3D 图形渲染(Graphics & Rendering)详解
前端·unity3d·游戏开发