目录
概述
因为项目的原因,需要对方向盘、摇杆的数据进行读取与使用。购买的是市面上比较一般的摇杆,没有什么API之类的供开发使用,所以自己写了两个脚本,用来控制和读取数据。还有很多不足,为了防止之后忘了~不对的地方还请大牛们指正。
这里参考了https://blog.csdn.net/huang9012/article/details/46815707来读取摇杆,定义Unity事件传参数方面参考https://blog.csdn.net/inlet511/article/details/46822907。这里先两个类的整体代码,再分解说明。
整体代码
首先是JoyStickHelper类,是作为自己代码的工具类使用的,里面负责获取摇杆状态,和一些基础的操作,比如按键状态的获取
//开发:VRJerry-------------------
//目的:为了使用摇杆写的辅助类----
//语言:C#------------------------
//平台:Unity---------------------
//使用:需要其他脚本调用相关函数--
//日期:2018.10.17----------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using System;
public static class JoyStickHelper {
/// <summary>
/// 存储摇杆信息
/// </summary>
public static JoyStickInfo infoEx = new JoyStickInfo();
/// <summary>
/// 本次更新后按钮按住的状态
/// </summary>
public static int ButtonState;
/// <summary>
/// 本次更新后按钮抬起的状态
/// </summary>
public static int ButtonUpState;
/// <summary>
/// 本次更新后按钮按下的状态
/// </summary>
public static int ButtonDownState;
/// <summary>
/// 存储各轴的值
/// </summary>
public static int[] AxisState = new int[6];
/// <summary>
/// 枚举按钮键值
/// </summary>
public enum JoyStickButtonCode
{
Button01 = 0x1,
Button02 = 0x2,
Button03 = 0x4,
Button04 = 0x8,
Button05 = 0x10,
Button06 = 0x20,
Button07 = 0x40,
Button08 = 0x80,
Button09 = 0x100,
Button10 = 0x200,
Button11 = 0x400,
Button12 = 0x800,
Button13 = 0x1000,
Button14 = 0x2000,
Button15 = 0x4000,
Button16 = 0x8000,
Button17 = 0x10000,
Button18 = 0x20000,
Button19 = 0x40000,
Button20 = 0x80000,
Button21 = 0x100000,
Button22 = 0x200000,
Button23 = 0x400000,
Button24 = 0x800000,
Button25 = 0x1000000,
Button26 = 0x2000000,
Button27 = 0x4000000,
Button28 = 0x8000000,
Button29 = 0x10000000,
Button30 = 0x20000000,
Button31 = 0x40000000,
Button32 = -0x80000000,
none =-1
}
/// <summary>
/// 枚举控制方向
/// </summary>
public enum ControlDirection
{
X = 0,
Y = 1,
Z = 2,
U = 3,
V = 4,
R = 5
}
/// <summary>
/// 获取摇杆参数
/// </summary>
/// <param name="uJoyID"></param>
/// <param name="pji">摇杆信息,是个自定义类</param>
/// <returns></returns>
[DllImport("winmm")]
private static extern int joyGetPosEx(int uJoyID, ref JoyStickInfo pji);
/// <summary>
/// 从windowsAPI获取JoyStick的状态
/// </summary>
/// <returns></returns>
public static int UpdateInfoEx()
{
if (infoEx.dwSize == 0)
{
infoEx.dwSize = Marshal.SizeOf(typeof(JoyStickInfo));
infoEx.dwFlags = 0x00000080;
}
int beforeBtnState = ButtonState;
int result=joyGetPosEx(0, ref infoEx);
ButtonState = infoEx.dwButtons;
ButtonUpState = beforeBtnState & (beforeBtnState ^ ButtonState);
ButtonDownState = (~beforeBtnState) & (beforeBtnState ^ ButtonState);
AxisState[0] = infoEx.dwXpos;
AxisState[1] = infoEx.dwYpos;
AxisState[2] = infoEx.dwZpos;
AxisState[3] = infoEx.dwUpos;
AxisState[4] = infoEx.dwVpos;
AxisState[5] = infoEx.dwRpos;
return result;
}
/// <summary>
/// 获取某一个轴按的动作大小,请获取前注意刷新
/// </summary>
/// <param name="axis">轴</param>
/// <returns></returns>
public static int GetAxisPosition(ControlDirection axis)
{
return AxisState[Convert.ToInt32(axis)];
}
/// <summary>
/// 获取某按钮是否按住
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static bool GetButton(JoyStickButtonCode code)
{
if (code == JoyStickButtonCode.none)
return false;
string buttonName = Enum.GetName(typeof(JoyStickButtonCode), code);
int index = int.Parse(buttonName.Substring(buttonName.Length - 2));
int mask = 1 << (index - 1);
return (ButtonState & mask)>0;
}
/// <summary>
/// 获取某按钮是否抬起
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static bool GetUpButton(JoyStickButtonCode code)
{
if (code == JoyStickButtonCode.none)
return false;
string buttonName = Enum.GetName(typeof(JoyStickButtonCode), code);
int index = int.Parse(buttonName.Substring(buttonName.Length - 2));
int mask = 1 << (index - 1);
return (ButtonUpState & mask) > 0;
}
/// <summary>
/// 获取某按钮是否按下
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static bool GetDownButton(JoyStickButtonCode code)
{
if (code == JoyStickButtonCode.none)
return false;
string buttonName = Enum.GetName(typeof(JoyStickButtonCode), code);
int index = int.Parse(buttonName.Substring(buttonName.Length - 2));
int mask = 1 << (index - 1);
return (ButtonDownState & mask) > 0;
}
/// <summary>
/// 按键信息结构
/// </summary>
public struct JoyStickInfo
{
/// <summary>
/// Size, in bytes, of this structure.
/// </summary>
public int dwSize;
/// <summary>
/// Flags indicating the valid information returned in this structure. Members that do not contain valid information are set to zero.
/// </summary>
public int dwFlags;
/// <summary>
/// Current X-coordinate.
/// </summary>
public int dwXpos;
/// <summary>
/// Current Y-coordinate.
/// </summary>
public int dwYpos;
/// <summary>
/// Current Z-coordinate.
/// </summary>
public int dwZpos;
/// <summary>
/// Current position of the rudder or fourth joystick axis.
/// </summary>
public int dwRpos;
/// <summary>
/// Current fifth axis position.
/// </summary>
public int dwUpos;
/// <summary>
/// Current sixth axis position.
/// </summary>
public int dwVpos;
/// <summary>
/// Current state of the 32 joystick buttons. The value of this member can be set to any combination of JOY_BUTTONn flags, where n is a value in the range of 1 through 32 corresponding to the button that is pressed.
/// </summary>
public int dwButtons;
/// <summary>
/// Current button number that is pressed.
/// </summary>
public int dwButtonNumber;
/// <summary>
/// Current position of the point-of-view control. Values for this member are in the range 0 through 35,900. These values represent the angle, in degrees, of each view multiplied by 100.
/// </summary>
public int dwPOV;
/// <summary>
/// Reserved; do not use.
/// </summary>
public int dwReserved1;
/// <summary>
/// Reserved; do not use.
/// </summary>
public int dwReserved2;
}
}
有了这个类之后就可以获取摇杆信息了,注意每次获取要使用UpdateInfoEx获取一下数据,建议在应用的类的LateUpdate()中。
为了我自己使用方便,可以从Inspector中定义按钮及事件,写了一个调用和控制的脚本,挂到物体上后可以调整相应的参数,和是否开启调试模式。
//开发:VRJerry-------------------
//目的:为了使用摇杆写的控制类----
//语言:C#------------------------
//平台:Unity---------------------
//使用:需要调用JoyStickHelper类--
//------需要其他脚本调用相关函数--
//日期:2018.10.17----------------
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEditor;
using System.Threading;
public class JoyStickController : MonoBehaviour {
/// <summary>
/// 按键按下的事件
/// </summary>
[SerializeField]
[Tooltip("按键按下的事件")]
public ButtonEventStruct[] ButtonDownEvent;
/// <summary>
/// 按键抬起事件
/// </summary>
[SerializeField]
[Tooltip("按键抬起事件")]
public ButtonEventStruct[] ButtonUpEvent;
/// <summary>
/// 按键按住事件
/// </summary>
[SerializeField]
[Tooltip("按键按住事件")]
public ButtonEventStruct[] ButtonEvent;
/// <summary>
/// 选择检测的轴及事件
/// </summary>
[SerializeField]
[Tooltip("设置控制的轴及其事件")]
public AxisEventStruct[] controlDirections;
/ <summary>
/ 是否开启数值显示(显示模式下,将显示各轴数值)
/ </summary>
[Tooltip("是否UI显示数值")]
public static bool DataTrigger = false;
/// <summary>
/// 是否开启数值调试(调试模式下,将不自动刷新数值)
/// </summary>
[SerializeField]
[Tooltip("是否开启数值调试模式(数值调试模式下,将不自动刷新数值)")]
public bool ValueDebugTrigger = false;
/// <summary>
/// 是否开启调试(调试模式下,将不自动刷新数值)
/// </summary>
[SerializeField]
[Tooltip("是否开启普通调试模式(调试模式下,数值调试模式不可用)")]
public bool NormalDebugTrigger = false;
/// <summary>
/// 调试UI参数,表示增加/减小的值
/// </summary>
private int ratePlus = 1;
/// <summary>
/// 调试UI参数,表示选择的轴,与枚举一致
/// </summary>
private int axis = 0;
/// <summary>
/// 调试UI参数,表示按钮本次按下之前的状态,用于触发按钮抬起或者按下的状态
/// </summary>
private int beforeBtnState;
/// <summary>
/// 调试UI参数,表示轴的字符串,用来显示
/// </summary>
private string axisString = "X";
/// <summary>
/// 调试UI参数,用于显示全部按钮的信息
/// </summary>
private string buttonStateString = "";
void Start()
{
}
private void Update()
{
//如果不处于调试模式,则不处理,如果处于调试模式,且两种调试模式均激活,则数值调试模式无效
if (!NormalDebugTrigger && !ValueDebugTrigger)
{
return;
}
else
{
if (NormalDebugTrigger)
ValueDebugTrigger = false;
}
//如果不在数值操作模式下,则摇杆数值更新由系统负责
if (!ValueDebugTrigger)
return;
//设定按钮参数,详情见UI提示文字,包括变更调整数值的大小、调整的轴与增减
if (Input.GetKeyUp(KeyCode.LeftArrow))
{
ratePlus = Mathf.Max(1, ratePlus / 10);
}
if (Input.GetKeyUp(KeyCode.RightArrow))
{
ratePlus = Mathf.Min(10000, ratePlus * 10);
}
if (Input.GetKeyUp(KeyCode.UpArrow))
{
JoyStickHelper.AxisState[axis] += Mathf.Min(ratePlus,65535 - JoyStickHelper.AxisState[axis]);
}
if (Input.GetKeyUp(KeyCode.DownArrow))
{
JoyStickHelper.AxisState[axis] -= Mathf.Min(ratePlus,JoyStickHelper.AxisState[axis]);
}
if (Input.GetKeyUp(KeyCode.X))
{
axis = 0;
axisString = "X";
}
if (Input.GetKeyUp(KeyCode.Y))
{
axis = 1;
axisString = "Y";
}
if (Input.GetKeyUp(KeyCode.Z))
{
axis = 2;
axisString = "Z";
}
if (Input.GetKeyUp(KeyCode.U))
{
axis = 3;
axisString = "U";
}
if (Input.GetKeyUp(KeyCode.V))
{
axis = 4;
axisString = "V";
}
if (Input.GetKeyUp(KeyCode.R))
{
axis = 5;
axisString = "R";
}
}
void LateUpdate()
{
//如果在数值操作模式下,则摇杆数值有本脚本设置
if (!ValueDebugTrigger)
JoyStickHelper.UpdateInfoEx();
//如果在任意调试模式下,则计算更新数值后各按钮的状态
if (NormalDebugTrigger || ValueDebugTrigger)
{
int tmp = JoyStickHelper.ButtonState;
buttonStateString = "";
for (int i = 1; i < 33; i++)
{
int mask = 1 << (i - 1);
buttonStateString += "Button" + i + ":" + ((tmp & mask) != 0);
if (i % 8 == 0)
buttonStateString += "\r\n";
else
buttonStateString += " ";
}
}
//---------------按键触发-----------------
//检测按下
for (int i = 0; i < ButtonDownEvent.Length; i++)
{
if (JoyStickHelper.GetDownButton(ButtonDownEvent[i].button))
ButtonDownEvent[i].buttonEvent.Invoke();
}
//检测按住
for (int i = 0; i < ButtonEvent.Length; i++)
{
if (JoyStickHelper.GetButton(ButtonEvent[i].button))
ButtonEvent[i].buttonEvent.Invoke();
}
//检测抬起
for (int i = 0; i < ButtonUpEvent.Length; i++)
{
if (JoyStickHelper.GetUpButton(ButtonUpEvent[i].button))
ButtonUpEvent[i].buttonEvent.Invoke();
}
//------------------------------------------
//--------------摇杆触发--------------------
for (int i = 0; i < controlDirections.Length; i++)
{
controlDirections[i].axisEvent.Invoke(controlDirections[i].ZeroValue, controlDirections[i].Sensitivity,JoyStickHelper.GetAxisPosition(controlDirections[i].Direction));
}
//------------------------------------------
}
void OnGUI()
{
//如果不在调试模式下,不显示任何UI
if (!NormalDebugTrigger && !ValueDebugTrigger)
return;
if (NormalDebugTrigger)
GUILayout.Label("基础调试模式启动,如需要数值模拟调试,请先关闭基础调试");
if (ValueDebugTrigger)
{
beforeBtnState = JoyStickHelper.ButtonState;
GUILayout.Label("数值调试模式启动,快捷操作为:\r\n← 减小数值上升率,→ 增加数值上升率,↑ 对应轴增加数值,↓ 对应轴减小数值\r\nX 选择X轴,Y 选择Y轴,Z 选择Z轴,U 选择U轴,V 选择V轴,R 选择R轴");
//每8个按钮一行,显示触发按钮,点击后改变按钮状态
int mask = 0;
for (int i = 1; i < 33; i++)
{
if (i % 8 == 1)
GUILayout.BeginHorizontal();
if (GUILayout.Button("button" + i))
mask += 1 << (i - 1);
if (i % 8 == 0)
GUILayout.EndHorizontal();
}
GUILayout.Label("触发按钮键值: " + mask);
JoyStickHelper.ButtonState = JoyStickHelper.ButtonState^mask;
//基于改变后的状态,计算抬起和按下
JoyStickHelper.ButtonUpState = beforeBtnState & (beforeBtnState ^ JoyStickHelper.ButtonState);
JoyStickHelper.ButtonDownState = (~beforeBtnState) & (beforeBtnState ^ JoyStickHelper.ButtonState);
GUILayout.Label("当前值变化率:rate=" + ratePlus + " 当前操作轴:" + axisString + "\r\n");
}
//任何调试模式下都要显示按钮和轴的情况
if (NormalDebugTrigger || ValueDebugTrigger)
{
GUILayout.Label("各轴偏移数值: X=" + JoyStickHelper.AxisState[0] + " Y=" + JoyStickHelper.AxisState[1] + " Z=" + JoyStickHelper.AxisState[2] + " U=" + JoyStickHelper.AxisState[3] + " V=" + JoyStickHelper.AxisState[4] + " R=" + JoyStickHelper.AxisState[5]);
GUILayout.Label("按钮触发状态:"+JoyStickHelper.ButtonState+"\r\n" + buttonStateString);
}
}
}
/// <summary>
/// 定义按钮事件
/// </summary>
[Serializable]
public class ButtonEventStruct
{
public JoyStickHelper.JoyStickButtonCode button;
public UnityEvent buttonEvent = new UnityEvent();
public ButtonEventStruct(JoyStickHelper.JoyStickButtonCode code)
{
button = code;
}
public ButtonEventStruct() { }
}
/// <summary>
/// 定义轴的事件
/// </summary>
[Serializable]
public class AxisEventStruct
{
public JoyStickHelper.ControlDirection Direction;
[Range(0, 1)]
public float ZeroValue=0.5f;
[Range(0,100)]
public int Sensitivity = 15;
public AxisEvent axisEvent = new AxisEvent();
public AxisEventStruct(JoyStickHelper.ControlDirection d)
{
Direction = d;
}
public AxisEventStruct() { }
}
/// <summary>
/// 由于UnityEvent<T0,T1,T2>是抽象类,因此要继承一下声明一个类型
/// </summary>
[Serializable]
public class AxisEvent : UnityEvent<float, int, int> { }
详细说明
1、JoyStickHelper
使用到的是winmm.dll,其中使用的方法是joyGetPosEx。
[DllImport("winmm")]
private static extern int joyGetPosEx(int uJoyID, ref JoyStickInfo pji);
其中,我们用于读取的遥感信息都存储在JoyStickInfo中了,它是个结构体,定义如下:
public struct JoyStickInfo
{
/// <summary>
/// Size, in bytes, of this structure.
/// </summary>
public int dwSize;
/// <summary>
/// Flags indicating the valid information returned in this structure. Members that do not contain valid information are set to zero.
/// </summary>
public int dwFlags;
/// <summary>
/// Current X-coordinate.
/// </summary>
public int dwXpos;
/// <summary>
/// Current Y-coordinate.
/// </summary>
public int dwYpos;
/// <summary>
/// Current Z-coordinate.
/// </summary>
public int dwZpos;
/// <summary>
/// Current position of the rudder or fourth joystick axis.
/// </summary>
public int dwRpos;
/// <summary>
/// Current fifth axis position.
/// </summary>
public int dwUpos;
/// <summary>
/// Current sixth axis position.
/// </summary>
public int dwVpos;
/// <summary>
/// Current state of the 32 joystick buttons. The value of this member can be set to any combination of JOY_BUTTONn flags, where n is a value in the range of 1 through 32 corresponding to the button that is pressed.
/// </summary>
public int dwButtons;
/// <summary>
/// Current button number that is pressed.
/// </summary>
public int dwButtonNumber;
/// <summary>
/// Current position of the point-of-view control. Values for this member are in the range 0 through 35,900. These values represent the angle, in degrees, of each view multiplied by 100.
/// </summary>
public int dwPOV;
/// <summary>
/// Reserved; do not use.
/// </summary>
public int dwReserved1;
/// <summary>
/// Reserved; do not use.
/// </summary>
public int dwReserved2;
}
这里我使用到的信息是:
数据 | 说明 |
---|---|
dwButton | 所有激活的额按钮信息,一共可以有32个按钮触发,每个按钮是否触发占32位整数的1位。 |
dwXpos | 摇杆X轴对应的位置,0-65535 |
dwYpos | 摇杆Y轴对应的位置,0-65535 |
dwZpos | 摇杆Z轴对应的位置,0-65535 |
dwUpos | 摇杆U轴对应的位置,0-65535 |
dwVpos | 摇杆V轴对应的位置,0-65535 |
dwRpos | 摇杆R轴对应的位置,0-65535 |
为了便于记录按钮值和方向情况,这里我声明了两个枚举类,用于存储键值和方向,用于以后确定按键触发和简明的了解方向。
/// <summary>
/// 枚举按钮键值
/// </summary>
public enum JoyStickButtonCode
{
Button01 = 0x1,
Button02 = 0x2,
Button03 = 0x4,
Button04 = 0x8,
Button05 = 0x10,
Button06 = 0x20,
Button07 = 0x40,
Button08 = 0x80,
Button09 = 0x100,
Button10 = 0x200,
Button11 = 0x400,
Button12 = 0x800,
Button13 = 0x1000,
Button14 = 0x2000,
Button15 = 0x4000,
Button16 = 0x8000,
Button17 = 0x10000,
Button18 = 0x20000,
Button19 = 0x40000,
Button20 = 0x80000,
Button21 = 0x100000,
Button22 = 0x200000,
Button23 = 0x400000,
Button24 = 0x800000,
Button25 = 0x1000000,
Button26 = 0x2000000,
Button27 = 0x4000000,
Button28 = 0x8000000,
Button29 = 0x10000000,
Button30 = 0x20000000,
Button31 = 0x40000000,
Button32 = -0x80000000,
none =-1
}
/// <summary>
/// 枚举控制方向
/// </summary>
public enum ControlDirection
{
X = 0,
Y = 1,
Z = 2,
U = 3,
V = 4,
R = 5
}
每一帧的时候,应该调用UpdateInfoEx函数更新摇杆的情况,更新后对其进行赋值与解析,获得以后使用方便的各种信息。
/// <summary>
/// 从windowsAPI获取JoyStick的状态
/// </summary>
/// <returns></returns>
public static int UpdateInfoEx()
{
if (infoEx.dwSize == 0)
{
infoEx.dwSize = Marshal.SizeOf(typeof(JoyStickInfo));
infoEx.dwFlags = 0x00000080;
}
int beforeBtnState = ButtonState;
int result=joyGetPosEx(0, ref infoEx);
ButtonState = infoEx.dwButtons;
ButtonUpState = beforeBtnState & (beforeBtnState ^ ButtonState);
ButtonDownState = (~beforeBtnState) & (beforeBtnState ^ ButtonState);
AxisState[0] = infoEx.dwXpos;
AxisState[1] = infoEx.dwYpos;
AxisState[2] = infoEx.dwZpos;
AxisState[3] = infoEx.dwUpos;
AxisState[4] = infoEx.dwVpos;
AxisState[5] = infoEx.dwRpos;
return result;
}
其中,分配空间是为了系统能够顺利写入。Flag的值使用的是参考文章中的,没有深入研究具体原因。
beforeBtnState是在获取最新数据前先存一下上一帧按钮的状态,用以对比实现判断按键是否抬起或者按下。存储后调用更新获取最新的数据,并对按钮数据进行判断。按钮利用32位整数存储32个状态,通过按位比对的方式能够更高效和快速的得到结果:
- 按钮抬起的状态:在前一帧处于1并且后一帧处于0的按钮
- 按钮按下的状态:在前一帧处于0并且后一帧处于1的按钮
我这里写的有点多余了,写的是前一帧的状态处在需要检测的位置并且与上发生了变化的按钮。当然,是可以简化过去的:
按钮检测后,就将各轴的数值赋值,检测过程就交给处理函数和脚本来进行了。
为了方便使用,获取几个肯定用到的函数:
- 获取某个坐标的值:直接从数组中找到并取出,只需要将坐标轴转换成索引即可
/// <summary> /// 获取某一个轴按的动作大小,请获取前注意刷新 /// </summary> /// <param name="axis">轴</param> /// <returns></returns> public static int GetAxisPosition(ControlDirection axis) { return AxisState[Convert.ToInt32(axis)]; }
- 获取按键按住的状态:将按键状态与键值进行对比获得
/// <summary> /// 获取某按钮是否按住 /// </summary> /// <param name="code"></param> /// <returns></returns> public static bool GetButton(JoyStickButtonCode code) { if (code == JoyStickButtonCode.none) return false; return (ButtonState & Convert.ToInt32(code)) >0; }
- 获取按键抬起的状态:将按键抬起状态与键值进行对比获得
/// <summary> /// 获取某按钮是否抬起 /// </summary> /// <param name="code"></param> /// <returns></returns> public static bool GetUpButton(JoyStickButtonCode code) { if (code == JoyStickButtonCode.none) return false; return (ButtonUpState & Convert.ToInt32(code)) > 0; }
- 获取按键按下的状态:将按键按下状态与键值进行对比获得
/// <summary> /// 获取某按钮是否按下 /// </summary> /// <param name="code"></param> /// <returns></returns> public static bool GetDownButton(JoyStickButtonCode code) { if (code == JoyStickButtonCode.none) return false; return (ButtonDownState & Convert.ToInt32(code)) > 0; }
以上内容供其他函数调用使用了。
2、JoyStickController
分别定义按钮相关的结构(包含事件)和轴相关的结构(包含事件),轴的事件需要传递参数,因此需要声明一个类继承自抽象类。
/// <summary>
/// 按键按下的事件
/// </summary>
[SerializeField]
[Tooltip("按键按下的事件")]
public ButtonEventStruct[] ButtonDownEvent;
/// <summary>
/// 按键抬起事件
/// </summary>
[SerializeField]
[Tooltip("按键抬起事件")]
public ButtonEventStruct[] ButtonUpEvent;
/// <summary>
/// 按键按住事件
/// </summary>
[SerializeField]
[Tooltip("按键按住事件")]
public ButtonEventStruct[] ButtonEvent;
/// <summary>
/// 选择检测的轴及事件
/// </summary>
[SerializeField]
[Tooltip("设置控制的轴及其事件")]
public AxisEventStruct[] controlDirections;
/// <summary>
/// 定义按钮事件
/// </summary>
[Serializable]
public class ButtonEventStruct
{
public JoyStickHelper.JoyStickButtonCode button;
public UnityEvent buttonEvent = new UnityEvent();
public ButtonEventStruct(JoyStickHelper.JoyStickButtonCode code)
{
button = code;
}
public ButtonEventStruct() { }
}
/// <summary>
/// 定义轴的事件
/// </summary>
[Serializable]
public class AxisEventStruct
{
public JoyStickHelper.ControlDirection Direction;
[Range(0, 1)]
public float ZeroValue=0.5f;
[Range(0,100)]
public int Sensitivity = 15;
public AxisEvent axisEvent = new AxisEvent();
public AxisEventStruct(JoyStickHelper.ControlDirection d)
{
Direction = d;
}
public AxisEventStruct() { }
}
/// <summary>
/// 由于UnityEvent<T0,T1,T2>是抽象类,因此要继承一下声明一个类型
/// </summary>
[Serializable]
public class AxisEvent : UnityEvent<float, int, int> { }
定义一些调试用的参数,帮助数值显示、进入调试模式等。
/// <summary>
/// 是否开启数值调试(调试模式下,将不自动刷新数值)
/// </summary>
[SerializeField]
[Tooltip("是否开启数值调试模式(数值调试模式下,将不自动刷新数值)")]
public bool ValueDebugTrigger = false;
/// <summary>
/// 是否开启调试(调试模式下,将不自动刷新数值)
/// </summary>
[SerializeField]
[Tooltip("是否开启普通调试模式(调试模式下,数值调试模式不可用)")]
public bool NormalDebugTrigger = false;
/// <summary>
/// 调试UI参数,表示增加/减小的值
/// </summary>
private int ratePlus = 1;
/// <summary>
/// 调试UI参数,表示选择的轴,与枚举一致
/// </summary>
private int axis = 0;
/// <summary>
/// 调试UI参数,表示按钮本次按下之前的状态,用于触发按钮抬起或者按下的状态
/// </summary>
private int beforeBtnState;
/// <summary>
/// 调试UI参数,表示轴的字符串,用来显示
/// </summary>
private string axisString = "X";
/// <summary>
/// 调试UI参数,用于显示全部按钮的信息
/// </summary>
private string buttonStateString = "";
在Update与LateUpdate中,分别处理调试模式下的按键与数据获取处理等。数值调试模式表示我们将用鼠标和键盘完全模拟摇杆的值,不自动刷新获取实际输入。普通调试模式则只是显示数据,值还是由设备获取并显示。
- 数值调试模式下的键盘按键响应(Update中),如果不是则不响应,直接返回,减少计算量。
//如果不在数值操作模式下,则摇杆数值更新由系统负责 if (!ValueDebugTrigger) return; //设定按钮参数,详情见UI提示文字,包括变更调整数值的大小、调整的轴与增减 if (Input.GetKeyUp(KeyCode.LeftArrow)) { ratePlus = Mathf.Max(1, ratePlus / 10); } if (Input.GetKeyUp(KeyCode.RightArrow)) { ratePlus = Mathf.Min(10000, ratePlus * 10); } if (Input.GetKeyUp(KeyCode.UpArrow)) { JoyStickHelper.AxisState[axis] += Mathf.Min(ratePlus,65535 - JoyStickHelper.AxisState[axis]); } if (Input.GetKeyUp(KeyCode.DownArrow)) { JoyStickHelper.AxisState[axis] -= Mathf.Min(ratePlus,JoyStickHelper.AxisState[axis]); } if (Input.GetKeyUp(KeyCode.X)) { axis = 0; axisString = "X"; } if (Input.GetKeyUp(KeyCode.Y)) { axis = 1; axisString = "Y"; } if (Input.GetKeyUp(KeyCode.Z)) { axis = 2; axisString = "Z"; } if (Input.GetKeyUp(KeyCode.U)) { axis = 3; axisString = "U"; } if (Input.GetKeyUp(KeyCode.V)) { axis = 4; axisString = "V"; } if (Input.GetKeyUp(KeyCode.R)) { axis = 5; axisString = "R"; }
- 数值调试模式下LateUpdate中需要从系统刷新摇杆的状态(LateUpdate中)
//如果在数值操作模式下,则摇杆数值有本脚本设置 if (!ValueDebugTrigger) JoyStickHelper.UpdateInfoEx();
- 一般调试模式下优先级高于数值调试模式,设置时变更数值调试模式状态为False(Update中)。
//如果不处于调试模式,则不处理,如果处于调试模式,且两种调试模式均激活,则数值调试模式无效 if (!NormalDebugTrigger && !ValueDebugTrigger) { return; } else { if (NormalDebugTrigger) ValueDebugTrigger = false; }
- 任一调试模式下,需要重新更新一下按钮当前状态供显示(LateUpdate中)。设定mask值为各键值
//如果在任意调试模式下,则计算更新数值后各按钮的状态 if (NormalDebugTrigger || ValueDebugTrigger) { int tmp = JoyStickHelper.ButtonState; buttonStateString = ""; for (int i = 1; i < 33; i++) { int mask = 1 << (i - 1); buttonStateString += "Button" + i + ":" + ((tmp & mask) != 0); if (i % 8 == 0) buttonStateString += "\r\n"; else buttonStateString += " "; } }
- 无论任何状态,均需在LateUpdate中遍历各种按钮摇杆事件有无添加,如果有添加的按钮或者摇杆的事件,则检测符合条件的触发。
//---------------按键触发----------------- //检测按下 for (int i = 0; i < ButtonDownEvent.Length; i++) { if (JoyStickHelper.GetDownButton(ButtonDownEvent[i].button)) ButtonDownEvent[i].buttonEvent.Invoke(); } //检测按住 for (int i = 0; i < ButtonEvent.Length; i++) { if (JoyStickHelper.GetButton(ButtonEvent[i].button)) ButtonEvent[i].buttonEvent.Invoke(); } //检测抬起 for (int i = 0; i < ButtonUpEvent.Length; i++) { if (JoyStickHelper.GetUpButton(ButtonUpEvent[i].button)) ButtonUpEvent[i].buttonEvent.Invoke(); } //------------------------------------------ //--------------摇杆触发-------------------- for (int i = 0; i < controlDirections.Length; i++) { controlDirections[i].axisEvent.Invoke(controlDirections[i].ZeroValue, controlDirections[i].Sensitivity,JoyStickHelper.GetAxisPosition(controlDirections[i].Direction)); } //------------------------------------------
- UI内显示相应内容,一般调试模式下不显示调整按钮的情况及轴的模拟数值情况,数值调试模式下显示模拟按钮及调整轴的相应参数等。
void OnGUI() { //如果不在调试模式下,不显示任何UI if (!NormalDebugTrigger && !ValueDebugTrigger) return; if (NormalDebugTrigger) GUILayout.Label("基础调试模式启动,如需要数值模拟调试,请先关闭基础调试"); if (ValueDebugTrigger) { beforeBtnState = JoyStickHelper.ButtonState; GUILayout.Label("数值调试模式启动,快捷操作为:\r\n← 减小数值上升率,→ 增加数值上升率,↑ 对应轴增加数值,↓ 对应轴减小数值\r\nX 选择X轴,Y 选择Y轴,Z 选择Z轴,U 选择U轴,V 选择V轴,R 选择R轴"); //每8个按钮一行,显示触发按钮,点击后改变按钮状态 int mask = 0; for (int i = 1; i < 33; i++) { if (i % 8 == 1) GUILayout.BeginHorizontal(); if (GUILayout.Button("button" + i)) mask += 1 << (i - 1); if (i % 8 == 0) GUILayout.EndHorizontal(); } GUILayout.Label("触发按钮键值: " + mask); JoyStickHelper.ButtonState = JoyStickHelper.ButtonState^mask; //基于改变后的状态,计算抬起和按下 JoyStickHelper.ButtonUpState = beforeBtnState & (beforeBtnState ^ JoyStickHelper.ButtonState); JoyStickHelper.ButtonDownState = (~beforeBtnState) & (beforeBtnState ^ JoyStickHelper.ButtonState); GUILayout.Label("当前值变化率:rate=" + ratePlus + " 当前操作轴:" + axisString + "\r\n"); } //任何调试模式下都要显示按钮和轴的情况 if (NormalDebugTrigger || ValueDebugTrigger) { GUILayout.Label("各轴偏移数值: X=" + JoyStickHelper.AxisState[0] + " Y=" + JoyStickHelper.AxisState[1] + " Z=" + JoyStickHelper.AxisState[2] + " U=" + JoyStickHelper.AxisState[3] + " V=" + JoyStickHelper.AxisState[4] + " R=" + JoyStickHelper.AxisState[5]); GUILayout.Label("按钮触发状态:"+JoyStickHelper.ButtonState+"\r\n" + buttonStateString); } }
结语
虽然接触Unity有一段时间了,但是其实算不上好好学习过,不断研究中,其中的问题、不好的地方还请大家指正~