MRが楽しい

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

MRTKを用いてHoloLensのダブルタップを検出する

本日は MRTK の技術調査枠です。
MRTKを用いてダブルクリックならぬダブルタップを検出する方法をまとめます。
以下の記事の応用になります。
bluebirdofoz.hatenablog.com

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

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

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

タップイベント

ダブルタップを検出するにはタップイベントを利用します。
前回タップの時刻から一定時刻の間に再びタップイベントが発生すればダブルタップとみなします。
更に連続タップの回数に応じて、ダブルタップ・トリプルタップなどを分けて検出してみます。

MRTK の基本設定を行っていれば IInputClickHandler インタフェースの以下の関数を利用してイベントを取得できます。
・OnInputClicked:タップイベント検出

実際にスクリプトを作成してみます。
ダブルタップとトリプルタップを分けて検出するため、Update 関数を利用して最終タップから一定時刻が経過した時点で連続タップを判定します。
・MultiTapEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// IInputClickHandler を利用するため InputModule を追加
using HoloToolkit.Unity.InputModule;

public class MultiTapEvent : MonoBehaviour,
IInputClickHandler // タップ操作検出
{
    /// <summary>
    /// グローバルリスナーの有無
    /// </summary>
    [Tooltip("グローバルリスナーの有無")]
    public bool IsGlobalListener = false;

    /// <summary>
    /// 連続タップ許容時間(秒)
    /// </summary>
    [SerializeField, Tooltip("連続タップ許容時間(秒)")]
    private float MultTapTime = 0.5f;

    /// <summary>
    /// 連続タップカウント
    /// </summary>
    private int p_MultTapCount;

    /// <summary>
    /// 連続タップ計測開始時刻
    /// </summary>
    private float p_MultTapStart;

    /// <summary>
    /// 起動時処理
    /// </summary>
    void Start ()
    {
        if (IsGlobalListener)
        {
            // 全てのジェスチャーイベントをキャッチする
            // 本設定を有効にしてColliderオブジェクトにアタッチした場合
            // オブジェクトへのタップとグローバルのタップが
            // 別々に検出される(2回処理が走る)ので注意
            InputManager.Instance.AddGlobalListener(gameObject);
        }
    }

    /// <summary>
    /// 定期実行
    /// </summary>
    void Update ()
    {
        // 連続タップ判定
        if (p_MultTapCount > 1)
        {
            // タップカウントが 2 以上の時、連続タップの発生チェック
            if ((Time.time - p_MultTapStart) > MultTapTime)
            {
                // 連続タップ許容時間が経過していればカウントに応じて処理を実行
                if (p_MultTapCount == 2)
                {
                    // ダブルタップ処理
                    Debug.Log("DoubleTap");
                    // オブジェクトを2倍の大きさに変更
                    transform.localScale = transform.localScale * 2.0f;
                }
                if (p_MultTapCount == 3)
                {
                    // トリプルタップ処理
                    Debug.Log("TripleTap");
                    // オブジェクトを1/2の大きさに変更
                    transform.localScale = transform.localScale * 0.5f;
                }
                p_MultTapCount = 0;
            }
        }
    }

    /// <summary>
    /// タップ検出
    /// </summary>
    /// <param name="eventData"></param>
    public void OnInputClicked(InputClickedEventData eventData)
    {
        Debug.Log("clicked!");

        // 現在時刻の取得
        float nowTime = Time.time;

        // 連続タップ確認
        float tapTime = nowTime - p_MultTapStart;
        if (tapTime > MultTapTime)
        {
            // 前回タップから連続タップ許容時間を超えていれば初回タップと再判定
            p_MultTapCount = 1;
        }
        else
        {
            // 前回タップから連続タップ許容時間内ならば連続タップと判定
            p_MultTapCount++;
        }
        // 連続タップ計測開始時刻を更新
        p_MultTapStart = nowTime;
    }
}

作成したスクリプトSphere オブジェクトにアタッチします。
f:id:bluebirdofoz:20190206060848j:plain

[再生]ボタンを押してシーンを開始します。
[Game]ビュー上で Sphere オブジェクトに視線を合わせ Shift + 左クリック でタップ操作を試験できます。
前回のタップから0.5秒以内に連続してタップを行うと、ダブルタップまたはトリプルタップが検出されます。
f:id:bluebirdofoz:20190206060902j:plain

Inspector ビューから MultiTapTime 変数を調整すると、連続タップを検出する許容時間を調整できます。
f:id:bluebirdofoz:20190206060919j:plain

MRTKを用いてHoloLensの長押しタップを検出する

本日は MRTK の技術調査枠です。
MRTKを用いて長押しタップを検出する方法をまとめます。

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

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

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

タップダウン・タップアップイベント

長押しタップを検出するにはタップを開始したタップダウンのイベントと、タップを終了したタップアップのイベントを取得します。
タップの開始時刻とタップの終了時刻を比較すれば、どれだけの時間タップを継続していたかが分かります。

MRTK の基本設定を行っていれば IInputHandler インタフェースの以下の関数を利用して各イベントを取得できます。
・OnInputDown:タップダウン検出
・OnInputUp:タップアップ検出

実際にスクリプトを作成してみます。
長押しタップを行うと、オブジェクトのサイズが大きくなるスクリプトを作成しました。
・LongTapEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// IInputHandler を利用するため InputModule を追加
using HoloToolkit.Unity.InputModule;

public class LongTapEvent : MonoBehaviour,
IInputHandler // タップダウン、タップアップ検出
{
    /// <summary>
    /// 長押しタップ検出時間(秒)
    /// </summary>
    [SerializeField, Tooltip("長押しタップ検出時間(秒)")]
    private float LongTapTime = 3.0f;
    /// <summary>

    /// 長押しタップ計測開始時刻
    /// </summary>
    private float p_LongTapStart;

    /// <summary>
    /// 起動時処理
    /// </summary>
    void Start()
    {
        // 全てのジェスチャーイベントをキャッチする
        InputManager.Instance.AddGlobalListener(gameObject);
        // スクリプトをアタッチしたオブジェクトに長押しタップしたときのみ
        // 動作させたい場合、本設定は不要
        // また、本設定を有効にしてColliderオブジェクトにアタッチした場合
        // オブジェクトへのタップとグローバルのタップが
        // 別々に検出される(2回処理が走る)ので注意する
    }

    /// <summary>
    /// タップダウン検出
    /// </summary>
    /// <param name="eventData"></param>
    public void OnInputDown(InputEventData eventData)
    {
        Debug.Log("TapDown!");

        // 現在の時刻を検出時刻として記録
        p_LongTapStart = Time.time;
    }

    /// <summary>
    /// タップアップ検出
    /// </summary>
    /// <param name="eventData"></param>
    public void OnInputUp(InputEventData eventData)
    {
        Debug.Log("TapUp!:" + LongTapTime);

        // 現在時刻の取得
        float nowTime = Time.time;

        // 長押しタップ検出時間より長くタップされていれば長押しと判定
        float tapTime = nowTime - p_LongTapStart;
        if (tapTime > LongTapTime)
        {
            // 長押しタップ処理
            Debug.Log("LongTap");
            // オブジェクトを2倍の大きさに変更
            transform.localScale = transform.localScale * 2.0f;
        }
    }
}

作成したスクリプトSphere オブジェクトにアタッチします。
f:id:bluebirdofoz:20190205010804j:plain

[再生]ボタンを押してシーンを開始します。
[Game]ビュー上で Shift + 左クリック でタップ操作を試験できます。
3秒以上の長押しタップを行うと、オブジェクトが大きくなります。
f:id:bluebirdofoz:20190205010813j:plain

Inspector ビューから LongTapTime 変数を調整すると、長押しタップを検出するタップ時間を調整できます。
f:id:bluebirdofoz:20190205010822j:plain

HoloLens特化のホロ恋子モデルを作成する その33(捻り対策を行う)

本日はホロ恋子モデル2の作成枠です。
今回は少し手順を戻して各関節に施した捻り対策をまとめます。
f:id:bluebirdofoz:20190204091955j:plain

捻りがある関節

前回、ポーズを変形してウェイト付けを行いました。
更に各関節の捻りについても考慮する必要があります。
しかし、捻りの対策はウェイトで実施するには限界があります。
f:id:bluebirdofoz:20190204092006j:plain

そこで今回はモデリングで幾つかの対策を行いました。
限界はありますが、ある程度は捻った際の挙動も確認しておきます。
Humanoidの設定をデフォルト(捻りを両端50%に分散)と想定しています。
japan.unity3d.com

人の挙動で大きく捻りが加わる可能性のあるHumanoid関節は以下の通りです。

・首の先関節:左右45度
f:id:bluebirdofoz:20190204092030j:plain

・首の元関節:左右45度
f:id:bluebirdofoz:20190204092043j:plain

・肩の関節:内外45度
f:id:bluebirdofoz:20190204092054j:plain

・肘の関節:内外90度
f:id:bluebirdofoz:20190204092106j:plain

・手首の関節:内外45度
f:id:bluebirdofoz:20190204092119j:plain

・股の関節:外45度
f:id:bluebirdofoz:20190204092129j:plain

・膝の関節:外45度
f:id:bluebirdofoz:20190204092140j:plain

メッシュの歪みを制御する

回転させたとき、「メッシュにどういった歪みが発生するか」は捻り対策でもっとも重要なポイントです。
首などは頂点を真っ直ぐ接続せず、交差状に接続することで回転時にメッシュの歪みが発生することを防いでいます。
f:id:bluebirdofoz:20190204092151j:plain

その他、股や膝は四角面は全て後ろから前方側へ辺を切る形で三角面にしています。
f:id:bluebirdofoz:20190204092223j:plain

こうすると、脚を外側に大きく捻ってもメッシュが歪みません。
f:id:bluebirdofoz:20190204092428j:plain

試しに逆方向に辺を切った場合を試してみます。
f:id:bluebirdofoz:20190204092440j:plain

この場合、以下のようにメッシュに大きな凹みが発生してしまいます。
f:id:bluebirdofoz:20190204092451j:plain

逆に、後ろから前方へ辺を切る形では、内側に脚を捻るとメッシュが凹む形になります。
しかし、人の動きでは脚を大きく内側に捻るという動作はないため、考慮しなくても問題になりません。
このように稼働後のメッシュの歪みを想定してメッシュを作成します。

球体関節のウェイト

今回は捻りによる破綻を防ぐため、肩と肘を球体関節にしています。
この場合は分かれたメッシュのブロックをそれぞれのボーンにのみ関連付けます。
f:id:bluebirdofoz:20190204092504j:plain

これにより、捻りによるメッシュの歪みは発生しません。
しかし、接続部のメッシュが連続しないため、テクスチャの考慮が必要です。

次からはモデルの着色を行います。
bluebirdofoz.hatenablog.com

HoloLens特化のホロ恋子モデルを作成する その32(ポーズを変更してウェイトを確認する)

本日はホロ恋子モデル2の作成枠です。
ウェイト設定を一通り行いました。
今回はポーズを変更してウェイトを確認する手順をまとめます。
f:id:bluebirdofoz:20190203201307j:plain

重視するポーズ

Humanoid対応を考えると、どうしてもボーンが足りず、綺麗に変形しない箇所は出てきます。
私の場合、頻出するポーズ変形を基準に破綻しないように調整しています。

・肩を下して肘を曲げたポーズ
f:id:bluebirdofoz:20190203201319j:plain

・脚の座りポーズ
f:id:bluebirdofoz:20190203201329j:plain

・握りこぶし
f:id:bluebirdofoz:20190203201341j:plain

ポーズを変更したままウェイトを調整する

ポーズを変更したままウェイトを調整する方法についてです。
まず[ポーズモード]でボーンを調整して、確認したいポーズに設定します。
f:id:bluebirdofoz:20190203201355j:plain

そのまま[ウェイトペイント]のモードに入ると、ポーズを維持したままウェイト値の変更ができます。
f:id:bluebirdofoz:20190203201406j:plain

頂点グループの値入力で設定したい場合は[頂点選択モード]のアイコンを有効にします。
すると、頂点が選択できるようになり、頂点グループの値を直接設定できるようになります。
f:id:bluebirdofoz:20190203201417j:plain

ポーズを元に戻したい場合はその29で説明した通り、以下の手順を実施します。
1.[ポーズモード]で元の位置/回転/スケールに戻したいボーンを選択する
2.メニューから ポーズ -> トランスフォームをクリア -> すべて を選択する
f:id:bluebirdofoz:20190203201428j:plain

次は捻り対策が必要な関節と、対策例についてまとめます。
bluebirdofoz.hatenablog.com

HoloLens特化のホロ恋子モデルを作成する その31(ウェイト設定による頂点の動き)

本日はホロ恋子モデル2の作成枠です。
ウェイト設定による頂点の動きについてまとめます。
f:id:bluebirdofoz:20190202175351j:plain

ウェイトの影響力

ウェイトの設定値による影響力は絶対値ではなく、比率できまります。
以下は指先のウェイト値を 1.0 で設定した場合と 0.5 で設定した場合で、指先のボーンを90度回転させた例です。
・指先ウェイト値 1.0
f:id:bluebirdofoz:20190202175403j:plain

・指先ウェイト値 0.5
f:id:bluebirdofoz:20190202175414j:plain

全く同じ曲がり方をしていることが分かります。
ウェイトの設定値による影響力は絶対値では変化しないため、1.0 を 0.5 に設定しても影響力が半分にはなりません。

ウェイトペイントは絶対値で色を表示します。
このため、ウェイトペイントの表示では異なる配色であるにも関わらず、ボーンの動作では全く同じ変形をするということが起こりえます。
・指先ウェイト値 1.0
f:id:bluebirdofoz:20190202175427j:plain

・指先ウェイト値 0.5
f:id:bluebirdofoz:20190202175439j:plain

ウェイトの影響力を確認する際は、絶対値ではなく、各頂点に設定されたウェイトの比率を確認する必要があります。

複数ウェイトの設定

このように、ウェイトは頂点に対する影響力の比率を示すものなので、影響するボーンを増やすと動きが変わります。
先ほどの 0.5 を設定した例で第二関節のウェイト値を追加して、同じく指先のボーンを90度回転させます。
・指先ウェイト値 0.5、中関節ウェイト値 0.5
f:id:bluebirdofoz:20190202175450j:plain

指関節の曲がり具合の影響力が半分になった事が分かります。
因みにこちらも絶対値を変更しても動きに変化はありません。
・指先ウェイト値 1.0、中関節ウェイト値 1.0
f:id:bluebirdofoz:20190202175502j:plain

複数ウェイト設定時の注意点

複数ボーンのウェイトを設定する場合は、対象のボーンの親子関係に注意が必要です。
ボーンの動きは伝播するため、親関係にあるボーンでウェイトを設定していれば親ボーンの移動でも破綻しません。
・人差し指の指先ウェイト値 0.5、人差し指の中関節ウェイト値 0.5
f:id:bluebirdofoz:20190202175513j:plain

しかし以下のように親子関係にない複数ボーンにウェイト値を設定してしまうと、その親ボーンを移動した際に破綻するケースが発生します。
・人差し指の指先ウェイト値 0.5、中指の指先ウェイト値 0.5
f:id:bluebirdofoz:20190202175524j:plain

次は実際にポーズを変更してウェイト値を調整する手順についてです。
bluebirdofoz.hatenablog.com

Blenderで部屋をデザインする(Archimeshアドオン)

本日は Blender の技術調査枠です。
先日、ATL広尾バーチャル勉強会に参加しました。
uuupa.hatenablog.com

その際、Archimeshアドオンという Blender で部屋を作るためのアドオンが紹介されました。
使い所によっては非常に便利だと感じたので少し試してみます。
f:id:bluebirdofoz:20190201094551j:plain

Archimeshアドオンのインストール

Archimeshアドオンは Blender 2.78 以降、標準アドオンになっており、アドオンの有効化のみで利用できます。
Blender を起動し、[ファイル] -> [ユーザ設定]でユーザ設定ダイアログを開きます。
f:id:bluebirdofoz:20190201092949j:plain

[アドオン]タブを開き、Archimesh で検索すると[Archimesh]のアドオンが表示されます。
チェックボックスにチェックを入れて「ユーザー設定の保存」をクリックします。
f:id:bluebirdofoz:20190201093000j:plain

[オブジェクトモード]のメニューから 追加 -> メッシュ を開き、Archimesh が追加されていればインストールは完了です。
f:id:bluebirdofoz:20190201093011j:plain

Archimeshオブジェクトの配置

試しに Archimesh オブジェクトを利用してみます。
初期の Cube オブジェクトは削除し、追加 -> メッシュ -> Archimesh から Add Room を選択します。
f:id:bluebirdofoz:20190201093028j:plain

Room オブジェクトが追加されます。
また、ツールシェルフの[作成]タブに[Archimesh]パネルが追加されており、ここからもこれらのオブジェクト追加が行えます。
f:id:bluebirdofoz:20190201093038j:plain

[Display hints]の[Show]ボタンを有効化すると、Archimesh オブジェクトの情報が3Dビューに表示されるようになります。
オブジェクトの名称や、寸法などが一目で分かるので、予め有効にしておくと便利です。
f:id:bluebirdofoz:20190201093345j:plain

Archimesh オブジェクトはプロパティシェルフからステータスによる編集が可能です。
ツールシェルフを開くと、編集を行うための[Room]パネルが追加されています。
f:id:bluebirdofoz:20190201093530j:plain

[高さ]や[幅]は部屋の壁の高さや幅の調整をい行います。
[Number of Walls]は部屋の壁の枚数を指定します。
その他、壁ごとに長さや角度、カーブの設定などを行えます。
[baseboard]は壁と床の部分にある幅木の設定です。
f:id:bluebirdofoz:20190201093109j:plain

同じような方法でドアや窓なども追加し、パラメータで変形させることが可能です。
パーツを組み合わせるだけで部屋っぽいモデルが完成するので、使い所によっては便利そうです。
ただし基本パーツのポリゴン数が割と大きく、オブジェクトを増やすとあっという間に重くなるので注意が必要です。
f:id:bluebirdofoz:20190201093121j:plain

HoloLens特化のホロ恋子モデルを作成する その30(ウェイトの手動調整)

本日はホロ恋子モデル2の作成枠です。
今回は手動でウェイト付けの調整を行います。
f:id:bluebirdofoz:20190131093434j:plain

ウェイトペイントによる調整

手動でウェイトペイントを行う前に、ウェイトについても左右対称に編集が行われるようにします。
[ウェイトペイント]のモードに切り替えて、ツールシェルフの[オプション]タブを開き、[X軸ミラー]にチェックを入れます。
オブジェクト毎の設定になるため、メッシュオブジェクトが複数ある場合は全てのオブジェクトで同様の設定を行います。
f:id:bluebirdofoz:20190131093446j:plain

まずは前回確認した顎回りのムラを修正します。
アーマチュアオブジェクトの[ポーズモード]を開き、ウェイト付けしたいボーン(頭部ボーン)を選択します。
f:id:bluebirdofoz:20190131093520j:plain

この状態で[ウェイトペイント]に切り替えます。
すると、ボーンが選択された状態でウェイトの分布が表示されます。
この状態でウェイト付けを行うことで、選択したボーンへのウェイトが設定されます。
f:id:bluebirdofoz:20190131093532j:plain

ツールシェルフの[ツール]タブでブラシの設定を行います。
ウェイトの設定値(最大値:1)、ブラシの半径、ブラシの減衰カーブの設定などが行えます。
今回は顎部分のムラを 1 と 0 ではっきりと分けたいので、ウェイトに 1 を設定し、減衰カーブもなくしました。
f:id:bluebirdofoz:20190131093544j:plain

そのままメッシュに色を塗るように左クリックで頂点をウェイト付けします。
顎部分のウェイトが 1 に設定され、ムラなく真っ赤な状態になったことが分かります。
頂点位置が分かりづらい場合は表示を[ワイヤーフレーム]に切り替えると見易くなります。
f:id:bluebirdofoz:20190131093600j:plain

頂点グループによる調整

より細かくウェイトを設定したい場合は頂点グループによる調整を行います。
[編集モード]を開き、ウェイトを編集したい頂点を選択します。
プロパティシェルフの[頂点ウェイト]パネルに現在その頂点に設定されているウェイトが表示されます。
f:id:bluebirdofoz:20190131093618j:plain

この値を調整することでもウェイトの調整が可能です。
f:id:bluebirdofoz:20190131093634j:plain

更に複数の頂点にまとめて同じ設定を行う事も可能です。
[編集モード]のまま、設定を行いたい頂点を複数選択します。
f:id:bluebirdofoz:20190131093702j:plain

この状態で[データ]タブを開き、[頂点グループ]パネルを開きます。
関連付けたいボーンを選択し、ウェイト値を設定して[割り当て]ボタンをクリックします。
もし逆に関連付けを外したい場合は[削除]ボタンをクリックしてウェイト値をクリアします。
f:id:bluebirdofoz:20190131093716j:plain

これで全ての頂点に同じウェイト値が設定されました。
f:id:bluebirdofoz:20190131093727j:plain

頂点グループの選択

因みに、[頂点グループ]パネルでボーン名を選択した状態で[選択]ボタンをクリックすると、対象のボーンのウェイト値を持つ頂点が全て選択されます。
f:id:bluebirdofoz:20190131093737j:plain

稼動の影響範囲を確認する場合など、覚えておくと色々と役立ちます。

次はウェイト設定による頂点の動きについて説明します。
bluebirdofoz.hatenablog.com