HTC VIVE 基礎開發1
此文章用於自己學習過程中的記錄,以便日後翻閱
開發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 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相關的程式碼全部登出後鐳射筆內建的直線就消失了,只剩下拋物線。
啟用單例模式製作拋物線顏色
在Parabola指令碼中新增一個新的單例模式 用來改變拋物線的顏色
回到LaserPointer指令碼,在指向判斷處新增控制顏色的程式碼