Edited at

UnityOSCではまる

More than 3 years have passed since last update.

oFなどの外部プログラムからOSCを受け取ってUnityを動かす場合、UnityOSCが有名っぽいので使ってみたがハマったのでメモ。


同じイベントを拾い続ける

マニュアルを読む限り、Update()の内部でOSCHandler.Instance.UpdateLogs()を呼んで新しいメッセージを取得しろということらしいが、内部的にはキューになっておらずUpdateLogs()を呼んでも古いメッセージが残ったままになっているので新着メッセージがどれだか分からない。Timestampを使おうと思っても全部0になっているので比較できない。

(このへんはIssueとして取り上げられている模様)


メッセージをこぼす

内部構造上UpdateLogs()で更新されるメッセージは最後に到着した1つだけなので、更新の間にメッセージが複数到着すると最後のメッセージ以外は取りこぼす。センサーデータとかだとこれは致命的。


OSCMessageとOSCBundle

ofxOscSenderを使ってメッセージを送ると、単発のメッセージでもOSCBundleとして投げられるっぽいので、受け手側はふつうのOSCMessageとして受けようとするとはまる。


対策

本体直してプルリクしろよという話なのですがとりあえずのワークアラウンドとして。

UnityOSC側:


OSCHandler.cs

public class OSCHandler : MonoBehaviour

{
public event PacketReceivedEventHandler PacketReceivedEvent;

void OnPacketReceived(OSCServer server, OSCPacket packet)
{
// パケット受信時にイベントを投げる
PacketReceivedEvent(server, packet);
}


使用するController側:


OSCController.cs


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へのメッセージ送信はうまく行きました。