如何为 iOS 构建 Unity3d 插件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14834626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:16:05  来源:igfitidea点击:

How to build Unity3d Plugin for iOS

iosobjective-cunity3d

提问by Ben Sinclair

I have a very tiny Objective-C library built for iOS and I want to export it to Unity. I understand the basic process of writing a csharp wrapper that marshals all the invocations to native library, but I completely have no idea where to start. Could anyone please explain step-by-step how to create a unity package with my library so I could also distribute it to other developers.

我有一个为 iOS 构建的非常小的 Objective-C 库,我想将它导出到 Unity。我了解编写将所有调用编组到本机库的 csharp 包装器的基本过程,但我完全不知道从哪里开始。任何人都可以逐步解释如何使用我的库创建统一包,以便我也可以将其分发给其他开发人员。

Unity3d documentation is pretty brief and does not explain anything.

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

Thanks.

谢谢。

回答by Ben Sinclair

Okay, after playing few days with Unity3d on Mac I finally figured it out. All the code in this guide is dummy. I have written this stuff in 15 minutes or so, so don't be bothered by mistakes and typos.

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

1) Open Unity, create new project (File -> New Project) and save it somewhere

1)打开Unity,创建新项目(File -> New Project)并将其保存在某处

2) When the project is generated it has the following structure:

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

  • ProjectName/Assets(That's what you need)
  • ProjectName/Library(Nevermind what's there)
  • ProjectName/ProjectSettings(You don't care about it)
  • ProjectName/ProjectName.sln(MonoDevelop project)
  • ProjectName/Assets(这就是你需要的)
  • ProjectName/Library(别管那里有什么)
  • ProjectName/ProjectSettings(你不在乎)
  • ProjectName/ProjectName.sln(MonoDevelop 项目)

3) Go to ProjectName/Assetsand create the following folders: Plugins/iOS, so in the end you'll have a folder structure like this: ProjectName/Assets/Plugins/iOS

3)转到ProjectName/Assets并创建以下文件夹:Plugins/iOS,因此最终您将拥有这样的文件夹结构:ProjectName/Assets/Plugins/iOS

4) Put your compiled library (.a) file and necessary headers inside of ProjectName/Assets/Plugins/iOSor copy the source code of your library there (.mm, .h, .m, etc..). Remember, normally you can only access C-functions from C#, so you'll have to wrap your Objective-C stuff in C-code somehow, in my case all Objective-C objects were implemented in a form of Singleton so it wasn't hard to make a C-style wrapper around, for instance:

4) 将编译后的库 (.a) 文件和必要的头文件放入ProjectName/Assets/Plugins/iOS或复制库的源代码(.mm、.h、.m 等)。请记住,通常您只能从 C# 访问 C 函数,因此您必须以某种方式将 Objective-C 的东西包装在 C 代码中,在我的情况下,所有的 Objective-C 对象都是以单例形式实现的,所以它不是'很难制作一个 C 风格的包装器,例如:

CWrapper.h:

CWrapper.h:

extern "C" void MySDKFooBarCFunction();

CWrapper.mm

CWrapper.mm

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

void MySDKFooBarCFunction() {
    [MyObjectiveCLibrary doSomeStuff];
}

5) Then go to ProjectName/Assetsand create a folder for CSharp wrapper class(es), call it whatever you want, for example: ProjectName/Assets/MySDK

5)然后转到ProjectName/Assets并为 CSharp 包装器类创建一个文件夹,随意调用它,例如:ProjectName/Assets/MySDK

6) Inside of MySDK folder create MySDK.cs file, the dummy example of C# wrapper would look like this:

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) Create a shell script to pack this stuff into .unitypackageand put it next to your project folder (not inside). Adjust EXPORT_PATHand PROJECT_PATHvariables in the script for your needs.

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) Run the created bash script to get your package build. All stuff from Assets will be included in XCode project for your Unity Project when you generate it via File -> Build Settings in Unity Editor. You can use generated package to distribute your code to other developers so they can simply include your library to their Unity projects by double clicking on the package file.

8) 运行创建的 bash 脚本来构建你的包。当您通过 Unity 编辑器中的 File -> Build Settings 生成它时,Assets 中的所有内容都将包含在您的 Unity 项目的 XCode 项目中。您可以使用生成的包将您的代码分发给其他开发人员,以便他们只需双击包文件即可将您的库包含到他们的 Unity 项目中。

Don't forget to shutdown Unity Editor when you run this script, otherwise it may fail to build a package.

运行此脚本时不要忘记关闭Unity Editor,否则可能无法构建包。

If you have some issues and package does not show up, this script always prints log to export.log

如果你有一些问题并且包没有显示出来,这个脚本总是将日志打印到 export.log

Next steps make sense only if you want to make a Demo unity project for your library (good for testing at least)

仅当您想为您的库制作 Demo 统一项目时,后续步骤才有意义(至少适合测试)

9) You can put created Unity project (ProjectName.unity) to Assets/MySDKDemoso you have a demo inside of your package.

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

10) Create a simple script for your Demo Unity3d scene at Assets/MySDKDemo/MySDKDemo.cs, for example:

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) Go to Unity Editor. Find the "Main Camera" in left sidebar in Unity Editor, select it and in the bottom of Inspector panel (right sidebar) click on AddComponent, select Scripts -> MySDKDemo script

11) 转到 Unity 编辑器。在 Unity Editor 的左侧边栏中找到“Main Camera”,选择它并在 Inspector 面板底部(右侧边栏)单击 AddComponent,选择 Scripts -> MySDKDemo 脚本

12) Build the XCode project and run on device.

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

Few notes

几个笔记

1) Plugins don't work in Unity Editor, simply because they're not compiled in the real-time, well, not sure but probably until you use C# in your plugins, probably C# stuff gets linked immidiately and works in Editor environment.

1) 插件在 Unity 编辑器中不起作用,仅仅是因为它们不是实时编译的,好吧,不确定,但可能直到您在插件中使用 C# 之前,C# 的东西可能会立即链接并在编辑器环境中工作。

2) This post does not cover marshaling, or data/memory management between native <-> managed code, as it is very well documented.

2) 这篇文章不包括编组,或本机 <-> 托管代码之间的数据/内存管理,因为它有很好的文档记录。

Interop with Native Libraries @ Mono project

与 Native Libraries @ Mono 项目互操作

3) Callbacks from C# to C can be passed using C# delegates, on C-side you use standard functions declarations, on C# side you declare delegates with the same signature. It seems that booleans, integers and strings (C: char*) are marshalled flawlessly (I don't talk about memory management policy and who's responsible to release memory or return value policies).

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

However it will not work on iOS builds out-of-box due to platform limitations, but C#-to-C callbacks still can be implemented using MonoPInvokeCallbackAttribute, useful links on this topic:

然而,由于平台限制,它不能在开箱即用的 iOS 构建上运行,但 C# 到 C 的回调仍然可以使用 MonoPInvokeCallbackAttribute 来实现,有关此主题的有用链接:

Actually in Unity 4 there's AOT.MonoPInvokeCallbackAttributealready implemented, it's limited to static delegates that can be passed to unmanaged code, but still better than nothing.

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

4) There's a way to get Unity RootViewController using UnityGetGLViewControllerfunction. Just declare this function in your implementation file, i.e.:

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

extern UIViewController *UnityGetGLViewController();

And use UnityGetGLViewController()whenever you need to get an access to RootViewController.

UnityGetGLViewController()在您需要访问 RootViewController 时使用。

5) There's much more magic and ugly stuff in details, keep your C interfaces as simple as possible otherwise marshalling can become your nightmare and also keep in mind that managed-to-unmanaged is generally expensive.

5) 在细节上有更多神奇和丑陋的东西,让你的 C 接口尽可能简单,否则编组可能成为你的噩梦,还要记住托管到非托管通常很昂贵。

6) You definitely use some frameworks in your native code and you don't want linker problems. For example, if you use Keychain in your library then you need to include Security.framework into Xcode project.

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

I suggest to give a try to XUPorter, it helps Unity to integrate any additional dependencies into Xcode project.

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

Good luck!

祝你好运!

回答by Ashwani K

I have written a post on how to create iOS plugin for unity. You can check it here. Hope it helps. Thanks.

我写了一篇关于如何为 Unity 创建 iOS 插件的文章。你可以在这里查看。希望能帮助到你。谢谢。