HTC Vive開發筆記之手柄震動


手柄震動的代碼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 ……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM