如何构建适用于iOS的Unity3d插件

68

我有一个非常小的Objective-C库,用于iOS,我想将其导出到Unity。我了解编写csharp包装器的基本过程,它会调用所有本地库,但我完全不知道从何开始。请问是否可以逐步说明如何创建带有我的库的Unity软件包,以便我也可以将其分发给其他开发人员。

Unity3d文档非常简要,没有解释任何内容。

谢谢。


4
最近在工作中我不得不这样做。Unity文档并不是很有用,我也无法在网络上找到任何有用的信息。如果没人先发布步骤,我需要一段时间来编写一个逐步操作的指南,稍后今天回答这个问题。 - ChrisH
也许Unity网站上的这个答案可以帮到你。请查看我的评论,以获取通过wayback机器链接到博客文章的方法。 - Kay
https://dev59.com/QV3Va4cB1Zd3GeqPDsd6#8333120和https://dev59.com/E1LTa4cB1Zd3GeqPXylV#7076887可能也会有所帮助。 - Kay
@Kay,谢谢,我已经查看了所有这些链接,没有太多关于如何构建Unity包的见解。Github搜索“unity ios plugin”比Google更有用。我明天会继续,现在我稍微了解插件项目的结构。 - pronebird
@ChrisH 看起来我已经解决了,不管怎样还是谢谢 :-) - pronebird
显示剩余2条评论
1个回答

90

好的,在Mac上玩了几天Unity3d之后,我终于搞定了。本指南中的所有代码都是虚构的。我只花了15分钟左右写这些东西,所以不要被错误和错别字困扰。

1)打开Unity,创建一个新项目(文件->新建项目),并将其保存到某个位置

2)项目生成后,它具有以下结构:

  • ProjectName/Assets(这就是你需要的)
  • ProjectName/Library(不用管里面有什么)
  • ProjectName/ProjectSettings(你不用关心它)
  • ProjectName/ProjectName.sln(MonoDevelop项目)

3)进入ProjectName/Assets文件夹,并创建以下文件夹:Plugins/iOS,最终你将拥有如下文件夹结构:ProjectName/Assets/Plugins/iOS

4)将已编译的库文件(.a)和必要的头文件放入ProjectName/Assets/Plugins/iOS中,或者将库的源代码复制到那里(.mm、.h、.m等)。记住,通常你只能从C#访问C函数,所以你需要以某种方式在C代码中包装你的Objective-C代码。在我的情况下,所有Objective-C对象都是以单例的形式实现的,所以很容易在C风格的包装器周围进行包装,例如:

CWrapper.h

extern "C" void MySDKFooBarCFunction();

CWrapper.mm

#import "CWrapper.h"
#import "MyObjectiveCLibrary.h" // your actual iOS library header

void MySDKFooBarCFunction() {
    [MyObjectiveCLibrary doSomeStuff];
}

5) 然后转到 ProjectName/Assets 并创建一个用于CSharp包装类的文件夹,可以随意命名,例如: ProjectName/Assets/MySDK

6) 在MySDK文件夹中创建MySDK.cs文件,C#包装器的示例代码如下:

using UnityEngine;
using System;
using System.Runtime.InteropServices;

public class MySDK
{
    // import a single C-function from our plugin
    [DllImport ("__Internal")]
    private static extern void MySDKFooBarCFunction();

    // wrap imported C-function to C# method
    public static void FooBarCFunction() {
        // it won't work in Editor, so don't run it there
        if(Application.platform != RuntimePlatform.OSXEditor) {
            MySDKFooBarCFunction();
        }
    }
}

7) 创建一个shell脚本把这些东西打包成.unitypackage文件,并将其放在你的项目文件夹旁边(而不是里面)。根据你的需要调整脚本中的EXPORT_PATHPROJECT_PATH变量。

#!/bin/sh

WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
UNITY_BIN="/Applications/Unity/Unity.app/Contents/MacOS/Unity"
EXPORT_PATH="${WORKDIR}/ProjectName.unitypackage"
PROJECT_PATH="${WORKDIR}/ProjectName"
ASSETS_PATH="Assets"

$UNITY_BIN -batchmode -quit \
-logFile export.log \
-projectPath $PROJECT_PATH \
-exportPackage $ASSETS_PATH $EXPORT_PATH

8)执行创建的 bash 脚本以获取软件包构建。在 Unity 编辑器中通过文件 -> 构建设置生成时,Assets 中的所有内容都将包含在您的 Unity 项目中。您可以使用生成的软件包将代码分发给其他开发人员,他们只需双击软件包文件即可将您的库简单地包含到他们的 Unity 项目中。

不要忘记在运行此脚本时关闭 Unity 编辑器,否则可能无法构建软件包。

如果您遇到一些问题并且软件包未显示,则此脚本始终会将日志打印到 export.log

接下来的步骤仅在您想为您的库创建演示 Unity 项目时才有意义(至少用于测试)

9)您可以将创建的 Unity 项目(ProjectName.unity)放在 Assets/MySDKDemo 中,这样您就可以在软件包内拥有一个演示。

10)为您的 Demo Unity3d 场景创建一个简单的脚本,位于 Assets/MySDKDemo/MySDKDemo.cs,例如:

using UnityEngine;
using System;
using System.Collections;

public class MySDKDemo : MonoBehaviour
{   
    private GUIStyle labelStyle = new GUIStyle();
    private float centerX = Screen.width / 2;

    // Use this for initialization
    void Start ()
    {   
        labelStyle.fontSize = 24;
        labelStyle.normal.textColor = Color.black;
        labelStyle.alignment = TextAnchor.MiddleCenter;
    }

    void OnGUI ()
    {
        GUI.Label(new Rect(centerX - 200, 20, 400, 35), "MySDK Demo", labelStyle);
        if (GUI.Button(new Rect(centerX - 75, 80, 150, 35), "DoStuff"))
        {
            MySDK.FooBarCFunction();
        }
    }

}

11) 进入Unity编辑器。在左侧边栏中找到“Main Camera”,选择它,在检查器面板的底部(右侧边栏)上点击AddComponent,选择Scripts-> MySDKDemo脚本。

12) 构建XCode项目并在设备上运行。

注意事项

1)插件在Unity编辑器中不起作用,因为它们没有实时编译,不确定,但可能只有在插件中使用C#时,C#内容立即链接并在编辑器环境中工作。

2)此帖子未涵盖与本机代码<->托管代码之间的数据/内存管理或处理方式,因为已经有很好的文档记录了这些内容。

Mono项目中的本机库互操作性

3)从C#回调到C可以使用C#委托传递,您在C端使用标准函数声明,在C#端声明具有相同签名的委托。布尔值、整数和字符串(C:char*)似乎都能够无缝地进行封送(我不谈论内存管理政策以及谁负责释放内存或返回值政策)。

但是,由于平台限制,这在iOS构建中不会立即生效,但仍然可以使用MonoPInvokeCallbackAttribute实现C#-to-C回调。有关此主题的有用链接:

实际上,在Unity 4中已经实现了AOT.MonoPInvokeCallbackAttribute,它仅限于可以传递到非托管代码的静态委托,但仍然比没有好。

4)有一种方法可以使用UnityGetGLViewController函数获取Unity RootViewController。只需在您的实现文件中声明此函数,例如:

extern UIViewController *UnityGetGLViewController();

每当需要访问RootViewController时,请使用UnityGetGLViewController()

5) 在细节中有更多的魔法和丑陋的东西,尽可能保持C接口简单,否则封送可能会成为你的噩梦,并且要记住托管到非托管通常是昂贵的。

6) 您在本地代码中肯定使用了一些框架,而您不希望出现链接器问题。例如,如果您在库中使用Keychain,则需要将Security.framework包含在Xcode项目中。

我建议尝试一下 XUPorter ,它可以帮助Unity将任何其他依赖项���成到Xcode项目中。

祝好运!


谢谢,您的示例只调用了一个函数。您能否使用上述相同方法通过引用传递变量或使函数返回对象从Objective-C中获取数据? - Boon
@Boon 这是一个使用Unity3d创建的项目,场景是在Unity编辑器中创建的。这一步是可选的。 - pronebird
明白了。Andy,你的示例主要展示了如何从Unity调用本地方法。使用你的示例,我该如何将数据从本地发送到Unity?是否有一个列表告诉我们各种类型本地数据如何跨越(例如,char *到字符串)进行编组? - Boon
@Boon 有关本地和托管数据传输的信息已经得到了很好的记录,当然不止这篇文章。请查看 http://www.mono-project.com/Interop_with_Native_Libraries 的“4. Marshaling”部分。 - pronebird
1
@dumbledad 你为什么认为这是一个目录?它是一个可执行文件。如果你在Windows上,你必须用Windows安装路径替换它,可能应该有Unity.exe在里面。 - pronebird
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接