Swift3整合Unity5.6

前言

因為公司項目需要,通過Unity做一個AR項目,并且整合到現(xiàn)有項目中,其中遇到了一些坑,也被很多人幫助過,特來記錄一下,希望大家能少走一些彎路。
其中涉及到的開發(fā)工具及相關(guān)語言有

  • Unity 5.6.1
  • xcode 8.3.3
  • Swift 3.1
  • AR引擎 EasyAR 2.0
  • cocoapods

參考案例

準(zhǔn)備工作

因為項目中使用到了EasyAR的AR SDK,但并不是每個人都需要,所以有需要的請自行到EasyAR官網(wǎng)下載并注冊成為開發(fā)者,目前最新版本為2.0(官方分為Basic和Pro兩個版本),本項目使用的是Pro(包括以前的1.3.1版本其實都是類似的)。

開始

一. Unity導(dǎo)出iOS工程

新建一個新的Unity工程


image.png

把從EasyAR官網(wǎng)下載的unitypackage包導(dǎo)入到項目中,由于EasyAR不是這里的重點,所以相關(guān)設(shè)置等請自行官網(wǎng)解決。設(shè)置完成后導(dǎo)出iOS工程


image.png

以下是導(dǎo)出成功的iOS工程(oc版本)


image.png

到這里Unity的工作就結(jié)束了,建議大家先運行導(dǎo)出的iOS工程看手機上是否正常。

二. 新建iOS工程(Swift3)

1. 添加Unity.xcconfig文件到項目

相關(guān)文件地址

//
//  Unity.xcconfig
//
//
//  Created by Stefans on 2017/6/14.
//  Copyright ? 2016年 Stefans. All rights reserved.
//

UNITY_RUNTIME_VERSION = 5.6.1;
UNITY_SCRIPTING_BACKEND = il2cpp;
GCC_THUMB_SUPPORT = NO;
GCC_USE_INDIRECT_FUNCTION_CALLS = NO
UNITY_IOS_EXPORT_PATH = $(PROJECT_DIR)/unity_ios;
GCC_PREFIX_HEADER = $(UNITY_IOS_EXPORT_PATH)/Classes/Prefix.pch;

OTHER_LDFLAGS = -weak-lSystem -weak_framework CoreMotion -weak_framework GameKit -weak_framework iAd -framework CoreGraphics -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework SystemConfiguration -framework CoreLocation -framework MediaPlayer -framework CFNetwork -framework AudioToolbox -framework OpenAL -framework QuartzCore -framework OpenGLES -framework UIKit -framework Foundation -liconv.2 -liPhone-lib;

HEADER_SEARCH_PATHS = $(inherited) $(UNITY_IOS_EXPORT_PATH) $(UNITY_IOS_EXPORT_PATH)/Classes $(UNITY_IOS_EXPORT_PATH)/Classes/Native $(UNITY_IOS_EXPORT_PATH)/Classes/UI $(UNITY_IOS_EXPORT_PATH)/Libraries $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include $(UNITY_IOS_EXPORT_PATH)/Libraries/bdwgc/include;
LIBRARY_SEARCH_PATHS = $(inherited) $(UNITY_IOS_EXPORT_PATH) $(UNITY_IOS_EXPORT_PATH)/Libraries $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include;

ENABLE_BITCODE = NO;

//請根據(jù)自己的項目修改
SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/$(PRODUCT_NAME)/UnityBridge.h;

OTHER_CFLAGS = -DINIT_SCRIPTING_BACKEND=1;
CLANG_CXX_LANGUAGE_STANDARD = compiler-default;
CLANG_CXX_LIBRARY = libc++;
CLANG_WARN_BOOL_CONVERSION = NO;
CLANG_WARN_CONSTANT_CONVERSION = NO;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
CLANG_WARN_EMPTY_BODY = NO;
CLANG_WARN_ENUM_CONVERSION = NO;
CLANG_WARN_INT_CONVERSION = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES;
CLANG_WARN_UNREACHABLE_CODE = NO;
CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_ENABLE_OBJC_EXCEPTIONS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_THUMB_SUPPORT = NO;
GCC_USE_INDIRECT_FUNCTION_CALLS = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64] = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = NO;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
GCC_WARN_UNUSED_FUNCTION = NO;
GCC_NO_COMMON_BLOCKS = NO;
CLANG_ENABLE_MODULES = NO;

CLANG_WARN_DOCUMENTATION_COMMENTS = NO;

CLANG_WARN_EMPTY_BODY = NO;
CLANG_WARN_INFINITE_RECURSION = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
CLANG_WARN_UNREACHABLE_CODE = NO;

GCC_WARN_UNUSED_FUNCTION = NO;

CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;

//CLANG_CXX_LANGUAGE_STANDARD = "c++0x";

下載Unity.xcconfig后如下圖做相應(yīng)配置


image.png

2. 添加UnityBridge.h UnityUtils.h UnityUtils.mm 到項目

//
//  UnityBridge.h
//
//  Created by Adam Venturella on 10/28/15.
//

#ifndef UnityBridge_h
#define UnityBridge_h

#import "UnityUtils.h"
#import "UnityAppController.h"
#import "Unity/UnityInterface.h"
#endif /* UnityBridge_h */



/**
 * Replacement Function for UnityAppController.h
 *
 */
/*
 NS_INLINE UnityAppController* GetAppController(){
 NSObject<UIApplicationDelegate>* delegate = [UIApplication sharedApplication].delegate;
 UnityAppController* currentUnityController = (UnityAppController *)[delegate valueForKey:@"currentUnityController"];
 return currentUnityController;
 }
 */
//
//  UnityUtils.h
//
//  Created by Adam Venturella on 10/28/15.
//

#ifndef UnityUtils_h
#define UnityUtils_h


void custom_unity_init(int argc, char* argv[]);

#endif /* UnityUtils_h */

//
//  UnityUtils.m
//
//  Created by Adam Venturella on 10/28/15.
//
// this is taken directly from the unity generated main.mm file.
// if they change that initialization, this will need to be updated
// as well.


#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>


// Hack to work around iOS SDK 4.3 linker problem
// we need at least one __TEXT, __const section entry in main application .o files
// to get this section emitted at right time and so avoid LC_ENCRYPTION_INFO size miscalculation
static const int constsection = 0;

void UnityInitTrampoline();


extern "C" void custom_unity_init(int argc, char* argv[])
{
    @autoreleasepool
    {
        UnityInitTrampoline();
//        UnityParseCommandLine(argc, argv); //Unity 5.3+
        UnityInitRuntime(argc, argv); //Unity 5.6+,5.4和5.5用哪個我沒試過,可以根據(jù)報錯情況選擇。
        
        RegisterMonoModules();
        NSLog(@"-> registered mono modules %p\n", &constsection);
        RegisterFeatures();
        
        // iOS terminates open sockets when an application enters background mode.
        // The next write to any of such socket causes SIGPIPE signal being raised,
        // even if the request has been done from scripting side. This disables the
        // signal and allows Mono to throw a proper C# exception.
        std::signal(SIGPIPE, SIG_IGN);
    }
}

image.png

3. 將Unity導(dǎo)出的文件導(dǎo)入到xcode工程中

只需要導(dǎo)入Classes、Data、Libraries三個文件夾

image.png

新建文件夾并重命名為:unity_ios,把這三個文件夾拷貝進去

image.png
image.png

確認一下工程配置

image.png

以及更改下Unity_Runtime_Path為5.6.1(填寫你自己的unity版本就好了)


image.png

4. 添加Run Script到Build Phases

rm -rf "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data";
cp -Rf "$UNITY_IOS_EXPORT_PATH/Data" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data";
image.png

5. Classes、Data、Libraries文件導(dǎo)入到項目中

Classes, Libraries 設(shè)置 Create groups

image.png

Data 設(shè)置 Create folder references

image.png
image.png

原版里面建議刪除一些引用,雖然會加快編譯速度,但是每次更新都刪除的話也是挺耗時的,所以如果有需要的話建議看看原版。

6. 變更Unity里的方法

找到main.mm

//int main(int argc, char* argv[])
//{
//    signed long long startTime = mach_absolute_time();
//    @autoreleasepool
//    {
//        UnitySetStartupTime(startTime);
//        UnityInitTrampoline();
//        UnityInitRuntime(argc, argv);
//
//        RegisterMonoModules();
//        NSLog(@"-> registered mono modules %p\n", &constsection);
//        RegisterFeatures();
//
//        // iOS terminates open sockets when an application enters background mode.
//        // The next write to any of such socket causes SIGPIPE signal being raised,
//        // even if the request has been done from scripting side. This disables the
//        // signal and allows Mono to throw a proper C# exception.
//        std::signal(SIGPIPE, SIG_IGN);
//
//        UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: AppControllerClassName]);
//    }
//
//    return 0;
//}
//替換為
int main_unity_default(int argc, char* argv[])
{
    @autoreleasepool
    {
        UnityInitTrampoline();
//        UnityParseCommandLine(argc, argv); //Unity 5.3+
        UnityInitRuntime(argc, argv); //Unity 5.6+,5.4和5.5用哪個我沒試過,可以根據(jù)報錯情況選擇。
        
        RegisterMonoModules();
        NSLog(@"-> registered mono modules %p\n", &constsection);
        RegisterFeatures();
        
        // iOS terminates open sockets when an application enters background mode.
        // The next write to any of such socket causes SIGPIPE signal being raised,
        // even if the request has been done from scripting side. This disables the
        // signal and allows Mono to throw a proper C# exception.
        std::signal(SIGPIPE, SIG_IGN);
        
        //UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
        //        UIApplicationMain(argc, argv, nil, NSStringFromClass([UnitySubAppDelegate class]));
        UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
    }
    
    return 0;
}

找到UnityAppController.h

//最上面添加
#import <UIKit/UIKit.h>
//注釋該方法
//inline UnityAppController*  GetAppController()
//{
//    return (UnityAppController*)[UIApplication sharedApplication].delegate;
//}
//替換為此方法
NS_INLINE UnityAppController* GetAppController()
{
    NSObject<UIApplicationDelegate>* delegate = [UIApplication sharedApplication].delegate;
    UnityAppController* currentUnityController = (UnityAppController *)[delegate valueForKey:@"currentUnityController"];
    return currentUnityController;
}

7. 修改自己的項目

找到AppDelegate.swift并作如下修改

//
//  AppDelegate.swift
//  SwiftCombineUnityProject
//
//  Created by Stefans on 2017/6/14.
//  Copyright ? 2017年 Stefans. All rights reserved.
//

import UIKit

//注釋@UIApplicationMain, 讓swift從main.swift啟動
//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var currentUnityController: UnityAppController!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        currentUnityController = UnityAppController()
        currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)
        
        window = UIWindow.init(frame: UIScreen.main.bounds)
        let sb = UIStoryboard.init(name: "Main", bundle: nil)
        let vc = sb.instantiateInitialViewController()
        window?.rootViewController = vc!;
        window!.makeKeyAndVisible()
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        currentUnityController.applicationWillResignActive(application)
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        currentUnityController.applicationDidEnterBackground(application)
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        currentUnityController.applicationWillEnterForeground(application)
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        currentUnityController.applicationDidBecomeActive(application)
    }

    func applicationWillTerminate(_ application: UIApplication) {
        currentUnityController.applicationWillTerminate(application)
    }
}

添加一個新的main.swift文件到項目

//
//  main.swift
//  SwiftCombineUnityProject
//
//  Created by Stefans on 2017/6/15.
//  Copyright ? 2017年 Stefans. All rights reserved.
//

import Foundation
import UIKit

custom_unity_init(CommandLine.argc, CommandLine.unsafeArgv)

UIApplicationMain(
    CommandLine.argc,
    UnsafeMutableRawPointer(CommandLine.unsafeArgv)
        .bindMemory(
            to: UnsafeMutablePointer<Int8>.self,
            capacity: Int(CommandLine.argc)),
    nil,
    NSStringFromClass(AppDelegate.self)
)

8. 添加依賴庫

依賴庫是最煩的,按照需求一個個添加吧,特別注意的是使用easyar或者其他ar sdk需要添加libc++.tbd ,其他的看項目。

image.png
image.png

9. 加載Unity View

//
//  ViewController.swift
//  SwiftCombineUnityProject
//
//  Created by Stefans on 2017/6/14.
//  Copyright ? 2017年 Stefans. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    @IBAction func showUnity(_ sender: UIButton) {
        let unityview = UnityGetGLView()
        unityview?.frame = self.view.bounds;
        unityview?.center = self.view.center
        self.view.addSubview(unityview!)
        self.view.bringSubview(toFront: unityview!)
    }
}

完成之后就可連接真機調(diào)試了Let's GO?。?!

三. 可能遇到的問題

  • 目前該方式只支持真機調(diào)試,這點會有些不方便,特別是Unity本身不是完全核心,外面一層殼也有很多功能的時候

可以把Unity模塊卸載掉,至于如何卸載,大家可以按照上面操作反向?qū)嶒灐?/p>

  • 如果用到了AR SDK,會報錯

大部分是因為沒有給Camera權(quán)限,記得plist文件中給Camera的權(quán)限


image.png
  • 使用EasyAR可能會導(dǎo)致黑屏

在unity_ios/Libraries/Plugins/iOS/EasyARAppController.mm中找到以下代碼,并復(fù)制到UnityAppController.mm中

extern "C" void ezarUnitySetGraphicsDevice(void* device, int deviceType, int eventType);
extern "C" void ezarUnityRenderEvent(int marker);
image.png

并在UnityAppController.mm中實現(xiàn)以下內(nèi)容:

    UnityRegisterRenderingPlugin(&ezarUnitySetGraphicsDevice, &ezarUnityRenderEvent);
}```

##四. 大功告成
到此Unity項目就整合進我們的Swift3工程中了,因為Unity版本也一直在更新變化,所以有些小細節(jié)部分也會變化,包括最開始整合的是Unity5.3版本,到現(xiàn)在5.6版本發(fā)生了些變化,大家根據(jù)報錯提示做適當(dāng)修改即可。后續(xù)會繼續(xù)把cocoapod整合進去。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容