精华内容
下载资源
问答
  • 大众 CAN 网关实现切歌方法 简单易懂
  • 遇到的需求是 方向盘模拟仿真UI,旋转是按住方向盘转动,限制只能左转一圈半和右转一圈半,松手时方向盘自动复位。  先上最终效果图:    下面说一下具体实现过程。首先是要实现方向盘跟着鼠标位置旋转。思路是...

          遇到的需求是 方向盘模拟仿真UI,旋转是按住方向盘转动,限制只能左转一圈半和右转一圈半,松手时方向盘自动复位。

          先上最终效果图:

          


          下面说一下具体实现过程。首先是要实现方向盘跟着鼠标位置旋转。思路是按下鼠标后每一帧都记录按下的点跟以方向盘0中心为坐标原点的y坐标轴的角度,如下图黄色∠1所示(黑点为鼠标上一帧位置,红点为鼠标当前帧位置)

         

          然后用当前帧角度蓝色∠2跟上一帧角度黄色∠1比较,得出一个差值黑色∠3。用transform.Rotate()方法旋转这个差值即可让方向盘跟着鼠标转动。

          第二个需求是让方向盘左右旋转不能超过一圈半,也就是450°。这个需求的实现是自定义一个float字段,用来记录旋转的总角度,用∠1和∠2的差值正负判定当前鼠标是在顺时针转还是逆时针转,判定当前角度对于总角度是加还是减。

          这儿说一下unity自带的俩方法:

          Vector3.Angle(Vector3 from,Vector3 to) 这个方法用来计算两个位置相对于坐标(0,0,0)的夹角,返回的值始终在[0,180]之间。

          Vector3.Cross(Vector3 lhs,Vector3 rhs)这个方法用来计算两个位置的叉乘结果,返回一个Vector3 v3,计算顺逆时针是在平面内进行的,忽略y轴,则v3.z>0,rhs在lhs的顺时针方向;v3.z = 0,rhs跟lhs平行;v3.z<0,rhs在lhs的逆时针方向。

          (关于点乘叉乘更详细的介绍戳: http://blog.csdn.net/liqiangeastsun/article/details/50331933)


          复位很简单,就是监测到鼠标放开方向盘时让之前说的记录旋转的总角度递加或者递减至0,让方向盘角度等于这个总角度就可以了。

          以下是代码:

    public Canvas CanvasRoot;//画布 
        private RectTransform m_RectTransform;//坐标
    
        private bool m_IsFirst = true;           //用于记录第一帧按下鼠标时鼠标的位置,便于计算
        private Vector3 m_CurrentPos;            //记录当前帧鼠标所在位置
        private bool m_IsClockwise;              //是否顺时针
        private float m_RoundValue = 0;          //记录总的旋转角度 用这个数值来控制一圈半
        private bool m_IsTuringSteeringWheel;    //是否在转方向盘 用这个判断复位
    
        void Start()
        {
            CanvasRoot = GameObject.Find("Canvas").GetComponent<Canvas>();
            m_RectTransform = CanvasRoot.transform as RectTransform;
        }
    
    
        void Update()
        {
    
            if (Input.GetMouseButton(0) && EventSystem.current.currentSelectedGameObject == gameObject)                 //当鼠标点击到方向盘时
            {
                m_IsTuringSteeringWheel = true;
                Vector2 pos;
                if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_RectTransform, Input.mousePosition, CanvasRoot.worldCamera, out pos))    //获取鼠标点击位置
                {
    
                    pos.x = pos.x + (Screen.width / 2) - GetComponent<RectTransform>().position.x;
                    pos.y = pos.y + (Screen.height / 2) - GetComponent<RectTransform>().position.y;
    
                    Vector3 pos3 = new Vector3(pos.x, pos.y, 0);                           //计算后鼠标以方向盘圆心为坐标原点的坐标位置
    
                    if (m_IsFirst)
                    {
                        m_CurrentPos = pos3;
                        m_IsFirst = false;
                    }
    
                    Vector3 currentPos = Vector3.Cross(pos3, m_CurrentPos);             //计算当前帧和上一帧手指位置 用于判断旋转方向
                    if (currentPos.z > 0)
                    {
                        m_IsClockwise = true;
                    }
                    else if (currentPos.z < 0)
                    {
                        m_IsClockwise = false;
                    }
    
                    if(m_CurrentPos != pos3)                                 //范围内让方向盘随着手指转动
                    {
                        if(m_IsClockwise)
                        {
                            if (m_RoundValue <= 540)
                            {
                                m_RoundValue += Vector3.Angle(m_CurrentPos, pos3);
    
                                transform.Rotate(new Vector3(0, 0, -Vector3.Angle(m_CurrentPos, pos3)));
                            }
                        }
    
                        else
                        {
                            if (m_RoundValue >= -540)
                            {
                                m_RoundValue -= Vector3.Angle(m_CurrentPos, pos3);
    
                                transform.Rotate(new Vector3(0, 0, Vector3.Angle(m_CurrentPos, pos3)));
                            }
                        }
    
                    }
                    m_CurrentPos = pos3;
    
                }
    
            }
            if (Input.GetMouseButtonUp(0))
            {
                m_IsFirst = true;
                m_IsTuringSteeringWheel = false;
            }
    
            if (!m_IsTuringSteeringWheel && m_RoundValue != 0)               //复位
            {
                if (m_RoundValue >= 0)
                {
                    m_RoundValue -= 8f;               //复位速度
                    if (m_RoundValue < 0)
                        m_RoundValue = 0;
                    transform.rotation = Quaternion.Euler(new Vector3(0, 0, -m_RoundValue));
                }
                else
                {
    
                    m_RoundValue += 8f;
                    if (m_RoundValue > 0)
                        m_RoundValue = 0;
                    transform.rotation = Quaternion.Euler(new Vector3(0, 0, -m_RoundValue));
    
                }
            }
        }
    }

    展开全文
  • Unity开发 罗技方向盘 G29 白话版

    千次阅读 多人点赞 2019-04-20 00:42:53
    Unity开发罗技方向盘 大白话版欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中...

    目录

    前言

    最近在做罗技G29的方向盘的Unity开发,因为设备很贵(2000+)所以在买之前先进行了一波调研,看看用Unity开发的可行性。在翻看大量的文章之后我发现大多数文章的内容都是复制粘贴的,很蠢。在实际的开发中遇到了许多坑,我自己一点点爬了出来。想把这些个人的心得写出来,省的后人再去踩这些坑。

    居然还有好多文章讲解用Unity的InputManager来调节摁键。人家罗技都提供SDk了 还教别人用Input.GetKeyDown(KeyCode.JoystickButton0) 这种方法。看着就气。还有什么Unity最多支持24个摁键的 离合要自己写的。。。不要坑我们萌新了好么。

    萌新一名 有错误还请大神指点。
    就是因为萌新 好多东西需要百度,百度一个不会的 会出现三个不会的名词来给你解释,到头来不会的东西更多了,所以这篇文章会以掰皮说陷的方式讲解

    不要问我 为什么要这样做,这么做对了就行 详细的自己去看英文文档去

    准备工作

    罗技官网下载最新版的SDK
    我在用的时候是8.75.30版 可以参考下

    Asset Store下载Logitech Gaming SDK
    我在开发时是1.7版本
    把这个在AssetStore下载号的包 导入Unity
    注意第一个坑
    找到罗技官网下载的SDK文件夹 用该文件夹下的LogitechSteeringWheelEnginesWrapper.dll文件 替换掉 你的工程名\Asset\Logitech SDK\目录下 同名文件
    目前版本AssetStore上的SDK有这个问题 不替换掉会报错,而且这个问题在上一个版本的SDK中也存在(就是罗技的人懒得改)。未来可能会在某个版本修复这个问题吧。

    下载Logitech游戏软件
    这里第二个坑
    罗技这个驱动软件越做越烂。 这里尽量下载 Logitech游戏软件 不要选择Logitech G Hub
    G Hub 下载不稳定有的时候会很慢 而且有的时候会识别不到方向盘,所以还是选择老版本的Logitech游戏软件吧
    这个坑在于这个软件是 区分Windows版本的 win7 win8 win10 32位 64位 会有区别 请选择跟自己电脑适配的版本
    我这里开发使用是 Win10 64位

    测试阶段

    开始测试时 请先打开Logitech游戏软件 保持后台运行,先确保游戏软件已经识别到了 方向盘。
    将\Assets\Logitech SDK\Script Sample下的LogitechSteeringWheel.cs脚本添加到摄影机上(这个文件下的其他脚本是控制其他的输入设备的,有兴趣可以了解一下)。
    运行场景

    一切正常 就会是这个样子的,这时候操作你的方向盘 挡杆 脚踏板 来看看都对应哪些数值吧。VS打开这个脚本 就可以找到对应的代码了。

    运行失败的解决

    红色报错的 / x64 / logitechlcdengineswrapper.dll 类似这样
    按照上面的方法 替换下 logitechlcdengineswrapper.dll 这个文件

    没有红色报错 项目可以运行 但是没有数值 提示
    PLEASE PLUG IN A STEERING WHEEL OR A FORCE FEEDBACK
    CONTROLLER(请插入方向盘或力反馈控制器)
    这种的 请按照一下说明一次排查。

    1. 可能是缺少一下C++基础库
      这个建议你搜索下Aio runtimes 这是一个检测电脑缺什么基础库的软件 很无脑 不点击 30秒后也会自己安装。
      重新启动Unity 项目查看是否正常运行。

    2. 检查下Logite游戏软件是否检测到方向盘,是否可以正常改键(改一个按键 对应鼠标的右键 在桌面试试 )不正常重新插拔设备,检查驱动。

    3. 在Windows下正常 在Unity测试界面仍然显示 请插入设备 并且拔下设备 重新插入设备 Unity的控制台会输出 Joystick disconnected(“G29 Driving force Racing Wheel”)
      Joystick reconnected(“G29 Driving force Racing Wheel”)这种情况请参考
      罗技官方交流社区 终极解决办法
      简单翻译一下就是 卸载Logitech游戏软件 找到设备管理器(我的电脑右键属性-设备管理器)
      找到 声音、视频和游戏控制器 卸载这里的罗技设备驱动 拔掉设备 重启计算机 完整安装一次 Logitech游戏软件 最后重新插入 罗技方向盘。
      我的5个电脑有3台碰到了这个问题 最后靠这条官方解释修好的。据我猜测可能是因为windows自己安装的驱动会跟Logitech游戏软件的驱动有冲突吧。插入设备的时候Windows会自动帮我们装方向盘的驱动,所以我尝试在离线状态安装 Logitech游戏软件 插入方向盘 最后都好用了。也许是玄学吧。总之这个问题困扰了我2天半的时间 最后终于修好了

    接入Unity

    Demo的使用方法

    场景成功运行会得到 上一章图片一样的结果。这时候我们可以操作方向盘 脚踏板 档杆 来查看这些输入对应哪一个值。
    下面还会告诉你 摁 s c d 方向键 等键位调整不同的效果。
    到这里 能力强一点的就可以 自己阅读源码 来更改这些数值 然后用到自己的工程中了
    这里 我仅列出一些比较重要的,震动等可以自行翻看官方文档(SDK包里面就有)
    方向盘 X-axis Position 右32767 初始0 左-32767
    油门 Y-axis Position 踩死-32767 初始0 抬起 32767
    制动 z-axis rotation 踩死 -32767初始0抬起 32767
    离合器 extra axes positions 1 :踩死 -32767初始0抬起 32767
    挡杆 button 12 -18 对应1-6挡+倒挡|

    力反馈 需要在Logitech游戏软件中 设置-G29-允许在游戏中调节力反馈

    在测试中s键开启
    源码中是下面这一句
    参数1 是设备编号(就是第几个设备,我们就1个方向盘所以是第一个 填0)
    参数2是中心位置(Demo里是50 所以你摁下s键 方向盘会自己动。0就是方向盘正中间的位置)
    参数3 参数4 你去翻百度也看不懂的 一个是管松紧 一个管力度(大概)
    总之你可以调试几次 找到自己满意的值。这里33 28 是我比较满意的手感。

    LogitechGSDK.LogiPlaySpringForce(0, 0, 33, 28);
    

    这里Demo中还有好多 可以设置的 比如方向盘的震动等,有需要可以自己一个一个试,参考操作手册。就不一一解释了 关于这方面的博文有好多 基本都是机翻,看个人理解了

    接入自己的项目

    这里就非常简单了 参考Demo的写法
    Awake()或Start()方法中初始化

    LogitechGSDK.LogiSteeringInitialize(false)
    

    Update()方法里检测这些输入
    这里我倾向进行一次封装 在Update里调用Input()
    把检测输入的逻辑写入Input()

    Update()
    {
      //一些其他需要帧调用的方法
      //TODO
        Input()
       //进行封装 更加工整 更易修改
    } 
    

    在Input里进行 设备输入检测

    Input(){
           if (LogitechGSDK.LogiUpdate() && LogitechGSDK.LogiIsConnected(0))
              {
               LogitechGSDK.DIJOYSTATE2ENGINES rec;
               rec = LogitechGSDK.LogiGetStateUnity(0);
               //TODO 这里就可以获取 想要的方向盘数据了
              }   
    }
    

    if判断直接参考Demo写 就是SDK的帧调用判断设备是否接入
    DIJOYSTATE2ENGINES是一个结构体 存储输入设备值的
    这里 rec.lx 就直接得到方向盘旋转数据了。(自己对照Demo找)
    rec.ly油门 rec.lRz刹车

    摁键
    方向盘按键的三个方法 按下 抬起 按住 参数1 设备号 参数2 按键号
    bool LogiButtonTriggered(int index, int buttonNbr); 按键检测
    bool LogiButtonIsPressed(int index, int buttonNbr); 按键释放
    bool LogiButtonReleased(int index, int buttonNbr); 按键按下
    这里建议使用 按住与抬起做检测,因为我做项目的时候 这个按下检测可能会触发多次 不知道是硬件还是软件的问题。抬起就是触发一次 没有问题

    问题与总结

    问题

    这里说结果我见到的提问

    • 游戏进出 油门 离合 刹车 方向盘 初始值都是0 怎么破 必须动一下才正常。
      你设计一个打火摁键不就好了 这样不仅游戏更真实了,还解决了这个问题。
      其实随便摁一个键子都可以的。

    • 有的时候调试调试 方向盘就不好使了。
      罗技文档里有写,方向盘必须在页面最前端才是好用的(就是那个 初始化 判断那块 )所以有的时候我们 切屏或者打开Unity的控制台 就会这样。因为这个时候Game视图不是最前端的视图了。

    • 力反馈没有
      检查你开没开Logitech游戏软件 设置 允许游戏中调节力反馈 勾选没有(这个软件主要管的就是力反馈这些东西,你可以尝试 在游戏运行过程中 关掉这个软件,方向盘一下子就软了。。。。。)

    • 为什么我开车会翻啊?

    1. 你wheelCollider这块没玩好。很多数值需要调试
    2. 你做的太真实了,现实中你这么开业翻车了

    总结

    • 可别再Input.GetButtonDown(“Button3”)
      Input.GetKeyDown(KeyCode.JoystickButton0)
      这样了好么,人家有现成的不用,又蠢又丢人。
    • 这里推荐一篇我觉得写得不错的
      https://blog.csdn.net/Sakura_Jun/article/details/87718845
    • 开车插件
      Realistic Car Controller
      还请大家支持正版。我就不给大家提供了,网盘见去吧,有什么不明白可以问我 大家共同学习。
    • 插件可能会更新 情况也许会不一样,没准那一天罗技修复了呢。

    第一次写 有不对的请赐教,写得不好见谅,我会改进的。

    展开全文
  • 游戏方向盘原理

    千次阅读 2012-10-24 13:59:27
     要简要说明方向盘原理,首先需要说明一下一个概念:控制器的“轴”(AXIS):玩家能在一定的范围内圆滑操作控制器达到类比控制需要,而控制器能将玩家的移动量的位置信息量化以后独立输出一组控

    原文地址:http://hi.baidu.com/zotkyahbdocpsxe/item/3e541f462d906daa61d7b9e4

      方向盘是一种特别为赛车游戏设计的专用游戏控制器。

      要简要说明方向盘原理,首先需要说明一下一个概念:控制器的“轴”(AXIS):玩家能在一定的范围内圆滑操作控制器达到类比控制需要,而控制器能将玩家的移动量的位置信息量化以后独立输出一组控制数据的就叫一个“轴”(AXIS)。

      一个方向盘大约有1-4个控制轴,通常将他们分别叫X、Y、Z和S轴。细分起来又可将每个轴分为“负半轴”和“正半轴”,用“X/RX”或“-X/+X”这样的表示方法分别表示。

      单轴式方向盘:只有一个控制轴,通常用做方向控制,如“–X”表示“方向左”;“+X”表示“方向右”等,而油门和刹车等其他控制仍然用按键实现,这类方向盘通常属于“入门级”产品。

      2轴式方向盘:通常X轴用作方向控制,另一个轴由油门和刹车合用,即“-Y”用做油门,“+Y”用作刹车;这一类方向盘的油门和刹车也是类比控制了,但因为油门和刹车共用一个轴,所以油门和刹车并不能同时起作用。

      3轴式方向盘:方向、油门、刹车各用一个独立的轴,从而实现了分轴控制,也就是说,刹车和油门能同时起作用了。

      4轴式方向盘:在3轴式方向盘的基础上再增加一个轴,用做离合器控制,这一类方向盘通常都属于“发烧级”的了。

      实际进行游戏的时候,控制器是这样工作的:玩家操作控制器的某个“轴”,控制器内部的处理芯片将玩家操作的该轴的位置信息量化后输出给电脑的DirectInput,DirectInput将收到的轴位置信息线性放大到0-65535的范围,游戏主程序则直接从DirectInput获得玩家的操作信息从而实现游戏的操作。

      以上过程中,玩家对某一个轴的操作可能是无极的,但控制器内部处理芯片将轴位置信息量化后不可能是无极的,每一个轴都会用一定的数值范围来代表相应的轴位置信息,然后处理芯片会将这个范围划分成若干段(通常为等分)来表示实际的轴位置信息,“将某一个轴的位置信息量化的级数”就是说,在控制器的输出端,能将某一个轴分成多少个过渡的中间状况,也就是该控制器的某一给定轴的精度,控制器的各轴的精度是控制器固有的内在品质,也是衡量控制器性能的重要指标之一。

      注意量化范围并不一定就是实际精度,比如微软PrecisionWheel方向盘的X轴(方向轴),量化范围是-512←→+512,但它转动方向轴的时候,数值每次并不是以1为单位跳动,而是每次跳动5或6.所以,实际它在-512←→+512这个量化范围内只有100+100(左右)个过渡状况,也就是说它的方向轴的实际精度是100+100左右。而不是512+512.而它的油门和刹车两个轴的量化范围分别都是0←→63,并且是以1为单位跳动的,所以说它的油门和刹车轴的实际精度分别都是64级。

      在实际的游戏控制中,我们可能需要对控制器有一些特殊设置,比如控制器中心位置的校准、呆区(Deadzone)设置、以及某些情况下需要的控制器非线性设置等等。下面就谈谈这些:

      方向盘设置

      一方向盘的安装

      方向的物理安装以及与电脑的电气连接问题从略,请查阅相关产品说明书。

      这里只说一下有关方向盘的驱动的安装问题。目前大部分方向盘都是使用USB接口与电脑实行连接的。当正确连接了方向盘以后,Windows随即就会找到新设备并自动安装。Windows对目前大多数USB接口的方向盘都能提供基本的支持,也就是说,即使不安装方向盘自带的驱动,大部分方向盘也都是可以实现基本功能的,但一些方向盘的特殊功能如果不安装自带的驱动则可能不能正常工作,如某些震动方向盘的震动功能,力回馈功能,以及其他比如微软方向盘的按键编程功能等。正是因为Windows会自动识别出方向盘并立即自动安装了驱动,如果这时候再安装方向盘自带的驱动,某些情况可能会出现意外,如外面流传的“微软自己出的方向盘不能与自己的操作系统兼容”这样的问题等。如果你也遇到“安装了方向盘自带的驱动以后方向盘不能正常工作”这一类似的问题,那么你可以试试如下办法:

      对于USB接口的方向盘,那么在开机的状况下将方向盘连接到电脑的USB插头“热拔”下来,再重新插上,看看是否解决问题。

      如果无效,那么就先卸载方向自带的驱动;然后打开“设备管理器”,刷新(扫描硬件改动)找到你的方向盘设备,卸载它。(这时候别再刷新,否则Windows又会立即重新找到方向盘并自动安装)。这时候再重新安装方向盘自带的驱动,完成后再打开“设备管理器”,“扫描硬件更新”以便找到你的方向盘,再试试看,可能这时候方向盘就能正常工作了。其实,这个办法的实质是:“先安装方向盘带的驱动,后连接方向盘”,如果你先连接方向盘后装驱动出现问题,不妨试试这个办法。

      未安装方向盘自带的驱动而是让Windows自动识别出的方向盘,通常在“控制面板/游戏控制器”里,可以进行方向盘的校准工作,但安装了驱动以后,Windows里的这个校准功能就“消失了”,而很多方向盘自带的驱动并没有提供手动校准功能,这时候如果你需要对方向盘进行手动校准,可能就需要使用其他的工具了(后文有专述),如果你的控制器能被Windows自动识别并能使用方向盘的基本功能,并且你也不想使用方向盘驱动带来的“额外的功能”(比如微软的按键编程功能),那么我推荐你不必安装方向盘带的驱动而直接使用它。

      二方向盘的校准

      1.校准的原理

      前面说过,“…DirectInput将收到的轴位置信息线性放大到0-65535的范围…”方向盘输出的轴位置信息在送交DirectInput前是必须经过校准程序校准的,没有经过任何校准的控制器是不能工作的,校准程序所做的事情其实是告诉DirectInput,控制器所发送过来的轴位置数据,哪里是最小(Min)、哪里是最大(Max)、哪里是中点(Cen),而DirectInput并不追究Min、Cen、Max为什么会是这些指定的数值,它只管把得到的Min←→Cen之间的数据线性放大到0←→32768.而把Cen←→Max之间的数据线性放大到32768←→65535。任何低于Min的数据都当作Min处理,任何高于Max的数据都当作Max处理。如此,我们就可以通过特意指定不同的Min、Cen、Max的数值来达到合理使用控制器的行程的目的,一个总的原则是:减少控制的有效行程来使控制器显得更加灵敏;而扩充(到)控制器的最大行程以获得最高的控制精度。

      如下图,某方向盘输出端的量化数据范围是-512←→+512,通过校准以指定不同的Min、Cen、Max数值时,方向盘的工作情况,每图中上方箭头表示游戏中的虚拟方向盘,下方箭头表示游戏控制器方向盘的物理行程。

      左上(校准行程=物理行程):Min=-512,Cen=0,Max=512,这时候的方向盘操作与游戏中的虚拟方向盘正好是一一对应的。既使用了控制器方向盘的全部精度,又能达到最大的控制范围。此为使用得最为普遍的一类校准方式。

      右上(校准行程物理行程):此为一种特殊的校准方式。通过人工指定Min、Cen、Max的数值,使它大于控制器所能输出的数值,这时候的控制器方向盘的全部行程只对应游戏中虚拟方向盘的一部分行程,因为控制器无法输出超出它的量化范围的那一部分数据,所以,游戏中的虚拟方向盘就永远也打不到极限位置,此类校准方式的理念是“集中所有能输出的控制精度来控制游戏中虚拟方向盘的一部分行程”,也就是说“牺牲控制范围以获取更大的控制精度”,一般只用于特定场合。

      上述校准都属于行程校准,并且,上面所例均为左右对称式的校准方式,这里都是假设方向盘操作的物理行程与所输出的量化数据之间的对应关系是准确的,也就说,当方向盘自动回中的时候的自然位置,输出的数据正好是它所能输出的最大量化范围的中点,同时方向盘转到左右两个极限位置的时候输出的也正好是最大和最小的数据。而实际上受制造工艺的限制,以及随着使用过程造成的磨损等等多种因素的影响,很多方向盘并不(总)是那么的准确。但是,只要我们明白了校准原理,这些问题都是可以通过校准来改善的。

      例:假设某方向盘的方向轴的理论输出数据范围是-512←0→+512,而实际使用中测试到该方向盘自动回中时的自然位置输出的数据是20,如果这时候仍然指定Min=-512,Cen=0,Max=512的话,那么在游戏中,因为其自然的中心位置输出的数据并不是0,而是20,该方向盘就会总是偏向一边,这时候的解决办法是通过校准指定其Cen=20.就能解决问题了。

      还有一类校准问题是“呆区”(Deadzone)设置,所谓“呆区”,是指控制器模拟真实方向盘的自由间隙,也就是说,在其有效行程的某一部分输出的数据不做处理,比如方向盘的方向轴的呆区,是指其自由回中的位置左右的一小段行程操作会在游戏中没有反应,用以模拟方向盘的自由间隙,呆区的数值一般用百分比表示,比如方向轴的呆区如果设置成10%,则是表示,方向盘在其中间位置的,其总有效行程的10%的行程范围为呆区,方向盘在该区域范围内的任何移动都被当作“方向正中”处理。其内在的含义实际是指定Cen为一定范围内的数据,而不是指定Cen为单一的具体数据。呆区设置为0%即为没有呆区。

      请注意,在WindowsNT/2000/XP操作系统中不支持保存呆区设置,而在Windows98/ME中是可以的。

      以上说的校准和呆区设置,都是指的在进入游戏前,使用Windows的校准程序、方向盘自带的驱动中的校准程序、以及第三方开发的专用的游戏控制器校准程序进行的校准工作,这些工作实际上都是在控制器输出的数据送交到DirectInput之前所做出的处理,而很多游戏在进入游戏以后也提供各种有关的控制器设置和校准功能,因为游戏在启动后可以通过“驻留处理”或者在游戏主程序中对DirectInput数据进行实时处理,这样就当然不受上述的“NT类操作系统不支持呆区设置”的限制了。

      2.校准的操作

      明白了校准的原理以后,下面再来说说校准的实际操作,我这里将校准分为“自动校准”、“半自动校准”、“手动校准”三种情况来叙述。

      首先说说半自动校准:这里以Windows“控制面板/游戏控制器/控制器属性/校准”提供的校准程序为例说明半自动校准的操作(大部分方向盘如果安装了盘带的驱动,则屏蔽掉了控制面板里的这个校准功能而用驱动程序的相关设置界面取而代之了)

      在提示“将手柄转动几圈,然后按控制器上的按钮”的时候,勾寻显示原始数据”可以看到控制器输出的实际数据。这时候,转动方向盘,输出的数据会实时显示出来,注意,在这个过程中,校准程序会将实际输出的所有数据的最小部分设置为Min,而将收到的最大的数据设定为Max,在“下一步”即“将手柄放在中间并按控制器上的按钮的时候”,它将你那时候的控制器位置输出的实际数据设置为Cen。这里实际上就提供了比较完善的手动校准功能了,如果你想做一个如前文所述的“校准行程<物理行程”的校准方式,那么在转动方向盘的时候,你就不要把方向盘转到头,而只转到你想要设置的位置就可以了。比如你的方向盘实际物理行程有左右各90度,而你想只用左右各60度,那么在“将手柄转动几圈,然后按控制器上的按钮”的时候,你只将方向盘转动到左右各60度,然后就按钮确定就可以了。半自动校准程序通常可以进行“校准行程=物理行程”和“校准行程<物理行程”这两类方式的校准。另外说明一下,某些游戏里在进入游戏后提供的控制器校准功能基本也属于这一类。操作类似。

      自动校准:某些方向盘具备自动校准功能(比如“微软方向盘”),它会在每次初始化方向盘的时候[比如系统开机、重启、方向盘接头的“热插拔”(USB接口类的)等]自动将方向盘当前位置设置为控制器的默认位置。对于方向轴,初始化方向盘的时候,你的方向轴实际所在的位置就被设置为方向轴的中心(Cen)。而踏板,则自动将当前踏板位置设置为踏板的行程起点。所以,这一类方向盘在初始化的时候只需让其处于自然位置即可(通常是:方向盘处于方向控制的自然回中位置,而踏板则处于未踏下的位置),“自动校准”功能会自动按这些位置进行校准。(有兴趣的朋友可以测试:初始化的时候故意让它不在自然位置,看看会有什么结果)

      另外有一些并不具备真正的“自动校准”功能的方向盘,只是在初始化方向盘的时候指定Min和Max分别为该控制器理论上(设计上)所能输出的数据的最小和最大的数据,并指定Cen=Min+(Max-Min)/2。也就是说,它实际上并没有进行任何真正的“校准”动作,而是把Min、Cen、Max分别指定成“内定的”数值通知DirectInput。同时并不提供(或不完整提供)用户进行手动校准的界面,这些方向盘在并没有真正的“自动校准”功能的前提下还想“使产品使用起来显得更加简洁容易”,结果是一旦控制器因为某种原因发生校准问题的时候,使得想进行手动校准的用户无从下手。这应归咎于厂家对自己的产品过于自信。如果这时候卸载方向盘带的驱动,转而用Windows的控制面板的校准程序,又会失去方向盘的一些特殊功能。如果游戏里提供控制器的校准倒还好,可以启动到游戏里再进行校准工作,问题是某些游戏也没有提供完整的控制器校准功能(比如F1C就没有控制器的行程校准和中点校准功能),我们只能对部分厂家的这种盲目自信行为表示遗憾!

      手动校准:遗憾归遗憾,游戏我们还是得玩,该校准的控制器我们也还是得想办法校准。如果安装了方向盘自带的驱动以后找不到手动校准的界面了,并且所玩的游戏里也没有提供完善的校准功能,那么我们就得借助其他的专用的校准工具了,这里介绍大家使用一个叫“DXTweak”的工具,这实际上是Logitech公司在其网站上提供的一个控制器校准工具,(可见Logitech作为知名的外设大厂,是有其大厂风范的,后勤工作做的还是比较到位)。

      这是一个小巧易用的工具,只有一个文件:DXTweak2.exe,免安装,直接双击运行即可。见图:

      启动DXTweak后,在左上的“Polleddevice”下应该就能找到你已经安装了的控制器了,如果你安装了多个游戏控制器,可点击“Next”进行选择。

      转动你的方向盘,这时候可看到界面的左边会用数据和滑杆图像同时显示你的控制器各轴的工作状况,其中value数据就是DirectInput的实际数据了,而Raw数据就是控制器实际输出的量化数据,界面的右边提供了手动指定Min、Cen、Max数值的功能,使用这个工具校准控制器非常简单,以校准方向盘的中心位置为例说说具体的操作方法:转动方向盘的方向轴,查看界面上是哪个轴对应的数据在动(方向轴通常是X轴),这样你就能知道你所操作的轴对应的是界面上的哪个轴了,然后把方向盘放在它的自然中点,看看Raw数据是多少,然后点击右边的X轴,在Cen处输入该数值再点Apply就OK了。如此简单!

      工具的左下有一个“MyTweaks”部分,提供的是把你的校准方案用一个你自己取的名字保存起来的功能,这为需要使用好几种不同的校准方案并时常切换的玩家提供了方便,注意这里当你Save或Load某方案的时候它会同时就自动Apply了。

      注意事项:该工具的在WindowsNT/2000/XP等NT类操作系统下设置的Deadzone(呆区)不能被保存(这其实是操作系统内部结构的原因,与该校准程序无关);该工具的自述文件里建议用户不要在游戏运行的时候使用该工具,对于校准来说,Apply以后就可以退出它并进入游戏了,如果在游戏的时候让它仍然保持运行,一则它非常消耗系统资源,二则,如果游戏运行的时候你在DXTweak里更改了校准设置,那么控制器在游戏里可能会发生工作异常,甚至有可能找不到控制器了。(详细说明可点击它的Readme按钮查看)

      (特别鸣谢-Z-先生将DXTweak推荐和介绍给我们)

      关于方向盘在游戏前的通用校准事项,基本就这么多了,至于各方向盘自带的驱动里提供的其他一些特殊功能,因为本人使用过的控制器非常有限,无法一一给大家进行详细的介绍。请大家查阅各自的控制器说明书以获得相关信息。这里只以微软PrecisionWheel为例简单介绍一下具有代表性的关于“分轴”的问题。微软PrecisionWheel共有三个能独立输出的轴,属于三轴式方向盘,但微软在它的驱动程序里为油门轴和刹车轴提供了两种不同的工作方式

      这两种工作方式分别叫做separate和combined其中separate就是我们说的“分轴”模式,即两个踏板是分开独立工作的,这种方式下是标准的三轴式方向盘模式,油门和刹车在游戏里能独立分开地起作用而互不影响。而combined模式的意思是把油门和踏板组合成一个轴,这种模式下–Y轴为油门,+Y轴为刹车,这样实际上也就是把这个方向盘变成了一个标准的两轴式方向盘了,这种模式下油门和刹车不能在游戏里同时互不影响地独立工作。在这里我推荐大家使用separate也就是“分轴”模式,因为任何真实的汽车中,油门踏板和刹车踏板都是独立工作的,不会互相牵扯,同时,分轴模式还能使用某些特殊的驾驶技巧(如“跟趾”)

      一个特别的现象是:微软的方向盘在Win9x/ME和Win2000操作系统里,如果不安装方向盘带的驱动而让Windows直接认出来,那么它的油门和刹车踏板是工作在“分轴”模式的(不可选择)而在WinXP里,如果不装驱动,它们却是工作在组合模式的(同样不可选择).据悉还有某些其他的三轴式方向盘也在驱动里为两个踏板轴提供了类似的两种工作模式)。

      从上图可以看到,这里还有一些简单的与校准有关的设置,如右边的所谓Sensitivity(灵敏度)设置,实际上是控制器有效行程设置,当设置到最左边Low的位置的时候相当于使用控制器的全部有效行程,也即是前文所述的“校准行程=物理行程”的意思,而越往右边High方向设置,则使用越少的控制器物理行程,这时候相当于前文说的“校准行程<物理行程”的校准方式,即方向盘会显得更加灵敏(但控制精度下降),而左边的DeadZone一目了然就是常说的“呆区”设置了,需要注意的是这个设置同样是只能在Win9x/ME下正常工作,如果你使用Win2000/XP等NT类操作系统,那么这个设置并不能正常有效。  

    展开全文
  • 罗技方向盘SDK开发笔记

    千次阅读 2020-03-16 15:46:37
    这段时间因为项目需求,接触到了罗技G29方向盘的SDK开发,能够参考的资料比较有限,一路磕磕碰碰遇见不少问题,硬着头走了下去,不过最后还是成功了,写下这篇笔记来记录下我的开发过程,也给有需要的人参考,少走点...

    这段时间因为项目需求,接触到了罗技G29方向盘的SDK开发,能够参考的资料比较有限,一路磕磕碰碰遇见不少问题,硬着头走了下去,不过最后还是成功了,写下这篇笔记来记录下我的开发过程,也给有需要的人参考,少走点弯路

    一.开发环境和开发工具
    开发环境:win10
    开发工具:vs2017
    方向盘型号:罗技G29

    开发前的准备
    (1).去罗技官网上下载罗技方向盘SDK
    https://www.logitechg.com.cn/zh-cn/innovation/developer-lab.html
    文件中有相关的.h和.lib文件,以及三个mfc例程以及相关的说明文档

    (2).下载罗技游戏软件
    这里要说一下,在SDK文档里提出了方向盘得在罗技游戏软件运行的情况下才能进行相关的SDK开发,所以这个软件在开发中需要全程运行,注意下载后它会提醒你下载新的罗技 G support,别理它就是了,罗技 G support根本就识别不出来罗技G29方向盘.
    下载链接:https://support.logi.com/hc/zh-cn/articles/360025298053

    (3).检测方向盘是否正常工作
    正常情况下,在方向盘上电并接入电脑后,方向盘会自动旋转几圈然后拨正,打开罗技游戏软件的时候也会有这个现象,接好方向盘并且打开罗技游戏软件,我们来通过官方提供的demo来检测一下方向盘是否能够正常工作.
    先介绍一下这几个demo
    在这里插入图片描述第一个是个比较完善的demo,打开后的界面大概如下
    在这里插入图片描述正常情况下,在点击INIT按钮后,界面上就会出现方向盘的相关数据,这里我没有接入方向盘,所以就没有显示,我们需要记录下一些数据,首先是确认下数据是出现在哪块区域的,就是divice 0 还是divice 1,这关系到后面相关api的调用参数,还有就是按下方向盘上的十字按键后,是在POV 0 还是POV 1显示数据的理由同上。

    第二个demo我也没搞懂究竟是干什么用的,不过我这边的开发也用不到这个demo,感兴趣的可以去自己研究

    第三个demo比较有意思,这个demo是罗技官方提供给开发者用来测试SDK中提供的相关API是否能够正常工作的MFC程序

    在这里插入图片描述具体的代码逻辑就是你点击相关的API按钮,它就会调用相关的API,然后将返回结果显示在下方的信息栏里面,不过这个demo没有写完整,很多按钮的功能都还没有实现,你点击的话它会提示这个功能还没有写完(???,官方拖更),不过并不影响我们正常获取方向盘数据

    二.阅读SDK文档,弄清相关的API调用顺序

    (1).这里我就简单介绍一下几个关键的函数,首先是两个初始化函数
    在这里插入图片描述bool LogiSteeringInitialize(CONST bool ignoreXInputControllers)
    这个初始化函数会自动寻找当前处于最前端的窗口句柄并传入给这个函数进行方向盘的初始化,不过我在实际使用中这个函数经常抽风,返回值一直不稳定,所以我在后面的程序开发中舍弃了使用这个初始化的打算

    bool LogiSteeringInitializeWithWindow(CONST bool ignoreXInputControllers, HWND hwnd)

    这个函数的效果和上一个函数是一样的,第一个参数是是否忽略X-IPU的参数,第二个参数是当前程序的窗口句柄,这个函数的不同之处在于我们需要手动将你写的程序界面的句柄传入到这个函数里面去,那么什么是句柄呢?
    我也看了不少资料,我是把它理解为界面程序的类似pid号的一个东西,让windows系统能够找到你的界面程序,这是我遇见的第一个坑,当初我想如果用windows编程创建一个窗口的话太麻烦了,底层的代码又臭又长,想直接通过获取win32控制台程序的句柄传入到这个函数里面去,但初始化结果一直都是失败的,折腾了很久一直都没有搞定,最好还是老老实实写了一个空界面来初始化,值得一提的是,这个初始化函数要求当前界面处于所有应用的最顶端,意思就是如果你把这个界面缩小,获取点击其他界面后,这个程序就拉跨了,所以在使用的时候要确保这一点

    (2).相关API的调用顺序
    这里贴一张图,是来自于其他博主的,使用的C#的sdk,不过流程都是一致的
    在这里插入图片描述在C/C++使用的时候直接把前面的类名去掉即可,然后将初始化的函数换成
    LogiSteeringInitializeWithWindow,值得一提的是,如果方向盘初始化成功的话,方向盘的旋转阻力会变成0,所以可以通过这个方法来判断方向盘是否初始化成功.

    三.搭建开发环境

    创建一个空的windows桌面应用程序,并且将SDK包中提供的头文件和库文件复制到项目目录下面,并且配置好,具体的配置过程可以参考下面
    在这里插入图片描述然后在你的main.cpp里面添加

    #pragma comment(lib, "LogitechSteeringWheelLib.lib")
    

    先贴一下完整的代码,我这个程序主要是获取方向盘的基础数据,转向,油门,刹车等信息后上传到局域网内的服务器上

    #include <Windows.h>
    #include <thread>
    #include <stdio.h>
    #include <iostream>
    #include<winsock.h>
    
    #include "LogitechSteeringWheelLib.h"
    
    #pragma comment(lib,"ws2_32.lib")
    #pragma comment(lib, "LogitechSteeringWheelLib.lib")
    #pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")
    
    
    using namespace std;
    
    
    
    void initialization() {
    	//初始化套接字库
    	WORD w_req = MAKEWORD(2, 2);//版本号
    	WSADATA wsadata;
    	int err;
    	err = WSAStartup(w_req, &wsadata);
    	if (err != 0) {
    		cout << "初始化套接字库失败!" << endl;
    	}
    	else {
    		//cout << "初始化套接字库成功!" << endl;
    	}
    	//检测版本号
    	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
    		cout << "套接字库版本号不符!" << endl;
    		WSACleanup();
    	}
    	else {
    		//cout << "套接字库版本正确!" << endl;
    	}
    	//填充服务端地址信息
    
    }
    
    void SendMessage(int x, int y, int z)
    {
    	//定义长度变量
    	int send_len = 0;
    	int recv_len = 0;
    	//定义发送缓冲区和接受缓冲区
    	char send_buf[100];
    	char recv_buf[100];
    	//定义服务端套接字,接受请求套接字
    	SOCKET s_server;
    	//服务端地址客户端地址
    	SOCKADDR_IN server_addr;
    	initialization();
    	//填充服务端信息
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");
    	//server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    	//server_addr.sin_port = htons(2020);
    	server_addr.sin_port = htons(1888);
    	//创建套接字
    	s_server = socket(AF_INET, SOCK_STREAM, 0);
    	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
    		//cout << "服务器连接失败!" << endl;
    		WSACleanup();
    	}
    	else {
    		//cout << "服务器连接成功!" << endl;
    	}
    
    	//发送,接收数据
    	
    		//cout << "请输入发送信息:";
    		//cin >> send_buf;
    		sprintf_s(send_buf, "%d,%d,%d", x, y, z);
    		send_len = send(s_server, send_buf, strlen(send_buf), 0);
    		if (send_len < 0) {
    
    			cout << "发送失败!" << endl;
    			
    		}
    		printf("x = %d,y = %d,z = %d\n", x, y, z);
    		Sleep(500);
    		
    		/*recv_len = recv(s_server, recv_buf, 100, 0);
    		if (recv_len < 0) {
    			cout << "接受失败!" << endl;
    			break;
    		}
    		else {
    			cout << "服务端信息:" << recv_buf << endl;
    		}*/
    
    	
    	//关闭套接字
    	closesocket(s_server);
    	//释放DLL资源
    	WSACleanup();
    
    }
    
    
    void WheelInit(HWND hwnd)
    {
    
    	//LogiSteeringInitialize(true);
    	
    		if (LogiSteeringInitializeWithWindow(true, hwnd))
    		{
    			
    				printf("init secuss\n");
    				//printf("%d\n", hwnd);
    				while(LogiUpdate() && LogiIsConnected(1))
    				{
    						//printf("connect secuss\n");
    						Sleep(100);
    						DIJOYSTATE2 * wheel = LogiGetState(1);
    						//输出角度,油门,刹车信息
    						
    						//printf("Angle = %d  Accelerator = %d  Brake = %d\n", wheel->lX, wheel->lY,wheel->lRz);
    						SendMessage(wheel->lX, wheel->lY, wheel->lRz);
    						//printf(wheel->rgdwPOV)
    						//std::cout << wheel->rgdwPOV[0] << endl;
    						switch (wheel->rgdwPOV[0])
    						{
    						case(0):
    							cout << "D-pad Up Button Pressed" << endl; break;
    						case(18000):
    							cout << "D-pad Down Button Pressed" << endl; break;
    						case(27000):
    							cout << "D-pad Left Button Pressed" << endl; break;
    						case(9000):
    							cout << "D-pad Right Button Pressed" << endl; break;
    						case(31500):
    							cout << "D-pad Left-up Button Pressed" << endl; break;
    						case(4500):
    							cout << "D-pad Right-up Button Pressed" << endl; break;
    						case(22500):
    							cout << "D-pad Left-down Button Pressed" << endl; break;
    						case(13500):
    							cout << "D-pad Right-down Button Pressed" << endl; break;
    
    						
    						}
    						
    						if (LogiButtonTriggered(1, 19)) 
    						{
    							printf("-----------------------\n");
    								printf("Button 19 Pressed\n");
    							printf("-----------------------\n");
    						}
    
    						if (LogiButtonTriggered(1, 20))
    						{
    							printf("-----------------------\n");
    							printf("Button 20 Pressed\n");
    							printf("-----------------------\n");
    						}
    
    						if (LogiButtonTriggered(1, 23))
    						{
    							printf("-----------------------\n");
    							printf("Button 23 Pressed\n");
    							printf("-----------------------\n");
    						}
    						
    
    				}
    			
    		}
    		else
    		{
    			printf("init faild");
    		}
    	
    }
    
    
    // 必须要进行前导声明  
    LRESULT CALLBACK WindowProc(
    	_In_  HWND hwnd,
    	_In_  UINT uMsg,
    	_In_  WPARAM wParam,
    	_In_  LPARAM lParam
    );
    
    int CALLBACK WinMain(
    	_In_  HINSTANCE hInstance,
    	_In_  HINSTANCE hPrevInstance,
    	_In_  LPSTR lpCmdLine,
    	_In_  int nCmdShow
    )
    {
    	// 类名  
    	TCHAR cls_Name[] = L"My Class";
    	// 设计窗口类  
    	WNDCLASS wc = { sizeof(WNDCLASS) };
    	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    	wc.lpfnWndProc = WindowProc;
    	wc.lpszClassName = cls_Name;
    	wc.hInstance = hInstance;
    	wc.style = CS_HREDRAW | CS_VREDRAW;
    
    	// 注册窗口类  
    	RegisterClass(&wc);
    
    	// 创建窗口
    	HWND hwnd = CreateWindow(
    		cls_Name,           //类名,要和刚才注册的一致  
    		L"方向盘Demo",          //窗口标题文字  
    		WS_OVERLAPPEDWINDOW,        //窗口外观样式  
    		38,             //窗口相对于父级的X坐标  
    		20,             //窗口相对于父级的Y坐标  
    		500,                //窗口的宽度  
    		500,                //窗口的高度  
    		NULL,               //没有父窗口,为NULL  
    		NULL,               //没有菜单,为NULL  
    		hInstance,          //当前应用程序的实例句柄  
    		NULL);              //没有附加数据,为NULL  
    	if (hwnd == NULL)                //检查窗口是否创建成功  
    		return 0;
    
    	// 显示窗口  
    	ShowWindow(hwnd, SW_SHOW);
    
    	// 更新窗口  
    	UpdateWindow(hwnd);
    
    	SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); //设置窗口最前端
    	thread GetWheelData(WheelInit, hwnd);
    	GetWheelData.detach();
    	
    	
    
    
    
    
    	//MessageBox(0, "调用了WinMain函数", "测试:", 0);
    
    
    	// 消息循环  
    	MSG msg;
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return 0;
    }
    
    // 在WinMain后实现  
    LRESULT CALLBACK WindowProc(
    	_In_  HWND hwnd,
    	_In_  UINT uMsg,
    	_In_  WPARAM wParam,
    	_In_  LPARAM lParam
    )
    {
    
    	switch (uMsg)
    	{
    	case WM_DESTROY:
    	{
    		PostQuitMessage(0);
    		return 0;
    	}
    	case WM_PAINT:
    	{
    		/*PAINTSTRUCT     ps;
    		HDC hdc = BeginPaint(hwnd, &ps);
    		int length;
    		TCHAR buff[1024];
    		length = wsprintf(buff, TEXT("the angle is : %d"), x);
    		TextOut(hdc, 20, 20, buff, length);
    		EndPaint(hwnd, &ps);
    		break;*/
    
    	}
    	default:
    		break;
    	}
    	return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    

    方向盘的基础信息是整合在一个 DIJOYSTATE2的结构体里面,然后通过

    DIJOYSTATE2* LogiGetState(const int index);
    

    方法来返回该结构体变量,然后输出变量成员来获取数据,这个函数的传入参数是设备的ID号,就是我在前面提到的divice 0 还是divice 1

    提一下方向盘按键数据的获取,主要是通过

    bool LogiButtonTriggered(const int index, const int buttonNbr);
    

    获取的,第一个参数的设备ID,第二个参数是按键编号,具体的编号可以打开罗技游戏软件,然后看方向盘上的编号,不过要注意一下,实际上调用的编号是软件上显示的编号-1。

    最后推荐一篇其他人的笔记,写的比我详细多了,想要深入研究的可以参考一下这篇文档

    G29开发笔记


    8.11日更新

    有同学反应只下载罗技游戏软件的话运行demo会识别不到方向盘,我看了下好像是软件版本的问题,如果使用的是最新版的罗技游戏软件的话就去官网上下载最新的罗技G hub,运行demo的时候把两个软件都打开就可以解决了

    展开全文
  • 炫龙笔记本从u盘启动有两种方法,一... 炫龙笔记本u盘启动快捷键是F12,下载u盘启动制作工具,将制作好的快启动u盘(u盘启动制作教程)连接到电脑后,重启电脑,看到开机画面后狂按F12会出现一个启动项顺序选择的窗
  • 使用宏碁笔记本的小伙伴在u盘装系统的时候,使用u盘启动制作工具制作启动都觉得很简单,但一提到设置u盘启动就蒙逼了,其实宏碁笔记本设置u盘启动并没有那么难,今天快启动小编就详细为大家介绍宏碁笔记本...
  • WatchOS开发教程之六: 表盘功能开发

    千次阅读 2018-09-03 18:32:49
    上面的方法, 定义了 TimeTravel 的方向, 过去还是未来, 或者两者都是。 optional public func getTimelineStartDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Swift.Void) ...
  • 新版Dell本本BIOS设置完全手册  目前,Dell品牌旗下的笔记本电脑分为4个系列:针对普通家庭用户的Inspiron系列、针对高端个人用户的XPS系列、商用笔记本电脑中,有面向大中型企业的Latitude系列,以及今年新推出的...
  • 参考: 1.在UBUNTU中使用北通USB游戏手柄 2.Linux获取/dev/input目录下的event对应的设备 1.cat命令 ... cat –help可以查看cat帮助信息,如各种参数使用方法,当然也可以用man cat来查看,建议大家...
  • 【MuMu模拟器】玩崩坏3的左摇杆(方向键)设置问题解决方法问题(Bug)描述版本说明具体情况功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的...
  • 但是这个文件目录太大,可能一个G,甚至更,导致c都快满了。 这可怎么办呢。 现在就来找解决方案。 如上图,在c会出现个类似这个文件夹的东西,占空间很大,可能超过一个G。默认的下面可能会有config和...
  • 1. 方向盘事件转换 假设方向盘是通过lin总线转换的,最终来到安卓侧就是标准的keyevent: /** Key code constant: Play/Pause media key. */ public static final int KEYCODE_MEDIA_PLAY_PAUSE= 85; /** Key code ...
  • 这篇文字是转载的,地址一套键鼠操控台电脑–Mouse Without Borders 设置教程 一套键鼠控机+文件秒传 微软神器《Mouse Without Borders》实战 你或许遇到过类似的问题: ●咱同时用好几台电脑,有的看资讯,...
  • 一套键鼠操控台电脑--Mouse Without Borders 设置教程

    万次阅读 多人点赞 2017-03-21 23:15:21
    一套键鼠控机+文件秒传 微软神器《Mouse Without Borders》实战 你或许遇到过类似的问题: ●咱同时用好几台电脑,有的看资讯,有的炒股,有的玩游戏,任务并行!虽然很酷,但也觉得切换来切换去的有些麻烦。 ●...
  • 比如说美版笔记本thinkpad呀、xps啊,价格优惠大,但是装盗版系统会丧失很必要的功能,装正版价格昂贵,资源难寻,而且nvme硬盘装机有诸多限制,不按一定方法安装,反而会让nvme硬盘速度难以发挥。这样就尴尬了,...
  • 装系统,是电脑爱好者“老生常谈”的一个话题,在“电脑百事网”能找到许多类似的教程文章,不过今天的教程与往期有些不一样,首先是制作启动U盘,选用了一些更安全的纯净工具,另外首次针对惠普笔记本为例,安装...
  • bios设置图解教程

    千次阅读 2016-03-31 13:50:47
    bios设置图解教程   本文转自:http://www.zolsky.com/cainiaoxuetang/1/41.htm   BIOS设置程序是储存在BIOS芯片中的,只有在开机时才可以进行设置。CMOS主要用于存储BIOS设置程序所设置的参数与数据,而BIOS...
  • java实现打印功能并控制打印方向

    千次阅读 2015-08-05 12:38:31
    本文主要参考了Brett Spell 的文章:Java Pro Programming: Printing ...2.通过调用接口中定义的createPrintJob()方法创建一个打印事件,作为DocPrintJob的一个实例。3.创建一个实现Doc接口的类来描述你想要
  • 腰间盘突出在现代社会是比较常见的一种病变,尤其是在一些办公室工作的朋友,整日坐在电脑桌前,容易出现这个问题,在这里给大家分享一些腰间盘突出的简单的锻炼方法。 步骤/方法 最简单实用的一个锻炼...
  • 基于Java的“多功能五子棋”游戏的设计和实现

    万次阅读 多人点赞 2016-12-09 19:26:09
    基于Java的“多功能五子棋”游戏的设计和实现 引言  随着经济社会的迅速发展,人们生活水平有了很大的提高,人们的生活观念也发生了巨大的改 变。高品质的生活、现代化得生活方式成为人们共同追求的目标;休闲、...
  • 本文介绍了最全面的给自己的电脑更换固态硬盘和内存条的具体过程,从制作软碟通系统启动U盘开始,到自行拆机再重装系统,一条龙制作过程(因为自己之前也搜索过一些教程,其中一些操作步骤表达的并不是那么清晰,以...
  • 最近尝试控制台小程序,以及快捷键组合的时候需要用到侦听键盘事件的操作,然后查阅了...如何按一个键就能自动执行很键 如何自动按键 如何用python实现按方向键可上下移动 python实现按一个键执行一个函数的功能
  • 基于STM32的多功能MP3设计 毕业设计(论文)开题报告
  • 1.解决myeclipse中耗内存的方法(启动加快) ①老是弹出Quick update error  这个问题的解决办法是关闭自动更新  Windows > Preferences > MyEclipse Enterprise Workbench > Community Essentials,  把选项 ...
  • 首先要做的就是了解电脑Bios设置,但多数人经常在这个操作中没法成功操作下来,其主要原因是对bios的了解不够全面,所以今天快启动小编即将为大家分享品牌笔记本进入BIOS的设置方法功能按键解说,不懂的话还是一...
  • BIOS设置图解教程

    千次阅读 2006-09-11 13:25:00
    BIOS设置图解教程 AWARD BIOS设置图解教程 
  • 对于那些表示只愿追求简单拒绝功能复杂而去使用三键鼠标的同学,我很怀疑他们是否真正了解到键鼠标或手势功能对电脑操作便捷上带来的重大意义。复制粘贴,前进后退,关闭等等这些操作看似简单,但有的需要键盘配合...
  • BIOS学习:BIOS设置图解教程

    千次阅读 多人点赞 2016-02-25 21:28:46
    BIOS学习:BIOS设置图解教程 Posted on 2010年05月22日 by 虾虾 暂无评论 对于大多数用户来说BIOS都是一个很神秘的东西,不只只是蓝色界面加纯英文的选项让人感到神秘,其功能往往也让人不可...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 106,324
精华内容 42,529
关键字:

多功能方向盘设置教程