我有一个为iOS构建的非常小的Objective-C库,我想将其导出到Unity.我了解编写csharp包装器的基本过程,该包装器将所有调用封送到本机库,但我完全不知道从哪里开始.有谁能一步一步地解释一下如何用我的库创建一个unity包,这样我就可以把它分发给其他开发者了.

Unity3d文档非常简短,没有任何解释.

谢谢

推荐答案

好吧,在Mac上玩了几天Unity3d之后,我终于明白了.本指南中的所有代码都是假的.我写了15分钟的错字,所以不必担心.

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

2) 生成项目时,其 struct 如下:

  • ProjectName/Assets(这就是你需要的)
  • ProjectName/Library(不管有什么)
  • ProjectName/ProjectSettings(你不在乎)
  • ProjectName/ProjectName.sln(单一开发项目)

3) 转到ProjectName/Assets并创建以下文件夹:Plugins/iOS,这样最后你就有了这样的文件夹 struct :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 file,C#wrapper的虚拟示例如下所示:

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) 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.

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

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

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

9) 你可以将创建的Unity项目(ProjectName.Unity)设置为Assets/MySDKDemo,这样你的软件包中就有了一个演示.

10) 为你的演示Unity3d场景创建一个简单的脚本,例如:

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

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

Few notes

1) 插件在Unity Editor中不起作用,仅仅是因为它们不是实时编译的,嗯,不确定,但在插件中使用C#之前,很可能C#stuff会立即链接并在编辑器环境中工作.

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

Interop with Native Libraries @ Mono project

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

然而,由于平台的限制,它无法在iOS的开箱即用版本上运行,但C#-to-C回调仍然可以使用MonoPInvokeCallbackAttribute实现,这是关于本主题的有用链接:

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

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

extern UIViewController *UnityGetGLViewController();

需要访问RootViewController时使用UnityGetGLViewController().

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

6) 您肯定在本机代码中使用了一些框架,并且不希望出现链接器问题.例如,如果您在库中使用密钥链,则需要包含安全性.将框架转换为Xcode项目.

我建议try 一下XUPorter,它有助于Unity将任何额外的依赖项集成到Xcode项目中.

祝你好运

Objective-c相关问答推荐

Objective-c 点表示法或方括号表示法中首选什么?

如何从 C 方法调用 Objective-C 方法?

如何从 Appdelegate 显示 UIAlertController

将长按手势和拖动手势结合在一起

有没有办法清除 UIImage 类使用的缓存?

负数的奇怪 Objective-C Mod 行为

在Objective C中迭代枚举?

我可以以编程方式滚动到 UIPickerView 中所需的行吗?

为什么我们必须在 Objective-C 中做 [MyClass class]?

将 NSArray 转换为 NSDictionary

使用 UIView animateWithDuration 进行动画处理时无法 UIButton

如何保存我的 iPhone 应用程序的用户首选项?

iOS6:supportedInterfaceOrientations 不起作用(被调用但界面仍然旋转)

如何禁用 UIButton?

两个 NSDate 对象之间的区别 - 结果也是一个 NSDate

Core Data使用原始数据类型的标量属性复选框

如何将 NSOperationQueue 与 NSURLSession 一起使用?

在 UITableView 中更改复选标记的 colored颜色

如何将 tintColor 应用于 UIImage?

iOS:开源 VoIP/SIP Objective-C 代码