はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity, XCode】XCodeでライブラリ(dylib, bundle, a)を作成してUnity(iOS)で利用してみる

はじめに

今回はXCodeでライブラリ(動的リンク, 静的リンク)を作成してUnityで利用してみたいと思います。

具体的には動的ライブラリ(共有ライブラリ)の.dylib,動的ライブラリ(バンドル)の.bundle,静的ライブラリの.aの3種類について紹介します。

またUnityの対応するプラグインの拡張子の一覧は以下に記載されていました。
docs.unity3d.com

XCodeでプロジェクトを作成する

XCodeを立ち上げCreate a new Xcode projectを選択。

Create a new Xcode project

動的ライブラリ(dylib)

新規プロジェクトのテンプレートをFramework &LibraryLibraryにします。

テンプレートを選択

TypeDynamicにすることでDynamicライブラリを作成できます。

Dynamicライブラリの作成

ファイル作成

.
|--- SampleDll.cpp
|--- SampleDll.hpp
// SampleDll.hpp
#pragma once

#ifndef SampleDll_hpp
#define SampleDll_hpp

#include <stdio.h>

class SampleDll
{
public:
    int FooPluginFunction();
};

extern "C" __declspec(dllexport) SampleDll* createSampleDll();
extern "C" __declspec(dllexport) void freeSampleDll(SampleDll* instance);
extern "C" __declspec(dllexport) int getResult(SampleDll* instance);

#endif /* SampleDll_hpp */
// SampleDll.cpp
#include "SampleDll.hpp"

int SampleDll::FooPluginFunction()
{
    return 5;
}

__declspec(dllexport) SampleDll* createSampleDll()
{
    return new SampleDll();
}

__declspec(dllexport) void freeSampleDll(SampleDll* instance)
{
    delete instance;
}

__declspec(dllexport) int getResult(SampleDll* instance)
{
    return instance->FooPluginFunction();
}

ビルド

メニューバーよりProduct -> Buildを選択してビルドを行うと、動的ライブラリであるdylibファイルが出力されているはずです。

Product -> Build
エラーが出てくるときの対処法

私の場合は'__declspec'に関するエラーが出てきてしまったので以下で対応しました。
【XCode】'__declspec' attributes are not enabled; use '-fdeclspec' or '-fms-extensions' to enable support for __declspec attributesというエラーの対処法 - はなちるのマイノート

Unityで利用する

public class Test : MonoBehaviour
{
    [DllImport("SampleDll")]
    private static extern IntPtr createSampleDll();

    [DllImport("SampleDll")]
    private static extern void freeSampleDll(IntPtr instance);

    [DllImport("SampleDll")]
    private static extern int getResult(IntPtr instance);

    private void Start()
    {
        var sampleDll = createSampleDll();
        var result = getResult(sampleDll);
        Debug.Log(result);

        freeSampleDll(sampleDll);
    }
}

DllImportの引数はライブラリの名前にしてあげればOKです。

動的ライブラリ(Bundle)

TempleteBundleで作成します。

Bundleの作成

ファイル作成

dylibのときと全く同じでOKです。

.
|--- SampleDll.cpp
|--- SampleDll.hpp

ビルド

メニューバーよりProduct -> Buildを選択してビルドを行うと、動的ライブラリであるbundleファイルが出力されているはずです。

Unityで利用する

Unityにインポートすると、フォルダでプラグインの設定ができたり、中身が少し表示されたりと結構面白い表示になります。

bundleのインポート

DllImportはフォルダ名と同じすることに注意してください。

public class Test : MonoBehaviour
{
    [DllImport("SampleBundle.bundle")]
    private static extern IntPtr createSampleDll();

    [DllImport("SampleBundle.bundle")]
    private static extern void freeSampleDll(IntPtr instance);

    [DllImport("SampleBundle.bundle")]
    private static extern int getResult(IntPtr instance);

    private void Start()
    {
        var sampleDll = createSampleDll();
        var result = getResult(sampleDll);
        Debug.Log(result);

        freeSampleDll(sampleDll);
    }
}

Staticライブラリ(.a)

新規プロジェクトのテンプレートをFramework &LibraryLibraryにします。

テンプレートを選択

TypeStaticにすることでStaticライブラリを作成できます。

Staticライブラリの作成

ファイル作成

dylibのときと全く同じでOKです。

.
|--- SampleDll.cpp
|--- SampleDll.hpp

ビルド

メニューバーよりProduct -> Buildを選択してビルドを行うと、動的ライブラリであるaファイルが出力されているはずです。

Unityで利用する

ここまで書いておいてなんですが、macOS Editor上ではうまく動作させることができませんでした。

どうしてもDllNotFoundExceptionを解決できず、DllImport(__internal)を利用してみたりもしたのですがダメなようです。

ちなみに以下の手法でiOSでは動かせたので、おそらくEditorでは利用できない感じ?なのですかね。(要調査)

iOSで.aを利用する

Xcode上でmacOS向けの静的ライブラリだとダメみたいなので、ちゃんとiOS向けのプロジェクトを作成します。

iOS向けのStatic Library

ファイル作成

今までと同じ。

.
|--- SampleDll.cpp
|--- SampleDll.hpp

Unityでのコード

iOSでは少し特殊で[DllImport ("__Internal")]を利用します。
ネイティブプラグイン - Unity マニュアル

public class Test : MonoBehaviour
{
    [SerializeField] private Text text;

#if UNITY_IPHONE && !UNITY_EDITOR
    [DllImport ("__Internal")]
#else
    [DllImport ("sampledll")]
#endif
    private static extern IntPtr createSampleDll();

#if UNITY_IPHONE && !UNITY_EDITOR
    [DllImport ("__Internal")]
#else
    [DllImport ("sampledll")]
#endif
    private static extern void freeSampleDll(IntPtr instance);

#if UNITY_IPHONE && !UNITY_EDITOR
    [DllImport ("__Internal")]
#else
    [DllImport ("sampledll")]
#endif
    private static extern int getResult(IntPtr instance);

    private void Start()
    {
        var sampleDll = createSampleDll();
        var result = getResult(sampleDll);
        Debug.Log(result);
        text.text = result.ToString();

        freeSampleDll(sampleDll);
    }
}

逆にiOSでは動的ライブラリは利用できないそうですね。

さいごに

正直どのプラットフォームはどの拡張子に対応していて・・・というのがイマイチ分かっておらず困ってたりします。

良かったらまとまっている記事等ありましたらコメントなどで教えてくださると嬉しいです。

後見返したらファイル名のSampleDllって明らかにおかしいと気づきましたが、直すのめんどくさいのでご了承いただけますと幸いです。(Dll作成したコードをほぼ流用したので・・・。)