SteamVR学习(一)V1.2.3

实现与物体的交互

以抓取物体为例

1.获取手柄引用

用GetComponent获取手柄引用作为跟踪物体

2.手柄与物体的碰撞检测

手柄需要添加colider组件并设置为trigger,碰撞物体需要添加rigidbody和colider组件

3.获取按钮事件

用device判断手柄按键是否按下

4.抓取过程:物体作为手柄的transform子物体,失去rigidbody相关属性

5.松开过程:物体的parent为空,重新获取自身rigidbody相关属性

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InteractWithBox : MonoBehaviour
{
    private SteamVR_TrackedObject trackedObject;
    private SteamVR_Controller.Device device;
    private GameObject interactBox;
    // Start is called before the first frame update
    void Start()
    {
        //获取手柄引用
        trackedObject = GetComponent();
        device = SteamVR_Controller.Input((int)trackedObject.index);
    }
    // Update is called once per frame
    void Update()
    {
        if(device != null)
        {
            return;
        }
        if(device.GetAxis().x!=0||device.GetAxis().y!=0)
        {
            Debug.Log(device.GetAxis().x + " | " + device.GetAxis().y);
        }

        //判断Trigger按下
        if(device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger))
        {
            if(interactBox != null)
            {
                interactBox.transform.parent = transform;
                Rigidbody rig = interactBox.GetComponent();
                rig.useGravity = false;//取消重力
                rig.isKinematic = true;//开启动力学,不受自身影响,只跟随手柄运动
            }
        }

        //判断Trigger松开
        if(device.GetPressUp(SteamVR_Controller.ButtonMask.Trigger))
        {
            interactBox.transform.parent = null;
            Rigidbody rig = interactBox.GetComponent();
            rig.useGravity = true;
            rig.isKinematic = false;
        }
    }

    //碰撞体进入处理函数
    private void OnTriggerEnter(Collider other)
    {
        interactBox = other.transform.gameObject;
    }

    //碰撞体脱离处理函数
    private void OnTriggerExit(Collider other)
    {
        interactBox = null;
    }

    //碰撞体碰撞过程中手柄震动
    private void OnTriggerStay(Collider other)
    {
        device.TriggerHapticPulse(700);
    }
}
实现凝视效果

以凝视一个Button为例子

1.放置场景,添加Canvas时需要将Render Mode设置为World Space,在Canvas下添加Botton,并添加box colider组件

2.添加准星GUI,将Texture Type(贴图类型)设置为Sprite转化为image显示

3.将准星设置为Camera(eye)的子物体,Camera(eye)—Canvas—image(Reticle)—copy image(Background),把准星的image拖到Source Image中

4.通过准星的环形填充来充当倒计时的功能,在Image组件的ImageType下可以选择Filled填充方式,Fill Amount从0到1的变化就是环形填充的过程,将背景的透明度降低

5.通过代码脚本实现检测视线凝视,并添加准星贴图,解决近大远小问题

6.用shader中的UIOverlay添加到一个Material材质中拖到准星和背景的image上解决准星贴图在游戏物体上闪烁问题

7.再写一个脚本用于响应凝视的物体

8.用不同的tag区分UI和物体,实现不同的响应,把脚本放到需要凝视响应的物体上

9.为物体添加高亮显示,先给物体一个Material,再凝视过程中替换Material

VRGazeItem.cs,在脚本中添加mat变量

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class VRGazeItem : MonoBehaviour
{

    public Material highlightMat;//高亮材质
    public Material normalMat;//正常材质
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

    //视线移入处理函数
    public void OnGazeIn()
    {
        if (gameObject.tag == "GazeUI")
        {
            //传递一个视线移入的消息,类似鼠标移入
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerEnterHandler);
        }
        else if (gameObject.tag =="GazeObj")
        {
            gameObject.GetComponent().material = highlightMat;
        }
    }

    //视线移出处理函数
    public void OnGazeOut()
    {
        if (gameObject.tag == "GazeUI")
        {
            //传递一个视线移出的消息,类似鼠标移出
            ExecuteEvents.Execute(gameObject, new PointerEventData(EventSystem.current), ExecuteEvents.pointerExitHandler);
        }
        else if (gameObject.tag == "GazeObj")
        {
            gameObject.gameObject.GetComponent().material = normalMat;
        }
    }
    
    //视线停留处理函数
    public void OnGazeFire(RaycastHit hit)
    {
        if (gameObject.tag == "GazeUI")
        {
            //填写凝视时实现的效果
        }
        else if (gameObject.tag == "GazeObj")
        {
            //在击中点施加一个力
            gameObject.GetComponent().AddForceAtPosition(hit.point.normalized * 100, hit.point);
        }
    }
}

GazeController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GazeController : MonoBehaviour
{
    public Canvas reticleCanvas;
    public Image reticleImage;
    private GameObject target;
    private Vector3 originPos;//初始位置
    private Vector3 originScale;//初始缩放
    private float countDownTime = 3;//倒计时时间
    private float nowTime = 0;//当前时间
    // Start is called before the first frame update
    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))//检测视线击中点
        {
            //将准星置于击中点的位置
            reticleCanvas.transform.position = hit.point;//将准星位置放置在击中点
            reticleCanvas.transform.localScale = originScale * hit.distance;//解决准星近大远小的问题
            reticleCanvas.transform.forward = hit.normal;//解决准星与摄像机平面垂直的问题,让准星的forward方向与击中点的法线方向保持一致

            if(hit.transform.gameObject != target)
            {
                //先移出原来有凝视效果的物体
                if(target != null)
                {
                    VRGazeItem oldItem = target.GetComponent();
                    if(oldItem)
                    {
                        oldItem.OnGazeOut();
                    }
                }
                //视线初次进入的处理
                target = hit.transform.gameObject;
                VRGazeItem newItem = target.GetComponent();//记录有凝视效果的物体
                if(newItem)
                {
                    newItem.OnGazeIn();
                }
            }
            else//视线在此处停留
            {
                nowTime += Time.deltaTime;//累加得到当前时间
                if((countDownTime - nowTime) > 0)
                {
                    reticleImage.fillAmount = nowTime / countDownTime;//未达到激活条件进行倒计时,环形填充动画
                }
                else
                {
                    //达到激活条件
                    VRGazeItem gazeFireItem = target.GetComponent();
                    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;
        }
    }
}

你可能感兴趣的:(学习,unity,vr)