在原生APP中集成Unity容器

隨着技術的發展,越來越多的APP期望擁有3D,AR的能力。要達到這個目標可以選擇使用原生開發,也可以使用Unity成熟的3D開發技術鏈,通過嵌入的方式將Unity容器嵌入到APP中。這裏介紹的是通過嵌入Unity容器的方式來實現APP的3D,AR能力的。
 
Unity集成到iOS應用的本質是將Unity中所有用到的資源,模型,代碼打包成一個framework動態庫,然後嵌入到APP的內部。APP啓動後加載Unity頁面時,會加載這個framework動態庫和裏面的所有資源。然後在APP中展示這個Unity頁面。
 
Unity集成到iOS應用的官方提供的方案是以Workspace的方式集成,具體步驟如下:
1.Unity項目通過Unity Editer 導出一個iOS項目,這個iOS項目裏包含了整個Unity項目的所有代碼和資源,可以直接運行在iOS系統上。
2.用Xcode創建一個原生的iOS項目
3.用Xcode創建一個Workspace, 然後通過add file 的形式將這2個項目添加到一個Workspace下面。
4.修改Unity-iPhone項目打包設置,使其打包出適合嵌入的UnityFramework動態庫,
5.修改NativeiOSApp項目配置,添加UnityFramework動態庫。

 

對Workspace下面的2個項目進行配置
Unity-iPhone創建
在生成UnityFramework動態庫前,需要將Unity項目導出一個iOS項目,導出的過程如下:
1.點擊File->Building Settings進入到選擇導出頁面

2.選擇要導出的平臺,這裏選擇iOS,然後點擊右下角的"Switch Plateform"按鈕,然後點擊上面的“Add Open Scenes”添加場景

3.點擊“Build”,導出iOS項目

 

Unity-iPhone設置
關閉以Workspace方式打開的Xcode,單獨打開Unity-iPhone項目
1.在Unity-iPhone項目中,設置Data 這個Group的 Target Memebership, 將裏面的UnitFramework項打勾,表示這個Data在生成Framework靜態庫時也要作爲其中的一員,放置進去。
2.將NativeCallProxy.h頭文件從Framework的 protect組移動到public組,將頭文件開放出來。路徑爲: Unity-iPhone / Libraries / Plugins / iOS / NativeCallProxy.h
3.手機連接電腦,運行,生成靜態庫。

 

NativeiOSApp原生項目設置
以Workspace方式打開項目
1.在NativeiOSApp Target - General - Frameworks, Libraries, and Embedded Content 通過點擊+ 將UnityFramework設置嵌入到項目中
2.在NativeiOSApp Target - Build Phases - Link Binary With Libraries中,點擊-,將UnityFramework移除,表示禁止在項目鏈接時將UnityFramework鏈接到可執行文件中,它只作爲內嵌在APP中的靜態庫在運行時加載使用。
3.手機連接電腦,運行,看到Demo運行的效果。
 
 
Unity與原生通信
Unity調用原生
1.首先在原生中實現代理協議NativeCallProxy中的方法(註冊由誰來處理unity的調用)和添加暴露給unity的方法。
NativeCallProxy.h文件中方法的聲明
__attribute__ ((visibility("default")))
@interface FrameworkLibAPI : NSObject
// call it any time after UnityFrameworkLoad to set object implementing NativeCallsProtocol methods
+(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi;

@end
NativeCallProxy.cpp文件中方法的實現
@implementation FrameworkLibAPI

id<NativeCallsProtocol> api = NULL;
+(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi
{
    api = aApi;
}

@end
給Unity暴露的原生調用方法
extern "C" {
    void showHostMainWindow(const char* color) { return [api showHostMainWindow:[NSString stringWithUTF8String:color]]; }
}
2.然後在原生的Unity啓動時,註冊這個處理的對象
- (void)initUnity
{
    [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];
 }
3.在Unity中,調用iOS的原生方法
Cube.cs腳本中聲明在APP中存在方法showHostMainWindow
#if UNITY_IOS || UNITY_TVOS
public class NativeAPI {
    [DllImport("__Internal")]
    public static extern void showHostMainWindow(string lastStringColor);
}
#endif
Cube.cs腳本中添加Unity 2d按鈕點擊事件,調用iOS原生方法
void OnGUI()
{
    GUIStyle style = new GUIStyle("button");
    style.fontSize = 45;
    if (GUI.Button(new Rect(10, 300, 600, 100), "Show Main With Color", style)) showHostMainWindow();
}


void showHostMainWindow()
{
#if UNITY_ANDROID
    try
    {
        AndroidJavaClass jc = new AndroidJavaClass("com.unity.mynativeapp.SharedClass");
        jc.CallStatic("showMainActivity", lastStringColor);
    } catch(Exception e)
    {
        appendToText("Exception during showHostMainWindow");
        appendToText(e.Message);
    }
#elif UNITY_IOS || UNITY_TVOS
    NativeAPI.showHostMainWindow(lastStringColor);
#endif
}

 

原生調用Unity
點擊原生髮送消息按鈕,發消息給Unity。
self.btnSendMsg = [UIButton buttonWithType: UIButtonTypeSystem];
[self.btnSendMsg setTitle: @"Send Msg" forState: UIControlStateNormal];
[self.btnSendMsg addTarget: self action: @selector(sendMsgToUnity) forControlEvents: UIControlEventPrimaryActionTriggered];


- (void)sendMsgToUnity
{
    [[self ufw] sendMessageToGOWithName: "Cube" functionName: "ChangeColor" message: "yellow"];
}

/*
goName: 場景中的遊戲物體GameObject
name: 這個遊戲物體掛載的腳本中的一個方法
msg: 參數
*/
- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg
{
    UnitySendMessage(goName, name, msg);
}

void  UnitySendMessage(const char* obj, const char* method, const char* msg);
Cube.cs腳本中的方法實現
string lastStringColor = "";
void ChangeColor(string newColor)
{
    appendToText( "Changing Color to " + newColor );

    lastStringColor = newColor;

    if (newColor == "red") GetComponent<Renderer>().material.color = Color.red;
    else if (newColor == "blue") GetComponent<Renderer>().material.color = Color.blue;
    else if (newColor == "yellow") GetComponent<Renderer>().material.color = Color.yellow;
    else GetComponent<Renderer>().material.color = Color.black;
}
 
Unity熱更新
在Unity項目中使用純C#構建的漸漸不能適應移動端的需求,移動端通常通過添加熱更新插件來實現熱更新功能。這個過程起關鍵作用的是Lua和起對應的解釋器。
lua是解釋型語言,並不需要事先編譯成塊,而是運行時動態解釋執行的。
這樣LUA就和普通的遊戲資源如圖片,文本沒有區別,因此可以在運行時直接從WEB服務器上下載到持久化目錄並被其它LUA文件調用。
Lua熱更新解決方案是通過一個Lua熱更新插件(如ulua、slua、tolua、xlua等)來提供一個Lua的運行環境以及和C#進行交互。
所以lua熱更新的流程可以簡單理解爲:將邏輯代碼使用腳本實現,再將腳本轉化爲文本資源,最後以更新資源的形式來更新程序,以此來實現熱更新。
注意:
在iOS應用內的Unity容器中,Untiy的C#資源和Lua資源都是在Unity容器內部進行加載的。如果Unity採用的Lua熱更新,則需要原生側在Lua腳本和資源下載完成後通知Unity進行加載展示。


Demo地址
NativeiOSApp項目地址:https://github.com/zhfei/uaal-example
將 Unity 集成到原生 iOS 應用程序中官方文檔:https://docs.unity3d.com/cn/2020.2/Manual/UnityasaLibrary-iOS.html
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章