HTC VIVE丨2. 实现凝视效果

本节目标

通过跟踪头部运动,设置一个代表光标的准星,碰触物体后改变位置和朝向,贴近被凝视物体。物体a被凝视一定时间后消失,物体b被凝视一定时间后被击中

实现原理

1、基于射线原理,Update—>Raycast

2、准星或十字线设置为相机子物体,等待操作的动画为圆环逐渐填满

3、被凝视的可是UI,也可是3D物体

4、击中物体一段时间后,可完成相关操作,如消失、缩放、材质变换等

5、元素一般分三种状态:准星进入、准星停留、准星退出(Collider)

 

实现步骤

1、Canvas

建立Canvas,Render Mode设置为World Space,缩放合适比例0.003

2、拖入[CameraRig]

 

3、UI和3D物体设置

Cube添加Rigidbody

UI添加BoxCollider,并调整合适大小

4、添加准星

添加准星,将准星Canvas放在[CameraRig]——Camera(head)——Camera(eye)下,Render Mode为World Space,实现如下效果

5、编写GazeController代码

编写代码GazeController,实现击中物体时UI的变化,挂载到Camera(eye)上

using UnityEngine;
using UnityEngine.UI;

public class GazeController : MonoBehaviour {

    //设置canvas位置、朝向
    public Canvas reticleCanvas;
    //设置准星填充效果等
    public Image reticleImage;
    //被击中的物体
    private GameObject target;
    //准星初始位置
    private Vector3 originPos;
    //准星初始缩放:当看远处时变大,看近处时变小
    private Vector3 originScale;
    //倒计时时间
    private float countDownTime = 3;
    //逝去时间(当前时间)
    private float nowTime = 0;

	void Start () {
        reticleImage.fillAmount = 0;
        originPos = reticleCanvas.transform.localPosition;
        originScale = reticleCanvas.transform.localScale;
    }

	void Update () {
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit hit;
        if(Physics.Raycast(ray,out hit, 100))
        {
            //将准星放到碰撞点上
            reticleCanvas.transform.position = hit.point;
            reticleCanvas.transform.localScale = originScale * hit.distance;
            //让准星的法线方向和被击中的物体法线方向一致
            reticleCanvas.transform.forward = hit.normal;

            //视线初次进入处理
            if (hit.transform.gameObject != target)
            {
                //视线凝视的上一个物体,完成退出操作
                if (target != null)
                {
                    VRGazeItem oldItem = target.GetComponent<VRGazeItem>();
                    if (oldItem)
                    {
                        oldItem.OnGazeOut();
                    }
                }

                //视线正处于的物体
                target = hit.transform.gameObject;
                VRGazeItem newItem = target.GetComponent<VRGazeItem>();
                if (newItem)
                {
                    newItem.OnGazeIn();
                }
            }
            //视线长久停留处理
            else
            {
                nowTime += Time.deltaTime;

                //正在读秒时间
                if (countDownTime > nowTime)
                {
                    reticleImage.fillAmount = nowTime / countDownTime;
                }
                //达到激活条件
                else
                {
                    VRGazeItem gazeFireItem = target.GetComponent<VRGazeItem>();
                    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;
        }
	}
}

6、编写VRGazeItem代码

该代码挂载到被击中的物体上,实现该物体被击中时的效果

与该代码配套的是两个material:HighlightMat和NormalMat,实现3D物体被凝视时材质的变换

项目一开始我们应将NormalMat挂载到3D物体上

using UnityEngine;
using UnityEngine.EventSystems;

public class VRGazeItem : MonoBehaviour {

    public Material highlightMat;
    public Material normalMat;

    //视线进入
    public void OnGazeIn()
    {
        if (gameObject.tag == "gazeUI")
        {
            //VR场景中UI实现鼠标移入效果
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerEnterHandler);
        }else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Renderer>().material = highlightMat;
        }
    }

    //视线移出
    public void OnGazeOut()
    {
        if (gameObject.tag == "gazeUI")
        {
            //VR场景中UI实现鼠标移出入效果
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerExitHandler);
        }
        else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Renderer>().material = normalMat;
        }
    }

    //凝视处理函数
    public void OnGazeFire(RaycastHit hit)
    {
        if (gameObject.tag == "gazeUI")
        {
            gameObject.SetActive(false);
        }
        else if (gameObject.tag == "gazeObj")
        {
            gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hit.point.normalized * 100, hit.point);
        }
    }
}

7、为准星添加shader

该名为UIOverlay的shader解决了准星面片贴近物体表面闪烁问题

1)、新建Material,选择UI——Overlay,即在此Material上添加了此shader

2)、将Material附加到UI上便可

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "UI/Overlay"
{
	Properties
	{
		[PerRendererData] _MainTex ("Font Texture", 2D) = "white" {}

		_Color("Tint", Color) = (1,1,1,1)

		_StencilComp ("Stencil Comparison", Float) = 8
		_Stencil ("Stencil ID", Float) = 0
		_StencilOp ("Stencil Operation", Float) = 0
		_StencilWriteMask ("Stencil Write Mask", Float) = 255
		_StencilReadMask ("Stencil Read Mask", Float) = 255

		_ColorMask ("Color Mask", Float) = 15
	}
	
	SubShader
	{
		LOD 100

		Tags
		{
			"Queue" = "Transparent"
			"IgnoreProjector" = "True"
			"RenderType" = "Transparent"
			"PreviewType"="Plane"
			"CanUseSpriteAtlas" = "True"
		}

		Stencil
		{
			Ref [_Stencil]
			Comp [_StencilComp]
			Pass [_StencilOp] 
			ReadMask [_StencilReadMask]
			WriteMask [_StencilWriteMask]
		}
		
		Cull Off
		Lighting Off
		ZWrite Off
		ZTest Always
		Offset -1, -1
		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask [_ColorMask]

		Pass
		{
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "UnityCG.cginc"
				#include "UnityUI.cginc"

				struct appdata_t
				{
					float4 vertex : POSITION;
					float2 texcoord : TEXCOORD0;
					float4 color : COLOR;
				};
	
				struct v2f
				{
					float4 vertex : SV_POSITION;
					half2 texcoord : TEXCOORD0;
					fixed4 color : COLOR;
				};
	
				sampler2D _MainTex;
				float4 _MainTex_ST;
				fixed4 _Color;
				fixed4 _TextureSampleAdd;
				
				v2f vert (appdata_t v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.color = v.color * _Color;
#ifdef UNITY_HALF_TEXEL_OFFSET
					o.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif

					return o;
				}
				
				fixed4 frag (v2f i) : SV_Target
				{
					fixed4 col = (tex2D(_MainTex, i.texcoord) + _TextureSampleAdd) * i.color;
					clip (col.a - 0.01);
					return col;
				}
			ENDCG
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_38239050/article/details/81229751