7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Iwaken Lab.Advent Calendar 2023

Day 5

HoloLens 2で視線情報を取得してCSVファイルに出力する

Last updated at Posted at 2023-12-04

こんにちは Iwaken Lab.のきょうすけです!
普段はワイクウーデザインにてXRエンジニアやってます

本記事は、Iwaken Lab.アドベントカレンダー2023の5日目の記事です
昨日のサックーさんの記事をご覧になってない方はそちらもぜひ

概要

「HoloLens 2のローカルストレージにCSVファイルを作成して書き込んで保存する」という、一見大したことなさそうな処理が一癖二癖あったのでその話をしようと思います
具体的には以下の話

  • Dispose()/Close()
  • UWPのライフサイクル

環境

  • Unity 2022.3.11f
  • MRTK 以下画像参照
    image.png

目標

この記事では以下を達成することを目標とします

  • 視線をカーソルとして可視化する
  • 視線の座標をCSVに書き込む
  • CSVをPCに保存する

そこで今回は以下のシチュエーションで上記目標を実装します

  • シーン上にボタンが配置されている
  • ボタンは視線が合った状態でピンチするとクリックできる
  • クリックしたときの視線の座標とタイムスタンプをCSVに書き込む
  • 保存されたCSVをPCに保存する

MRアプリケーション開発のセットアップ手順は公式トレーニングでも参考にしてください
ただし、2023年11月段階ではMRTK3だけはインポートしない方がいいです

実装例

アイトラッキングの有効化

今回は深く考えずデモシーンのプロファイルを使います

デモシーンの導入

Mixed Reality Feature Toolを使用し、Mixed Reality Toolkit Examplesをインポートします
image.png

その後、Package ManagerからアイトラッキングのデモシーンDemo - EyeTrackingをインポートします
image.png

プロファイルの設定

HierarchyのMixedRealityToolkitを選択し、EyeTrackingDemoConfigrationProfileを選択します
image.png

デモシーンでは視線オブジェクトが透明なものになっているので別のPrefabを設定します
inputタブ内のPointers > Gaze Settings > Gaze Cursor PrefabDefaultGazeCursorに変更します
image.png

ボタンの配置

MRTK付属のボタンをお好みで選んで適当にシーンに配置してください
今回はPressableButtonHoloLens2を使います
image.png

スクリプト

CSVに取得した視線情報を書き込む処理を記述しています

WriteCsv.cs
using UnityEngine;
using System;
using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.MixedReality.Toolkit;

public class WriteCsv : MonoBehaviour
{
    private StreamWriter _streamWriter;

    // ストリームを開く
    private void Start()
    {
        var address = Application.persistentDataPath + "/test.csv";
        Debug.Log(address);

        // ファイルが存在していなければ作成する
        if (!File.Exists(address))
        {
            _streamWriter = File.CreateText(address);
            _streamWriter.Flush();
            _streamWriter.Dispose();
        }
        
        _streamWriter = new StreamWriter(new FileStream(address, FileMode.Truncate, FileAccess.Write), Encoding.UTF8);

        string[] s1 = { "タイムスタンプ", "パネルX座標", "パネルY座標" };
        var s2 = string.Join(",", s1);
        _streamWriter.WriteLine(s2);
    }

    // ボタンを押したときにタイムスタンプと座標を書き込む
    public void Write()
    {   
        var timestamp = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
        var touchX = CoreServices.InputSystem.EyeGazeProvider.HitPosition.x;
        var touchY = CoreServices.InputSystem.EyeGazeProvider.HitPosition.y;
        var s2 = string.Join(",", timestamp, touchX.ToString(CultureInfo.CurrentCulture), touchY.ToString(CultureInfo.CurrentCulture));
        _streamWriter.WriteLine(s2);
    }

    // 終了処理
    // ストリームを閉じる
#if WINDOWS_UWP
    private void OnApplicationFocus(bool focus)
    {
        if (focus) return;
        _streamWriter.Flush();
        _streamWriter.Dispose();
        Windows.ApplicationModel.Core.CoreApplication.Exit();
    }
#else 
    private void OnApplicationQuit()
    {
        _streamWriter.Flush();
        _streamWriter.Dispose();
    }
#endif
}

保存先

保存先はApplication.persistentDataPath + "/test.csv"となっていますが、具体的にはデバイスポータルのSystem\File explorer\User Folder\LocalAppDat\${カンパニー名}\${アプリ名}\LocalState以下に保存されます

ストリームを閉じる処理

AndroidなどでCSVの書き込み処理をしたことある人は以下のようなコードを書いたことがあると思います

private void Write()
{
    _streamWriter.WriteLine(str);
    _streamWriter.Close();
}

この例ではストリームを閉じるときClose()を使っています
ですが、このClose()は.NETチームにとって忌み子なようで、UWPでは適応範囲外になっています(画像元:リファレンス)
image.png
そのため、HoloLensではDispose()を使います

DisposeやCloseについての話は以下の記事をご覧ください

ストリームを閉じるタイミング

今回はHoloLensのメニューからホームボタンを押したときにやります

ホームボタンを押すと白いウィンドウが表示され、アプリが終了したように見えるのでOnApplicationQuit()が呼び出されているような気もしますが、実は呼ばれません
と、言うより実は終了していません

まずHoloLensで動作するUnityアプリはUWPアプリに乗っかって動作します
そして、ホームボタンを押すとUWPのライフサイクルで言うところのSuspendedになります
この状態から閉じるボタンを押してアプリを終了させてもOnApplicationQuit()は呼ばれません

そこでHoloLensでの終了処理はプレイヤーフォーカスの動向を検知するOnApplicationFocus(bool)を使用します
HoloLensで動作するUnityアプリは、ホームボタンを押すとフォーカスが失われ、OnApplicationFocus(False)が呼び出されます
これを利用してストリームの閉じる処理を行います

    // 終了処理
    // ストリームを閉じる
#if WINDOWS_UWP
    private void OnApplicationFocus(bool focus)
    {
        if (focus) return;
        _streamWriter.Flush();
        _streamWriter.Dispose();
        Windows.ApplicationModel.Core.CoreApplication.Exit();
    }
#else 
    private void OnApplicationQuit()
    {
        _streamWriter.Flush();
        _streamWriter.Dispose();
    }
#endif

今回はストリームを閉じるついでにWindows.ApplicationModel.Core.CoreApplication.Exit()を呼び出し、UWPアプリごと終了させるようにしています

まとめ

UWPは癖が強く、ちょっとUnity触ったことある程度なら余裕で躓きそうですね

参考

7
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?