首页 » 技术分享 » HTC VIVE 基础开发1

HTC VIVE 基础开发1

 

此文章用于自己学习过程中的记录,以便日后翻阅

开发HTC VIVE 首先需要下载Steam 平台,然后在Steam的商店里搜索Steam VR下载安装就可以了。

创建新的Unity项目

创建一个新的Unity工程


下载Steam VR插件

打开unity的 Asset Store 


在Unity中的商店里搜索Steam vr插件


下载完成后点击Import按钮



将下载好的Steam VR插件全部导入到新的Unity工程中



导入中会有绿色的读条,如果导入完成后弹出API Update Required,这是在提示你API更新,可以不用管他,点击No Thanks即可。(原因应该是Steam VR 插件的版本不是最新的)


等待SteamVr_Settings弹出后点击Accept All。到这里导入就完成了



实现对手柄的控制

打开SteamVR_LaserPointer,在这里实现对激光笔的控制功能,首先把SteamVR_LaserPointer

Ctrl+d 复制一份,然后再该脚本的基础上进行修改


新建一个文件夹命名为Script,把SteamVR_LaserPointer脚本重命名为LaserPointer,然后拖入新建的Script文件夹中


进入LaserPointer脚本,然后修改类名为LaserPointer



删除掉选中的蓝色部分,然后保存


制作激光笔选中的目标点



在void Update 找到代码判断的语句进行扩充,并执行Hit判断,把Hit到的点存到刚刚创建的HitPoint变量中


在SteamVr文件夹中找到Prefabs文件夹然后把CameraRig放到场景中去


在场景中用Cube搭建一个简单的房间,然后把CameraRig的位置进行调整


展开CameraRig,选择Controller(left)对左手的手柄进行控制操作


给Controller(left)添加修改的Laser Point 赋予给CameraRig和SteamVR_TrackedController

然后去掉TrackSteamVR_Tracked Object



到了这一步,如果你的HTC VIVE设备调试正确,那就能在Unity的场景中看到他们了。



创建传送的脚本

创建Teleport

复制脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Teleport : MonoBehaviour {
    //手柄的引用
    public GameObject Left;
    //储存一些变量
    LaserPointer LP_Left;
    SteamVR_TrackedController ST_Left;
    Transform CurrentTransform;

    
	// Use this for initialization
	void Start () {
        //初始化变量  注册监听  获取激光笔和控制器
        LP_Left = Left.GetComponent<LaserPointer>();
        ST_Left = Left.GetComponent<SteamVR_TrackedController>();
        LP_Left.PointerIn += LeftPointIn;//手柄指向事件
        LP_Left.PointerOut += LeftPointOut;//手柄取消事件
        ST_Left.TriggerClicked += TriggerClicked;//手柄扳机事件

    }
	
    //手柄有物体指向
    void  LeftPointIn(object sender,PointerEventArgs e)
    {//当有物体指向  设置变量标识
        CurrentTransform = e.target;
    }

    //取消指向
    void LeftPointOut(object sender,PointerEventArgs e)
    {
        //取消指向事件 变量标识设置为空
        CurrentTransform = null;
    }


    void TriggerClicked(object sender,ClickedEventArgs e)
    {//当指向不为空的时候,进行移动
        if (CurrentTransform != null)
        {
            TeleportPosition(LP_Left.HitPoint);
        }
    }

    //移动的方法
    private void TeleportPosition(Vector3 targetPosition)
    {
        this.gameObject.transform.position = new Vector3(targetPosition.x - Left.transform.localPosition.x, targetPosition.y, targetPosition.z - Left.transform.localPosition.z);
    }
    // Update is called once per frame
    void Update () {
		
	}
}

赋予Teleport脚本给CameraRig组件,然后把Controller(left)赋值到Left



到这一步就完成了传送功能

实现多场景加载

创建一个新的Check脚本用来检测

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

public class Check : MonoBehaviour {

    //检测 条件完成 达到时 隐藏
    public GameObject _Object;
    // 加载场景名字
    public string SceneName;
    //异步加载控制器
    AsyncOperation _AsyncOperation;

    private void OnTriggerEnter(Collider other)
    {
        //当检测到碰撞时 检查碰撞物体是不是主相机  如果是进行场景加载  对__AsyncOperation赋值  进行标记
        if (other.tag == "MainCamera" && _AsyncOperation == null)
        {
            _AsyncOperation = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
        }
    }

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void FixedUpdate () {

        //通过_AsyncOperation.isDone 来检测场景是否加载完成  如果加载完成 就隐藏特殊物体 来展现新的场景 同时避免再度触发碰撞进行场景加载
        if (_AsyncOperation!=null && _AsyncOperation.isDone)
        {
            _AsyncOperation = null;
            _Object.SetActive(false);
        }
	}
}

把Check 赋予给一面墙(创建一个CUBE拉成一面墙的样子,既点击墙面之后跳转的场景),然后对这个墙壁添加BoxCollider组件,BoxCollider组件大小为包裹住墙壁。然后勾选IsTrigger(勾上代表触发器)





在CameraRig上的Camera(eye)上添加BoxCollider 组件,同意勾选上IsTrigger 选项

然后添加Rigidbody组件取消勾选UseGravity



创建场景管理

创建一个新的脚本 Manager 用来管理你的场景

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

public class Manager : MonoBehaviour {

    public static Manager Instance;

    Check CurrentCheck;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Debug.LogError("不能重复创建Manager");
        }
    }

    public void StartNewScene(Check _Check)
    {
        //检测当前是否有场景加载 如果没有将调用对象设置为currentCheck

        if (CurrentCheck == null)
        {
            CurrentCheck = _Check;
        }else if (CurrentCheck != _Check)//如果有  就调用CurrentCheck.Reset方法重置 并且更新CurrentCheck调用
        {
            CurrentCheck.Reset();
            CurrentCheck = _Check;
        }
    }
 
}


回到Check脚本,添加两行新的代码


然后在Check脚本中添加Reset方法


回到Unity里创建一个GameObject,然后把它的位置Reset,重命名为Manager,然后把Manager脚本挂上去


现在不会两个场景一起显示了,只会显示其中一个

到这里就完成了场景卸载的功能




制作不可传送区域

需要对传送进行限制,例如不能传送到界外,或者不能传送到屋顶或者水里了

例如把水面设置为不可传送区域,在搜索栏中搜索命名为water的所有模型(前提是你的模型的名字为water,如果是别的就搜索你模型的名字)

然后把Layer层的Default全部设置为Ignore Raycast

到这里就无法选择水面进行移动了。但是还没有明显的禁止移动提示。

接下来需要把禁止移动的提示制作的更明显

打开LaserPoint脚本进行编辑,需要先添加一个Material 来控制射线的颜色


因为上面定义了新的Material ,所以把下面的删掉


在指向判断这里添加新的更改,把可以只想设置为绿色,否则设置为红色



到这里就完成了点击不可传送地区射线变红的功能


制作抛物线---把激光笔的直线更改为抛物线

创建一个新的脚本parabola

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

public class Parabola : MonoBehaviour {

    //发射器位置
    public Transform ShootTransform;
    //起点
    public Vector3 StartPosition;
    //终点
    public Vector3 EndPosition;
    //重力加速度
    public float GravitationalAcceleration = 10;
    //绘制节点数量
    public int LineNodeNum = 10;
    //绘制抛物线
    public LineRenderer Line;

    Vector3[] Position;


	// Use this for initialization
	void Start () {
        //初始化线段绘制节点
        Position = new Vector3[LineNodeNum];
        //设置定点数量
        Line.SetVertexCount(LineNodeNum);
	}

    Vector3 GetPlaneVector(Vector3 v3)
    {
        return new Vector3(v3.x, 0, v3.z);
    }
	
	// Update is called once per frame
	void FixedUpdate () {
        //更新发射点的位置(曲线)
        ShootTransform.position = this.transform.position;
        ShootTransform.rotation = Quaternion.Euler(this.transform.rotation.eulerAngles.x - 30, this.transform.rotation.eulerAngles.y, 0);
        //当结束点为0但没有结束点的时候 将线段恢复为直线
        if (EndPosition == Vector3.zero)
        {
            ResetLine();
            return;
        }
        StartPosition = ShootTransform.position;
        //计算出水平和垂直上的位移
        float Sx = Vector3.Distance(GetPlaneVector(EndPosition), GetPlaneVector(StartPosition));
        float Sy = StartPosition.y - EndPosition.y;
        //计算出垂直方向和水平方向上的初始速度比值
        float tanA = -ShootTransform.forward.y / Vector3.Distance(Vector3.zero, GetPlaneVector(ShootTransform.forward));
        //计算运动时间
        float t = Mathf.Sqrt((2 * Sy - 2 * Sx * tanA) / GravitationalAcceleration);
        if(float.IsNaN(t))
        {
            ResetLine();
            return;
        }
        //推导出水平和垂直的初速度
        float Vx = Sx / t;
        float Vy = Vx * tanA;
        //绘制出线段
        float FirstLineNodeTime = t / LineNodeNum;
        Position[8] = StartPosition;
        for (int i = 1; i < LineNodeNum; i++)
        {
            float xz = GetX(Vx, FirstLineNodeTime * (i + 1));
            float y = GetY(FirstLineNodeTime * (i + 1), Vy);
            Position[i] = Vector3.Normalize(GetPlaneVector(ShootTransform.forward)) * xz + Vector3.down * y + ShootTransform.position;


        }
        Line.SetPositions(Position);

    }
    /// <summary>
    /// 计算水平方向的位移
    /// </summary>
    /// <param name="Speed">水平方向初速度</param>
    /// <param name="time">时间</param>
    /// <returns></returns>
    private float GetX(float Speed,float time)
    {
        float X = Speed * time;
        return X;
    }

    /// <summary>
    /// 计算垂直方向的位移
    /// </summary>
    /// <param name="time">时间</param>
    /// <param name="SpeedDownFloat">垂直方向的初速度</param>
    /// <returns></returns>
    private float GetY(float time,float SpeedDownFloat)
    {
        float Y = (float)(SpeedDownFloat * time + 0.5 * GravitationalAcceleration * time * time);
        return Y;
    }
    void  ResetLine()
    {
        for (int i = 0; i < LineNodeNum; i++)
        {
            Position[i] = transform.forward * i + transform.position;
            Line.SetPositions(Position);
        }
    }

}













回到LaserPointer脚本里去抛物线的处理

创建一个GameObject命名为Shooter,再创建一个命名为ShootLine



对ShootLine添加Line Renderer组件,然后创建一个材质球赋予它(颜色随意)



找到Controller (left)添加刚刚写好的抛物线脚本,然后把刚刚的Shooter、ShootLine指定过来


可以增加LineNodeNum的节点数让抛物线更平滑

到这里就实现了抛物线

这时由于激光笔自带的射线和制作的抛物线同时存在,需要把内置的直线取消掉,打开LaserPointer脚本注销掉所有pointer相关的代码

转载自原文链接, 如需删除请联系管理员。

原文链接:HTC VIVE 基础开发1,转载请注明来源!

0