此文章用于自己学习过程中的记录,以便日后翻阅
开发HTC VIVE 首先需要下载Steam 平台,然后在Steam的商店里搜索Steam VR下载安装就可以了。
创建新的Unity项目
创建一个新的Unity工程
下载Steam VR插件
打开unity的 Asset Store
在Unity中的商店里搜索Steam vr插件
下载完成后点击Import按钮
导入中会有绿色的读条,如果导入完成后弹出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 initializationvoid 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 framevoid 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 initializationvoid Start () {}// Update is called once per framevoid 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){//检测当前是否有场景加载 如果没有将调用对象设置为currentCheckif (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 initializationvoid 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 framevoid 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的节点数让抛物线更平滑
到这里就实现了抛物线
全部注销后激光笔内置的直线就消失了,只剩下抛物线。
启用单例模式制作抛物线颜色
在Parabola脚本中添加一个新的单例模式 用来改变抛物线的颜色
回到LaserPointer脚本,在指向判断处添加控制颜色的代码