1. 程式人生 > >[轉]iOS版與Unity3d互動

[轉]iOS版與Unity3d互動

前言

  • 最近在實驗室做了一個專案,用到了藍芽通訊和U3D的互動,都有很多坑,如:IOS與Unity3D介面之間的跳轉,資料的傳遞等操作過程其實不是很難,只是比較繁瑣,剛開始可能一頭霧水,慢慢學習瞭解後,就會發現其實很簡單。
  • 整篇文章主要介紹以下兩點
  1. IOS與團結之間的介面切換
  2. IOS與團結之間的函式呼叫以及傳值

IOS與團結之間的介面切換

在IOS和Unity3D跨平臺開發中,基本就是兩個平臺之間的資料互動和介面切換,而介面互動是重中之重。利用Unity3D可以實現一些IOS原生介面所不具有的效果,而IOS原生介面則在整個程式介面中又顯得更和諧一些。所以無論是功能還是美觀,兩者之間的介面互動都很常用。

前期統一準備

我們要用統一和IOS的介面切換和資料互動,就必然要先實現一個統一程式。我就預設讀者都會一些統一基礎和OC技術吧。

  • 首先建立一個新的統一程式,在場景中拖入一個多維資料集。我們可以利用這個立方體來展示團結和IOS之間的資料互動。

    一個簡單的團結場景
  • 我們在攝像頭下建立邦定一個指令碼,在指令碼中提供各類介面。
一個簡單的測試指令碼
  • 我們編輯指令碼,給出一些基本的介面。
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class
Test :
MonoBehaviour { public GameObject cube; // DllImport這個方法相當於是告訴Unity,有一個unityToIOS函式在外部會實現。 // 使用這個方法必須要匯入System.Runtime.InteropServices; [DllImport("__Internal")] private static extern void unityToIOS (string str); void OnGUI() { // 當點選按鈕後,呼叫外部方法 if (GUI.Button (new
Rect (100, 100, 100, 30), "跳轉到IOS介面")) { // Unity呼叫ios函式,同時傳遞資料 unityToIOS ("Hello IOS"); } } // 向右轉函式介面 void turnRight(string num){ float f; if (float.TryParse (num, out f)) {// 將string轉換為float,IOS傳遞資料只能用以string型別 Vector3 r = new Vector3 (cube.transform.rotation.x, cube.transform.rotation.y - 10f, cube.transform.rotation.z); cube.transform.Rotate (r); } } // 向左轉函式介面 void turnLeft(string num){ float f; if (float.TryParse (num, out f)) {// 將string轉換為float,IOS傳遞資料只能用以string型別 Vector3 r = new Vector3 (cube.transform.rotation.x, cube.transform.rotation.y - 10f, cube.transform.rotation.z); cube.transform.Rotate (r); } } }
  • 在給出介面之後,我們將Unity工程匯出到IOS工程,點選File-> Build Settings-> IOS,(在build之前,我們需要填寫一些ID資訊,其實這些資訊也可以不現在填,在匯出工程後,可以在Xcode的裡面填寫)然後點選建立。
匯出工程到的Xcode

從IOS介面切換到統一介面

  • 從IOS介面切換到統一介面,需要做的是攔截統一介面的啟動,當我們需要載入統一介面的時候才讓其啟動。而要攔截統一介面,就需要先搞明白統一介面載入的順序。main.mm檔案是整個統一工程的入口。在主函式中先進行一些初始化和註冊操作,然後執行UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]),這星級典句就執行了UnityAppController物件。在UnityAppController中,統一整個程式就算是啟動起來了。所以我們需要了解UnityAppController物件中一些方法的執行順序以便於我們攔截統一介面。在UnityAppController這個物件中,一些核心的方法已經有列印,如果想要知道所有方法的執行順序,我們可以在每一個方法裡面新增列印方法,此處為了簡單,我就利用已經有的列印函式。
const char* AppControllerClassName = "UnityAppController";
int main(int argc, char* argv[])
{
    @autoreleasepool
    {
        UnityInitTrampoline();
        UnityParseCommandLine(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;
}
  • 執行專案後,我們看到列印
列印結果
  • - (void)applicationDidBecomeActive:(UIApplication*)application這個方法中,了執行startUnity:方法。而這個方法一旦執行,統一介面就啟動起來了。所以需要我們做的攔截操作就在startUnity:之前,即- (void)applicationDidBecomeActive:(UIApplication*)application方法中呼叫IOS原生介面。
  • 一下觀察- (void)applicationDidBecomeActive:(UIApplication*)application方法,中間有一句[self performSelector:@selector(startUnity:) withObject:application afterDelay:0];,我們要做的就是把startUnity替換成我們自己的方法,在自己的方法中呼叫IOSUI,然後在UI中寫一些邏輯來讓IOS可以跳轉到統一介面。
  - (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(startSelfIOSView) withObject:application afterDelay:0];
    }
    _didResignActive = false;
}
- (void)startSelfIOSView
{
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view.backgroundColor = [UIColor blueColor];
    vc.view.frame = [UIScreen mainScreen].bounds;
    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 70, 30)];
    btn.backgroundColor = [UIColor whiteColor];
    [btn setTitle:@"跳轉到Unity介面" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(startUnity:) forControlEvents:UIControlEventTouchUpInside];
    [vc.view addSubview:btn];
    [_window addSubview:vc.view];
}
  • 執行結果,中間IOS介面執行的時候,我點選了白色的按鈕跳轉。
IOS介面跳轉到統一介面

從統一介面跳轉到IOS介面

在統一指令碼中,我們已經添加了一個按鈕,並且給了介面。所以我們可以利用這個按鈕實現跳轉到IOS介面。

在IOS工程中呼叫統一函式的方式是利用Ç語言介面。在IOS工程中利用形如

extern "C"
{
    void functionName(parameter){
    // do something
    }
}

這樣的形式來呼叫統一中的函式。

因為統一介面跳轉到IOS介面涉及到了暫停團結所以我們需要實現一個單例來判斷統一的暫停或啟動

//  LARManager.m
//  Unity-iPhone
//
//  Created by 柳鈺柯 on 2016/12/15.
//
//
#import "LARManager.h"
@implementation LARManager
+ (instancetype)sharedInstance
{
    static LARManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc] init];
    });
    return manager;
}
- (instancetype)init
{
    if (self = [super init]) {
        self.unityIsPaused = NO;
        NSLog(@"單例初始化成功");
    }
    return self;
}
@end
  • 跳轉到IOS介面,我們需要實現在Unity指令碼中宣告的unityToIOS函式。值得注意的是:在extern“C”中,不能用OC的selfself.window獲取到appControllerwindow,必須使用UnityAppController物件提供的方法GetAppController()UnityGetGLView()來獲取。返回之前的介面,所以我需要宣告一個強引用變數來引用之前的圖。在UnityAppController.h中宣告@property (strong, nonatomic) UIViewController *vc,在然後的剛才startSelfIOSView中新增星級典句self.vc = vc;
實現單例之後的startSelfIOSView和unityToIOS函式
- (void)startSelfIOSView
{
    // 單例變數unity沒有暫停,設定為no
    [LARManager sharedInstance].unityIsPaused = NO;
    // IOS原生介面
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view.backgroundColor = [UIColor blueColor];
    vc.view.frame = [UIScreen mainScreen].bounds;
    // 新增按鈕
    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 200, 30)];
    btn.backgroundColor = [UIColor whiteColor];
    btn.titleLabel.backgroundColor = [UIColor blackColor];
    [btn setTitle:@"跳轉到Unity介面" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(startUnity:) forControlEvents:UIControlEventTouchUpInside];
    // 在介面上新增按鈕
    [vc.view addSubview:btn];
    self.vc = vc;
    NSLog(@"設定介面為IOS介面");
    self.window.rootViewController = vc;
}
extern "C"
{
    // 對Unity中的unityToIOS方法進行實現
    void unityToIOS(char* str){
        // Unity傳遞過來的引數
        NSLog(@"%s",str);
        UnityPause(true);
        // 跳轉到IOS介面,Unity介面暫停
        [LARManager sharedInstance].unityIsPaused = YES;
        // GetAppController()獲取appController,相當於self
        // UnityGetGLView()獲取UnityView,相當於_window
        // 點選按鈕後跳轉到IOS介面,設定介面為IOS介面
        GetAppController().window.rootViewController = GetAppController().vc;
    }
}
  • 實現效果:
團結和IOS互相跳轉

IOS與團結之間的函式呼叫以及傳值

在上一步中,其實我們已經實現了IOS和統一函式呼叫和一部分傳值。現在更詳細的說明一下。

統一呼叫IOS函式並進行傳值

  • 統一呼叫IOS函式其實就是先在統一指令碼中宣告一個函式介面,然後在IOS程式中實現。其中
    [DllImport("__Internal")]
    private static extern void functionName (ParameterType Parameter);

為固定格式。這個表明會在外部引用一個叫functionName(ParameterType Parameter)的函式,引數為Parameter。在IOS程式中實現上一步中已經講過,不再贅述。

IOS呼叫團結函式並進行傳值

IOS呼叫團結函式需要用到UnitySendMessage方法,方法中有三個引數

UnitySendMessage(“gameobject”,“Method”,msg);
向Unity傳送訊息
引數為統一指令碼掛載的gameobject
引數二為統一指令碼中要呼叫的方法名
引數三為傳遞的資料* 注意:傳遞的資料只能是字元型別

我們利用在統一工程中已經寫好的介面來試試資料的傳遞。

  • 首先在我們需要新增2個按鈕,按鈕需要實現點選後,傳遞資料過去,使視野中的立方體(正方體)向左向右旋轉。
  • 我們只需要在原生的統一介面中新增按鈕。在原生介面中新增和在IOS工程中新增是差不多的。在介面還沒有顯示的時候,新增進去即可,我在- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions這狀語從句:方法中新增按鈕。程式碼如下:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    /*省略了一些Unity的操作*/
  /*.....*/
    [self createUI];
    [self preStartUnity];
  // 新增右旋按鈕
  UIButton *rightBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, 150, 100, 30)];
  rightBtn.backgroundColor = [UIColor whiteColor];
  [rightBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  [rightBtn setTitle:@"向右旋轉" forState:UIControlStateNormal];
  [rightBtn addTarget:self action:@selector(turnRight) forControlEvents:UIControlEventTouchUpInside];
  self.rightBtn = rightBtn;
  // 新增左旋按鈕
  UIButton *leftBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, 200, 100, 30)];
  leftBtn.backgroundColor = [UIColor whiteColor];
  [leftBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  [leftBtn setTitle:@"向左旋轉" forState:UIControlStateNormal];
  [leftBtn addTarget:self action:@selector(turnLeft) forControlEvents:UIControlEventTouchUpInside];
  self.leftBtn = leftBtn;
  // 在Unity介面新增按鈕
  [UnityGetGLViewController().view addSubview:_rightBtn];
  [UnityGetGLViewController().view addSubview:_leftBtn];
    // if you wont use keyboard you may comment it out at save some memory
    [KeyboardDelegate Initialize];
    return YES;
}
// 左旋按鈕響應事件
- (void)turnRight
{
    const char* str = [[NSString stringWithFormat:@"10"] UTF8String];
    UnitySendMessage("Main Camera", "turnRight", str);
}
// 右旋按鈕響應事件
- (void)turnLeft
{
    const char* str = [[NSString stringWithFormat:@"10"] UTF8String];
    UnitySendMessage("Main Camera", "turnLeft", str);
}
  • 在新增完成按鈕後,我們向攝像機發送訊息。因為我們的指令碼是繫結在主攝像頭上的,所以第一個引數為主攝像機的名字,第二個引數為指令碼中的方法,第三個引數為資料。
  • 因為我個人粗心,在統一指令碼中,關於旋轉的加減號忘記修改了,導致點選左旋按鈕後物體也是右旋只需要把指令碼中turnLeft函式的-號改成+就號行了執行如下:
IOS呼叫函式以及傳值到團結

總結

  • IOS切換到統一的介面切換主要通過攔截統一介面的執行,在我們想要使用統一介面的時候才讓統一介面的方法繼續執行。IOS介面切換到統一介面,統一如果介面不是第一次執行,記得執行UnityPause(假)方法,因為我們在切換到IOS的時候,會暫停統一介面
  • 統一切換到IOS將介面viewController更改自已分類中翻譯定義的介面控制器就可以了。切換到IOS的介面時候記得執行UnityPause(true)方法
  • IOS傳遞資料到統一介面主要通過UnitySendMessage()方法,* 引數一為統一指令碼掛載的遊戲物件,引數二為統一指令碼中要呼叫的方法名,第三個引數為資料,資料格式只能為char **,利用這個方法也可以呼叫Unity的函式。
  • 統一傳遞資料到IOS也是通過宣告外部函式,通過在統一指令碼呼叫外部函式的時候以引數的方式傳遞資料。
  • 宣告外部函式的格式為:
  // 宣告外部函式
    [DllImport("__Internal")]
    private static extern void functionName (ParameterType Parameter);
  • IOS實現統一中宣告的函式格式為:
extern "C"
{
    void functionName(parameter){
    // do something
    }
}

結語

  • 最近諮詢IOS和統一通訊的人也有點多,寫出這個部落格給大家作為參考。
  • 跨平臺通訊都是自己在琢磨,錯誤難免,敬請指正。
  • 有疑問可以給我發郵件,但是在傳送郵件前,請保證您深思過此問題。若問題沒有意義或者在部落格中有的話,我不會進行回覆,請諒解。郵箱:[email protected]
  • 最後附上Demo,點選下載。Unity 和IOS互動演示

相關推薦

[]iOSUnity3d互動

前言 最近在實驗室做了一個專案,用到了藍芽通訊和U3D的互動,都有很多坑,如:IOS與Unity3D介面之間的跳轉,資料的傳遞等操作過程其實不是很難,只是比較繁瑣,剛開始可能一頭霧水,慢慢學習瞭解後,就會發現其實很簡單。 整篇文章主要介紹以下兩點 IOS與團結之間的介面切換 IOS與團結之間的函式呼叫以及傳

Android Studio Unity3d互動。歸納

本來想自己整理下,發現有人整理了,直接搬運過來了。備忘。 原址:http://blog.csdn.net/u014230923/article/details/51363556 這次部落格講解 Android Studio 怎麼匯出專案到 Unity。  在這

iOS開發H5互動的整理總結

UIWebView delegate 協議方法 //UIWebView自帶了一個方法, 可以直接呼叫JS程式碼(轉化為string型別的js程式碼) - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString

iOS WebViewNative互動

我們在專案中不可避免的要使用到WebView,一般的用法就是WebView直接載入URL,做一些基本展示操作。但是對於一些特定的需求或邏輯,我們可能就需要WebView傳遞一些資料到Native,由Native來對資料做處理,比如有跨域限制或攔截WebView請

iOSJS互動Demo文件(ObjC

參考原文連結: http://mp.weixin.qq.com/s?__biz=MzIzMzA4NjA5Mw==&mid=214063688&idx=1&sn=903258ec2d3ae431b4d9ee55cb59ed89#rd http://ww

unity反饋訊息到ios Unityios互動

本文學習怎麼在unity裡返回訊息到iOS,現實功能(點選unity裡面的按鈕傳遞一個字串到ios顯示在訊息對話方塊上,並且開啟一個新的VeiwContoller)。言歸正傳 首先我們建立一個unity工程,建立一個MainScript指令碼,程式碼如下: using

iOSJS互動的4種方法

iOS與JS互動的方法: 1.攔截url(適用於UIWebView和WKWebView) 2.JavaScriptCore(只適用於UIWebView,iOS7+) 3.WKScriptMessageHandler(只適用於WKWebView,iOS8+) 4.WebViewJ

Swift WKWebView(二):iOSjs互動

在上一篇中我們介紹了Swift下WKWebView的基本使用方法,下面總結一下iOS與js互動的實現,最終的頁面效果如下圖所示: 其中,js有關程式碼如下: function navButtonAction(name,age){

目前iOSJS互動的方法選擇比對

只是針對目前存在的互動方式做個比對。不會涉及任何實現。如需知道具體的使用。可自行baidu或者Google。 攔截協議 攔截協議是最簡單的互動方式,在Android端和iOS端直接攔截,可以統一web前端的程式碼。web前端通過在連結中帶上引數。比如: 88gongxiang:

android,iosweb互動-—APP使用H5頁面

  一、ios與H5之間的互動 <!DOCTYPE html>   <html>   <meta charset="utf-8">

iOS OCH5網頁互動之OC傳值給JS(WKWebView)

1.OC傳值給JS的程式碼:// 頁面載入完成之後呼叫 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)

iOS中UIWebViewWKWebView、JavaScriptOC互動、Cookie管理看我就夠(下)

前言在前面的文章中,我們介紹了UIWebView、WKWebView一些使用,與JS的互動和一些坑,相信看過的小夥伴們,已經大概清楚了吧,如果有問題,歡迎提問。本文是本系列文章的最後一篇,主要為小夥伴們分享下Safari除錯、與前端的配合以及實際應用中一些需求的實現等:關於文

iOSHTML5互動方法總結

摘要 看了不少別人寫的部落格或者論壇,關於iOS與HTML5互動方法大概主要有5種方式: 1. 利用WKWebView進行互動(系統API) 2. 利用UIWebView進行互動(系統API) 3. 蘋果的javascriptcore.framework框架; 4. 跨平臺cordov

iOS 開發 Object-C和JavaScript互動詳解之OCJS互動在WKWebView中使用

1.OC與JS互動在UIWebView中使用 2. WKWebView的使用詳解 3.OC與JS互動在WKWebView中使用 // // ViewController.m // oc與js互動WKWebView // // Cr

iOS常用方法——WKWebViewh5互動的實現

隨著前端開發的強大,原生與h5的互動用的也越來越多。 為什麼選用WKWebView,我們可以做一個對比,同一個web頁面,用UIWebView載入和用WKWebView來載入,記憶體佔用情況很容易看出來,回到原生頁面之後,UIWebView對應的記憶體也不會降

iOSjs互動(WebView+WKWebView)

需求:1點選js登入按鈕將使用者賬號和密碼傳給移動端             2將客戶端的token傳給html端 據我瞭解有以下幾種方法 1:webview的javascriptCore 2:webkit 3:url攔截 4:第三方庫 我用的是1和2

ios jsoc原生WKWebView方法注入及互動傳值

    上篇文章中,我們整理了關於WKWebView的詳細使用,包含進度條、獲取web title等等內容,這篇文章我們整理下,專案中,我們可能使用到的oc 與 js 原生互動場景下的使用.如有興趣,

unity3Dweb互動初篇(一)

最近專案有客服系統的需求,任務分配到我頭上,第一次寫沒什麼經驗,查閱了一天的資源,磕磕碰碰總算是寫出來了,大概記錄一下流程和思路。 首先訪問web方法,一般都是用WWW類來訪問。程式碼很簡單。 using LuaInterface; using System; using

VC6下CHtmlView中最簡單最全面的程式網頁互動方法 【

寫的太實用了,未經允許就轉了,不行的話,我可以刪除。 VC6下CHtmlView中最簡單最全面的程式與網頁互動方法 簡單來說,終極目標------VC6和網頁相互呼叫對方的資料和方法;而呼叫方法時重點要捕獲返回值。 VC6下遠沒有VC7及其之後提供的操作豐富方便。

androidunity3d互相互動

請參看“Android和Unity3d互相呼叫的Demo”的資源 實際上Android調Unity就是用 //第一個引數為unity類所繫結的遊戲物件 //第二個引數為unity方法名 //第三個引數為unity方法的引數 UnityPlayer