SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)

SteamVR脚本功能分析

版本

作者

参与者

完成日期

备注

SteamVR_Functions_V01_1.0

严立钻

 

2019.01.16

 

 

 

 

 

 

 

##《SteamVR脚本功能分析》发布说明:

++++“SteamVR脚本功能分析”:是对SteamVR中脚本功能的介绍;(2019年度的重点技术突破点确立为:“SteamVR”,所以对SteamVR脚本功能的熟悉是非常关键的!)

++++“SteamVR脚本功能分析”:定位在一个介绍类说明,是立钻哥哥在探索SteamVR过程中的总结

++++OpenVR的Github:https://github.com/ValveSoftware/openvr

++++OpenXR官网:https://www.khronos.org/openxr

++++SteamVR的Github:https://github.com/ValveSoftware/steamvr_unity_plugin

 

 

##《SteamVR脚本功能分析》目录

#第一篇:网络杂谈

#第二篇:视频杂谈

#第三篇:文档杂谈

#第四篇:项目杂谈

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第1张图片

 

 

 

 

 

#第一篇:网络杂谈篇

#第一篇:网络杂谈篇

#第一篇:网络杂谈篇

++++立钻哥哥:“网络杂谈篇”是对SteamVR脚本知识的全网搜索,利用“SteamVR脚本”

++++A.1、SteamVR插件脚本详解

++++A.2、

++++A.3、

++++A.4、

++++A.5、

 

++SteamVR场景应用

++++立钻哥哥:开始研究学习SteamVR脚本功能前,我们先来看看SteamVR的作品吧:

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第2张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第3张图片

 

 

 

 

 

 

 

##A.1、SteamVR插件脚本详解

##A.1、SteamVR插件脚本详解

++A.1、SteamVR插件脚本详解

++++立钻哥哥:是不是一脸懵逼呀,好吧,从什么地方开始呢?

++++先附上一张SteamVR的目录结构,找一下感觉!!!

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第4张图片

 

++SteamVR目录结构

++++立钻哥哥:看了目录结构后,是不是还是没有方向?

++++那好,我们来Xmind分析一下:

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第5张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第6张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第7张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第8张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第9张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第10张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第11张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第12张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第13张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第14张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第15张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第16张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第17张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第18张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第19张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第20张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第21张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第22张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第23张图片

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第24张图片

 

 

 

 

++SteamVR/Scripts/下脚本功能说明

++++立钻哥哥:好吧,看完SteamVR的整体目录结构后,我们再来看一下“/Scripts/”目录吧;

++++还是利用Xmind分析一下:

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第25张图片

 

 

 

 

++SteamVR/Extra/下脚本功能说明

++++立钻哥哥:好吧,Xmind分析了一下“/Scripts/”,那我们接着看一下“SteamVR/Extras/”目录下的脚本吧;

++++还是利用Xmind分析一下:

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第26张图片

 

 

++[SteamVR/Extras/SteamVR_GazeTracker.cs]

++++立钻哥哥:\Assets\SteamVR\Extras\SteamVR_GazeTracker.cs

++++[SteamVR_GazeTracker.cs]:提供凝视时的事件

using UnityEngine;

using System.Collections;

 

namespace Valve.VR.Extras{

    public class SteamVR_GazeTracker : MonoBehaviour{

        public bool isInGaze = false;    //当前是否处于gaze状态

 

        //进入gaze状态回调,使用者可以通过代码添加自己的事件处理方法

        public event GazeEventHandler GazeOn;

        public event GazeEventHandler GazeOff;    //离开gaze状态回调

 

        //定义的进入gaze与离开gaze的距离范围

        public float gazeInCutoff = 0.15f;

        public float gazeOutCutoff = 0.4f;

 

        protected Transform hmdTrackedObject = null;    //头显的transform对象

 

        public virtual void OnGazeOn(GazeEventArgs gazeEventArgs){}

        public virtual void OnGazeOff(GazeEventArgs gazeEventArgs){}

 

        protected virtual void Update(){}

 

        public struct GazeEventArgs{

            public float distance;

        }

 

        public delegate void GazeEventHandler(object sender, GazeEventArgs gazeEventArgs);

 

    }    //立钻哥哥:public class SteamVR_GazeTracker:MonoBehaviour{}

}    //立钻哥哥:namespace Valve.VR.Extras{}

++++这个脚本的作用是判断当前物体是否被用户(头显)所注视,进入注视和离开注视都会有回调

++++处于注视状态的物体与实际注视点的距离范围定义为小于0.15米,而离开注视状态的距离范围为大于0.4

++++之所以有一个大概的范围,并且使用了一个平面来相交,是因为注视这个动作时比较粗略的,玩家比较难能精确注视

++++Gaze回调的事件结构体,只有一个参数,即距离,表示凝视点与物体(中心)的距离

 

++++好吧,那我们好好看看这个脚本具体是怎么样的吧:

using UnityEngine;

using System.Collections;

 

namespace Valve.VR.Extras{

    public class SteamVR_GazeTracker : MonoBehaviour{

        public bool isInGaze = false;    //当前是否处于gaze状态

 

        //进入gaze状态回调,使用者可以通过代码添加自己的事件处理方法

        public event GazeEventHandler GazeOn;

        public event GazeEventHandler GazeOff;    //离开gaze状态回调

 

        //定义的进入gaze与离开gaze的距离范围

        public float gazeInCutoff = 0.15f;

        public float gazeOutCutoff = 0.4f;

 

        protected Transform hmdTrackedObject = null;    //头显的transform对象

 

        public virtual void OnGazeOn(GazeEventArgs gazeEventArgs){

            //如果有注册GazeOn回调,则调用它

            if(null != GazeOn){

                GazeOn(this, gazeEventArgs);

            }

        }

 

        public virtual void OnGazeOff(GazeEventArgs gazeEventArgs){

            //如果有注册GazeOff回调,则调用它

            if(null != GazeOff){

                GazeOff(this, gazeEventArgs);

            }

        }

 

        protected virtual void Update(){

            //If we havent set up hmdTrackedObject find what the user is looking at

            if(null == hmdTrackedObject){

                //立钻哥哥:首次调用会去查找头显,方法是查找所有SteamVR_TrackedObject对象;所有的跟踪对象(比如头显、手柄、基站)都是SteamVR_TrackedObject对象(相应的对象上附加了SteamVR_TrackedObject脚本)

                SteamVR_TrackedObject[] trackedObjects = FindObjectOfType<SteamVR_TrackedObject>();

                foreach(SteamVR_TrackedObject tracked in trackedObject){

                    if(tracked.index == SteamVR_TrackedObject.EIndex.Hmd){

                        //立钻哥哥:找到头显设备,取其transform对象;头显设备的索引是0号索引

                        hmdTrackedObject = tracked.transform;

                        break;

                     }

                }

            }    //立钻哥哥:if(null == hmdTrackedObject){}

 

            if(hmdTrackedObject){

                //立钻哥哥:构造一条从头显正方向的射线

                Ray ray = new Ray(hmdTrackedObject.position, hmdTrackedObject.forward);

 

                //立钻哥哥:构造一个头显正方向,在当前物体位置的平面

                Plane plane = new Plane(hmdTrackedObject.forward, transform.position);

    

                //立钻哥哥:射线与物体平面正向相交,返回的enter为沿射线的距离;如果不相交,或者反向相交,则下面的Raycast返回false;

                float enter = 0.0f;

                if(plane.Raycast(ray, out enter)){

                    //立钻哥哥:intersect为射线与物体平面在三维空间的交点

                    Vector3 intersect = hmdTrackedObject.position + hmdTrackedObject.forward * enter;

        

                    //立钻哥哥:计算空间两点的距离,即物体当前位置与交点的距离

                    float dist = Vector3.Distance(intersect, transform.position);

                    //Debug.Log(立钻哥哥:Gaze dist = + dist);

 

                    if(dist < gazeInCutoff && !isInGaze){

                        //立钻哥哥:当前物体与凝视点的距离小于0.15米,则认为进入gaze状态

                        isInGaze = true;

                        GazeEventArgs gazeEventArgs;

                        gazeEventArgs.distance = dist;

                        OnGazeOn(gazeEventArgs);

 

                    }else if(dist >= gazeOutCutoff && isInGaze){

                        //立钻哥哥:当前物体与凝视点的距离超过0.4米,则认为离开gaze状态

                        isInGaze = false;

                        GazeEventArgs gazeEventArgs;

                        gazeEventArgs.distance = dist;

                        OnGazeOff(gazeEventArgs);

                    }

                }    //立钻哥哥:if(plane.Raycast(ray, out enter)){}

            }    //立钻哥哥:if(hmdTrackedObject){}

        }    //立钻哥哥:protected virtual void Update(){}

 

        public struct GazeEventArgs{

            public float distance;

        }

 

        public delegate void GazeEventHandler(object sender, GazeEventArgs gazeEventArgs);

 

    }    //立钻哥哥:public class SteamVR_GazeTracker:MonoBehaviour{}

}    //立钻哥哥:namespace Valve.VR.Extras{}

++++[GazeTracker]是通过头显的正视方向与物体相交来计算交点的

 

 

 

++SteamVR_LaserPointer.cs(??已弃用??)

++++立钻哥哥:这个脚本(SteamVR_LaserPointer.cs)在新版本中已弃用,可作为一个参考

public class SteamVR_LaserPointer : MonoBehaviour{

    public bool active = true;

    public Color color;    //激光的颜色

 

    //激光束的粗细(创建了一个立方体,按下面的scale, x, y是0.002,z是100,就能看到是一条很长的细线了)

    public float thickness = 0.002f;

 

    public GameObject holder;    //一个空的GameObject, 用于做激光束的parent

 

    //激光束本身,是用一个立方体来长来模拟的(立钻哥哥:为啥不用圆柱体?显然立方体要比圆柱体渲染简单得多,在很细的情况下,用立方体是明智的选择)

    public GameObject pointer;

 

    bool isActive = false;    //用来判断是否第一次调用

 

    //这个是暴露在Inspector中的属性,用于控制是否给激光束(长方体)添加刚体;光线本身是没有重量的,没有必要添加刚体吧? 所以这里缺省是false;

    public bool addRigidBody = false;

 

    public Transform reference;

 

    //用于触发激光命中和离开事件

    public Event PointerEventHandler PointerIn;

    public Event PointerEventHandler PointerOut;

 

    //上次激光命中的物体的transform对象,用于判断是否命中同一个物体

    Transform previousContact = null;

 

    void Start(){}

 

    public virtual void OnPointerIn(PointerEventArgs e){}

    public virtual void OnPointerOut(PointerEventArgs e){}

 

    void Update(){}

 

}    //立钻哥哥:public class SteamVR_LaserPointer:MonoBehaviour{}

 

public struct PointerEventArgs{}

 

public delegate void PointerEventHandler(object sender, PointerEventArgs e);

 

++++[LaserPointer]通过所谓的激光束来与物体相交的;激光束就是手柄指向的方向,可以在游戏里面把这个方向渲染出一条激光束出来,特别是在通过手柄进行菜单的UI操作的时候

 

++++立钻哥哥:好吧,接下来我们来好好看一下这个脚本吧:

public class SteamVR_LaserPointer : MonoBehaviour{

    public bool active = true;

    public Color color;    //激光的颜色

 

    //激光束的粗细(创建了一个立方体,按下面的scale, x, y是0.002,z是100,就能看到是一条很长的细线了)

    public float thickness = 0.002f;

 

    public GameObject holder;    //一个空的GameObject, 用于做激光束的parent

 

    //激光束本身,是用一个立方体来长来模拟的(立钻哥哥:为啥不用圆柱体?显然立方体要比圆柱体渲染简单得多,在很细的情况下,用立方体是明智的选择)

    public GameObject pointer;

 

    bool isActive = false;    //用来判断是否第一次调用

 

    //这个是暴露在Inspector中的属性,用于控制是否给激光束(长方体)添加刚体;光线本身是没有重量的,没有必要添加刚体吧? 所以这里缺省是false;

    public bool addRigidBody = false;

 

    public Transform reference;

 

    //用于触发激光命中和离开事件

    public Event PointerEventHandler PointerIn;

    public Event PointerEventHandler PointerOut;

 

    //上次激光命中的物体的transform对象,用于判断是否命中同一个物体

    Transform previousContact = null;

 

    void Start(){

        //在脚本被加载的时候,做一些初始化

 

        //首先创建一个holder(即激光束的父物体)

        holder = new GameObject();

 

        //holder的transform的parent设为当前脚本所在的物体(通常这个脚本会加到控制器手柄上面)

        holder.transform.parent = this.transform;

 

        //位置设在0点(本地坐标系,相对于父亲)

        holder.transform.localPosition = Vector3.zero;

        holder.transform.localRotation = Quaternion.identity;

 

        //创建激光束,用长方体模拟

        pointer = GameObject.CreatePrimitive(PrimitiveType.Cube);

 

        //将父亲设为上面的holder

        pointer.transform.parent = holder.transform;

 

        //设置localScale为(0.002, 0.002, 100),看起来就是一条很长的线

        pointer.transform.localScale = new Vector3(thickness, thickness, 100f);

 

        //立钻哥哥:位置设在父亲的(0, 0, 50)位置,因为对于立方体(长方体),其中心在立方体中心,因为上面被放大到了100倍,那移动位置到(0, 0, 50)可以让激光束的起点为父亲;

        pointer.transform.localPosition = new Vector3(0f, 0f, 50f);

        pointer.transform.localRotation = Quaternion.identity;

 

        //立钻哥哥:如果指定了addRigidBody为true,则为激光束添加一个刚体,对应的Collider则只设为触发器(不会执行碰撞,但会进入代码);否则,会把Collider销毁掉,也就是不需要Collider;

        BoxCollider collider = pointer.GetComponent<BoxCollider>();

        if(addRigidBody){

            if(collider){

                collider.isTrigger = true;

            }

            Rigidbody rigidbody = pointer.AddComponent<Rigidbody>();

            rigidbody.isKinematic = true;

 

        }else{

            if(collider){

                Object.Destroy(collider);

            }

        }

 

        //新建纯色材质并添加到MeshRender中;Color值通过Inspector设置;

        Material newMaterial = new Material(Shader.Find(Unlit/Color));

        newMaterial.SetColor(_Color, color);

        pointer.GetComponent<MeshRender>().material = newMaterial;

    }

 

    public virtual void OnPointerIn(PointerEventArgs e){

        //立钻哥哥:回调激光命中 委托

        if(null != PointerIn){

            PointerIn(this, e);

        }

    }

 

    public virtual void OnPointerOut(PointerEventArgs e){

        //立钻哥哥:回调激光不在命中 委托

        if(null != PointerOut){

            PointerOut(this, e);

        }

    }

 

    void Update(){

        if(!isActive){

            //立钻哥哥:第一次调用时将holder设为active(当前物体transform的第一个child就是holder)

            isActive = true;

            this.transform.GetChild(0).gameObject.SetActive(true);

        }

 

        //命中物体(或者说激光束)的最远距离记为100米

        float dist = 100f;

 

        //当前物体(手柄上)上还要挂一个SteamVR_TrackedController脚本

        SteamVR_TrackedController controller = GetComponent<SteamVR_TrackedController>();

 

        //构造一条射线

        Ray raycast = new Ray(transform.position, transform.forward);

        RaycastHit hit;

 

        //计算射线命中的场景中的物体

        bool bHit = Physics.Raycast(raycast, out hit);

 

        if(previousContact && previousContact != hit.transform){

            //立钻哥哥:如果之前已经有一个命中的物体,而当前命中的物体发生了变化,那么说明前一个命中的物体就要收到一个不再命中的通知

            PointerEventArgs args = new PointerEventArgs();

            if(null != controller){

                args.controllerIndex = controller.controllerIndex;

            }

            args.distance = 0f;

            args.flags = 0;

            args.target = previousContact;

            OnPointerOut(args);

            previousContact = null;

        }    //立钻哥哥:if(previousContact && previousContact != hit.transform){}

 

        if(bHit && previousContact != hit.transform){

            //通知命中新的物体

            PointerEventArgs argsIn = new PointerEventArgs();

            if(null != controller){

                argsIn.controllerIndex = controller.controllerIndex;

            }

 

            //hit.distance为射线原点到命中点的距离

            argsIn.distance = hit.distance;

            argsIn.flags = 0;

 

            //target记录的是命中物体的transform

            argsIn.target = hit.transform;

            OnPointerIn(argsIn);

 

            //记录上一次命中的物体的transform

            previousContact = hit.transform;

 

        }    //立钻哥哥:if(bHit && previousContact != hit.transform){}

 

        if(!bHit){

            previousContact = null;

        }

 

        if(bHit && hit.distance < 100f){

            //如果命中物体距离小于100,则记录下来,否则最远就是100米

            dist = hit.distance;

        }

 

        if(controller != null && controller.triggerPressed){

            //立钻哥哥:当按下扳机键时,将光束的粗细增大5倍,同时长度会设为dist,这样看起来光束就会到命中点截止,不会穿透物体

            pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist);

 

        }else{

            //按下扳机或者当前控制器没有添加SteamVR_TrackedController时,显示原始粗细的光束

            pointer.transform.localScale = new Vector3(thickness, thickness, dist);

        }

 

        //立钻哥哥:光束的位置总是设在光束长度的一半的位置,使得光束看起来总是从手柄发出来的

        pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f);

 

    }    //立钻哥哥:void Update(){}

 

}    //立钻哥哥:public class SteamVR_LaserPointer:MonoBehaviour{}

 

public struct PointerEventArgs{

    public uint controllerIndex;    //控制器(手柄)索引

    public uint flags;   

    public float distance;    //激光原点到命中点(交点)的距离

    public Transform target;    //命中物体的transform对象

}

 

public delegate void PointerEventHandler(object sender, PointerEventArgs e);

 

 

 

 

 

++示例:unity_teleport_sample

++++立钻哥哥:teleport瞬移场景:在场景中放一些球,然后在右控制器上做瞬移;做法是从控制器上发出一个激光束,按下扳机键就可以瞬移到激光束指到的地方;通过观察球的相对位置就能看到位置的变化;(一种是慢慢走过去;一种是直接跳过去;还有就是街景地图)

{

    public bool teleportOnClick = false;

    public TeleportType teleportType = TeleportType.TeleportTypeUseZeroY;

 

    Transform reference{

        get{

            //立钻哥哥:取的是最后渲染(depth最大)的相机(SteamVR_Camera)的原始点(这个origin实际就是将SteamVR_Camera添加到原始场景中的Camera的位置)

            var top = SteamVR_Render.Top();

 

            //立钻哥哥:SteamVR相机的层次结构是最上层是origin,然后下面有左右手柄和head, head下面有eye和ears

            return (top != null) ? top.origin : null;

        }

    }    //立钻哥哥:Transform reference{}

 

    void Start(){

        //立钻哥哥:这个脚本所在的物体必须添加SteamVR_TrackedController脚本(这个脚本的作用是将控制器的输入转换为事件回调),如果没有添加,则自动添加;这说明这个脚本所在的物体需要是控制器(手柄);

        var trackedController = GetComponent();

        if(null == trackedController){

            trackedController = gameObject.AddComponent();

        }

 

        //添加扳机按下的回调

        trackedController.TriggerClicked += new ClickedEventHandler(YanlzDoClick);

        if(teleportType == TeleportType.TeleportTypeUseTerrain){

            //Start the player at the level of the terrain

            var t = reference;

            if(null != t){

                //立钻哥哥:如果是地形类型,会将相机origin(基本上可以认为就是玩家的位置)的Y坐标线调整为地形的采样高度(即相机origin所在位置的地形的实际Y坐标:即将相机origin放到地形表面,也就是在地形表面的垂直投影的位置),这样可以避免人钻到地形里面了

                t.position = new Vector3(t.position.x, Terrain.activeTerrain.SampleHeight(t.position), t.position.z);

            }

        }    //立钻哥哥:if(teleportType==TeleportType.TeleportTypeUseTerrain){}

    }    //立钻哥哥:void Start(){}

 

    void YanlzDoClick(object sender, ClickedEventArgs e){

        //立钻哥哥:应该是通过这个变量来控制是否通过扳机键来瞬移,因为扳机键还可以用作其他用途,应该是在某种状态下才能通过扳机键来瞬移,比如:需要通过扳机键来瞬移时才需要将这个变量设为true

        if(teleportOnClick){

            var t = reference;

            if(null == t){

                return;

            }

 

            float refY = t.position.y

 

            //立钻哥哥:创建一个Y方向,-refY位置的平面(是-refY而不是refY的原因是这个是Plane的distance参数,而Plane的distance是原点到Plane的距离,而距离的正负决定了在平面的哪一边,为正表示原点在法线的正方向,为负表示原点在法线的反方向,这与通常的理解不一样,所以这里为-refY)

            Plane plane = new Plane(Vector3.up, -refY);

 

            //当前脚本应该绑定在手柄上,因此才会有手柄方向的一条射线

            Ray ray = new Ray(this.transform.position, transform.forward);

 

            //hasGroundTarget指:射线是否与地面相交,或者说是否射到了地面上

            bool hasGroundTarget = false;

 

            //dist为射线原点(即手柄的原点)与相交点的距离

            float dist = 0f;

            if(teleportType == TeleportType.TeleportTypeUseTerrain){

                //立钻哥哥:与地形进行碰撞

                RaycastHit hitInfo;

                TerrainCollider tc = Terrain.activeTerrain.GetComponent();

                hasGroundTarget = tc.RayCast(ray, out hitInfo, 1000f);

                dist = hitInfo.distance;

 

            }else if(teleportType == TeleportType.TeleportTypeUseCollider){

                //立钻哥哥:与场景中的碰撞体进行碰撞

                RaycastHit hitInfo;

                Physics.Raycast(ray, out hitInfo);

                dist = hitInfo.distance;

 

                //立钻哥哥:这里并没有设置hasGroundTarget为true,那后面的瞬移就无法完成,所以设置为TeleportTypeUseCollider应该就不能瞬移(实测确实不可以),那为什么要设置这种类型呢???:从实际意义来说,确实是不能:我们扳机指向哪就瞬移到哪,人不是什么地方都能去的

 

            }else{

                //立钻哥哥:与地面(Y方向的一个平面)相交

                hasGroundTarget = plane.Raycast(ray, out dist);

            }

 

            if(hasGroundTarget){

                //立钻哥哥:headPosOnGround是head(head就是头显的位置)在地面(Y=0)的投影,注意这里用的是localPosition

                Vector3 headPosOnGround = new Vector3(SteamVR_Render.Top().head.localPosition.x, 0.0f, SteamVR_Render.Top().head.localPosition.z);

 

                //立钻哥哥:这里就是将origin移动到扳机位置了,ray.origin + ray.direction*dist 得到的就是射线与地形/地面交点的位置;后面减去的两个点分别是手柄(这里取的是第一个子物体,实际上就是左控制器)和头显相对于origin的位置(XZ平面);感觉没有必要减,按照所见即所得,玩家看到的激光束的交点,就直接把位置定到那就好了,不需要考虑头显或手柄的偏移;

                t.position = ray.origin + ray.direction * dist - new Vector3(t.GetChild(0).localPosition.x, 0f, t.GetChild(0).localPosition.z) - headPosOnGround;

 

            }    //立钻哥哥:if(hasGroundTarget){}

        }    //立钻哥哥:if(teleportOnClick){}

    }    //立钻哥哥: void YanlzDoClick(object sender, ClickedEventArgs e){}

 

    public enum TeleportType{

        //瞬移类型是根据与地形的交点来确定目的位置的

        TeleportTypeUseTerrain,

 

        //碰撞体类型是根据与任何带碰撞体的物体的交点来确定目的位置的

        TeleportTypeUseCollider,

 

        //这个是与Y坐标为0的平面(通常就是地面)的交点来确定目的位置的

        TeleportTypeUseZeroY

    }    //立钻哥哥:public enum TeleportType{}

 

}    //立钻哥哥:unity_teleport_sample

 

 

 

 

 

++SteamVR_TestThrow.cs

++++立钻哥哥:\Assets\SteamVR\Extras\SteamVR_TestThrow.cs

++++[SteamVR_TestThrow.cs]这个脚本是一个测试IK(更准确地说应该是[SteamVR_IK.cs]的,反向运动,就是根据手柄的运动模拟带动手臂的运动)的示例场景;这个场景里有一个模拟手臂:分左右手,分别在左右两个手柄控制器下面,在场景中的样子是这样的:它这里只有两个关节(肩关节和腕关节,SteamVR_IK就只支持两个关节),然后有一个手指;这个应该是测试通过手柄扔出一个物体的例子,主要是测试[SteamVR_TestThrow.cs]这个脚本,可以看到这个脚本被添加到了左右两个手柄上面:Template这个物体是被扔的物体,它由一个圆及一个子物体立方体组成;通过扳机键创建一个物体并抓在手中,然后通过甩臂并同时松开扳机将物体扔出去

using UnityEngine;

using System.Collections;

 

namespace Valve.VR.Extras{

    [RequireComponent(typeof(SteamVR_TrackedObject))]

    public class SteamVR_TestThrow : MonoBehaviour{

        //要扔掉的物体,并不是一个真正的prefab,而是一个场景中已经创建好的物体

        public GameObject prefab;

 

        //立钻哥哥:这个是手柄上tip(手柄模型的一部分)下面的一个rigidbody;在手柄的模型中,所有的子组件(相对于父组件,即整个model)的位置都是(0,0,0),但下面会再带一个attach的子对象,这个子对象是一个刚体,然后真正的位置是通过它来确定的

        public Rigidbody attachPoint;

 

        [SteamVR_DefaultAction(Interact)]

        public SteamVR_Action_Boolean spawn;

 

        SteamVR_Behaviour_Pose trackedObj;    //手柄这个跟踪对象

        FixedJoint joint;    //固定关节

 

        private void Awake(){}

        private void FixedUpdate(){}

 

    }    //立钻哥哥:public class SteamVR_TestThrow:MonoBehaviour{}

}    //立钻哥哥:namespace Valve.VR.Extras{}

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第27张图片

 

 

++++立钻哥哥:[SteamVR_TestThrow.cs]这个脚本需要与SteamVR_TrackedObject.cs一起使用;实际上它会加到手柄上;那我们就来看看这个脚本吧:

using UnityEngine;

using System.Collections;

 

namespace Valve.VR.Extras{

    [RequireComponent(typeof(SteamVR_TrackedObject))]

    public class SteamVR_TestThrow : MonoBehaviour{

        //要扔掉的物体,并不是一个真正的prefab,而是一个场景中已经创建好的物体

        public GameObject prefab;

 

        //立钻哥哥:这个是手柄上tip(手柄模型的一部分)下面的一个rigidbody;在手柄的模型中,所有的子组件(相对于父组件,即整个model)的位置都是(0,0,0),但下面会再带一个attach的子对象,这个子对象是一个刚体,然后真正的位置是通过它来确定的

        public Rigidbody attachPoint;

 

        [SteamVR_DefaultAction(Interact)]

        public SteamVR_Action_Boolean spawn;

 

        SteamVR_Behaviour_Pose trackedObj;    //手柄这个跟踪对象

        FixedJoint joint;    //固定关节

 

        private void Awake(){

            //立钻哥哥:Awake()函数不管脚本是否启用都会调用

            trackedObj = GetComponent();

        }

 

        private void FixedUpdate(){

            If(joint == null && spawn.GetStateDown(trackedObj.inputSource)){

                //立钻哥哥:如果还没有建立关节,当按下扳机键时,建立物体与手柄的(关节)关联,相当于就是抓起了物体

 

                //先创建了物体(复制了场景中的一个物体)

                Var go = GameObject.Instantiate(prefab);

 

                //创建的物体的位置位于tip的关联点的位置

                Go.transform.position = attachPoint.transform.position;

        

                //添加固定关节,这样物体就能跟随手柄动而动了

                Joint = go.AddComponent();

 

                //将其与手柄tip的attach关联

                Joint.connectedBody = attachPoint;

 

            }else if(){

 

            }

 

        }

 

    }    //立钻哥哥:public class SteamVR_TestThrow:MonoBehaviour{}

}    //立钻哥哥:namespace Valve.VR.Extras{}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第28张图片

++立钻哥哥推荐的拓展学习链接(Link_Url)

立钻哥哥推荐的拓展学习链接(Link_Url)

++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/

++++HTC_VIVE开发基础https://blog.csdn.net/VRunSoftYanlz/article/details/81989970

++++Oculus杂谈https://blog.csdn.net/VRunSoftYanlz/article/details/82469850

++++Oculus安装使用https://blog.csdn.net/VRunSoftYanlz/article/details/82718982

++++SteamVR简介https://blog.csdn.net/VRunSoftYanlz/article/details/86484254

++++SteamVR脚本功能分析https://blog.csdn.net/VRunSoftYanlz/article/details/86531480

++++OpenXR简介https://blog.csdn.net/VRunSoftYanlz/article/details/85726365

++++VRTK杂谈https://blog.csdn.net/VRunSoftYanlz/article/details/82562993

++++VRTK快速入门(杂谈)https://blog.csdn.net/VRunSoftYanlz/article/details/82955267

++++VRTK官方示例(目录)https://blog.csdn.net/VRunSoftYanlz/article/details/82955410

++++VRTK代码结构(目录)https://blog.csdn.net/VRunSoftYanlz/article/details/82780085

++++VRTK(SceneResources)https://blog.csdn.net/VRunSoftYanlz/article/details/82795400

++++VRTK_ControllerEventshttps://blog.csdn.net/VRunSoftYanlz/article/details/83099512

++++VRTK_InteractTouchhttps://blog.csdn.net/VRunSoftYanlz/article/details/83120220

++++VR实验:以太网帧的构成https://blog.csdn.net/VRunSoftYanlz/article/details/82598140

++++FrameVR示例V0913https://blog.csdn.net/VRunSoftYanlz/article/details/82808498

++++FrameVR示例V1003https://blog.csdn.net/VRunSoftYanlz/article/details/83066516

++++SwitchMachineV1022https://blog.csdn.net/VRunSoftYanlz/article/details/83280886

++++PlaySceneManagerV1022https://blog.csdn.net/VRunSoftYanlz/article/details/83280886

++++Unity5.x用户手册https://blog.csdn.net/VRunSoftYanlz/article/details/81712741

++++Unity面试题ABChttps://blog.csdn.net/vrunsoftyanlz/article/details/78630687

++++Unity面试题Dhttps://blog.csdn.net/VRunSoftYanlz/article/details/78630838

++++Unity面试题Ehttps://blog.csdn.net/vrunsoftyanlz/article/details/78630913

++++Unity面试题Fhttps://blog.csdn.net/VRunSoftYanlz/article/details/78630945

++++Cocos2dx面试题https://blog.csdn.net/VRunSoftYanlz/article/details/78630967

++++禅道[zentao]https://blog.csdn.net/VRunSoftYanlz/article/details/83964057

++++Lua快速入门篇(Xlua拓展):https://blog.csdn.net/VRunSoftYanlz/article/details/81173818

++++Lua快速入门篇(XLua教程):https://blog.csdn.net/VRunSoftYanlz/article/details/81141502

++++Lua快速入门篇(基础概述)https://blog.csdn.net/VRunSoftYanlz/article/details/81041359

++++框架知识点https://blog.csdn.net/VRunSoftYanlz/article/details/80862879

++++游戏框架(UI框架夯实篇)https://blog.csdn.net/vrunsoftyanlz/article/details/80781140

++++游戏框架(初探篇)https://blog.csdn.net/VRunSoftYanlz/article/details/80630325

++++设计模式简单整理https://blog.csdn.net/vrunsoftyanlz/article/details/79839641

++++专题:设计模式(精华篇)https://blog.csdn.net/VRunSoftYanlz/article/details/81322678

++++U3D小项目参考https://blog.csdn.net/vrunsoftyanlz/article/details/80141811

++++Unity案例(Vehicle)https://blog.csdn.net/VRunSoftYanlz/article/details/82355876

++++UML类图https://blog.csdn.net/vrunsoftyanlz/article/details/80289461

++++PowerDesigner简介https://blog.csdn.net/VRunSoftYanlz/article/details/86500084

++++Unity知识点0001https://blog.csdn.net/vrunsoftyanlz/article/details/80302012

++++Unity知识点0008https://blog.csdn.net/VRunSoftYanlz/article/details/81153606

++++U3D_Shader编程(第一篇:快速入门篇)https://blog.csdn.net/vrunsoftyanlz/article/details/80372071

++++U3D_Shader编程(第二篇:基础夯实篇)https://blog.csdn.net/vrunsoftyanlz/article/details/80372628

++++Unity引擎基础https://blog.csdn.net/vrunsoftyanlz/article/details/78881685

++++Unity面向组件开发https://blog.csdn.net/vrunsoftyanlz/article/details/78881752

++++Unity物理系统https://blog.csdn.net/vrunsoftyanlz/article/details/78881879

++++Unity2D平台开发https://blog.csdn.net/vrunsoftyanlz/article/details/78882034

++++UGUI基础https://blog.csdn.net/vrunsoftyanlz/article/details/78884693

++++UGUI进阶https://blog.csdn.net/vrunsoftyanlz/article/details/78884882

++++UGUI综合https://blog.csdn.net/vrunsoftyanlz/article/details/78885013

++++Unity动画系统基础https://blog.csdn.net/vrunsoftyanlz/article/details/78886068

++++Unity动画系统进阶https://blog.csdn.net/vrunsoftyanlz/article/details/78886198

++++Navigation导航系统https://blog.csdn.net/vrunsoftyanlz/article/details/78886281

++++Unity特效渲染https://blog.csdn.net/vrunsoftyanlz/article/details/78886403

++++Unity数据存储https://blog.csdn.net/vrunsoftyanlz/article/details/79251273

++++Unity中Sqlite数据库https://blog.csdn.net/vrunsoftyanlz/article/details/79254162

++++WWW类和协程https://blog.csdn.net/vrunsoftyanlz/article/details/79254559

++++Unity网络https://blog.csdn.net/vrunsoftyanlz/article/details/79254902

++++C#事件https://blog.csdn.net/vrunsoftyanlz/article/details/78631267

++++C#委托https://blog.csdn.net/vrunsoftyanlz/article/details/78631183

++++C#集合https://blog.csdn.net/vrunsoftyanlz/article/details/78631175

++++C#泛型https://blog.csdn.net/vrunsoftyanlz/article/details/78631141

++++C#接口https://blog.csdn.net/vrunsoftyanlz/article/details/78631122

++++C#静态类https://blog.csdn.net/vrunsoftyanlz/article/details/78630979

++++C#中System.String类https://blog.csdn.net/vrunsoftyanlz/article/details/78630945

++++C#数据类型https://blog.csdn.net/vrunsoftyanlz/article/details/78630913

++++Unity3D默认的快捷键https://blog.csdn.net/vrunsoftyanlz/article/details/78630838

++++游戏相关缩写https://blog.csdn.net/vrunsoftyanlz/article/details/78630687

++++UnityAPI.Rigidbody刚体https://blog.csdn.net/VRunSoftYanlz/article/details/81784053

++++UnityAPI.Material材质https://blog.csdn.net/VRunSoftYanlz/article/details/81814303

++++UnityAPI.Android安卓https://blog.csdn.net/VRunSoftYanlz/article/details/81843193

++++UnityAPI.AndroidJNI安卓JNIhttps://blog.csdn.net/VRunSoftYanlz/article/details/81879345

++++UnityAPI.Transform变换https://blog.csdn.net/VRunSoftYanlz/article/details/81916293

++++UnityAPI.WheelCollider轮碰撞器https://blog.csdn.net/VRunSoftYanlz/article/details/82356217

++++UnityAPI.Resources资源https://blog.csdn.net/VRunSoftYanlz/article/details/83155518

++++JSON数据结构https://blog.csdn.net/VRunSoftYanlz/article/details/82026644

++++CocosStudio快速入门https://blog.csdn.net/VRunSoftYanlz/article/details/82356839

++++Unity企业内训(目录)https://blog.csdn.net/VRunSoftYanlz/article/details/82634668

++++Unity企业内训(第1讲)https://blog.csdn.net/VRunSoftYanlz/article/details/82634733

++++Unity企业内训(第2讲)https://blog.csdn.net/VRunSoftYanlz/article/details/82861180

++++Unity企业内训(第3讲)https://blog.csdn.net/VRunSoftYanlz/article/details/82927699

++++Unity企业内训(第4讲)https://blog.csdn.net/VRunSoftYanlz/article/details/83479776

++++Unity企业内训(第5讲)https://blog.csdn.net/VRunSoftYanlz/article/details/83963811

++++Unity企业内训(第6讲)https://blog.csdn.net/VRunSoftYanlz/article/details/84207696

++++插件https://blog.csdn.net/VRunSoftYanlz/article/details/83963905

++++计算机组成原理(教材篇)https://blog.csdn.net/VRunSoftYanlz/article/details/82719129

++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/

--_--VRunSoft:lovezuanzuan--_--

SteamVR脚本功能分析(Yanlz+SteamVR+OpenVR+Teleport+Valve+VR+Ray+RaycastHit+立钻哥哥+==)_第29张图片

你可能感兴趣的:(SteamVR)