はじめに
本記事では、Unityで OSCCore を用いた、OSC通信を実装します。
前回の記事とは異なり、OSCCoreのWrapperクラスを独自実装します。
ここでは、int型を使用したシンプルな例を記載しています。
OSCCoreの追加方法や、Sceneのオブジェクト構成などは前回の記事を参考にしてください。
対象読者
- UnityアプリでOSC通信を実装したい方
環境
- MacBookPro M3
- Unity 2022.3.62f1
1. OSCSender クラス実装 (Client側)
処理の流れ
OSCメッセージを送信する流れは以下の通りです:
- OscClientを初期化 → 送信先のIPアドレスとポート番号を設定
- Sendメソッドを呼び出す → データ型に応じたSendメソッドで送信
実装
using UnityEngine;
using OscCore;
using UnityEngine.UI;
namespace CustomSample
{
public class OSCSender : MonoBehaviour
{
[Header("OSC Settings")]
[Tooltip("The IP address to send to")]
[SerializeField]
string _ipAddress = "127.0.0.1";
[SerializeField]
string _addressInt = "/sample/int";
[Tooltip("The port number to send to")]
[SerializeField]
int _portNumber = 9000;
// OSCメッセージを送信するクライアント
OscClient _client;
[Header("UI")]
[SerializeField] Button _sendButton;
void Start()
{
Initialize();
// ボタンが押されたら、int型の値10を送信
_sendButton.onClick.AddListener(() => {
_client?.Send(_addressInt, 10);
});
}
void OnDestroy()
{
_client = null;
}
void Initialize()
{
_client = null;
// 新しいクライアントを作成
_client = new OscClient(_ipAddress, _portNumber);
}
}
}
コードの詳細解説
OscClientの初期化
_client = new OscClient(_ipAddress, _portNumber);
ポイント:
-
OscClientはOSCメッセージを送信するためのクライアント - 送信先のIPアドレス(
_ipAddress)とポート番号(_portNumber)を指定 - 今回は、
127.0.0.1はローカルホスト(自分のPC)を指すIPアドレスを指定
Sendメソッド
_client?.Send(_addressInt, 10);
ポイント:
-
Send(address, value)- OSCアドレスと値を指定して送信 - この例では、
"/sample/int"というアドレスに、int型の値10を送信
その他の送信例
Sendメソッドはオーバーロード(同名で引数が異なるメソッド)として実装されており、様々な型のデータを送信できます。
// int型
_client.Send("/address", 10);
// float型
_client.Send("/address", 3.14f);
// string型
_client.Send("/address", "Hello");
// Vector2型
_client.Send("/address", new Vector2(1.0f, 2.0f));
// Vector3型
_client.Send("/address", new Vector3(1.0f, 2.0f, 3.0f));
// bool型
_client.Send("/address", true);
// その他: double, long, Color32, byte[], char なども送信可能
2. OSC Receiver クラス実装(Server側)
処理の流れ
OSCメッセージを受信して、受信に応じた処理を実行する流れは以下の通りです:
-
OSCメッセージ受信 (バックグラウンドスレッド) →
ReadIntで値を読み取り -
メインスレッドで処理 →
ProcessIntOnMainThreadでEventを発火 -
Unityで利用 → 他のコンポーネントが
OnIntReceivedイベントを受け取る
実装
using System;
using UnityEngine;
using OscCore;
namespace CustomSample
{
public class OSCReceiver : MonoBehaviour
{
[Header("OSC Settings")]
[Tooltip("The address to listen for incoming messages on")]
[SerializeField]
string _address = "/sample/int";
[Tooltip("The local port to listen for incoming messages on")]
[SerializeField]
int _portNumber = 9000;
OscServer _server;
// 外部のスクリプトから購読できるイベント
public event Action<int> OnIntReceived;
// バックグラウンドスレッドで読み取った値を一時保存
int _receivedInt;
void Start()
{
Initialize();
}
void Update()
{
// メインスレッドで実行待ちの処理を実行
_server?.Update();
}
void OnDestroy()
{
// ポートを解放
_server?.Dispose();
}
void Initialize()
{
try
{
// 既存のサーバーがあれば削除
OscServer.Remove(_portNumber);
// 新しいサーバーを作成
// _server = new OscServer(_portNumber); ← 非推奨
_server = OscServer.GetOrCreate(_portNumber);
// メッセージ受信時の処理を登録
_server.TryAddMethodPair(_address, ReadInt, ProcessIntOnMainThread);
}
catch (System.Exception e)
{
Debug.LogError($"Failed to initialize OSC server on port {_portNumber}: {e.Message}");
}
}
// バックグラウンドスレッドで値を読み取る
void ReadInt(OscMessageValues values)
{
_receivedInt = values.ReadIntElement(0);
Debug.Log($"[Background Thread] Read int: {_receivedInt}");
}
// メインスレッドで値を処理してEventを発火
void ProcessIntOnMainThread()
{
Debug.Log($"[Main Thread] Processing int: {_receivedInt}");
OnIntReceived?.Invoke(_receivedInt);
}
}
}
コードの詳細解説
OscServerの初期化
_server = OscServer.GetOrCreate(_portNumber);
ポイント:
-
OscServerはOSCメッセージを受信するためのサーバー -
受信するポート番号(
_portNumber)のみを指定(IPアドレスは指定しない) - サーバーは指定したポートで待ち受け、どのIPアドレスからのメッセージも受信可能
インスタンス生成方法:
- ❌
new OscServer(_portNumber)は非推奨 - ✅
OscServer.GetOrCreate(_portNumber)を推奨
GetOrCreateメソッドの使用を推奨する理由
- 同じポートに対して既にサーバーが存在する場合は、それを再利用
- 存在しない場合は新規作成し、内部の
OscServer.PortToServerに自動登録 - ポートの重複使用によるエラーを防止
-
OscServer.Remove()などの管理機能が正しく動作
new OscServer(_portNumber) によるポート重複エラー例
// ❌ これはエラーになります
void Start() {
Initialize();
Initialize(); // ←2回目の初期化でエラー
}
Removeによる管理機能が動作しない。そのため、Failed to initialize OSC server on port 9000: Address already in useのエラーとなってしまう。
TryAddMethodPair
_server.TryAddMethodPair(_address, ReadInt, ProcessIntOnMainThread);
ポイント:
- 第一引数: 監視するOSCアドレスを指定
- 第二引数: バックグラウンドで実行する処理
- 第三引数: メインスレッドで実行する処理
OSCメッセージはバックグラウンドスレッドで受信されます。しかし、UnityのAPI(GameObject操作、UI更新、Event発火など)はメインスレッドでしか実行できないので、TryAddMethodPairを使用します。
TryAddMethodを使用した場合のエラー例
// ❌ これはエラーになります
_server.TryAddMethod(_address, (values) => {
int value = values.ReadIntElement(0);
// バックグラウンドスレッドからEvent発火するとエラーになる!
OnIntReceived?.Invoke(value);
});
値の読み取り
void ReadInt(OscMessageValues values)
{
// 受信したメッセージから、0番目の要素をint型で取得
_receivedInt = values.ReadIntElement(0);
Debug.Log($"[Background Thread] Read int: {_receivedInt}");
}
ポイント:
-
ReadIntElement(0)- メッセージの1番目の値をint型で取得 - フィールド変数
_receivedIntに保存することで、メインスレッドに値を渡す - この時点ではまだUnityのAPIは使用してはならない
メインスレッドでの処理
void ProcessIntOnMainThread()
{
Debug.Log($"[Main Thread] Processing int: {_receivedInt}");
// Eventを発火して、他のスクリプトに通知
OnIntReceived?.Invoke(_receivedInt);
}
ポイント:
- メインスレッドで実行されるため、UnityのAPIが使用可能
-
OnIntReceived?.Invoke()でイベントを発火し、購読している他のスクリプトに値を通知
Update() でサーバーを更新
void Update()
{
_server?.Update();
}
ポイント:
- サーバーは、メインスレッドキューに溜まったコールバックを処理するために、Update()メソッドを呼び出す必要がある
The server must have its Update() method ticked to handle main thread queued callbacks.
Updateを忘れると、メインスレッドで実行したい処理ProcessIntOnMainThreadが実行されません。
ポートの解放
void OnDestroy()
{
_server?.Dispose();
}
ポイント:
- OnDestroy で OscServer を忘れずに破棄して、ポートを解放する
- ポート番号やAddressを更新したタイミングなど、適切なタイミングで破棄する
適切に破棄しない場合、既に指定した番号のポートを使用してしまっているErrorになる可能性があります。OscServer・OscClient 共に適切なタイミング破棄することを忘れずに行いましょう。また、OscServer.Removeメソッドなどもうまく活用してみてください。
3. TextDisplayer クラス実装
OSCReceiverのイベントを購読する側を実装します。
実装
using UnityEngine;
using UnityEngine.UI;
namespace CustomSample
{
public class TextDisplayer : MonoBehaviour
{
[SerializeField] Text _displayText;
[SerializeField] OSCReceiver _oscReceiver;
void Start()
{
// OSCReceiverのイベントを購読
_oscReceiver.OnIntReceived += Display;
}
void OnDestroy()
{
// イベントの購読を解除
_oscReceiver.OnIntReceived -= Display;
}
// イベント発火時に呼ばれるメソッド
public void Display(int message)
{
Debug.Log($"Displaying message: {message}");
_displayText.text = message.ToString();
}
}
}
OSCReceiverから渡された値をmessageとして受け取り、ToString()でint型を文字列に変換したものを、Textに表示する機能を実装しました。
4. 動作確認
正しく実装できていれば、以下の流れで動作します:
- OSCSenderのボタンをクリック → int型の値10を送信
-
OSCReceiverが受信 →
OnIntReceivedイベント発火 -
TextDisplayerが反応 →
Displayメソッドが呼ばれる - UIに表示 → Textに "10" が表示される
OscCore MonitorWindow
また、Window > OscCore > Monitorを選択すると、MonitorWindow を表示できます。
こちらで、メッセージ受信時の以下の情報を確認できます:
- 使用中のポート番号
- 受信したOSCメッセージのアドレス
- 受信した型と値
まとめ
本記事では、OSCCoreのOscClientとOscServerを利用した、Wrapperクラスを実装する方法を紹介しました。
今回はint型を使用したシンプルな例でしたが、OscClientのSendメソッドは様々な型に対応しています。プロジェクトの要件に応じて適切な型を選択してください。
エラーの原因となるポイントなどもまとめたので、通信が上手くいかない場合の参考になればと思います。
ぜひこの実装を活かして、より面白いOSC連携アプリケーションを開発してみてください!
楽しい開発をはじめましょう!
参考文献
