zoukankan      html  css  js  c++  java
  • 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 ……

  • 相关阅读:
    python+requests+re匹配抓取猫眼上映电影信息
    Qt 5.12 LTS 部署
    Apache 日志记录相关设置
    php curl 相关知识
    Apache缓存相关配置
    Apache开启GZIP 压缩网页
    Apache 相关 mod_rewrite ,RewriteCond,{HTTP_HOST}
    Andriod you must restart adb and eclipse
    JDK 环境变量的配置
    http 协议详解
  • 原文地址:https://www.cnblogs.com/unity3ds/p/5908677.html
Copyright © 2011-2022 走看看