黑魂复刻游戏的手柄输入支持——Unity随手记
阅读原文时间:2021年06月16日阅读:1

今天实现的内容:

Input Manager

要配置好手柄,我们将使用Unity自带的Input Manager,这个系统能够很好的消除输入设备的差异,统一各项输入,还能自定义新输入,并提供了输入相关的参数可供修改。

仔细观察可以发现,默认的Input Manager里每种输入形式都设置了两个。以Horizontal为例,其中一个是键盘输入,另一个是手柄输入,当我们使用键盘时,Horizontal得到的是使用键盘输入,换成手柄以后Horizontal会自动变成手柄,非常智能。当然如果你有意要区别手柄,你也可以将第二个Horizontal改个名字。
下面的图展示了我设置的XBOX手柄输入设置。

JoystickInput类

JoystickInput类对标原PlayerInput类,原PlayerInput类被我们写成了键鼠专用,所以现在被我改名为MouseKeyboardInput。

 JoystickInput代码如下,类似MouseKeyboardInput:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class JoystickInput : MonoBehaviour
{
[Header("==== 手柄按键 ====")] // 角色移动控制轴
public string movementAxis_X = "X axis"; public string movementAxis_Y = "Y axis"; // 摄像机控制轴
public string cameraAxis_X = "4th axis"; public string cameraAxis_Y = "5th axis"; // 游戏功能键
public string buttonRun_jab_roll = "A"; //冲刺/后跃/翻滚
public string buttonJump = "LS"; //跳跃键
public string buttonAttackRHand = "RB"; //攻击键
public string keyD;

\[Header("\==== 输出信号 ====")\] // 角色控制信号 damp之后的 向上/向右 方向值
public float dirUp; public float dirRight; // 玩家的输入模长量 用于当成向前量大小作为动画控制
public float dirMag; // 玩家的方向 用于旋转模型
public Vector3 dirVec; // 摄像机控制信号
public float cameraUp; public float cameraRight; // 按压信号 
public bool run; //奔跑信号 是否正在奔跑 // 一次性信号
public bool jab\_roll; //后跃/翻滚信号
public bool jump; //跳跃信号
public bool attack; //攻击信号

[Header("==== 其它参数 ====")] // 模块软开关
public bool inputEnabled = true; // 输入的damp时间
protected float dampTime = 0.1f; // 向上/向右 的最终方向值 用于将四键输入转换为双轴输入
protected float m_targetDirUp; protected float m_targetDirRight; // 向上/向右 damp的currentVelocity参数
protected float m_velocityDirUp; protected float m_velocityDirRight; // 映射后的输入双轴
protected Vector2 m_dirAxis; // 按键按压计时器
protected float pressTimer; // 单击的间隔时间 小于等于这个时间是单击 大于这个时间是长按(Long Press)
protected float clickIntervalTime = 0.2f; // Update is called once per frame
void Update()
{ // 摄像机信号
cameraUp = Input.GetAxis(cameraAxis_Y);
cameraRight = Input.GetAxis(cameraAxis_X); // 角色信号 // 将上下键的输入整合 计算向上方向的输入大小
m_targetDirUp = Input.GetAxis(movementAxis_Y); // 将左右键的输入整合 计算向右方向的输入大小
m_targetDirRight = Input.GetAxis(movementAxis_X); // 输入模块是否关闭 这段代码一定要放在这个位置
if (!inputEnabled)
{
m_targetDirUp = 0;
m_targetDirRight = 0;
} // 运用SmoothDamp来获得渐变的输入变化
dirUp = Mathf.SmoothDamp(dirUp, m_targetDirUp, ref m_velocityDirUp, dampTime);
dirRight = Mathf.SmoothDamp(dirRight, m_targetDirRight, ref m_velocityDirRight, dampTime); // 计算输入模长
dirMag = Mathf.Sqrt(dirUp * dirUp + dirRight * dirRight); // 计算玩家的方向
dirVec = dirRight * transform.right + dirUp * transform.forward; // 冲刺/后跃/翻滚信号
Jab_RollOrRun(buttonRun_jab_roll); // 攻击信号
attack = Input.GetButtonDown(buttonAttackRHand); // 跳跃信号
if (run && Input.GetButtonDown(buttonJump))
jump = true; else jump = false;
} // 长按还是单击 决定是冲刺还是翻滚/后跃
private void Jab_RollOrRun(string _key)
{ if (Input.GetButtonDown(_key))
{
pressTimer = 0;
} if (Input.GetButtonUp(_key) && pressTimer <= clickIntervalTime) { jab_roll = true; } else { jab_roll = false; } if (Input.GetButton(_key)) { pressTimer += Time.deltaTime; if (pressTimer > clickIntervalTime)
run = true;
} else {
run = false;
}
}
}

用抽象类统一不同的输入设备

到目前为止,我们的游戏键鼠输入和手柄输入是分开的,其中有很多一样的东西,我们可以使用一个抽象类来将这些不同的输入设备统一起来。我们这个新的抽象类就取以前的老名字,叫IPlayerInput,I表示它做为接口。

public abstract class IPlayerInput : MonoBehaviour
{
[Header("==== 输出信号 ====")] // 角色控制信号 damp之后的 向上/向右 方向值
public float dirUp; public float dirRight; // 玩家的输入模长量 用于当成向前量大小作为动画控制
public float dirMag; // 玩家的方向 用于旋转模型
public Vector3 dirVec; // 摄像机控制信号
public float cameraUp; public float cameraRight; // 按压信号
public bool run; //奔跑信号 是否正在奔跑 // 一次性信号
public bool jab_roll; //后跃/翻滚信号
public bool jump; //跳跃信号
public bool attack; //攻击信号
[Header("==== 其它参数 ====")] // 模块软开关
public bool inputEnabled = true; // 输入的damp时间
protected float dampTime = 0.1f; // 向上/向右 的最终方向值 用于将四键输入转换为双轴输入
protected float m_targetDirUp; protected float m_targetDirRight; // 向上/向右 damp的currentVelocity参数
protected float m_velocityDirUp; protected float m_velocityDirRight; // 映射后的输入双轴
protected Vector2 m_dirAxis; // 按键按压计时器
protected float pressTimer; // 单击的间隔时间 小于等于这个时间是单击 大于这个时间是长按(Long Press)
protected float clickIntervalTime = 0.2f;
}

接下来,我们的具体输入设备类只要继承自IPlayerInput,就不再需要再声明IPlayerInput中已有的变量了。现在,PlayerController和CameraController中的pi只要是IPlayerInput类型,在代码中就不需要指明现在使用何种输入类。

// 当前使用的输入模块
private IPlayerInput current\_pi;

由于我们实际有两个输入模块,所以我们需要选择我们想要的那个输入类赋值到current_pi,下面的代码让我们可以通过取消沟选所有不用的输入模块来告诉代码我们要使用特定的输入模块,比如我们想使用手柄,那么我们可以取消游戏对象上挂载的MouseKeyboardInput勾选。

// 所有输入模块
private IPlayerInput\[\] inputs; void Awake()
{ // ... 

inputs = GetComponents(); foreach (var input in inputs)
{ if (input.enabled)
{
current_pi = input; break;
}
}
}