1. 程式人生 > >HTC Vive互動開發——凝視效果實現

HTC Vive互動開發——凝視效果實現

凝視效果相關:

  • 通過跟蹤頭部移動,設定一個代表游標的準星,當停留在某處足夠長的時間之後,激發選中邏輯。
  • 類似Kinect自然語言互動。
  • 多用於移動VR,如Cardboard、GearVR等。
  • VIVE平臺使用凝視效果可以增強使用者體驗。

凝視效果實現原理

 1. 基於射線原理,通過Raycast判斷擊中的物體,在Update裡面進行邏輯判斷;
 2. 準星或者十字線基於UGUI,設定為相機的子物體,等待操作過程一般為圓環逐漸填充動畫或者進度條動畫;
 3. 被凝視的物體可以是動畫也可以是UI;
 4. 如果在一段時間內擊中的物體是同一個物體,則認為該元素被選中,在此邏輯內撰寫相應處理函式,如消失、變換材質、移動、縮放等;
 5. 元素一般分為三個狀態響應:準星進入、準星退出、準星停留時間到。

凝視效果時間示例

  • 使元素響應視線進入、退出;
  • 凝視按鈕一段時間後實現點選,使其消失;
  • 凝視一個cube,經過一段時間後用眼神擊中它;
  • 是準星垂直於物體表面。

這裡寫圖片描述
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GazeController : MonoBehaviour {
    /// <summary>
    /// 準星容器
    /// </summary>
    public
Canvas reticleCanvas; /// <summary> /// 準星圖片 /// </summary> public Image reticleImage; /// <summary> /// 選中的目標 /// </summary> private GameObject target; private Vector3 originPos; /// <summary> /// 初始縮放 /// </summary> private Vector3 originScale; ///
<summary>
/// 倒計時時間 /// </summary> private float countDownTime=2; /// <summary> /// 但當前時間 /// </summary> private float nowTime=0; // Use this for initialization void Start () { reticleImage.fillAmount = 0; originPos = reticleCanvas.transform.localPosition; originScale = reticleCanvas.transform.localScale; } // Update is called once per frame void Update () { Ray ray = new Ray(transform.position, transform.forward); RaycastHit hit; if (Physics.Raycast(ray,out hit,150)) { reticleCanvas.transform.position = hit.point; reticleCanvas.transform.localScale = originScale * hit.distance; //準星與擊中點的法線方向保持一致 reticleCanvas.transform.forward = hit.normal; if (hit.transform.gameObject!=target) { if (target!=null) { GazeItem oldItem = target.GetComponent<GazeItem>(); if (oldItem) { oldItem.OnGazeOut(); } } //視線初次進入的處理 target = hit.transform.gameObject; GazeItem newItem = target.GetComponent<GazeItem>(); if (newItem) { newItem.OnGazeIn(); } } // 視線停留 else { nowTime += Time.deltaTime; if ((countDownTime-nowTime)>0) { //尚未達到啟用條件 reticleImage.fillAmount = nowTime / countDownTime; } else { //達到啟用條件 GazeItem gazeFireItem = target.GetComponent<GazeItem>(); if (gazeFireItem) { gazeFireItem.OnGazeFire(hit); } nowTime = 0; } } } else { reticleCanvas.transform.localPosition = originPos; reticleCanvas.transform.localScale = originScale; reticleCanvas.transform.forward = Camera.main.transform.forward; reticleImage.fillAmount = 0; } } }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class GazeItem : MonoBehaviour {

    public Material highLightMat;
    public Material normalMat;
    // Use this for initialization
    void Start ()
    {

    }

    // Update is called once per frame
    void Update ()
    {

    }
    /// <summary>
    /// 視線移入處理函式
    /// </summary>
    public void OnGazeIn()
    {
        if (gameObject.tag=="GazeUI")
        {
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerEnterHandler);
        }
        else if (gameObject.tag=="GazeObject")
        {
            gameObject.GetComponent<Renderer>().material = highLightMat;
        }
    }
    /// <summary>
    /// 視線移出處理函式
    /// </summary>
    public void OnGazeOut()
    {
        if (gameObject.tag == "GazeUI")
        {
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerExitHandler);
        }
        else if (gameObject.tag == "GazeObject")
        {
            gameObject.GetComponent<Renderer>().material = normalMat;
        }
    }
    /// <summary>
    /// 視線凝視處理函式
    /// </summary>
    public void OnGazeFire(RaycastHit hit)
    {
        if (gameObject.tag == "GazeUI")
        {
            gameObject.SetActive(false);
        }
        else if (gameObject.tag == "GazeObject")
        {
            gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hit.point.normalized*150,hit.point);
        }
    }
}