Unity3D和移動端(IOS)混合開發(一)

上一篇:Unity3D和Android原生混合開發app


本片總結Unity3D和IOS混合開發。
Unity3D和IOS混合開發和Android也同樣,有兩個方式:
ide

  • 以Unity3D爲主導混合開發
  • 以IOS爲主導混合開發

1.Unity3D爲主導,調用Ios的方法。

因爲U3D沒法直接調用Objc或者Swift語言聲明的接口,幸虧U3D的主要語言是C#,所以能夠利用C#的特性來訪問C語言所定義的接口,而後再經過C接口再調用ObjC的代碼(對於Swift代碼則還須要使用OC橋接)函數

(1)用Xcode新建test.m和test.h兩個文件,內容以下ui

// test.h
extern "C"
{
  extern void outputAppendString (char *str1, char *str2);
}
/// test.m
#import <Foundation/Foundation.h>
void outputAppendString (char *str1, char *str2)
{
  NSString *string1 = [[NSString alloc] initWithUTF8String:str1];
  NSString *string2 = [[NSString alloc] initWithUTF8String:str2];
  NSLog(@"###%@", [NSString stringWithFormat:@"%@ %@", string1, string2]);
}

IOS回調Unity中的方法有兩種,以下this

  • UnitySendMessage方法
    //str1:Unity中掛載腳本對象的名稱
    //str2:調用的方法名
    //resultStr.UTF8String:傳遞的參數
    UnitySendMessage("str1", "str2", resultStr.UTF8String);
  • 非託管方法方式

Unity腳本中創建一個delegate聲明,並使用UnmanagedFunctionPointer特性來標識該delegate是非託管方法,其中的CallingConvention.Cdel爲調用時轉換爲C聲明接口。atom

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ResultHandler(string resultString);

而後聲明一個靜態方法,並使用MonoPInvokeCallback特性來標記爲回調方法,目的是讓iOS中調用該方法時能夠轉換爲對應的託管方法。url

[MonoPInvokeCallback(typeof(ResultHandler))]
static void resultHandler (string resultStr)
{

}

注意:MonoPInvokeCallback特性參數是上一步中定義的非託管delegate。方法的聲明必定要與delegate定義一致,而且必須爲static進行修飾(iOS不支持非靜態方法回調),不然會致使異常。spa

IOS這邊Xcode文件test.m文件中定義一個接口,在C中須要定義一個與C#的delgate相同的函數指針ResultHandler。而後新增的outputAppendString2方法中多了一個回調參數resultHandler。這樣就可以把C#傳入的方法進行調用了。.net

typedef void (*ResultHandler) (const char *object);

void output(char *str1,char *str2,ResultHandler resultHandler){
   resultHandler(參數)
}
UnitySendMessage方式 非託管方法方式
接口聲明固定,只能是void method(string message) 接口靈活,能夠爲任意接口
不能帶有返回值 能夠帶返回值
必需要掛載到對象後才能調用 能夠不用掛載對象,但須要經過接口傳入該調用方法

(2)將上面的這兩個文件放到Unity3D工程Assets下,分別選擇這兩個文件,設置平臺Unity3D和移動端(IOS)混合開發(一)
(3)Unity3D新建一個腳本,實現下面內容
3d

using System.Runtime.InteropServices;
//引入聲明 
    [DllImport("__Internal")]
    static extern void outputAppendString (string str1, string str2);

        void Start () {
    #if UNITY_IPHONE    
    outputAppendString("test1", "test2");
    #endif
   }

注意:對於指定平臺的方法,必定要使用預處理指令#if來包括起來。不然在其餘平臺下面執行會致使異常。

對於上面第二步中採用非託管方法進行交互,Unity 實現方式區別就是要獲取指針。

//引入申明
[DllImport("__Internal")]
static extern void output (string str1, string str2, IntPtr resultHandler);
ResultHandler handler = new ResultHandler(resultHandler);
//使用Marshal的GetFunctionPointerForDelegate來獲取resultHandler的指針。
IntPtr fp = Marshal.GetFunctionPointerForDelegate(handler);
output ("Hello", "World", fp);

關於Marshal

Marshal類型主要是用於將C#中託管和非託管類型進行一個轉換的橋樑。其提供了一系列的方法,這些方法包括用於分配非託管內存、複製非託管內存塊、將託管類型轉換爲非託管類型,此外還提供了在與非託管代碼交互時使用的其餘雜項方法等。
本質上U3D與iOS的交互過程就是C#與C的交互過程,因此Marshal就成了交互的關鍵,由於C#與C的交互正正涉及到託管與非託管代碼的轉換。下面將舉例說明,如何將一個C#的引用類型轉換到對應的OC類型。
注意:Marshal申請的內存不是自動回收的,所以調用後須要經過顯示方法FreeHGlobal調用釋放。

Marshal.FreeHGlobal(存儲的內容);

(4)unity3D 打包導出Ios項目,檢查導出文件中是否有以前建立的test.m和test.h兩個文件,而後編譯。

2.以IOS爲主導,添加Unity3D模塊混合開發方式

(1)分別用Unity和Xcode建立兩個項目,Unity3D打包IOS的工程包;
(2)將Unity3D導出的IOS包中的Classes文件夾、Data文件夾、Libraries、MapFileParser、MapFileParser.sh這五個複製到Xcode建立的IOS工程中。
Unity3D和移動端(IOS)混合開發(一)
將Classes、Libraries以如下方式添加
Unity3D和移動端(IOS)混合開發(一)
將Data、QCAR(QCAR文件在Data->Raw裏面)文件夾以如下方式添加
Unity3D和移動端(IOS)混合開發(一)
打開從unity中導出的iOS工程,查看其引用的庫文件,添加到Xcode的工程中。
(3)在build settings中關閉bitcode。
在 other Linker Flags 添加-weak_framework CoreMotion -weak-lSystem
Unity3D和移動端(IOS)混合開發(一)
在Other C Flags中添加 -DINIT_SCRIPTING_BACKEND=1
Unity3D和移動端(IOS)混合開發(一)
添加pch文件
Unity3D和移動端(IOS)混合開發(一)













注意:
若是項目中有多個pch文件,請將其合併成一個pch文件,再添加

在Header Search Paths 添加頭文件引用(路徑本身打)

(4)打開從unity導出的工程,查看Header Search Paths中添加的頭文件,在Library Search Path 中添加庫引用
Unity3D和移動端(IOS)混合開發(一)
打開從unity導出的工程,查看ibrary Search Path中添加的頭文件,將其添加到本身工程的ibrary Search Path中
Unity3D和移動端(IOS)混合開發(一)
打開從unity導出的工程, 在build settings中查看user-Defined,並將其添加到本身項目的 user-Defined中
Unity3D和移動端(IOS)混合開發(一)
打開從unity導出的工程, 在build phases中查看Run Script
Unity3D和移動端(IOS)混合開發(一)
(5)更改main.m文件爲main.mm文件。將Classes中的main.mm中的內容複製,粘貼到原來工程的main.mm中。而後刪除Classes中的main文件。並將main函數中的 UIApplicationMain方法修改成







UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

(6)在Classes的UnityAppController.mm中的
(void)applicationDidBecomeActive:(UIApplication*)application方法中,
註釋掉[self performSelector:@selector(startUnity:) withObject: application afterDelay: 0]方法,這樣作的目的,是爲防止unity的內容隨着應用的啓動,而啓動;

- (void)applicationDidBecomeActive:(UIApplication*)application
{
    ::printf("-> applicationDidBecomeActive()\n");

    [self removeSnapshotView];

    if (_unityAppReady)
    {
        if (UnityIsPaused() && _wasPausedExternal == false)
        {
            UnityWillResume();
            UnityPause(0);
        }
        UnitySetPlayerFocus(1);
    }
    else if (!_startUnityScheduled)
    {
        _startUnityScheduled = true;
       // [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
    }
    _didResignActive = false;
}

(7)修改appDelegate.h

#import <UIKit/UIKit.h>
@class UnityAppController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong,nonatomic)UINavigationController *navi;
@property (strong, nonatomic) UIWindow *window;
@property (strong,nonatomic)UnityAppController* unityAppController;
- (void)shouldAttachRenderDelegate;
@end

(8)將appDelegate.m修改成appDelegate.mm,並對其內容進行修改

#import "AppDelegate.h"
 #import "ViewController.h"
 #import "UnityAppController.h"
 @interface AppDelegate ()
 @end
 extern "C" void VuforiaSetGraphicsDevice(void* device, int deviceType, int eventType);
 extern "C" void VuforiaRenderEvent(int marker);
 @implementation AppDelegate

 (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    BOOL returnBool;
    if (_unityAppController == nil) {

        _unityAppController = [[UnityAppController alloc] init];
    }
    [_unityAppController application:application didFinishLaunchingWithOptions:launchOptions]; 
    ViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]instantiateViewControllerWithIdentifier:@"viewVC"];
    self.navi = [[UINavigationController alloc] initWithRootViewController:vc];
    self.window.rootViewController=self.navi;
    [self.window makeKeyAndVisible];
    return YES;
}

 (void)applicationWillResignActive:(UIApplication *)application {
    [_unityAppController applicationWillResignActive:application];
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

 (void)applicationDidEnterBackground:(UIApplication *)application {

    [_unityAppController applicationDidEnterBackground:application];
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

 (void)applicationWillEnterForeground:(UIApplication *)application {
    [_unityAppController applicationWillEnterForeground:application];
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

  (void)applicationDidBecomeActive:(UIApplication *)application {
    [_unityAppController applicationDidBecomeActive:application];
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

 (void)applicationWillTerminate:(UIApplication *)application {
    [_unityAppController applicationWillTerminate:application];
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
 (void)shouldAttachRenderDelegate
{
    UnityRegisterRenderingPlugin(&VuforiaSetGraphicsDevice, &VuforiaRenderEvent);
}
 @end

(9)在Classes的UnityAppController.h中,更改GetAppController()方法(固然要先引入#import "AppDelegate.h")

inline UnityAppController*  GetAppController()
{
    AppDelegate *dele = (AppDelegate *)[UIApplication sharedApplication].delegate;
    return (UnityAppController *)dele.unityAppController;
}

注意:若是有錯,能夠用下面方法代替

NS_INLINE UnityAppController*    GetAppController()
{
    AppDelegate *dele = (AppDelegate *)[UIApplication sharedApplication].delegate;
    return (UnityAppController *)dele.unityAppController;
}

在UnityAppController.mm中重寫- (void)shouldAttachRenderDelegate方法

(void)shouldAttachRenderDelegate
{
 AppDelegate *deleg = [UIApplication sharedApplication].delegate;
 [deleg shouldAttachRenderDelegate];
}

上面的步驟就是把Unity的功能導入了IOS工程中,下面就是引用(調)Unity的功能:

(10)用Xcode建立一個ViewController.m,導入

#import "UnityAppController.h"
#import "AppDelegate.h"

啓動Unity 實現

[GetAppController() preStartUnity];
  [GetAppController() startUnity:[UIApplication sharedApplication]];
  [UnityGetMainWindow() makeKeyAndVisible];

在UnityAppController.h中申明-(void) restartUnity,在UnityAppController.mm中實現

(void) restartUnity
{
 _window.rootViewController=_rootController;
 [_window makeKeyAndVisible];
 [UnityGetMainWindow() makeKeyAndVisible];
 if (_didResignActive) {
     UnityPause(false);

     _didResignActive=NO;
 }
}

這就啓動Unity模塊了,可是注意關閉或隱藏unity模塊,也須要代碼操做

在UnityAppController.mm中的- (void)startUnity:(UIApplication*)application方法裏,添加實現方法

(void)doHideenUnity
{
 UnityPause(true);
 _didResignActive=YES;
 Profiler_UninitProfiler();
 AppDelegate *delet=[UIApplication sharedApplication].delegate;
     [delet.window makeKeyAndVisible];
}

打包這就完成了IOS添加Unity模塊的開發。

總結:須要注意的點

  • 重複的main.mm,記得刪除Classes文件的main.mm
  • 若是有多個pch文件,記得進行合併
  • 記得改AppDelegate.mm
  • Privacy - Camera Usage Description App須要你的贊成,才能訪問攝像頭
  • -weak_framework CoreMotion -weak-lSystem橫着寫在一個格子裏

下一篇:IOS集成Unity3D庫的混合開發

相關文章
相關標籤/搜索