Swift だけで Unity の iOS の Native Plugin を作る
- Unity は Native Plugin を作成することで、Unity から提供されていない、OS 固有の機能を使用することができます
- 例えば iOS では、Bluetooth や MP4 動画の作成、アルバムの利用などが出来るようになります
- これらの Native の機能を Swift で実装し、C# から呼び出す手順です
- Swift Package Manager (以下 SwiftPM) を使って、Framework を作成し、Unity に組み込みます
- ついでに、ライブラリの開発用の Native のアプリターゲットを作成します
-
Framework 化するメリット
- PostProcess の設定が不要です
- Unity の Inspector 上で Embed の設定が完了します
- 合わせて Native でアプリターゲットを実装する事で、動作確認のために Unity のビルドをせずに、ライブラリの開発が可能です
- 今回の話のリポジトリです↓
やりたい事
- 今回は、Swift で文字列型の数値を long 型に変換する Native Plugin を実装します
-
[DllImport("__Internal")]
を付けて定義された、関数swiftPmPlugin_toNumber
が Swift で実装された関数です - 今回は、C# にも存在する機能ですが、呼び出した先の Swift で iOS 固有の機能を利用することが可能です
public class Cube : MonoBehaviour
{
// Swift で実装された関数の定義
[DllImport("__Internal")]
private static extern long swiftPmPlugin_toNumber(string numberString);
void Update()
{
// 呼び出し
Debug.Log("number is: " + swiftPmPlugin_toNumber("30"));
}
}
Native Plugin の実装
- Swift Package Manager (SwiftPM) を使って実装し Framework を出力します
- 上記の
swiftPmPlugin_toNumber
が実装された SwiftPmPlugin.framework を作成します
環境
- maxOS 11.3.1
- Xcode 12.5
- Swift 5.4.0
- Unity 2020.3.5f1
Swift Package Manager
- type に libraryを、name に 作りたいパッケージの名前 (今回は SwiftPmPlugin) を指定して、package を初期化します
- library package が作成されるので、Package.swift を開きます
- Xcode が起動するので、SwiftPmPlugin.swift に実装していきます
swift package init --type=library --name=SwiftPmPlugin
open Package.swift
SwiftPmPlugin の実装
- Swift でロジックを実装します
- SwiftPmPlugin クラスに、文字列を数値に変換する toNumber 関数を実装しました
-
Int(string) ?? 0
として数値変換に失敗したときは、0 を返すようにしました
class SwiftPmPlugin {
var text = "Hello, World!"
static func toNumber(string: String) -> Int {
return Int(string) ?? 0
}
}
Swift で関数を公開する
- Swift で公開する関数を作成します
-
@_cdecl
を使って、C の関数として定義します - char 配列のポインタを受け取って、Swift の String 型に変換します
- 先ほど作った SwiftPmPlugin.toNumber を使って、String から Int に変換します
- C# で定義した関数が戻り値が long 型で 64bit 整数なので戻り値も、揃えます
@_cdecl("swiftPmPlugin_toNumber")
public func swiftPmPlugin_toNumber(_ stringPtr: UnsafePointer<CChar>?) -> Int64 {
let str = String(cString: stringPtr!)
return Int64(SwiftPmPlugin.toNumber(string: str))
}
Framework でビルドする
- SwiftPM から xcodeproj を出力して
- xcodebuild を使って出力した xcodeproj から framework を作成します
- CONFIGURATION_BUILD_DIR に指定したディレクトリに、SwiftPmPlugin.framework 作成されています
swift package generate-xcodeproj --skip-extra-files
xcodebuild -project SwiftPmPlugin.xcodeproj -scheme SwiftPmPlugin-Package -configuration Release -sdk iphoneos CONFIGURATION_BUILD_DIR=.
- Framework の Headers を確認すると、先ほど実装した swiftPmPlugin_toNumber が公開されていることが分かります
open SwiftPmPlugin.framework/Headers/SwiftPmPlugin-Swift.h
Unity でビルドする
Framework の設定
-
Plugins/iOS/
を作成して、SwiftPmPlugin.framework を配置します - SwiftPmPlugin.framework の Inspector で、Add To Embedded Binaries を有効にします
- Add To Embedded Binaries を有効にすることで、ビルドしたときに Framework が自動で Embed され
ます
- Add To Embedded Binaries を有効にすることで、ビルドしたときに Framework が自動で Embed され
iOSアプリをビルドする
- Unity で、通常通り iOS 向けにビルドします
- PostProcess などは、設定していないです
- 作成した target の frameworks に、SwiftPmPlugin.framework があり、
- Embed の項目が、Embed & Sign になっていることを確認します
- 自動で追加されていない場合は、Unity の設定に失敗している可能性があります
- 手動で追加しても問題ないです
- 確認できれば通常通り iOS アプリを実行できるはずです
(おまけ)Framework 開発用の Native のアプリターゲットを作る
- Framework の開発が便利になる tips 的なやつです
- Unity で使う Framework の動作確認のために、Framework のビルド→Unityのビルド→iOSのサイクルを回すのは大変です
- そこで、SwiftPM で作ったライブラリを取り込んだ、アプリターゲットを作成し、開発すると効率的だと考えています
xcworkspace を作成する
- xcworkspace を作成し、Package を取り込み、アプリターゲットのターゲットを用意します
※ アプリを Objective-C で作る必要はないです、Swift でも問題ないです
- アプリターゲットの Frameworks に SwiftPmPlugin を追加します
- コードから呼び出すことが可能なので、呼び出します
#import "ViewController.h"
@import SwiftPmPlugin;
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"value: %lld", swiftPmPlugin_toNumber("20"));
}
@end
- Swift からも呼び出すことが可能です
import SwiftUI
import SwiftPmPlugin
struct ContentView: View {
var body: some View {
Text("number: \(swiftPmPlugin_toNumber("20"))")
.padding()
}
}
おわりに
- Native Plugin を実装することで、ゲームの開発は Unity に集中しつつ、OS 固有の機能を十分に利用することが可能です
- Swift で実装し、SwiftPM で管理し、Native アプリで動作確認することで、効率的な開発を目指しています
関連記事です
- この実装を拡張して、Unity の Texture をやりとりする事が可能です
- SendMessage のやり方はこちら
- この実装を拡張して、Unity から関数のポインタを受け取って、コールバックすることも可能です
- Bundle target を作成することで Unity Editor と共通化させて実装することが可能です