MRが楽しい

MRやVRについて学習したことを書き残す

HoloLensで受信したUDPメッセージを表示する その1(UnityEditor上のデバッグ)

本日は Unity と HoloLens の技術調査枠です。
UWPアプリでUDP受信を行う方法をまとめます。
f:id:bluebirdofoz:20190311185851j:plain

いつも通り、まずは UnityEditor 上での確認からです。

UnityEditorでのUDP受信

UnityEditor上でのUDP受信には UdpClient クラスを利用します。
docs.microsoft.com

プロジェクトとシーンの準備

以下の記事を元にHoloLens(WindowsMR)プロジェクトを作成します。
bluebirdofoz.hatenablog.com

2019/3/11現在、MRTK 2017 の最新バージョンは 2017.4.3.0 です。
f:id:bluebirdofoz:20190311185926j:plain

UdpClientを用いたサンプルコード

以下の処理を行うサンプルコードを作成しました。
1.ポート 4602 に受信する全てのUDPメッセージの非同期受信を開始する
2.UDPメッセージを受信すると、UnityEventで指定された関数に受信したバイト列を渡します。

・UDPMessageReceiver.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// UnityEvent を利用するため Events を追加
using UnityEngine.Events;

// 引数にByte列を受け取る UnityEvent<T0> の継承クラスを作成する
// UDP受信したバイト列を引数として渡す
// Inspector ビューに表示させるため、Serializable を設定する
[System.Serializable]
public class MyIntEvent : UnityEvent<byte[]>
{
}

public class UDPMessageReceiver : MonoBehaviour {
    /// <summary>
    /// UDPメッセージ受信時実行処理
    /// </summary>
    [SerializeField, Tooltip("UDPメッセージ受信時実行処理")]
    private MyIntEvent UDPReceiveEventUnityEvent;

    /// <summary>
    /// UDP受信ポート
    /// </summary>
    [SerializeField, Tooltip("UDP受信ポート")]
    private int UDPReceivePort = 4602;

    /// <summary>
    /// UDP受信データ
    /// </summary>
    private byte[] p_UDPReceivedData;

    /// <summary>
    /// UDP受信イベント検出フラグ
    /// </summary>
    private bool p_UDPReceivedFlg;

    /// <summary>
    /// 起動時処理
    /// </summary>
    void Start()
    {
        // 検出フラグOFF
        p_UDPReceivedFlg = false;

        // 初期化処理
        UDPClientReceiver_Init();
    }
    
    /// <summary>
    /// 定期実行
    /// </summary>
    void Update()
    {
        if (p_UDPReceivedFlg)
        {
            // UDP受信を検出すればUnityEvent実行
            // 受信データを引数として渡す
            UDPReceiveEventUnityEvent.Invoke(p_UDPReceivedData);
            // 検出フラグをOFF
            p_UDPReceivedFlg = false;
        }
    }

    /// <summary>
    /// UDP受信時処理
    /// </summary>
    private void UDPReceiveEvent(byte[] receiveData)
    {
        // 検出フラグONに変更する
        // UnityEventの実行はMainThreadで行う
        p_UDPReceivedFlg = true;

        // 受信データを記録する
        p_UDPReceivedData = receiveData;
    }
    
    /// <summary>
    /// UDP受信初期化
    /// </summary>
    private void UDPClientReceiver_Init()
    {
        // UDP受信ポートに受信する全てのメッセージを取得する
        System.Net.IPEndPoint endPoint =
            new System.Net.IPEndPoint(System.Net.IPAddress.Any, UDPReceivePort);

        // UDPクライアントインスタンスを初期化
        System.Net.Sockets.UdpClient udpClient =
            new System.Net.Sockets.UdpClient(endPoint);

        // 非同期のデータ受信を開始する
        udpClient.BeginReceive(OnReceived, udpClient);
    }

    /// <summary>
    /// UDP受信時コールバック関数
    /// </summary>
    private void OnReceived(System.IAsyncResult a_result)
    {
        // ステータスからUdpClientのインスタンスを取得する
        System.Net.Sockets.UdpClient udpClient =
        (System.Net.Sockets.UdpClient)a_result.AsyncState;
        
        // 受信データをバイト列として取得する
        System.Net.IPEndPoint endPoint = null;
        byte[] receiveBytes = udpClient.EndReceive(a_result, ref endPoint);

        // 受信データを受信時処理に引き渡す
        UDPReceiveEvent(receiveBytes);

        // 非同期受信を再開する
        udpClient.BeginReceive(OnReceived, udpClient);
    }
}

UnityEvent で呼び出す処理として、バイト列を文字列に変換してテキスト表示する関数を作成します。
・ChangeText.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ChangeText : MonoBehaviour {
    /// <summary>
    /// 文字列を反映するテキストフィールド
    /// </summary>
    [SerializeField, Tooltip("文字列を反映するテキストフィールド")]
    private Text TargetTextField;

    /// <summary>
    /// byte列を文字列に変換してテキストフィールドに反映する
    /// </summary>
    /// <param name="message"></param>
    public void SetASCIIBytes(byte[] bytes)
    {
        // データを文字列に変換
        string getMessage = System.Text.Encoding.ASCII.GetString(bytes);
        TargetTextField.text = getMessage;
    }
}

作成した2つのスクリプトを Text オブジェクトにアタッチします。
f:id:bluebirdofoz:20190311185953j:plain

UDPMessageReceiver.cs の UnityEvent に Text オブジェクト自身を指定します。
呼び出し関数として、テキスト表示の関数を設定します。
f:id:bluebirdofoz:20190311190028j:plain

ChangeText.cs の TargetTextField にも Text オブジェクト自身を指定します。
f:id:bluebirdofoz:20190311190039j:plain

動作確認用Pythonスクリプトの作成

動作確認用にUDPメッセージを送信するpythonスクリプトを作成します。
・UDPSend.py

# UDP用import
import socket

# UDP接続のための設定項目
holoaddress = '192.168.10.100'
holoport = 4602

# 受信関数
def sendudp_message(msg):

    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.sendto(msg.encode(), (holoaddress, holoport))
    print(msg)

# 実行指定で呼び出されているかチェック
if __name__ == "__main__":

    sendudp_message('UDPTestMessage')

UnityEditorでの動作確認

[再生]ボタンをクリックしてプロジェクトを動作確認します。
f:id:bluebirdofoz:20190311190050j:plain

先ほど作成したpythonスクリプトを実行すると、送信したメッセージがテキストに表示されました。
f:id:bluebirdofoz:20190311190102j:plain

次は UWP 向けコードを追加して HoloLens 上での動作確認を行います。
bluebirdofoz.hatenablog.com

参考ページ

dobon.net