手柄震動的代碼SteamVR_Controller腳本的最上面的注釋里面就有說明,其實也很簡單
// Example usage: //這個栗子是左手柄震動 右手震動只需把Leftmost換成Rightmost即可 // var deviceIndex = SteamVR_Controller.GetDeviceIndex(SteamVR_Controller.DeviceRelation.Leftmost); // if (deviceIndex != -1 && SteamVR_Controller.Input(deviceIndex).GetPressDown(SteamVR_Controller.ButtonMask.Trigger)) // SteamVR_Controller.Input(deviceIndex).TriggerHapticPulse(1000); // //============================================================================= using UnityEngine; using Valve.VR; /// <summary> /// 手柄 /// </summary> public class SteamVR_Controller { //按鈕 public class ButtonMask { public const ulong System = (1ul << (int)EVRButtonId.k_EButton_System); // reserved 為Steam系統保留,用來調出Steam系統菜單 public const ulong ApplicationMenu = (1ul << (int)EVRButtonId.k_EButton_ApplicationMenu); public const ulong Grip = (1ul << (int)EVRButtonId.k_EButton_Grip); public const ulong Axis0 = (1ul << (int)EVRButtonId.k_EButton_Axis0); public const ulong Axis1 = (1ul << (int)EVRButtonId.k_EButton_Axis1); public const ulong Axis2 = (1ul << (int)EVRButtonId.k_EButton_Axis2); public const ulong Axis3 = (1ul << (int)EVRButtonId.k_EButton_Axis3); public const ulong Axis4 = (1ul << (int)EVRButtonId.k_EButton_Axis4); public const ulong Touchpad = (1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad); public const ulong Trigger = (1ul << (int)EVRButtonId.k_EButton_SteamVR_Trigger); } //設備 public class Device { public Device(uint i) { index = i; } public uint index { get; private set; } public bool valid { get; private set; } public bool connected { get { Update(); return pose.bDeviceIsConnected; } } public bool hasTracking { get { Update(); return pose.bPoseIsValid; } } public bool outOfRange { get { Update(); return pose.eTrackingResult == ETrackingResult.Running_OutOfRange || pose.eTrackingResult == ETrackingResult.Calibrating_OutOfRange; } } public bool calibrating { get { Update(); return pose.eTrackingResult == ETrackingResult.Calibrating_InProgress || pose.eTrackingResult == ETrackingResult.Calibrating_OutOfRange; } } public bool uninitialized { get { Update(); return pose.eTrackingResult == ETrackingResult.Uninitialized; } } // These values are only accurate for the last controller state change (e.g. trigger release), and by definition, will always lag behind // the predicted visual poses that drive SteamVR_TrackedObjects since they are sync'd to the input timestamp that caused them to update. //這些值只在最近狀態發生改變的才准確(例如 釋放扳機),通過定義,將會總是在預測視覺動作(這個預測的信息用來驅動SteamVR_TrackedObjects)之后延遲,因為他們是和輸入的時間戳同步更新的 public SteamVR_Utils.RigidTransform transform { get { Update(); return new SteamVR_Utils.RigidTransform(pose.mDeviceToAbsoluteTracking); } } public Vector3 velocity { get { Update(); return new Vector3(pose.vVelocity.v0, pose.vVelocity.v1, -pose.vVelocity.v2); } } public Vector3 angularVelocity { get { Update(); return new Vector3(-pose.vAngularVelocity.v0, -pose.vAngularVelocity.v1, pose.vAngularVelocity.v2); } } //獲取狀態 之前的狀態 動作 public VRControllerState_t GetState() { Update(); return state; } public VRControllerState_t GetPrevState() { Update(); return prevState; } public TrackedDevicePose_t GetPose() { Update(); return pose; } VRControllerState_t state, prevState; TrackedDevicePose_t pose; int prevFrameCount = -1; public void Update() { if (Time.frameCount != prevFrameCount) { prevFrameCount = Time.frameCount; prevState = state; var system = OpenVR.System; if (system != null) { valid = system.GetControllerStateWithPose(SteamVR_Render.instance.trackingSpace, index, ref state, ref pose); UpdateHairTrigger(); } } } //長按 按下 抬起 兩種參數buttonMask buttonId public bool GetPress(ulong buttonMask) { Update(); return (state.ulButtonPressed & buttonMask) != 0; } public bool GetPressDown(ulong buttonMask) { Update(); return (state.ulButtonPressed & buttonMask) != 0 && (prevState.ulButtonPressed & buttonMask) == 0; } public bool GetPressUp(ulong buttonMask) { Update(); return (state.ulButtonPressed & buttonMask) == 0 && (prevState.ulButtonPressed & buttonMask) != 0; } public bool GetPress(EVRButtonId buttonId) { return GetPress(1ul << (int)buttonId); } public bool GetPressDown(EVRButtonId buttonId) { return GetPressDown(1ul << (int)buttonId); } public bool GetPressUp(EVRButtonId buttonId) { return GetPressUp(1ul << (int)buttonId); } //觸摸 按下 抬起 public bool GetTouch(ulong buttonMask) { Update(); return (state.ulButtonTouched & buttonMask) != 0; } public bool GetTouchDown(ulong buttonMask) { Update(); return (state.ulButtonTouched & buttonMask) != 0 && (prevState.ulButtonTouched & buttonMask) == 0; } public bool GetTouchUp(ulong buttonMask) { Update(); return (state.ulButtonTouched & buttonMask) == 0 && (prevState.ulButtonTouched & buttonMask) != 0; } public bool GetTouch(EVRButtonId buttonId) { return GetTouch(1ul << (int)buttonId); } public bool GetTouchDown(EVRButtonId buttonId) { return GetTouchDown(1ul << (int)buttonId); } public bool GetTouchUp(EVRButtonId buttonId) { return GetTouchUp(1ul << (int)buttonId); } //獲取軸心 此處是用來返回手指在觸摸板Touchpad上的位置 public Vector2 GetAxis(EVRButtonId buttonId = EVRButtonId.k_EButton_SteamVR_Touchpad) { Update(); var axisId = (uint)buttonId - (uint)EVRButtonId.k_EButton_Axis0; switch (axisId) { case 0: return new Vector2(state.rAxis0.x, state.rAxis0.y); case 1: return new Vector2(state.rAxis1.x, state.rAxis1.y); case 2: return new Vector2(state.rAxis2.x, state.rAxis2.y); case 3: return new Vector2(state.rAxis3.x, state.rAxis3.y); case 4: return new Vector2(state.rAxis4.x, state.rAxis4.y); } return Vector2.zero; } //上面提到的震動方法 public void TriggerHapticPulse(ushort durationMicroSec = 500, EVRButtonId buttonId = EVRButtonId.k_EButton_SteamVR_Touchpad) { var system = OpenVR.System; if (system != null) { var axisId = (uint)buttonId - (uint)EVRButtonId.k_EButton_Axis0; system.TriggerHapticPulse(index, axisId, (char)durationMicroSec); } } //扳機扣下或釋放的量才可以改變狀態 public float hairTriggerDelta = 0.1f; // amount trigger must be pulled or released to change state float hairTriggerLimit; bool hairTriggerState, hairTriggerPrevState; /// <summary> /// 更新扳機狀態 /// </summary> void UpdateHairTrigger() { hairTriggerPrevState = hairTriggerState; var value = state.rAxis1.x; // trigger if (hairTriggerState) { if (value < hairTriggerLimit - hairTriggerDelta || value <= 0.0f) hairTriggerState = false; } else { if (value > hairTriggerLimit + hairTriggerDelta || value >= 1.0f) hairTriggerState = true; } hairTriggerLimit = hairTriggerState ? Mathf.Max(hairTriggerLimit, value) : Mathf.Min(hairTriggerLimit, value); } public bool GetHairTrigger() { Update(); return hairTriggerState; } public bool GetHairTriggerDown() { Update(); return hairTriggerState && !hairTriggerPrevState; } public bool GetHairTriggerUp() { Update(); return !hairTriggerState && hairTriggerPrevState; } } private static Device[] devices; /// <summary> /// 輸入的具體設備 /// </summary> /// <param name="deviceIndex">Device index.</param> public static Device Input(int deviceIndex) { if (devices == null) { devices = new Device[OpenVR.k_unMaxTrackedDeviceCount]; for (uint i = 0; i < devices.Length; i++) devices[i] = new Device(i); } return devices[deviceIndex]; } public static void Update() { for (int i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) Input(i).Update(); } // This helper can be used in a variety of ways. Beware that indices may change // as new devices are dynamically added or removed, controllers are physically // swapped between hands, arms crossed, etc. //這個枚舉幫手用很多用法.注意索引也許會因為動態的新增或者移除而改變 //或者控制器物理上的在雙手/雙臂之間交換,等等 public enum DeviceRelation { First, // radially Leftmost, Rightmost, // distance - also see vr.hmd.GetSortedTrackedDeviceIndicesOfClass FarthestLeft, FarthestRight, } /// <summary> /// 獲取設備的索引 /// </summary> /// <returns>The device index.</returns> /// <param name="relation">Relation.</param> /// <param name="deviceClass">Device class.</param> /// <param name="relativeTo">Relative to.</param> public static int GetDeviceIndex(DeviceRelation relation, ETrackedDeviceClass deviceClass = ETrackedDeviceClass.Controller, int relativeTo = (int)OpenVR.k_unTrackedDeviceIndex_Hmd) // use -1 for absolute tracking space { var result = -1; var invXform = ((uint)relativeTo < OpenVR.k_unMaxTrackedDeviceCount) ? Input(relativeTo).transform.GetInverse() : SteamVR_Utils.RigidTransform.identity; var system = OpenVR.System; if (system == null) return result; var best = -float.MaxValue; for (int i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) { if (i == relativeTo || system.GetTrackedDeviceClass((uint)i) != deviceClass) continue; var device = Input(i); if (!device.connected) continue; if (relation == DeviceRelation.First) return i; float score; var pos = invXform * device.transform.pos; if (relation == DeviceRelation.FarthestRight) { score = pos.x; } else if (relation == DeviceRelation.FarthestLeft) { score = -pos.x; } else { var dir = new Vector3(pos.x, 0.0f, pos.z).normalized; var dot = Vector3.Dot(dir, Vector3.forward); var cross = Vector3.Cross(dir, Vector3.forward); if (relation == DeviceRelation.Leftmost) { score = (cross.y > 0.0f) ? 2.0f - dot : dot; } else { score = (cross.y < 0.0f) ? 2.0f - dot : dot; } } if (score > best) { result = i; best = score; } } return result; } }
另外分享一個別人的關於手柄震動的教程,比較詳細~~
新建yzx_controller腳本,掛在手柄上即可
一、 手柄震動一下(真的只震動一下,也許不注意都感受不到!)
using UnityEngine; using System.Collections; public class yzx_controller : MonoBehaviour { SteamVR_TrackedObject Hand; SteamVR_Controller.Device device; // Use this for initialization void Start () { Hand = GetComponent<SteamVR_TrackedObject>(); //獲得SteamVR_ TrackedObject組件 } // Update is called once per frame void Update () { if (Hand.isValid) { //防止Start函數沒加載成功,保證SteamVR_ TrackedObject組件獲取成功! Hand = GetComponent<SteamVR_TrackedObject>(); } //根據index,獲得手柄 device = SteamVR_Controller.Input((int)Hand.index); //如果手柄的Trigger鍵被按下了 if (device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger)) { Debug.Log("Trigger is pressed!"); //控制台輸出Trigger is pressed! device.TriggerHapticPulse(500); //手柄震動函數,500代表振幅,是一個ushort類型 } }
運行程序,扣一下Trigger鍵,咦,好像不對勁,都沒感覺發生了什么。也許你會懷疑人生,是不是都沒有捕捉到Trigger鍵被按下的事件?
No,No,No,明明控制台打印了Trigger is pressed !
這是為什么?……
因為,手柄只震動了一下下,你根本感覺不到。當然,你也可以修改這句代碼device.TriggerHapticPulse(500); 將參數500調成1000,甚至更大(數值越大,振幅越大), 然而,震感還是太低了,因為,持續時間太短了……
二、手柄持續震動(這次,真的很有震感了……)
自然想到,如果要讓手柄有震感,那么震動持續的時間一定要有保證!!!
下面的代碼中,你可以隨便修改代碼StartCoroutine(“Shock”,0.5f) 的第二個參數(代表震動持續時間),0.5f 代表手柄持續震動0.5s,想震多久就震多久……Cool !!!
我的想法:就是通過協程去執行手柄震動,然后通過Invoke函數來決定延遲時間,即控制手柄震動的持續時間。
全面修改 yzx_controller.cs腳本,代碼如下:
using UnityEngine; using System.Collections; public class yzx_controller : MonoBehaviour { SteamVR_TrackedObject Hand; SteamVR_Controller.Device device; bool IsShock = false; //布爾型變量IsShock // Use this for initialization void Start () { Hand = GetComponent<SteamVR_TrackedObject>(); //獲得SteamVR_ TrackedObject組件 } // Update is called once per frame void Update () { //防止Start函數沒加載成功,保證SteamVR_ TrackedObject組件獲取成功! if (Hand.isValid) { Hand = GetComponent<SteamVR_TrackedObject>(); } device = SteamVR_Controller.Input((int)Hand.index); //根據index,獲得手柄 //如果手柄的Trigger鍵被按下了 if (device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger)) { IsShock = false; //每次按下,IsShock為false,才能保證手柄震動 StartCoroutine("Shock",0.5f); //開啟協程Shock(),第二個參數0.5f 即為協程Shock()的形參 } } //定義了一個協程 IEnumerator Shock(float durationTime) { //Invoke函數,表示durationTime秒后,執行StopShock函數; Invoke("StopShock", durationTime); //協程一直使得手柄產生震動,直到布爾型變量IsShock為false; while (!IsShock) { device.TriggerHapticPulse(500); yield return new WaitForEndOfFrame(); } } void StopShock() { IsShock = true; //關閉手柄的震動 } }
運行程序,Amazing!手柄真的真的震動起來了,而且可以隨意調節震動的持續時間, So cool ……