【Unity】UnityOSCの使い方

UnityのOSCライブラリであるUnityOSCについて調べました。
[jorgegarcia/UnityOSC: Open Sound Control \(OSC\) C\# classes interface for the Unity3d game engine](https://github.com/jorgegarcia/UnityOSC)

Githubリポジトリ内で言及されていますが、UnityOSCは現在開発されておらず、他のOSCライブラリの使用が推奨されています。
本記事の内容はUnityバージョン2021.2.9f1、エディタ上でのみ検証しています。

## インストール

Githubリポジトリをクローンまたはダウンロードし、srcディレクトリ内のファイルをUnityプロジェクトのAssetsディレクトリ以下の任意の場所にコピーします。

## 使い方

Githubリポジトリのdocsディレクトリ内にマニュアルとAPIリファレンスがあります。

### OSCの受信

UnityOSCでOSCを受信するには`OSCHandler`を使う方法と`OSCReceiver`を使う方法の2つがあります。`OSCReceiver`の方が使用が簡単なため、ほとんどの場合には`OSCReceiver`を使う方がいいと思います。

#### OSCHandlerを使う方法

`OSCHandler`から`OSCServer`を取得してOSCを受信します。`OSCServer#PacketReceivedEvent`にOSC受信時に実行するメソッドを登録します。このメソッドはメインスレッド外で実行されるのでほとんどのUnity APIは使用できません。Unity APIを使用するためにはメッセージのキューイングなどを自分で実装する必要があります。

```c#:UnityOscHandlerReceiveSample.cs
using UnityEngine;
using UnityOSC;

public class UnityOscHandlerReceiveSample : MonoBehaviour
{
    [SerializeField] string serverId = "local";
    [SerializeField] int port = 8000;
    OSCServer oscServer;

    void Awake()
    {
        OSCHandler.Instance.Init();
        oscServer = OSCHandler.Instance.CreateServer(serverId, port);
    }

    void OnEnable()
    {
        oscServer.PacketReceivedEvent += OscReceivedCallback;
    }

    void OnDisable()
    {
        oscServer.PacketReceivedEvent -= OscReceivedCallback;
    }

    void OscReceivedCallback(OSCServer sender, OSCPacket packet)
    {
        if (packet.IsBundle())
        {
            var bundle = (OSCBundle)packet;
            foreach (object data in bundle.Data)
            {
                LogOscMessage((OSCMessage)data);
            }
        }
        else
        {
            LogOscMessage((OSCMessage)packet);
        }
    }

    void LogOscMessage(OSCMessage message)
    {
        if (message.Address == "/UnityOSC/sample")
        {
            Debug.Log($"UnityOSC receive: {(string)message.Data[0]} {(float)message.Data[1]} {(int)message.Data[2]}");
        }
    }
}
```

#### OSCReceiverを使う方法

`OSCReceiver`を使う方法です。OSCバンドルの展開とOSCメッセージのキューイングを`OSCReceiver`がしてくれるので、`OSCHandler`を使う方法よりも実装が単純になります。`OSCReceiver#hasWaitingMessages`でキューイングされたメッセージがあるか確認し、`OSCReceiver#getNextMessage`でキューイングされたメッセージを取得します。

```c#:UnityOscOscReceiverSample.cs
using UnityEngine;
using UnityOSC;

public class UnityOscReceiverSample : MonoBehaviour
{
    [SerializeField] int port = 8000;
    OSCReciever oscReceiver;

    void OnEnable()
    {
        oscReceiver = new OSCReciever();
        oscReceiver.Open(port);
    }

    void OnDisable()
    {
        oscReceiver.Close();
        oscReceiver = null;
    }

    void Update()
    {
        while(oscReceiver.hasWaitingMessages())
        {
            var message = oscReceiver.getNextMessage();
            if (message.Address == "/UnityOSC/sample")
            {
                Debug.Log($"UnityOSC receive: {(string)message.Data[0]} {(float)message.Data[1]} {(int)message.Data[2]}");
            }
        }
    }
}
```

`OSCHandler`を使う方法でも`OSCReceiver`を使う方法でも`OnDisable`を呼び出すタイミングで以下のエラーがでますが、UnityOSC側を修正せずに対応するのは難しそうでした。

:::note alert
Exception: Can't create server at port 8000
:::

### OSCの送信

`OSCHandler#SendMessageToClient`でOSCを送信します。

```c#:UnityOscHandlerSendSample.cs
using System.Collections.Generic;
using System.Net;
using UnityEngine;

public class UnityOscHandlerSendSample : MonoBehaviour
{
    [SerializeField] string clientId = "local";
    [SerializeField] string ipAddress = "127.0.0.1";
    [SerializeField] int port = 8080;

    void Start()
    {
        OSCHandler.Instance.Init();
        OSCHandler.Instance.CreateClient(clientId, IPAddress.Parse(ipAddress), port);
    }

    void Update()
    {
        OSCHandler.Instance.SendMessageToClient<System.Object>(clientId, 
            "/UnityOSC/sample", new List<System.Object>(){ "hoge", 123.456f, 100 });
    }
}
```

送信する値が1つの場合には次のように、`SendMessageToClient`のオーバーロードメソッドを使用します。

```c#
// send single value
OSCHandler.Instance.SendMessageToClient<float>(clientId, "/UnityOSC/single", 123.456f);
```

### モニタリング

`Menu > Window > OSC Helper`で`OSC Helper`ウィンドウを表示します。`OSC Helper`ウィンドウで`OSC Handler`によるOSCの送受信をモニタリングすることができます。

## まとめ

UnityOSCの使い方について解説しました。
他のOSCライブラリと比較して機能的にシンプルなので割と容易に使い方を理解できますが、単純なOSCの送受信でも自分でスクリプトを書く必要があります。
UnityOSC自体は既に開発が停止しているので、[uOSC](https://github.com/hecomi/uOSC)など簡易にOSCを利用できる他のライブラリの使用を検討してもいいと思います。