Swift だけで作った Native Plugin で Unity に SendMessage
- Swift だけで、Unity の iOS の Native Plugin を作る方法を紹介しました
- Swift から Unity へコールバックしたい場合がよくあります
- Unity の Native Plugin のドキュメントには、UnitySendMessage を使う方法が紹介されています
- UnitySendMessage を使うと、とても容易にコールバックを実装可能です
- ゲームオブジェクト名、スクリプト名、呼び出されたメソッドに渡す文字列を指定します
UnitySendMessage("GameObjectName1", "MethodName1", "Message to send");
- これを Swift だけで作った Native Plugin から呼び出す話です
今回のサンプル
- リポジトリはこちらです
- [Swift だけで、Unity の iOS の Native Plugin を作る](Swift だけで、Unity の iOS の Native Plugin を作る方法) の内容を前提知識にしています
処理の流れ
- Unity で実行されるゲームオブジェクトとスクリプトを用意する
== ここまで C# ここから Swift === - Swift で、UnityFramework を読み込む
- UnityFramework を使って、UnitySendMessage を実行する
Unity の実装
最終目標
- "Cube" オブジェクトの "Sent" 関数に任意の文字を渡して実行することを目標とします
public class Cube : MonoBehaviour
{
public void Sent(string message)
{
Debug.Log("Sent message -> " + message);
}
}
- これで UnitySendMessage からメッセージを受け取る事が可能です
Swift の実装
やること
- UnityFramework.framework には、UnityFramework クラスが実装されています
- UnityFramework のシングルトンのインスタンスを利用します
- sendMessageToGO 関数を呼ぶと UnitySendMessage が実行されます
let objectName = "Cube" as NSString
let scriptName = "Sent" as NSString
let message = "Hello" as NSString
UnityFramework.getInstance()
.sendMessageToGO(withName: objectName.utf8String,
functionName: scriptName.utf8String,
message: message.utf8String)
実装計画
- Framework を事前にビルドする形だと、ビルド時に UnityFramework とリンクできません
- そのため、事前にビルドした Framework から名前を指定して実行します
UnityFramework のシングルトンのインスタンスを取得する
- UnityFramework.framework の PrincipalClass として UnityFramework クラスが設定されているので利用します
- getInstance の値を名前を指定して取得します
let bundlePath = Bundle.main.bundlePath.appending("/Frameworks/UnityFramework.framework")
let bundle = Bundle(path: bundlePath)!
let principalClass = bundle.principalClass as! NSObject.Type
let unityFramework: NSObject = principalClass.value(forKey: "getInstance") as! NSObject
これで、UnityFramework.getInstance()
に相当する処理を実装できました
sendMessageToGOWithName:functionName:message
を実行する
やりたいこと
- 上で取得した unityFramework インスタンスで sendMessageToGOWithName の関数を実行したいです
- この関数を名前を指定して実行します
unityFramework.sendMessageToGO(withName: objectName.utf8String,
functionName: scriptName.utf8String,
message: message.utf8String)
文字列から関数を実行する
-
class_getMethodImplementation
で IMP を取得し、unsafeBitCast を使って、methodType にキャストして実行します - sendMessageToGOWithName は引数として、
char*
を受け取るので、Swift で同等のUnsafePointer<CChar>
として実行します
private func callInstanceMethod<T: NSObject>(targetInstance: T,
selector: Selector,
argCStr1: UnsafePointer<CChar>,
argCStr2: UnsafePointer<CChar>,
argCStr3: UnsafePointer<CChar>) {
typealias methodType = @convention(c) (
Any, Selector,
// args
UnsafePointer<CChar>, UnsafePointer<CChar>, UnsafePointer<CChar>
) -> Void
let methodImplementation: IMP = class_getMethodImplementation(type(of: targetInstance), selector)!
let methodInvocation = unsafeBitCast(methodImplementation,
to: methodType.self)
methodInvocation(targetInstance, selector, argCStr1, argCStr2, argCStr3)
}
実行
- sendMessageToGOWithName 関数のセレクタの sendMessageToGOWithNameSelector を作成します
- unityFramework のインスタンスで、sendMessageToGOWithNameSelector を実行します
let sendMessageToGOWithNameSelector: Selector = NSSelectorFromString("sendMessageToGOWithName:functionName:message:")
callInstanceMethod(targetInstance: unityFramework,
selector: sendMessageToGOWithNameSelector,
argCStr1: ("Cube" as NSString).utf8String!,
argCStr2: ("Sent" as NSString).utf8String!,
argCStr3: ("from send message" as NSString).utf8String!)
Framework の出力と設定
- こちらの記事の
Framework でビルドする
の章以降と同じ手順で実行ビルド可能です
-
callInstanceMethod
を実行したときに、Cube.Sent
が指定した引数で実行されていることを確認します
おわりに
- UnitySendMessage は Unity で複雑な実装をせずに、Swift から Unity へコールバックする事が可能です
- 1frame 遅れたり、文字列1つしか送れない、などが問題ない場合は十分に利用する事が可能だと思います
- Swift から Unity コールバックするのは、応用の幅が広いので、活用して行きたいです