VIVE开发基础(A、快速入门篇)(Yanlz+HTC+VIVE+VR+AR+MR+XR+SteamVR+CameraRig+LightHouse+HeadSet+Teleport+立钻哥哥+)

HTC_VIVE开发基础

版本git

做者github

参与者面试

完成日期数据库

备注编程

HTC_VIVE_V01_1.0canvas

严立钻设计模式

 

2018.08.23api

 

 

 

 

 

 

 

 

 

HTC_VIVE开发基础》发布说明:网络

++++“HTC_VIVE开发基础”是2018年最重要的一个技术突破点,VR虚拟现实将改变人们的生活,之后VR内容开发将成为一个爆发点,期待,期待,期待!app

++++“HTC_VIVE开发基础”:定位是一个入门级的基础知识,经过这篇博文,带你们进入一个魅力爆棚的VR世界;

 

 

 

##《HTC_VIVE开发基础》目录

#第一篇:快速入门

#第二篇:基础夯实

#第三篇:中级进阶

#第四篇:高级成魔

 

 

 

 

 

 

 

#第一篇:快速入门篇

#第一篇:快速入门篇

#第一篇:快速入门篇

++++A.一、VR行业介绍

++++A.二、SteamVR开发环境配置

++++A.三、SteamVR Plugins简单使用

++++A.四、SteamVR简单案例开发

++++A.五、Teleport系统

++++A.六、Vive与UI交互

++++A.七、Vive Controller Haptic

++++A.八、案例分析

++++A.九、立钻哥哥带您Vive快速入门

 

 

 

 

 

 

##A.一、VR行业介绍

##A.一、VR行业介绍

++A.一、VR行业介绍

++++A.1.一、VR概念

++++A.1.二、VR行业兴起及主流硬件

++++A.1.三、VR主流应用商店及领域

 

 

###A.1.一、VR概念

###A.1.一、VR概念

++A.1.一、VR概念

++++立钻哥哥:虚拟现实技术是一种能够建立和体验虚拟世界的计算机仿真系统,它利用计算机生成一种模拟环境,是一种多源信息融合的、交互式的三维动态视景和实体行为的系统仿真,使用户沉浸到该环境中

 

 

 

###A.1.二、VR行业兴起及主流硬件

###A.1.二、VR行业兴起及主流硬件

++A.1.二、VR行业兴起及主流硬件

++++立钻哥哥:

++PC端

++++2014年Facebook收购Oculus,总交易额约为20亿美金,其中包括4亿美圆现金;

++++Oculus前后推出DK一、DK二、CV1;

++++SteamVR联合HTC发布HTC VIVE,是目前效果体验最好,开发者最友好的设备;

++++PS推出基于PlayStation的PSVR;

++++Microsoft联合各大OEM推出内置追踪系统的MixedReality头盔;

 

++移动端

++++2014年Google发布廉价、简易的VR设备Cardboard;

++++Sumsung发布GearVR;

++++Google发布Tango设备(Motion Tracking);

++++SamSung发布GearVR2(With Controller);

++++HTC发布移动端VR一体机

++++Google发布Daydream平台;

 

 

 

###A.1.三、VR主流应用商店及领域

###A.1.三、VR主流应用商店及领域

++A.1.三、VR主流应用商店及领域

++++立钻哥哥:主流商店:Viveport、Oculus Store、SteamVE

 

++应用领域

++++娱乐、游戏;(RoboRecall:https://www.epicgames.com/roborecall/en-US/home#recall

++++StoryTelling;(Henry)

++++教育;(CalcFlow:https://store.steampowered.com/app/547280/Calcflow/

++++模拟;(The Body VR)

++++Creativity;(TiltBrush:https://store.steampowered.com/app/327140/Tilt_Brush/

 

 

 

 

 

 

##A.二、SteamVR开发环境配置

##A.二、SteamVR开发环境配置

++A.二、SteamVR开发环境配置

++++A.2.一、硬件基本配置

++++A.2.二、ViveSetUp

++++A.2.三、Unity开发环境配置

 

 

###A.2.一、硬件基本配置

###A.2.一、硬件基本配置

++A.2.一、硬件基本配置

++++立钻哥哥:

 

 

 

###A.2.二、Vive SetUp

###A.2.二、Vive SetUp

++A.2.二、Vive SetUp

++++立钻哥哥:刚入手的Vive须要:Vive设置教程、Viveport、ViveHome、SteamVR、测试及体验等步骤

 

++Vive设置教程

++++安装软件

++++硬件连线

++++Room SetUp

++++测试

 

++Viveport

 

++ViveHome

++++立钻哥哥:测试程序或默认启动程序

 

++SteamVR

 

++测试及体验

++++立钻哥哥:经过任意一款VR应用,让咱们来体验一下

 

 

 

 

###A.2.三、Unity开发环境配置

###A.2.三、Unity开发环境配置

++A.2.三、Unity开发环境配置

++++立钻哥哥:借助SteamVR开发的插件才能与VIVE硬件进行沟通(如监听硬件按钮,捕捉硬件位移和旋转等)

++++步骤:打开AssetStore,搜索SteamVR,找到SteamVR Plugin下载并导入到Unity中;(SteamVR:https://assetstore.unity.com/packages/templates/systems/steamvr-plugin-32647

++++测试:借助Scene过滤器,运行官方自带的案例,查看运行是否正确

 

 

 

 

 

 

 

##A.三、SteamVR Plugins简单使用

##A.三、SteamVR Plugins简单使用

++A.三、SteamVR Plugins简单使用

++++A.3.一、目录简介

++++A.3.二、Prefab简介

++++A.3.三、主要做用

++++A.3.四、Simple Test Scene

++++A.3.五、Unity5.6的一个bug

 

 

 

###A.3.一、目录简介

###A.3.一、目录简介

++A.3.一、目录简介

++++立钻哥哥:主要有Plugins和SteamVR两个目录

++++SteamVR主要存储openvr为Unity提供的接口,包括EditorPrefabScript等相关文件;

 

++Plugins主要存储openvr做用到的各个平台的库文件(.so)

++++A、x86存储32位平台库;x86_64储存64位平台库

++++B、openvr_api.cs用于定义C#端的部分接口

++++C、openvr_api.bundle用于存储Mac平台对应的库文件

 

 

 

 

###A.3.二、Prefab简介

###A.3.二、Prefab简介

++A.3.二、Prefab简介

++++立钻哥哥:Prefab经常使用:CameraRig和SteamVR

++++[CameraRig]提供访问HeadSetController的能力

++++[SteamVR]处理SteamVR的渲染等底层

 

 

 

 

###A.3.三、Plugins主要做用

###A.3.三、Plugins主要做用

++A.3.三、Plugins主要做用

++++立钻哥哥:

++++方便快速的开发应用

++++当调出System菜单时SteamVR会暂停游戏而且会同步物理和渲染系统

++++提供访问底层硬件的接口(TouchPad的数值等),而不用关心底层代码实现

++++处理Room-Scale中相机和手柄的平滑移动

 

 

 

 

###A.3.四、Simple Test Scene

###A.3.四、Simple Test Scene

++A.3.四、Simple Test Scene

++++立钻哥哥:建立一个简单的SteamVR场景并测试

 

++目的

++++立钻哥哥:建立最基本的VR场景,能够应用于场景漫游类的应用

 

++步骤

++++一、删除默认的Camera

++++2、添加CameraRigSteamVR预制体

 

++测试

++++立钻哥哥:运行场景,查看是否追踪了HeadSetController

 

 

 

 

 

###A.3.五、Unity5.6的一个bug

###A.3.五、Unity5.6的一个bug

++A.3.五、Unity5.6的一个bug

++++立钻哥哥:

a bug introduced in Unity5.6 where the controller wouldnt be tracked

++++With Camera(eye) still selected, add a Steam VR_Update Poses component to it

 

 

 

 

 

 

 

 

##A.四、SteamVR简单案例开发

##A.四、SteamVR简单案例开发

++A.四、SteamVR简单案例开发

++++A.4.一、简单输入控制

++++A.4.二、高级输入控制

 

 

###A.4.一、简单输入控制

###A.4.一、简单输入控制

++A.4.一、简单输入控制

++++立钻哥哥:简单输入控制:向计算机输入数据和信息的设备;是计算机与用户或其余设备通讯的桥梁

++++输入设备是用户和计算机系统之间进行信息交换的主要装置之一;

++++键盘,鼠标,摄像头,扫描仪,光笔,手写输入板,游戏杆,语音输入装置等都属于输入设备;

++++输入设备(InputDevice):是人或外部与计算机进行交互的一种装置,用于把原始数据和处理这些数的程序输入到计算机中;

++++计算机可以接收各类各样的数据,既能够是数值型的数据,也能够是各类非数值型的数据,如图形、图像、声音等均可以经过不一样类型的输入设备输入到计算机中,进行存储、处理和输出;

 

++输入类型

++++立钻哥哥:LightHouse、HeadSet、Vive Controller

 

++跟踪原理浅析

 

++应用场景

++++立钻哥哥:自从Controller出现以后,几乎全部的VR设备都离不开这种控制器,并且这种控制器在应用中的做用也五花八门

++++[画笔]:TiltBrush;

++++[Hand]

++++[工具和武器]

++++[导航设备]

 

++Unity中实现

++++立钻哥哥:经过SteamVR_Controller.Input((int)SteamVR_TrackedObject);获取SteamVR_Controller.Device获取手柄设备

SteamVR_Controller.Input((int)SteamVR_TrackedObject);

SteamVR_Controller.Device;

++++获取输入的方法:

Device.GetAxis();

Device.GetHairTrigger();

Device.GetPressDown(SteamVR_Controller.ButtonMask);

 

++示例代码参考

using System.Collections;

using System.Collectons.Generic;

using UnityEngine;

 

public class ViveInputTest : MonoBehaviour{

    private SteamVR_TrackedObject trackedObject;

 

    private SteamVR_Controller.Device controller{

        get{

            return SteamVR_Controller.Input((int)trackedObject.index);

        }

    }

 

    private void Start(){

        trackedObject = GetComponent<SteamVR_TrackedObject>();

    }

 

    void Update(){

        if(controller.GetAxis() != Vector2.zero){

            Debug.Log(立钻哥哥:TouchPad Pressed:  + controller.GetAxis());

        }

 

        if(controller.GetHairTriggerDown()){

            Debug.Log(立钻哥哥:Trigger Down.);

 

            //测试代码

            Renderer[] renderers = GetComponentsInChildren<Renderer>();

            foreach(Renderer render in renderers){

                render.enabled = true;

            }

        }

 

        if(controller.GetHairTriggerUp()){

            Debug.Log(Trigger Up);

 

            //测试代码

            Renderer[] renderers = GetComponentsInChildren<Renderer>();

            foreach(Renderer render in renderers){

                render.enabled = false;

            }

        }

 

        //获取非预设键(也能够本身封装成上述形式)

        if(controller.GetPressDown(SteamVR_Controller.ButtonMask.Grip)){

            Debug.Log(立钻哥哥:Grip Down.);

        }

 

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

 

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

 

 

 

 

 

###A.4.二、高级输入控制

###A.4.二、高级输入控制

++A.4.二、高级输入控制

++++立钻哥哥:高级输入控制咱们从“抓取物体”和“发射射线”来分析突破

++++[抓取物体]:在VR中当手柄靠近物体时,能够经过某些操做(如按下Trigger),能够将物体“抓”起来,让物体跟随手柄移动和旋转;

++++[发射射线]:当玩家按下TouchPad时会从手柄当前位置朝前发射一条射线;

 

++A.4.2.一、抓取物体

++++立钻哥哥:在VR中当手柄靠近物体时,能够经过某些操做(如按下Trigger),能够将物体“抓”起来,让物体跟随手柄移动和旋转;(而且当执行某些操做(如松开Trigger)时可“放开”物体,不跟随手柄移动和旋转)

++++抓取类型:带物理属性和不带物理属性;

 

++++[带物理属性]松开物体后,物体会有物理行为(以下落或者被扔出来)

--借助FixedJoint对物体进行物理约束,以后[Break the Joint]便可,扔出去的时候给物体添加力

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MyGrabObject : MonoBehaviour{

    private SteamVR_TrackedObject trackedObj;

    private GameObject collidingObject;    //碰撞器碰撞到的物体

    private GameObject objectInHand;    //手中抓到的物体

 

    //获取控制器

    private SteamVR_Controller.Device Controller{

        get{

            return SteamVR_Controller.Input((int)trackedObj.index);

        }

    }    //立钻哥哥:private SteamVR_Controller.Device Controller{}

 

    void Awake(){

         trackedObj = GetComponent<SteamVR_TrackedObject>();

    }

 

    #region 更新CollidingObject

    public void OnTriggerEnter(Collider other){

        SetCollidingObject(other);

    }

 

    public void OnTriggerExit(Collider other){

        if(!collidingObject){

            return;

        }

 

        collidingObject = null;

    }

    #endregion

 

    void Update(){

        if(Controller.GetHairTriggerDown()){

            if(collidingObject){

                GrabObject();

            }

        }

 

        if(Controller.GetHairTriggerUp()){

            if(objectInHand){

                ReleaseObject();

            }

        }

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

 

    private void SetCollidingObject(Collider col){

        if(collidingObject || !col.GetComponent<Rigidbody>){

            return;

        }

 

        collidingObject = col.gameObject;

    }    //立钻哥哥:private void SetCollidingObject(){}

 

    private void GrabObject(){

        objectInHand = collidingObject;

        collidingObject = null;

 

        var joint = AddFixedJoint();

        joint.connectedBody = objectInHand.GetComponent<Rigidbody>();

    }    //立钻哥哥:private void GrabObject(){}

 

    private FixedJoint AddFixedJoint(){

        FixedJoint fx = gameObject.AddComponent<FixedJoint>();

        fx.breakForce = 20000;

        fx.breakTorque = 20000;

 

        return fx;

    }    //立钻哥哥:private FixedJoint AddFixedJoint(){}

 

    private void ReleaseObject(){

        if(GetComponent<FixedJoint>){

            GetComponent<FixedJoint>().connectedBody = null;

            Destroy(GetComponent<FixedJoint>);

 

            //若是物体受重力影响则扔出去,不然停留在当前位置

            ThrowObjectWithCondition();

        }

 

        objectInHand = null;

    }    //立钻哥哥:private void ReleaseObject(){}

 

    //根据物体是否受重力影响来判断物体是否会飞出去

    void ThrowObjectWithCondition(){

        Rigidbody handObjectRigidbody = objectInHand.GetComponent<Rigidbody>();

    

        if(handObjectRigidbody.useGravity){

            objectInHand.GetComponent<Rigidbody>().velocity = Controller.velocigy;

            objectInHand.GetComponent<Rigidbody>().angularVelocity = Controller.angularVelocity;

        }

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

 

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

 

++++[不带物理属性]松开物体后,物体停留在被松开的位置,不会有任何物理行为

--借助FixedJoint对物体进行物理约束,以后[Break the Joint]便可,扔出去的时候不给物体添加了

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MyGrabObject : MonoBehaviour{

    private SteamVR_TrackedObject trackedObj;

    private GameObject objectInHand;    //碰撞器碰撞到的物体

    private GameObject objectInHand;    //手中抓到的物体

 

    //获取控制器

    private SteamVR_Controller.Device Controller{

        get{    return SteamVR_Controller.Input((int)trackedObj.index);  }

    }

 

    void Awake(){

        trackedObj = GetComponent<SteamVR_TrackedObject>();

    }

 

    #region 更新CollidingObject

    public void OnTriggerEnter(Collider other){

        SetCollidingObject(other);

    }

 

    public void OnTriggerExit(Collider other){

        if(!collidingObject){

            return;

        }

 

        collidingObject = null;

    }

    #endregion

 

    void Update(){

        if(Controller.GetHairTriggerDown()){

            if(collidingObject){

                GrabObject();

            }

        }

 

        if(Controller.GetHairTriggerUp()){

            if(objectInHand){

                ReleaseObject();

            }

        }

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

 

    private void SetCollidingObject(Collider col){

        if(collidingObject || !col.GetComponent<Rigidbody>()){

            return;

        }

 

        collidingObject = col.gameObject;

    }

 

    private void GrabObject(){

        objectInHand = collidingObject;

        collidingObject = null;

    

        var joint = AddFixedJoint();

        joint.connectedBody = objectInHand.GetComponent<Rigidbody>();

    }

 

    private FixedJoint AddFixedJoint(){

        FixedJoint fx = gameObject.AddComponent<FixedJoint>();

        fx.breakForce = 20000;

        fx.breakTorque = 20000;

        return fx;

    }

 

    private void ReleaseObject(){

        if(GetComponent<FixedJoint>){

            GetComponent<FixedJoint>().connectedBody = null;

            Destroy(GetComponent<FixedJoint>);

 

            //无论物体是否是受重力影响都扔出去

            ThrowObject();

        }

 

        objectInHand = null;

    }

 

    //设置物体的速度,让物体能够“飞”出去

    void ThrowObject(){

        objectInHand.GetComponent<Rigidbody>().velocity = Controller.velocity;

        objectInHand.GetComponent<Rigidbody>().angularVelocity = Controller.angularVelocity;

    }

 

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

 

 

 

 

 

 

 

##A.五、Teleport系统

##A.五、Teleport系统

++A.五、Teleport系统

++++A.5.一、概念

++++A.5.二、使用场景

++++A.5.三、分类

++++A.5.四、实现

 

 

 

###A.5.一、Teleport概念

###A.5.一、Teleport概念

++A.5.一、Teleport概念

++++立钻哥哥:在VR中玩家的移动有两种方式

++++第一种:靠头盔的移动(支持头盔移动的设备)

++++第二种:靠瞬移;(Teleport:即玩家从当前位置瞬间移动到另外一个位置

 

 

 

 

###A.5.二、Teleport使用场景

###A.5.二、Teleport使用场景

++A.5.二、Teleport使用场景

++++立钻哥哥:

++++适用于较大的场景,超过Room-Scale的大小;(参考案例中《紫禁城》)

++++适用于不能检测头盔移动的VR设备,如Oculus DK1 DK2;(设备自己只支持旋转,不支持位移;因此若是想位移必须借助Teleport

 

 

 

 

 

###A.5.三、Teleport分类

###A.5.三、Teleport分类

++A.5.三、Teleport分类

++++立钻哥哥:按照复杂程度和效果能够分为三类:Simple Line Teleport;Complex Line Teleport;Arc-Line Teleport

++++[Simple Line Teleport]:从手柄处朝向正前方发射一条射线,在HitPoint处显示一个Marker标识要移动到的目标;

++++[Complex Line Teleport]:从手柄处朝向正前方发射一条射线,在HitPoint处显示一个Maker标识要移动到的目标点;

++++[Arc-Line Teleport]:从手柄朝向手柄正前方发射一条弧线(Arc),弧度会随着手柄与地面的夹角变化而变换;

 

++[Simple Line Teleport]

++++立钻哥哥:从手柄处朝向正前方发射一条射线,在HitPoint处显示一个Marker标识要移动到的目标点

++++[表现形式]:一条射线,外加一个Maker

++++[缺点]:射线能够在指定的LayerMask中自由移动射线和Marker可是当进入非指定LayerMask后射线则不刷新

 

++[Complex Line Teleport]

++++立钻哥哥:从手柄处朝向正前方发射一条射线,在HitPoint处显示一个Marker标识要移动的目标点可是解决了射线在指定LayerMask以外时也刷新

++++[表现形式]:一样为一条射线,外加一个Maker可是当在指定LayerMask以内会呈现绿色;在指定LayerMask以外会呈现红色

++++[缺点]:功能虽然能实现,可是不够美观

 

++[Arc-Line Teleport]

++++立钻哥哥:从手柄处朝手柄正前方发射一条弧线(Arc),弧度会随着手柄与地面的夹角变化而变换而且在HitPoint处显示Marker

++++[表现形式]一条弧线,外加一个Marker一样支持检测是否在指定的LayerMask以内,若是在则为正常颜色;若是不在则变为红色

 

 

 

 

 

###A.5.四、Teleport实现

###A.5.四、Teleport实现

++A.5.四、Teleport实现

++++立钻哥哥:Teleport实现:Simple Line Teleport;Complex Line Teleport;Arc-Line Teleport

 

++[Simple Line Teleport]

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MySimpleLineTeleport : MonoBehaviour{

    public Transform cameraRigTransform;

    public GameObject teleportReticlePrefab;

    public Transform headTransform;

    public Vector3 teleportReticleOffset;

    public LayerMask teleportMask;

    private bool shouldTeleport;

    private SteamVR_TrackedObject trackedObj;

    public GameObject laserPrefab;

    private GameObject laser;

    private Transform laserTransform;

    private Vector3 hitPoint;

    private GameObject reticle;

    private Transform teleportReticleTransform;

 

    private SteamVR_Controller.Device Controller{

        get{  return SteamVR_Controller.Input((int)trackedObj.index);  }

    }

 

    void Awake(){

        trackedObj = GetComponent<SteamVR_TrackedObject>();

    }

 

    void Start(){

        laser = Instantiate(laserPrefab);

        laserTransform = laser.transform;

        reticle = Instantiate(teleportReticlePrefab);

        teleportReticleTransform = reticle.transform;

    }

 

    void Update(){

        if(Controller.GetPress(SteamVR_Controller.ButtonMask.Touchpad)){

            RaycastHit hit;

 

            if(Physics.Raycast(trackedObj.transform.position, transform.forward, out hit, 100, teleportMask)){

                hitPoint = hit.point;

                ShowMyLaser(hit);

 

                reticle.SetActive(true);

                teleportReticleTransform.position = hitPoint + teleportReticleOffset;

                shouldTeleport = true;

            }    //立钻哥哥:if(Physics.Raycast()){}

 

        }else{

            laser.SetActive(false);

            reticle.SetActive(false);

        }

 

        if(Controller.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad) && shouldTeleport){

            MyTeleport();

        }

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

 

    private void ShowMyLaser(RaycastHit hit){

        laser.SetActive(true);

        laser.Transform.position = Vector3.Lerp(trackedObj.transform.position, hitPoint, 0.5f);

        laserTransform.LookAt(hitPoint);

        laserTransform.lcoalScale = new Vector3(laserTransform.localScale.x, laserTransform.localScale.y, hit.distance);

    }    //立钻哥哥:private void ShowMyLaser(){}

 

    private void MyTeleport(){

        shouldTeleport = false;

        reticle.SetActive(false);

    

        Vector3 difference = cameraRigTransform.position - headTransform.position;

        difference.y = 0;

        cameraRigTransform.position = hitPoint + difference;

    }    //立钻哥哥:private void MyTeleport(){}

 

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

 

++[Complex Line Teleport]

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MyComplexLineTeleportWithError : MonoBehaviour{

    public Transform cameraRigTransform;

    public GameObject teleportReticlePrefab;

    public Transform headTransform;

    public Vector3 teleportReticleOffset;

    public string teleportMaskName;

    private bool shouldTeleport;

    private SteamVR_TrackedObject trackedObj;

    public GameObject laserPrefab;

    private GameObject laser;

    private Transform laserTransform;

    private Vector3 hitPoint;

    private GameObject reticle;

    private Transform teleportReticleTransform;

 

    private SteamVR_Controller.Device Controller{

        get{  return SteamVR_Controller.Input((int)trackedObj.index);  }

    }

 

    void Awake(){

        trackedObj = GetComponent<SteamVR_TrackedObject>();

    }

 

    void Start(){

        laser = Instantiate(laserPrefab);

        laserTransform = laser.transform;

        reticle = Instantiate(teleportReticlePrefab);

        teleportReticleTransform = reticle.transform;

    }

 

    void Update(){

        if(Controller.GetPress(SteamVR_Controller.ButtonMask.Touchpad)){

            RaycastHit hit;

      

            if(Physics.Raycast(trackedObj.transform.position, transform.forward, out hit, 100)){

                hitPoint = hit.point;

                ShowMyLaser(hit);

                reticle.SetActive(true);

 

                if(hit.collider.gameObject.layer == LayerMask.NameToLayer(teleportMaskName)){

                    reticle.GetComponent<Renderer>().material.color = Color.white;

                    teleportReticleTransform.position = hitPoint + teleportReticleOffset;

                    laser.GetComponent<Renderer>().material.SetColor(_Color, Color.green);

                    shouldTeleport = true;

 

                }else{

                    reticle.GetComponent<Renderer>().material.color = Color.red;

                    teleportReticleTransform.position = hitPoint + teleportReticleOffset;

                    laser.GetComponent<Renderer>().material.SetColor(_Color, Color.red);

                    shouldTeleport = false;

                }

            }    //立钻哥哥:if(Physics.Raycast()){}

 

        }else{

            laser.SetActive(false);

            reticle.SetActive(false);

        }    //立钻哥哥:if(Controller.GetPress()){}

 

        if(Controller.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad) && shouldTeleport){

            MyTeleport();

        }    //立钻哥哥:if(Controller.GetPressUp()){}

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

 

    private void ShowMyLaser(RaycastHit hit){

        laser.SetActive(true);

        laserTransform.position = Vector3.Lerp(trackedObj.transform.position, hitPoint, 0.5f);

        laserTransform.LookAt(hitPoint);

        laserTransform.localScale = new Vector3(laserTransform.localScale.x, laserTransform.localScale.y, hit.distance);

    }    //立钻哥哥:private void ShowLaser(){}

 

    private void MyTeleport(){

        shouldTeleport = false;

        reticle.SetActive(false);

        Vector3 difference = cameraRigTransform.position - headTransform.position;

        difference.y = 0;

        cameraRigTransform.position = hitPoint + difference;

    }    //立钻哥哥:private void MyTeleport(){}

 

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

 

++[Arc-Line Teleport]

++++立钻哥哥:“Unity Vive Teleport”

++++一、建立[ViveNavMesh]预制体,烘培[NavMesh],以后设置[ViveNavMesh],点击[UpdateNavMeshData]

++++二、建立[Pointer]预制体,设置[NavMesh]

++++三、为[SteamVR->Camera(Eye)]添加[ViveTeleportor]组件并设置[Pointer][OriginTransform][HeadTransform][Controllers]等便可

 

 

 

 

 

 

 

 

##A.六、Vive与UI交互

##A.六、Vive与UI交互

++A.六、Vive与UI交互

++++A.6.一、2D UI和3D UI

++++A.6.二、3D UI的实现方式

 

 

###A.6.一、2D UI和3D UI

###A.6.一、2D UI和3D UI

++A.6.一、2D UI和3D UI

++++立钻哥哥:

++++[UI的三种模式]:ScreenSpace-Overlay;ScreenSpace-Camera;World

++++[VR中使用3D UI]的必要性

--VR自己强调沉浸感

--2D UI在VR中没有立体感且没法交互

 

 

 

###A.6.二、3D UI的实现方式

###A.6.二、3D UI的实现方式

++A.6.二、3D UI的实现方式

++++立钻哥哥:3D UI的实现方式:UI+碰撞器扩展Vive插件

 

++[UI+碰撞器]

++++立钻哥哥:在UI上加BoxCollider,在OnTriggerEnter中调用UI的方法

++++缺点1:有些UI(如Button)能够利用上述方法来实现事件调用;可是调用Normal、Higlight、Pressed、Disabled等状态很是麻烦;

++++缺点2:有些UI(如Slider,Dropdown)没法利用上述方法实现,虽然能够指定碰撞器,可是必须得足够小且要实时刷新位置;实现起来很是很是很是麻烦,甚至有些复杂UI是无解的,如UI嵌套,下拉菜单和ScrollView;

++++缺点3:即便按照上面作可以实现功能;但每个UI都添加一个碰撞器很麻烦;若是3D UI比较复杂,界面比较多,后期维护也很是麻烦;

====>[解决方案]:鉴于上述方法很是麻烦,BUG比较多,并且效率不高;那就用“扩展Vive插件: ViveUGUIModule

//立钻哥哥:在UI上加BoxCollider,在OnTriggerEnter中调用UI的方法

private void OnTriggerEnter(Collider collider){

    if(collider == viveController){

        btn = GetComponent<Button>();

        UnityEvent btnClick = btn.onClick;

 

        if(btnClick != null){

            btnClick.Invoke();

        }

    }

}    //立钻哥哥:private void OnTriggerEnter(){}

 

++[扩展Vive插件]

++++立钻哥哥:“ViveUGUIModule”https://github.com/VREALITY/ViveUGUIModule

++++优势1:该项目是在GitHub上的一个开源库,经过应用该项目中几个简单的操做能够实现ViveController与普通的UGUI交互

++++优势2:项目是开源的,能拿到源码,能够根据本身的需求进行更改;且没有商业受权等纠纷

++++优势3:体量小,只有两个脚本(ViveControllerInput.cs和UIIgnoreRaycast.cs),且几乎没有依赖关系

 

++利用ViveControllerInput和UIIgnoreRaycast脚本

++++立钻哥哥:将默认的InputModule移动到VRCamera的子物体,去掉默认的StandardInputModule并添加ViveController,指定CursorSprite便可正常使用

++++若是不正常则设置EventCamera便可

 

++[UIIgnoreRaycast.cs]

++++立钻哥哥:ViveUGUIModule-master\Assets\UIIgnoreRaycast.cs

using UnityEngine;

using System.Collections;

 

namespace UnityEngine.UI{

    [AddComponentMenu(UI/Raycast Filters/Ignore Raycast Filter)]

    public class UIIgnoreRaycast : MonoBehaviour, ICanvasRaycastFilter{

        public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera){

            return false;

        }

    }    //立钻哥哥:public class UIIgnoreRaycast:MonoBehaviour,ICanvasRaycastFilter{}

}    //立钻哥哥:namespace UnityEngine.UI{}

 

++[ViveControllerInput.cs]

++++立钻哥哥:ViveUGUIModule-master\Assets\ViveControllerInput.cs

using UnityEngine;

using UnityEngine.EventSystems;

using UnityEngine.UI;

using System.Collections;

using System.Collections.Generic;

 

public class ViveControllerInput : BaseInputModule{

     public static ViveControllerInput Instance;

 

    [Header([Cursor setup])]

    public Sprite CursorSprite;

    public Material CursorMaterial;

    public float NormalCursorScale = 0.00025f;

 

    [Space(10)]

 

    [Header([Runtime variables])]

    [Tooltip(Indicates whether or not the gui was hit by any controller this frame)]

    public bool GuiHit;

 

    [Tooltip(Indicates whether or not a button was used this frame)]

    public bool ButtonUsed;

 

    [Tooltip(Generated cursors)]

    public RectTransform[] Cursors;

 

    private GameObject[] CurrentPoint;

    private GameObject[] CurrentPressed;

    private GameObject[] CurrentDragging;

 

    private PointerEventData[] PointEvents;

    private bool Initialized = false;

 

    [Tooltip(Generated non rendering camera(use for raycasting ui))]

    public Camera ControllerCamera;

 

    private SteamVR_ControllerManager ControllerManager;

    private SteamVR_TrackedObject[] Controllers;

    private SteamVR_Controller.Device[] ControllerDevices;

 

    protected override void Start(){

        base.Start();

 

        if(Initialized == false){

            Instance = this;

 

            ControllerCamera = new GameObject(Controller UI Camera).AddComponent<Camera>();

            ControllerCamera.clearFlags = CameraClearFlags.Nothing;   //Flag.Depth;

            ControllerCamera.cullingMask = 0;    //1<<LayerMask.NameToLayer(UI);

 

            ControllerManager = GameObject.FindObjectOfType<SteamVR_ControllerManager>();

            Controllers = new SteamVR_TrackedObject[]{

                ControllerManager.left.GetComponent<SteamVR_TrackedObject>(),

                ControllerManager.right.GetComponent<SteamVR_TrackedObject>()

            };

            ControllerDevices = new SteamVR_Controller.Device[Controllers.Length];

            Cursors = new RectTransform[Controllers.Length];

 

            for(int index = 0;  index < Cursors.Length;  index++){

                GameObject cursor = new GameObject(Cursor  + index);

                Canvas canvas = cursor.AddComponent<Canvas>();

                cursor.AddComponent<CanvasRenderer>();

                cursor.AddComponent<CanvasScaler>();

                cursor.AddComponent<UIIgnoreRaycast>();

                cursor.AddComponent<GraphicRaycaster>();

 

                canvas.renderMode = RenderMode.WorldSpace;

                canvas.sortingOrder = 1000;    //Set to be on top of everything

 

                Image image = cursor.AddComponent<Image>();

                image.sprite = CursorSprite;

                image.material = CursorMaterial;

 

                if(CursorSprite == null){

                    Debug.LogError(立钻哥哥:Set CursorSprite on + this.gameObject.name +  to the sprite you want to use as your cursor. + this.gameObject);

                }

 

                Cursors[index] = cursor.GetComponent<RectTransform>();

            }

 

            CurrentPoint = new GameObject[Cursors.Length];

            CurrentPressed = new GameObject[Cursors.Length];

            CurrentDragging = new GameObject[Cursors.Length];

            PointEvents = new PointerEventData[Cursors.Length];

 

            Canvas[] canvases = GameObject.FindObjectsOfType<Canvas>();

            foreach(Canvas canvas in canvases){

                canvas.worldCamera = ControllerCamera;

            }

 

            Initialized = true;

        }

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

 

    //use screen midpoint as locked pointer location, enabling look location to be the mouse

    private bool GetLookPointerEventData(int index){

        if(PointEvents[index] == null){

            PointEvent[index] = new PointerEventData(base.eventSystem);

        }else{

            PointEvent[index].Reset();

        }

 

        PointEvents[index].delta = Vector2.zero;

        PointEvents[index].position = new Vector2(Screen.width/2, Screen.height/2);

        PointEvents[index].scrollDelta = Vector2.zero;

 

        base.eventSystem.RaycastAll(PointEvents[index], m_RaycastResultCache);

        PointEvents[index].pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);

        if(PointEvents[index].pointerCurrentRaycast.gameObject != null){

            GuiHit = true;    //gets set to false at the beginning of the process event

        }

 

        m_RaycastResultCache.Clear();

 

        return true;

    }    //立钻哥哥:private bool GetLookPointerEventData(){}

 

    //update the cursor location and whether it is enabled

    //this code is based in Unitys DragMe.cs code provided int the UI drag and drop example

    private void UpdateCursor(){

        if(PointEvent[index].pointerCurrentRaycast.gameObject != null){

            Cursors[index].gameObject.SetActive(ture);

 

            if(pointData.pointerEnter != null){

                RectTransform draggingPlane = pointData.pointerEnter.GetComponent<RectTransform>();

                Vector3 globalLookPos;

                if(RectTransformUtility.ScreenPointToWorldPointInRectangle(draggingPlane, pointData.position, pointData.enterEventCamera, out globalLookPos)){

                    Cursors[index].position = globalLookPos;

                    Cursors[index].rotation = draggingPlane.rotation;

 

                    //scale cursor based on distance to camera

                    float lookPointDistance = (Cursors[index].position - Camera.main.transform.position).magnitude;

                    float cursorScale = lookPointDistance * NormalCursorScale;

                    if(cursorScale < NormalCursorScale){

                        cursorScale = NormalCursorScale;

                    }

 

                    Cursors[index].localScale = Vector3.one * cursorScale;

                }

            }

        }else{

            Cursor[index].gameObject.SetActive(false);

        }

    }    //立钻哥哥:private void UpdateCursor(){}

 

    //clear the current selection

    public void ClearSelection(){

        if(base.eventSystem.currentSelectedGameObject){

            base.eventSystem.SetSelectedGameObject(null);

        }

    }

 

    //select a game object

    private void Select(GameObject go){

        ClearSelection();

 

        if(ExecuteEvents.GetEventHandler<ISelectHandler>(go)){

            base.eventSystem.SetSelectedGameObject(go);

        }

    }

 

    //send update event to selected object

    //needed for InputField to receive keyboard input

    private bool SendUpdateEventToSelectedObject(){

        if(base.eventSystem.currentSelectedGameObject == null){

            return false;

        }

 

        BaseEventData data = GetBaseEventData();

        ExecuteEvents.Execute(base.eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);

 

        return data.used;

    }

 

    private void UpdateCameraPosition(int index){

        ControllerCamera.transform.position = Controllers[index].transform.position;

        ControllerCamera.transform.forward = Controllers[index].transform.forward;

    }

 

    private void InitializeControllers(){

        for(int index = 0;  index < Controllers.Length;  index++){

            if(Controllers[index] != null && Controllers[index].index != SteamVR_TrackedObject.EIndex.None){

                ControllerDevices[index] = SteamVR_Controller.Input((int)Controllers[index].index);

            }else{

                ControllerDevices[index] = null;

            }

        }

    }    //立钻哥哥:private void InitializeControllers(){}

 

    //Process is called by UI system to process events

    public override void Process(){

        InitializeControllers();

 

        GuiHit = false;

        ButtonUsed = false;

 

        //send update events if there is a selected object - this is important for InpuField to receive keyboard events

        SendUpdateEventToSelectedObject();

 

        //see if there is a UI element that is currently being looked at

        for(int index = 0;  index < Cursors.Length;  index++){

            if(Controllers[index].gameObject.activeInHierarchy == false){

                if(Cursors[index].gameObject.activeInHierarchy == true){

                    Cursors[index].gameObject.SetActive(false);

                }

 

                continue;

            }

 

            UpdateCameraPosition(index);

 

            bool hit = GetLookPointerEventData(index);

            if(hit == false){

                Continue;

            }

            CurrentPoint[index] = PointEvents[index].pointerCurrentRaycast.gameObject;

 

            //handle enter and exit events(highlight)

            base.HandlePointerExitAndEnter(PointEvents[index], CurrentPoint[index]);

 

            //update cursor

            UpdateCursor(index, PointEvents[index]);

 

            if(Controllers[index] != null){

                if(ButtonDown(index)){

                    ClearSelection();

 

                    PointEvents[index].pressPosition = PointEvents[index].position;

                    PointEvents[index].pointerPressRaycast = PointEvents[index].pointerCurrentRaycast;

                    PointEvents[index].pointerPress = null;

 

                    if(CurrentPoint[index] != null){

                        CurrentPressed[index] = CurrentPoint[index];

                        GameObject newPressed = ExecuteEvents.ExecuteHierarchy(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerDownHandler);

 

                        if(newPressed == null){

                            //some UI elements might not have click handler and not pointer down handler

                            newPressed = ExecuteEvents.ExecuteHierarchy(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerClickHandler);

                            if(newPressed != null){

                                CurrentPressed[index] = newPressed;

                            }

                        }else{

                            CurrentPressed[index] = newPressed;

                            //we want to do click on button down at same time, unlike regular mouse processing which does click when mouse goes up over same object it went down on reason to do this is head tracking might be jittery and this makes it easier to click buttons

                            ExecuteEvents.Execute(newPressed, PointEvents[index], ExecuteEvents.pointerClickHandler);

                        }

 

                        if(newPressed != null){

                            PointEvents[index].pointerPress = newPressed;

                            CurrentPressed[index] = newPressed;

                            Select(CurrentPressed[index]);

                            ButtonUsed = true;

                        }

 

                        ExecuteEvent.Execute(CurrentPressed[index], PointEvent[index], ExecuteEvents.beginDragHandler);

                        PointEvents[index].pointerDrag = CurrentPressed[index];

                        CurrentDragging[index] = CurrentPressed[index];

                    }

                }

 

                if(ButtonUp(index)){

                    if(CurrentDragging[index]){

                        ExecuteEvents.Execute(CurrentDragging[index], PointEvents[index], ExecuteEvents.endDragHandler);

                        if(CurrentPoint[index] != null){

                            ExecuteEvents.ExecutedHierarchy(CurrentPoint[index], PointEvents[index], ExecuteEvents.dropHandler);

                         }

                         PointEvents[index].pointerDrag = null;

                         CurrentDragging[index] = null;

                     }

 

                    if(CurrentPressed[index]){

                         ExecuteEvents.Execute(CurrentPressed[index], PointEvents[index], ExecuteEvents.pointerUpHandler);

                        PointEvents[index].rawPointerPress = null;

                        PointEvents[index].pointerPress = null;

                        CurrentPressed[index] = null;

                    }

                }

 

                //drag handing

                if(CurrentDragging[index] != null){

                    ExecuteEvents.Execute(CurrentDragging[index], PointEvents[index], ExecuteEvents.dragHandler);

                }

 

            }    //立钻哥哥:if(Controllers[index] != null){}

        }    //立钻哥哥:for(int index = 0; index < Cursors.Length; index++){}

    }    //立钻哥哥:public override void Process(){}

 

    private bool ButtonDown(int index){

        return (ControllerDevices[index] != null && ControllerDevices[index].GetPressDown(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger) == true);

    }

 

    private bool ButtonUp(int index){

        return (ControllerDevices[index] != null && ControllerDevices[index].GetPressUp(Valve.VR.EVRButtonId.K_EButton_SteamVR_Trigger) == true);

    }

 

}    //立钻哥哥:public class ViveControllerInput:BaseInputModule{}

 

 

 

 

 

 

 

 

##A.七、Vive Controller Haptic

##A.七、Vive Controller Haptic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

++++HTC_VIVE开发http://www.javashuo.com/article/p-mhcdmelt-bt.html

++++Unity5.x用户手册http://www.javashuo.com/article/p-ufnzpmga-s.html

++++Unity面试题ABChttp://www.javashuo.com/article/p-mwacxwca-gm.html

++++Unity面试题Dhttp://www.javashuo.com/article/p-wuwcrclr-s.html

++++Unity面试题Ehttp://www.javashuo.com/article/p-hmabbtmc-ba.html

++++Unity面试题Fhttp://www.javashuo.com/article/p-olslkfao-cq.html

++++Cocos2dx面试题http://www.javashuo.com/article/p-daozmsii-cz.html

++++Lua快速入门篇(Xlua拓展):http://www.javashuo.com/article/p-rrszijom-cm.html

++++Lua快速入门篇(XLua教程):http://www.javashuo.com/article/p-pduvmusb-ho.html

++++Lua快速入门篇(基础概述)http://www.javashuo.com/article/p-shernvtt-u.html

++++框架知识点http://www.javashuo.com/article/p-eufbowgf-u.html

++++游戏框架(UI框架夯实篇)http://www.javashuo.com/article/p-cvemoigb-cu.html

++++游戏框架(初探篇)http://www.javashuo.com/article/p-zfpoilbc-hy.html

++++设计模式简单整理http://www.javashuo.com/article/p-rngqugib-hg.html

++++专题:设计模式(精华篇)http://www.javashuo.com/article/p-nbohnaya-hw.html

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

++++UML类图http://www.javashuo.com/article/p-sxberuew-bm.html

++++Unity知识点0001http://www.javashuo.com/article/p-ryvdxxjr-ep.html

++++Unity知识点0008http://www.javashuo.com/article/p-kxgstxls-gu.html

++++U3D_Shader编程(第一篇:快速入门篇)http://www.javashuo.com/article/p-kyppgrac-gz.html

++++U3D_Shader编程(第二篇:基础夯实篇)http://www.javashuo.com/article/p-qkyowtli-hv.html

++++Unity引擎基础http://www.javashuo.com/article/p-beommoeb-ka.html

++++Unity面向组件开发http://www.javashuo.com/article/p-eigmuvut-dt.html

++++Unity物理系统http://www.javashuo.com/article/p-nqvvciwv-kd.html

++++Unity2D平台开发http://www.javashuo.com/article/p-ycaagdtj-hs.html

++++UGUI基础http://www.javashuo.com/article/p-rukxwckw-mc.html

++++UGUI进阶http://www.javashuo.com/article/p-wcatruhq-gt.html

++++UGUI综合http://www.javashuo.com/article/p-dkccmqii-gg.html

++++Unity动画系统基础http://www.javashuo.com/article/p-mbrdouxy-dq.html

++++Unity动画系统进阶http://www.javashuo.com/article/p-aqaqpbkh-bp.html

++++Navigation导航系统http://www.javashuo.com/article/p-dswwllas-t.html

++++Unity特效渲染http://www.javashuo.com/article/p-ckojjyfj-bp.html

++++Unity数据存储http://www.javashuo.com/article/p-bvlzynso-m.html

++++Unity中Sqlite数据库http://www.javashuo.com/article/p-ejutsbxl-ca.html

++++WWW类和协程http://www.javashuo.com/article/p-dbwmhsav-cy.html

++++Unity网络http://www.javashuo.com/article/p-sqrlntgh-dw.html

++++C#事件http://www.javashuo.com/article/p-zmwruvql-gm.html

++++C#委托http://www.javashuo.com/article/p-uozpymaf-gh.html

++++C#集合http://www.javashuo.com/article/p-sfqfdqsf-ex.html

++++C#泛型http://www.javashuo.com/article/p-xrttqngo-ee.html

++++C#接口http://www.javashuo.com/article/p-vhlfplgv-dm.html

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

++++C#中System.String类http://www.javashuo.com/article/p-olslkfao-cq.html

++++C#数据类型http://www.javashuo.com/article/p-hmabbtmc-ba.html

++++Unity3D默认的快捷键http://www.javashuo.com/article/p-wuwcrclr-s.html

++++游戏相关缩写http://www.javashuo.com/article/p-mwacxwca-gm.html

++++UnityAPI.Rigidbody刚体http://www.javashuo.com/article/p-phaztrtw-w.html

++++UnityAPI.Material材质http://www.javashuo.com/article/p-ntyoqcng-q.html

++++UnityAPI.Android安卓http://www.javashuo.com/article/p-fyyfgkck-q.html

++++UnityAPI.AndroidJNI安卓JNIhttp://www.javashuo.com/article/p-kvxjsnzf-w.html

++++UnityAPI.Transform变换http://www.javashuo.com/article/p-bfgrrhxl-cq.html

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

 

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