在原生APP中集成Unity容器

APP,容器,Unity · 浏览次数 : 204

小编点评

**Unity 3D嵌入到 iOS 应用中的方法** **1. 创建 Unity 项目** * 使用 Unity Editer 创建一个新的 iOS 项目。 * 将 Unity 项目导出为一个 Xcode 项目。 **2. 配置 Native iOS 项目** * 在 Native iOS 项目中设置以下配置: * 添加 UnityFramework 动态库。 * 在 `NativeCallProxy.h` 头文件中设置 `Data` Group 的 Target Memebership。 * 在 `NativeiOSApp Target - Build Phases - Link Binary With Libraries` 中移除 UnityFramework 动态库。 **3. 实现 Unity 与原生通信** * 在 `Cube.cs` 中添加以下方法: * `showHostMainWindow()` 方法用于调用 iOS 的原生方法。 * 在 `UnitySendMessage()` 方法中使用 `[DllImport]` 和 `Marshal.PtrToVoid` 等方法进行跨平台消息传递。 **4. 发送消息给 Unity** * 在 `Cube.cs` 中添加以下方法: * `sendMessageToGOWithName()` 方法用于发送字符串消息给 Unity。 * 通过 `UnitySendMessage()` 方法将字符串 "yellow" 发送给 `Cube` 对象的 `ChangeColor` 方法。 **5. 热更新** * 在 Unity 项目中设置热更新。 * 使用 Lua 热更新插件下载和加载资源。 * 在 `Cube.cs` 中添加一个 `ChangeColor` 方法,该方法使用 Lua 代码对游戏物体颜色进行修改。 * 在 `Unity` 中使用 `Mono.InvokeAsync()` 方法异步调用 `ChangeColor` 方法。 **6. 测试** * 将应用程序运行并确保游戏对象颜色正确改变。 * 使用调试工具检查 LUA 和 C# 脚本的运行状态。

正文

随着技术的发展,越来越多的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
 
 
 
 
 

与在原生APP中集成Unity容器相似的内容:

在原生APP中集成Unity容器

随着技术的发展,越来越多的APP期望拥有3D,AR的能力。要达到这个目标可以选择使用原生开发,也可以使用Unity成熟的3D开发技术链,通过嵌入的方式将Unity容器嵌入到APP中。这里介绍的是通过嵌入Unity容器的方式来实现APP的3D,AR能力的。 Unity集成到iOS应用的本质是将Unit

flutter系列之:在flutter中使用相机拍摄照片

简介 在app中使用相机肯定是再平常不过的一项事情了,相机肯定涉及到了底层原生代码的调用,那么在flutter中如何快速简单的使用上相机的功能呢? 一起来看看吧。 使用相机前的准备工作 flutter中为使用camera提供了一个叫做camera的插件,我们首先需要安装这个插件。 安装插件的步骤很简

APP中RN页面渲染流程-ReactNative源码分析

在APP启动后,RN框架开始启动。等RN框架启动后,就开始进行RN页面渲染了。 RN页面原生侧页面渲染的主要逻辑实现是在RCTUIManager和RCTShadowView完成的。 通过看UIMananger的源码可以看到,UIMananger导出给JS端的API接口在对UI的操作上,基本都会同时对

APP中Web容器的核心实现

现在的业务型APP中,采用纯原生开发策略的已经很少了,大部分都使用的混合开发。如原生,H5,ReactNative,Flutter,Weex它们之间任意的组合就构成了混合开发。 其中原生+H5是出现最早的,老牌混合方案,即使过来多年,在现在的混合开发方案中H5也是使用率非常高的。在APP中嵌入Web

Flutter调优--深入探究MediaQuery引起界面Rebuild的原因及解决办法

app界面逐渐复杂时,我们不得不考虑去优化界面性能。本文中介绍的例子在开发中是很常见的,如果不了解MediaQuery.of的机制,可能会引起大量使用此方法的界面发生重绘操作,造成页面卡顿、帧率下降。我们详细分析了背后的源码逻辑,介绍了解决办法,希望能给大家的调优工作提供些许帮助。

扫一扫,原来这么简单

二维码识别技术已广泛应用在移动支付、实用工具、电商购物、社交通讯等场景。然而,在实际生活中,二维码容易遇到距离远、暗光、强光、污损、模糊和大角度倾斜等复杂场景,导致识别困难,扫码体验差。华为HMS Core 统一扫码服务(Scan Kit)为开发者们的APP带来一站式扫码解决方案,并且拥有高识别率和

Axure 母版与元件

需要重复使用的元件,建议创建成母版; 如果修改了母版,所有页面中的母版元件将会被同步修改 元件:添加后,所有的 Axure 都可以使用 母版:只适用当前的 Axure 原型 拖放行为: 任意位置:可以拖到任意指定的页面位置 固定位置:取决说元件在母版中的位置,提高原型的质量、标准(手机APP框架)

Flutter热更新技术探索

APP发布到市场后,难免会遇到严重的BUG阻碍用户使用,因此有在不发布新版本APP的情况下使用热更新技术立即修复BUG需求。原生APP(例如:Android & IOS)的热更新需求已经比较成熟,但Flutter技术栈目前还缺少类似的技术方案,因此Flutter研发团队,也需要类似的热更新技术。

Hybird 技术讨论:热更新原理解析

原生应用 VS 混合应用 大家对于原生应用和混合应用已经非常熟悉了,这里就不再进行详细的介绍,用通俗易懂的话解释下他们的一些特点。 1、原生应用 在 Android、iOS 等移动平台上利用提供的开发语言、开发类库、开发工具进行 App 软件开发。比如 Android 是用 Java、Eclipse

Hybrid App 技术路径带动性能的提升

说到 Hybrid App(混合应用)大家都不陌生,因为这种开发模式大行其道发展的这些年取代了很多原生和 Web 应用,为什么大家对这种「Native + HTML5」的开发模式额外偏爱呢? 因为一方面在一定程度上兼顾了原生应用的优质体验,另一方面又兼顾到了 HTML5 灵活的开发模式。 这种模式的