LoginSignup
3
3

More than 1 year has passed since last update.

[Unity]Swift だけで作ったNative PluginでUnityにSendMessage

Posted at

Swift だけで作った Native Plugin で Unity に SendMessage

UnitySendMessage("GameObjectName1", "MethodName1", "Message to send");
  • これを Swift だけで作った Native Plugin から呼び出す話です

今回のサンプル

  • リポジトリはこちらです

処理の流れ

  1. Unity で実行されるゲームオブジェクトとスクリプトを用意する
    == ここまで C# ここから Swift ===
  2. Swift で、UnityFramework を読み込む
  3. 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 コールバックするのは、応用の幅が広いので、活用して行きたいです
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3