HTC Vive互動開發——凝視效果實現
阿新 • • 發佈:2019-01-09
凝視效果相關:
- 通過跟蹤頭部移動,設定一個代表游標的準星,當停留在某處足夠長的時間之後,激發選中邏輯。
- 類似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);
}
}
}