美文网首页UnitySwift & Obj-C
iOS原生集成unity—framework形式集成(一)

iOS原生集成unity—framework形式集成(一)

作者: 小蜗牛吱呀之悠悠 | 来源:发表于2020-08-17 18:18 被阅读0次

    以下所有内容均为个人观点,转载请注明出处<简书--小蜗牛吱呀之悠悠 >,谢谢!

    最近需要在原生项目集成unity导出的工程,并作为子模块存在,网上教程不少,大多数都尝试了一遍,但都不能用,最终,还是总结出一套最为简单、快速、有效的方法。

    一、背景

    unity导出的工程,不仅可以以APP的形式独立上线,同时也可以将其囊括成framework的形式,集成进入我们已有的原生工程中,下面个将介绍已有原生上线项目,如何集成unity。
    unity的集成有三种方式:

    1、直接拖拽导入

    这种方式网上资料很多,但我尝试后都失败了,也许与我这边使用的unity版本有关,此处不再赘述

    2、将unity作为一个target导入,并建立关联

    此方法也资料很多,仍然没有成功
    上述两种方法我这边都失败了,目前初步猜测是与使用的unity版本有关,如有大神知道原因,欢迎留言~

    3、将unity囊括成framework,并将unity的内容作为子项目导入到原生的workspace中,并建立两者之间的关联。

    二、准备工作

    1、确认当前使用的unity版本是否高于2019.3.a2,如果低于此版本,本文将不适用,建议使用上述方式1、2。Xcode版本需要大于9.4,作者使用的是11.5版本。
    2、unity导出环境配置

    a. 首先在Unity编辑器打开UnityProject项目,选择Menu -> Window -> Package Manager,因为2.0.8版本不兼容使用Unity作为库,所以要移除Ads资源包,或更新Ads资源包到v 3.*版本。
    b. 配置Bundle Identification和Signing Team ID,此步骤非必须,可以在导出后再配置,但作者是统一配置的,所以也一并提一下。
    选择Menu -> Edit -> Player Settings -> Player -> iOS设置标签页 -> Identification Section

    c.导出unity项目时,要注意区分是否支持模拟器,此处特别重要,如果弄错了,将导致后续集成失败,如果你的原生工程是真机调试,那直接导出真机的工程即可。

    正确导出unity工程后,就可以开始进行集成了

    三、集成

    集成分为两个步骤:workspace配置 、代码配置
    如果你的原生项目使用的是cocopods,直接跳过此1.1步骤,从1.2开始。

    1.1workspace配置

    此步骤为没有使用cocopods的项目集成用,将原生工程和unity导出的工程放在同一个文件夹中,如下图


    项目放在同一个文件夹下.png

    打开原生项目,左上角点击File->New->workspace,并建立的workspace保存在上图中统一文件加下


    新建workspace
    此时,关闭原生工程,打开新建的workspace,点击Xcode左下角的"+"号,将原生工程、unity导出的工程添加进来
    导入workspace

    导入完成后,请从步骤2继续集成

    1.2原生项目有使用cocopods

    将unity导出的工程拷贝到原生工程文件夹中,得到如下图结构


    拷贝unity工程

    打开workspace,点击Xcode左下角的"+"号,将unity导出的工程添加进来


    导入unity
    2.将unity工程集成为UnityFramework.framework

    a.展开unity原生工程,在products文件夹下找到UnityFramework.framework,右击show in finder


    UnityFramework.framework路径

    b.在workspace中选中nativeiOS工程文件,点击下图的“+”号


    点击加号
    添加其他
    此操作比较关键,很多人找不到UnityFramework.framework。打开刚才UnityFramework.framework的路径文件夹,直接将文件夹拖拽进刚才的路径查找器中
    拖进去

    添加完成以后,注意检查一下下图项


    image.png

    此时,你的原生空间下的Frameworks下将会出现UnityFramework.framework,且带有展开箭头,否则就是错误的


    有箭头
    3.配置UnityFramework.framework和桥接文件

    a.选中unity工程Data文件夹,按下图配置

    image.png
    b.选中unity工程下的NativeCallProxy.h文件,按下图配置,注意,需要public
    好多同学说NativeCallProxy.h文件找不到,这里特别说明一下:这个文件是需要在导出unity工程之前,将NativeCallProxy.h文件导入,然后再导出unity工程;并且这个文件是不可以在unity工程导出后添加的,因为unity导出过程,会建立NativeCallProxy.h与unity工程的关联,后期添加则没有这个关联,编译不通过
    image.png
    c.build一下UnityFramework.framework,这一步一定要,否则容易出现文件找不到的问题
    image.png
    到这里为止,整个工程的配置就结束了,build一下你的原生工程,正常情况下是OK的,接下来就是代码的配置

    四、背景

    1、新建一个继承于NSObject的单例类,并添加以下两个属性

    @property (nonatomic, assign) int gArgc;
    @property (nonatomic, assign) char** gArgv;
    

    2、打开main.m文件,在main函数中添加下面代码,ConfigObj.h为刚才新建的单例类

    [ConfigObj shareInstance].gArgc = argc;
    [ConfigObj shareInstance].gArgv = argv;
    

    3、打开AppDelegate.h文件,添加以下代码

    #import <UIKit/UIKit.h>
    #include <UnityFramework/UnityFramework.h>
    
    #include <UnityFramework/NativeCallProxy.h>
    #import "ConfigObj.h"
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate,UnityFrameworkListener, NativeCallsProtocol>
    @property (strong, nonatomic) UIWindow *window;
    @end
    

    打开AppDelegate.m文件,添加以下代码

    #import "AppDelegate.h"
    
    UnityFramework* UnityFrameworkLoad()
    {
        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;
    }
    @interface AppDelegate ()
    @property (nonatomic, strong) UnityFramework *ufw;
    
    @end
    
    @implementation AppDelegate
    
    
    - (bool)unityIsInitialized { return [self ufw] && [[self ufw] appController]; }
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self initUnityWithOptions:launchOptions];
        });
        return YES;
    }
    
    - (void)initUnityWithOptions:(NSDictionary *)launchOptions
    {
    
        
        [self setUfw: UnityFrameworkLoad()];
        // Set UnityFramework target for Unity-iPhone/Data folder to make Data part of a UnityFramework.framework and uncomment call to setDataBundleId
        // ODR is not supported in this case, ( if you need embedded and ODR you need to copy data )
        [[self ufw] setDataBundleId: "com.unity3d.framework"];
        [[self ufw] registerFrameworkListener: self];
        [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
        
        [[self ufw] runEmbeddedWithArgc: [ConfigObj shareInstance].gArgc argv: [ConfigObj shareInstance].gArgv appLaunchOpts: launchOptions];
        
        UIView *view = [[[self ufw] appController] rootView];
        
    }
    - (void)applicationWillResignActive:(UIApplication *)application { [[[self ufw] appController] applicationWillResignActive: application]; }
    - (void)applicationDidEnterBackground:(UIApplication *)application { [[[self ufw] appController] applicationDidEnterBackground: application]; }
    - (void)applicationWillEnterForeground:(UIApplication *)application { [[[self ufw] appController] applicationWillEnterForeground: application]; }
    - (void)applicationDidBecomeActive:(UIApplication *)application { [[[self ufw] appController] applicationDidBecomeActive: application]; }
    - (void)applicationWillTerminate:(UIApplication *)application { [[[self ufw] appController] applicationWillTerminate: application]; }
    @end
    

    集成后,你可能会发现,将unity作为你的子模块启动时,在加载unity的启动页之前,会先加载一次原生的启动页,可以将SplashScreen.mm文件下ShowSplashScreen函数里下面的代码注释

            UIStoryboard *storyboard = [UIStoryboard storyboardWithName: launchScreen bundle: [NSBundle mainBundle]];
    
            // as we still support xcode pre-11 we must do this weird dance of checking for both sdk and runtime version
            // otherwise it fails to compile (due to unknown selector)
        #if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
            if (@available(iOS 13.0, tvOS 13.0, *))
            {
                _controller = [storyboard instantiateInitialViewControllerWithCreator:^(NSCoder *coder) {
                    return [[UnityViewControllerStoryboard alloc] initWithCoder: coder];
                }];
            }
            else
        #endif
            {
                _controller = [storyboard instantiateInitialViewController];
            }
    
    有小伙伴联系我说想知道如何作为target存在于工程中,其实上述文章的集成方式本质上还是target关联的方式存在,结构图如下:
    结构图

    到这里,整个集成就完成了,运行一下,就可以展示unity的内容了,后面具体的unity交互内容将另起文章说明。

    相关文章

      网友评论

        本文标题:iOS原生集成unity—framework形式集成(一)

        本文链接:https://www.haomeiwen.com/subject/etmqjktx.html