oFなどの外部プログラムからOSCを受け取ってUnityを動かす場合、UnityOSCが有名っぽいので使ってみたがハマったのでメモ。
同じイベントを拾い続ける
マニュアルを読む限り、Update()
の内部でOSCHandler.Instance.UpdateLogs()
を呼んで新しいメッセージを取得しろということらしいが、内部的にはキューになっておらずUpdateLogs()
を呼んでも古いメッセージが残ったままになっているので新着メッセージがどれだか分からない。Timestampを使おうと思っても全部0になっているので比較できない。
(このへんはIssueとして取り上げられている模様)
メッセージをこぼす
内部構造上UpdateLogs()
で更新されるメッセージは最後に到着した1つだけなので、更新の間にメッセージが複数到着すると最後のメッセージ以外は取りこぼす。センサーデータとかだとこれは致命的。
OSCMessageとOSCBundle
ofxOscSenderを使ってメッセージを送ると、単発のメッセージでもOSCBundleとして投げられるっぽいので、受け手側はふつうのOSCMessageとして受けようとするとはまる。
対策
本体直してプルリクしろよという話なのですがとりあえずのワークアラウンドとして。
UnityOSC側:
public class OSCHandler : MonoBehaviour
{
public event PacketReceivedEventHandler PacketReceivedEvent;
void OnPacketReceived(OSCServer server, OSCPacket packet)
{
// パケット受信時にイベントを投げる
PacketReceivedEvent(server, packet);
}
使用するController側:
using UnityEngine;
using System.Collections;
using UnityOSC;
public class OSCController : MonoBehaviour {
private Queue queue;
void Start () {
queue = new Queue();
queue = Queue.Synchronized(queue);
OSCHandler.Instance.Init();
// パケット受信時のイベントハンドラを登録
OSCHandler.Instance.PacketReceivedEvent += OnPacketReceived;
}
void OnPacketReceived(OSCServer server, OSCPacket packet) {
// 来たパケットをキューに積んでおく
queue.Enqueue(packet);
}
void Update () {
while (0 < queue.Count) {
OSCPacket packet = queue.Dequeue() as OSCPacket;
if (packet.IsBundle()) {
// OSCBundleの場合
OSCBundle bundle = packet as OSCBundle;
foreach (OSCMessage msg in bundle.Data) {
// メッセージの中身にあわせた処理
}
} else {
// OSCMessageの場合はそのまま変換
OSCMessage msg = packet as OSCMessage;
// メッセージの中身にあわせた処理
}
}
}
一応これでopenFrameworksからUnityへのメッセージ送信はうまく行きました。