はじめに
前回のデモで iPad と HoloLens を OSC で通信しようとした際に通信周りが面倒だったのでまとめておきます。
ちなみに OSC はアドレス付きのデータを扱えるプロトコルで、UDP ベースのものがよく使われます。
薄いプロトコルなので速いですし、生の UDP よりもアドレスや型情報がついている分、圧倒的に使いやすいのでオススメです。
デモ
前回の一例ですが、ここではタッチ座標を iPad から OSC で送信して、それを HoloLens 側で受け取って、その位置に線を描く、というものになっています。
概要
Unity では System.Net.Sockets.UdpClient
クラスを利用して簡単に UDP 通信が可能です。
using UnityEngine; using System.Net; using System.Net.Sockets; public class UdpTest : MonoBehaviour { [SerializeField] int listenPort = 3333; UdpClient udpClient_; IPEndPoint endPoint_; void Start() { endPoint_ = new IPEndPoint(IPAddress.Any, listenPort); udpClient_ = new UdpClient(endPoint_); } void Update() { while (udpClient_.Available > 0) { byte[] data = udpClient_.Receive(ref endPoint_); // data を使って何か処理する } } }
ただ、HoloLens 上で動く UWP アプリでは UdpClient
クラスが含まれていません。
代わりに、Windows.Networking.Sockets.DatagramSocket
を使う必要があります。
参考: Hololens UDP server? — Mixed Reality Developer Forum
コード
ここでは UDP 経由で受け取ったデータは OSC のメッセージに変換して処理するものとします。例として OSC への変換には keijiro さんの unity-osc を利用しています。
using UnityEngine; using UnityEngine.Assertions; #if UNITY_EDITOR using System.Net; using System.Net.Sockets; #else using System; using System.IO; using Windows.Networking.Sockets; #endif public class TouchOscServer : MonoBehaviour { [SerializeField] int listenPort = 3333; Osc.Parser osc_ = new Osc.Parser(); void OnMessage(Osc.Message msg) { // ここで適当に処理する Debug.LogFormat("{0} => {1}", msg.path, msg.data[0]); } #if UNITY_EDITOR UdpClient udpClient_; IPEndPoint endPoint_; void Start() { Assert.IsNotNull(handler, "should set handler."); endPoint_ = new IPEndPoint(IPAddress.Any, listenPort); udpClient_ = new UdpClient(endPoint_); } void Update() { while (udpClient_.Available > 0) { var data = udpClient_.Receive(ref endPoint_); osc_.FeedData(data); } while (osc_.MessageCount > 0) { var msg = osc_.PopMessage(); OnMessage(msg); } } #else DatagramSocket socket_; object lockObject_ = new object(); const int MAX_BUFFER_SIZE = 1024; byte[] buffer = new byte[MAX_BUFFER_SIZE]; async void Start() { try { socket_ = new DatagramSocket(); socket_.MessageReceived += OnMessage; await socket_.BindServiceNameAsync(listenPort.ToString()); } catch (System.Exception e) { Debug.LogError(e.ToString()); } } void Update() { lock (lockObject_) { while (osc_.MessageCount > 0) { var msg = osc_.PopMessage(); OnMessage(msg); } } } async void OnMessage(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { using (var stream = args.GetDataStream().AsStreamForRead()) { await stream.ReadAsync(buffer, 0, MAX_BUFFER_SIZE); lock (lockObject_) { osc_.FeedData(buffer); } } } #endif } }
Unity と UWP 側で UDP の処理を切り分けています。Unity Eidtor 上の C# プロジェクトでは #else
ブロックの内容が補完付きで編集できませんが、ビルド後の C# プロジェクトの中であれば補完付きで編集やデバッグもできるので、手を加えたい場合はビルド後のプロジェクトに対してやるのをオススメします。
本例では OSC 側でデータをキューに詰めてもらってますが(Osc.Parser.FeedData()
)、生の UDP のデータとして使いたい場合も同様に受信部でキューしておいて、メインスレッドで処理されるよう次回の Update 時にキューから取り出して処理、みたいな流れになると思います。
おわりに
HoloLens といろんなものを連携させて是非遊んでみてください。