Hololens開發(fā)手記 - 移動全息對象(Manipulation)

本文將以官方案例來說明如何實現(xiàn)移動全息對象操作(211 - chapter4)栖忠。此案例主要涉及到了語音狸相、手勢識別。

實現(xiàn)思路:

  • 通過添加的關(guān)鍵字來初始化KeywordRecognizer瘸右;
  • 創(chuàng)建一個GestureRecognizer用于實現(xiàn)Manipulation Holographic;
  • 通過語音識別調(diào)用對應(yīng)的函數(shù)操作實現(xiàn)選擇不同的GestureRecognizer;
  • 通過不同的手勢識別操作來對應(yīng)地向Gaze射線凝視的物體發(fā)送消息實現(xiàn)操作;

以MSDN上官網(wǎng)的demo為例:

Paste_Image.png

首先可以創(chuàng)建一個空物體來管理相關(guān)的Gaze肃晚、Hand惋鸥、Gesture相關(guān)操作。在這里我們將對全息對象的管理腳本(也就是包含語音操作命令)也掛載在此空物體Manager上。

語音輸入 Voice Command


  1. 聲明一個KeywordRecognizer ;
  2. 聲明一個Dictionary<string, KeywordAction> keywordCollection用于保存關(guān)鍵字和對應(yīng)的關(guān)鍵字函數(shù);
  3. 在Start()函數(shù)中進(jìn)行添加語音關(guān)鍵字暮胧;
  4. 在Start()函數(shù)中由添加的關(guān)鍵字字典keywordCollection進(jìn)行對KeywordRecognizer 初始化并開始關(guān)鍵字識別;
  5. 對關(guān)鍵字對應(yīng)的操作函數(shù)進(jìn)行編寫。此案例中為MoveAstronautCommand函數(shù),主要實現(xiàn)將GestureManager.cs腳本中的不同的GestureRecognizer進(jìn)行切換;
**AstronautManager.cs**

using HoloToolkit;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;

public class AstronautManager : Singleton<AstronautManager>
{
    // KeywordRecognizer object.
    KeywordRecognizer keywordRecognizer;

    // Defines which function to call when a keyword is recognized.
    delegate void KeywordAction(PhraseRecognizedEventArgs args);
    Dictionary<string, KeywordAction> keywordCollection;

    void Start()
    {
        keywordCollection = new Dictionary<string, KeywordAction>();

        // Add keyword to start manipulation.
        keywordCollection.Add("Move Astronaut", MoveAstronautCommand); 

        // Initialize KeywordRecognizer with the previously added keywords.
        keywordRecognizer = new KeywordRecognizer(keywordCollection.Keys.ToArray());
        keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        keywordRecognizer.Start();
    }

    private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
    {
        KeywordAction keywordAction;

        if (keywordCollection.TryGetValue(args.text, out keywordAction))
        {
            keywordAction.Invoke(args);
        }
    }

    private void MoveAstronautCommand(PhraseRecognizedEventArgs args)
    {
        GestureManager.Instance.Transition(GestureManager.Instance.ManipulationRecognizer);
    }

    void OnDestroy()
    {
        keywordRecognizer.Dispose();
    }

}

手勢輸入 Gesture Input


手勢識別是HoloLens交互的重要輸入方法之一薯演。HoloLens提供了底層API和高層API,可以滿足不同的手勢定制需求帝嗡。底層API能夠獲取手的位置和速度信息巢寡,高層API則借助手勢識別器來識別預(yù)設(shè)的手勢(包括,單擊舆蝴、雙擊谦絮、長按菱皆、平移等等)。本部分主要是高級API使用挨稿,通過輸入源來識別手勢仇轻。

只需要很少的步驟就能使用GestureRecognizer集成手勢識別:

  1. 創(chuàng)建GestureRecognizer實例
  2. 注冊指定的手勢類型
  3. 訂閱手勢事件
  4. 開始手勢識別
**GestureManager.cs**

using HoloToolkit;
using UnityEngine;
using UnityEngine.VR.WSA.Input;

public class GestureManager : Singleton<GestureManager>
{
    // Manipulation gesture recognizer.
    public GestureRecognizer ManipulationRecognizer { get; private set; }

    // Currently active gesture recognizer.
    //此腳本原先有多個GestureManager ,因此由此代表當(dāng)前激活的GestureManager 
    public GestureRecognizer ActiveRecognizer { get; private set; }

    public bool IsManipulating { get; private set; }

    public Vector3 ManipulationPosition { get; private set; }

    void Awake()
    {
       
        // Instantiate the ManipulationRecognizer.
        ManipulationRecognizer = new GestureRecognizer();

        // Add the ManipulationTranslate GestureSetting to the ManipulationRecognizer's RecognizableGestures.
        ManipulationRecognizer.SetRecognizableGestures(
            GestureSettings.ManipulationTranslate);

        // Register for the Manipulation events on the ManipulationRecognizer.
        ManipulationRecognizer.ManipulationStartedEvent += ManipulationRecognizer_ManipulationStartedEvent;
        ManipulationRecognizer.ManipulationUpdatedEvent += ManipulationRecognizer_ManipulationUpdatedEvent;
        ManipulationRecognizer.ManipulationCompletedEvent += ManipulationRecognizer_ManipulationCompletedEvent;
        ManipulationRecognizer.ManipulationCanceledEvent += ManipulationRecognizer_ManipulationCanceledEvent;

    }

    void OnDestroy()
    {
        // Unregister the Manipulation events on the ManipulationRecognizer.
        ManipulationRecognizer.ManipulationStartedEvent -= ManipulationRecognizer_ManipulationStartedEvent;
        ManipulationRecognizer.ManipulationUpdatedEvent -= ManipulationRecognizer_ManipulationUpdatedEvent;
        ManipulationRecognizer.ManipulationCompletedEvent -= ManipulationRecognizer_ManipulationCompletedEvent;
        ManipulationRecognizer.ManipulationCanceledEvent -= ManipulationRecognizer_ManipulationCanceledEvent;
    }

    /// <summary>
    /// Transition to a new GestureRecognizer.
    /// </summary>
    /// <param name="newRecognizer">The GestureRecognizer to transition to.</param>
    public void Transition(GestureRecognizer newRecognizer)     //切換此腳本中的兩個不同功能的手勢識別
    {
        if (newRecognizer == null)      //切換手勢識別必須指定一個Recognizer
        {
            return;
        }

        if (ActiveRecognizer != null)
        {
            if (ActiveRecognizer == newRecognizer)      //如果切換的新的手勢識別是現(xiàn)在正激活的手勢識別奶甘,就說明沒必要切換篷店,直接return
            {
                return;
            }

            ActiveRecognizer.CancelGestures();
            ActiveRecognizer.StopCapturingGestures();
        }

        newRecognizer.StartCapturingGestures();          //此腳本中的兩個手勢識別都是由此語句激活開始的
        ActiveRecognizer = newRecognizer;
    }

    private void ManipulationRecognizer_ManipulationStartedEvent(InteractionSourceKind source, Vector3 position, Ray ray)
    {
        if (HandsManager.Instance.FocusedGameObject != null)
        {
            IsManipulating = true;

            ManipulationPosition = position;

            //HandsManager.Instance.FocusedGameObject追究到下面就是Gaze射線凝視的對象
            HandsManager.Instance.FocusedGameObject.SendMessageUpwards("PerformManipulationStart", position);
        }
    }

    private void ManipulationRecognizer_ManipulationUpdatedEvent(InteractionSourceKind source, Vector3 position, Ray ray)
    {
        if (HandsManager.Instance.FocusedGameObject != null)
        {
            IsManipulating = true;

            ManipulationPosition = position;

            HandsManager.Instance.FocusedGameObject.SendMessageUpwards("PerformManipulationUpdate", position);
        }
    }

    private void ManipulationRecognizer_ManipulationCompletedEvent(InteractionSourceKind source, Vector3 position, Ray ray)
    {
        IsManipulating = false;
    }

    private void ManipulationRecognizer_ManipulationCanceledEvent(InteractionSourceKind source, Vector3 position, Ray ray)
    {
        IsManipulating = false;
    }

}

此腳本中在訂閱的手勢事件中對Gaze凝視的對象進(jìn)行SendMessageUpwards發(fā)送消息:

  • ManipulationRecognizer_ManipulationStartedEvent中對Gaze凝視的對象發(fā)送的是PerformManipulationStart函數(shù);
  • ManipulationRecognizer_ManipulationUpdatedEvent中對Gaze凝視的對象發(fā)送的是PerformManipulationUpdate函數(shù)臭家;

Holographic Action 對象函數(shù)動作


**GestureAction**

using UnityEngine;

/// <summary>
/// GestureAction performs custom actions based on 
/// which gesture is being performed.
/// </summary>
public class GestureAction : MonoBehaviour
{
    private Vector3 manipulationPreviousPosition;

    void PerformManipulationStart(Vector3 position)
    {
        manipulationPreviousPosition = position;
    }

    void PerformManipulationUpdate(Vector3 position)
    {
        if (GestureManager.Instance.IsManipulating)
        {
            Vector3 moveVector = Vector3.zero;
            //Calculate the moveVector as position - manipulationPreviousPosition.
            moveVector = position - manipulationPreviousPosition;

            //Update the manipulationPreviousPosition with the current position.
            manipulationPreviousPosition = position;

            // Increment this transform's position by the moveVector.
            transform.position += moveVector;
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疲陕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钉赁,更是在濱河造成了極大的恐慌蹄殃,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件你踩,死亡現(xiàn)場離奇詭異诅岩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)带膜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門吩谦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人膝藕,你說我怎么就攤上這事式廷。” “怎么了芭挽?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵滑废,是天一觀的道長。 經(jīng)常有香客問我袜爪,道長蠕趁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任饿敲,我火速辦了婚禮妻导,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怀各。我一直安慰自己倔韭,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布瓢对。 她就那樣靜靜地躺著寿酌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪硕蛹。 梳的紋絲不亂的頭發(fā)上醇疼,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天硕并,我揣著相機(jī)與錄音,去河邊找鬼秧荆。 笑死倔毙,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乙濒。 我是一名探鬼主播陕赃,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颁股!你這毒婦竟也來了么库?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甘有,失蹤者是張志新(化名)和其女友劉穎诉儒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亏掀,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡忱反,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了幌氮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缭受。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖该互,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情韭畸,我是刑警寧澤宇智,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胰丁,受9級特大地震影響随橘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锦庸,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一机蔗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧甘萧,春花似錦萝嘁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怪得,卻和暖如春咱枉,著一層夾襖步出監(jiān)牢的瞬間卑硫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工蚕断, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留欢伏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓亿乳,卻偏偏與公主長得像硝拧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子风皿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容