UFL9-2——输入控制模块的改键功能

分析改键功能需求

  1. 改键功能应该是针对某一个行为的
    游戏中的行为是固定的,我们需要改变的是触发该行为的按键
    比如:角色有技能1、技能2、技能3,我们改变的是触发这些行为的输入
  2. 具体键位的触发不应该写死,而是需要根据存储的键位数据进行初始化或进行修改
  3. 键位的修改可以是键盘输入,也可以是鼠标输入,输入类型也可以是任意的
    比如:触发技能1,可以修改为键盘输入,也可以修改为鼠标输入
    可以是按下、可以是抬起、也可以是长按,具体是哪种应该根据你的需求变化

根据需求整理思路并制作

输入管理器主要做的事情,根据输入的信息,触发对应的事件,其中输入信息可变,触发的事件也可变

  1. 改键功能应该是针对某一个行为的,触发的事件类型应该是针对行为的,因为行为一般在游戏中是固定的

    因此,我们在事件中心触发的事件不应该再是某种操作类型,而是某种行为,
    因此我们可以把原来的操作类型事件如:E_Mouse​ 等删除或者注释,转而声明各种行为的事件名
    (热键事件诸如水平轴和垂直轴不能删除,因为它们的修改不是在管理器内实现的)

    假设我们有三种事件需要去监听设备输入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public enum E_EventType
    {
    /// <summary>
    /// 输入系统触发技能1 行为
    /// </summary>
    E_Input_Skill1,
    /// <summary>
    /// 输入系统触发技能1 行为
    /// </summary>
    E_Input_Skill2,
    /// <summary>
    /// 输入系统触发技能1 行为
    /// </summary>
    E_Input_Skill3,
    /// <summary>
    /// 水平热键
    /// </summary>
    E_Input_Horizontal,
    /// <summary>
    /// 竖直热键 -1~1的事件监听
    /// </summary>
    E_Input_Vertical,
    }

    同时,将之前的输入模块内的实现的各种使用原来的操作事件的监听方法删除(监听轴的改变的方法除外)

  2. 具体键位的触发不应该写死,输入管理器中应该声明字典容器

    • 键:触发事件的类型
    • 值:具体的输入信息(键盘还是鼠标、按下还是抬起还是长按、那个键)

    首先声明一个输入消息类,用来表示需要监听什么样的输入,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    /// <summary>
    /// 输入消息
    /// </summary>
    public class InputInfo
    {
    public enum E_KeyOrMouse
    {
    /// <summary>
    /// 键盘
    /// </summary>
    Key,
    /// <summary>
    /// 鼠标
    /// </summary>
    Mouse,
    }

    public enum E_InputType
    {
    /// <summary>
    /// 按下
    /// </summary>
    Down,
    /// <summary>
    /// 抬起
    /// </summary>
    Up,
    /// <summary>
    /// 按住
    /// </summary>
    Always,
    }

    public E_KeyOrMouse keyOrMouse; //具体输入的类型 —— 键盘还是鼠标
    public E_InputType inputType; //输入的类型 —— 抬起、按下、长按
    public KeyCode key;
    public int mouseID;

    /// <summary>
    /// 键盘输入初始化
    /// </summary>
    /// <param name="inputType">输入类型 —— 抬起、按下、长按</param>
    /// <param name="key">监听哪个键</param>
    public InputInfo(E_InputType inputType, KeyCode key)
    {
    this.keyOrMouse = E_KeyOrMouse.Key;
    this.inputType = inputType;
    this.key = key;
    }

    /// <summary>
    /// 鼠标输入初始化
    /// </summary>
    /// <param name="inputType">输入类型 —— 抬起、按下、长按</param>
    /// <param name="mouseID">监听哪个键</param>
    public InputInfo(E_InputType inputType, int mouseID)
    {
    this.keyOrMouse = E_KeyOrMouse.Mouse;
    this.inputType = inputType;
    this.mouseID = mouseID;
    }
    }

    然后就可以在管理器内声明字典,可用来查找某个事件需要由键盘或者鼠标的哪个按键输入触发

    1
    private Dictionary<E_EventType, InputInfo> inputDic = new Dictionary<E_EventType, InputInfo>();
  3. 键位的修改可以是键盘输入,也可以是鼠标输入,输入类型也可以是任意的,输入管理器应该提供初始化行为对应的键位方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    /// <summary>
    /// 提供给外部改键或初始化的方法(改为键盘事件)
    /// </summary>
    /// <param name="eventType">行为事件类型</param>
    /// <param name="key">要监听哪个键</param>
    /// <param name="inputType">输入类型</param>
    public void ChangeKeyboardInfo(E_EventType eventType, KeyCode key, InputInfo.E_InputType inputType)
    {
    //初始化
    if (!inputDic.ContainsKey(eventType))
    {
    inputDic.Add(eventType, new InputInfo(inputType, key));
    }
    //改键
    else
    {
    //如果之前是鼠标,我们必须要修改它的按键类型
    inputDic[eventType].keyOrMouse = InputInfo.E_KeyOrMouse.Key;
    inputDic[eventType].key = key;
    inputDic[eventType].inputType = inputType;
    }
    }

    /// <summary>
    /// 提供给外部改键或初始化的方法(改为鼠标事件)
    /// </summary>
    /// <param name="eventType">行为事件类型</param>
    /// <param name="mouseID">要监听鼠标按键的ID</param>
    /// <param name="inputType">输入类型</param>
    public void ChangeMouseInfo(E_EventType eventType, int mouseID, InputInfo.E_InputType inputType)
    {
    //初始化
    if (!inputDic.ContainsKey(eventType))
    {
    inputDic.Add(eventType, new InputInfo(inputType, mouseID));
    }
    //改键
    else
    {
    //如果之前是键盘,我们必须要修改它的按键类型
    inputDic[eventType].keyOrMouse = InputInfo.E_KeyOrMouse.Mouse;
    inputDic[eventType].mouseID = mouseID;
    inputDic[eventType].inputType = inputType;
    }
    }
  4. 在帧更新方法内的遍历字典去检测输入

    根据事件对应的输入消息,检测对应的输入,如果这一帧执行了输入,就触发事件,并且无需传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    private Dictionary<E_EventType, InputInfo> inputDic = new Dictionary<E_EventType, InputInfo>();
    //当前遍历取出的遍历消息
    private InputInfo nowInputInfo;

    private bool isCheckInput;

    private InputManager()
    {
    MonoManager.Instance.AddUpdateListener(InputUpdate);
    }

    private void InputUpdate()
    {
    //如果外部未开启检测功能,就不检测输入
    if (!isCheckInput)
    return;

    foreach (E_EventType eventType in inputDic.Keys)
    {
    nowInputInfo = inputDic[eventType];
    //如果是键盘输入
    if (nowInputInfo.keyOrMouse == InputInfo.E_KeyOrMouse.Key)
    {
    //根据输入类型检测对应的输入,传入输入消息设置的键位
    switch (nowInputInfo.inputType)
    {
    case InputInfo.E_InputType.Down:
    if (Input.GetKeyDown(nowInputInfo.key))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    case InputInfo.E_InputType.Up:
    if (Input.GetKeyUp(nowInputInfo.key))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    case InputInfo.E_InputType.Always:
    if (Input.GetKey(nowInputInfo.key))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    }
    }
    //如果是鼠标输入
    else if (nowInputInfo.keyOrMouse == InputInfo.E_KeyOrMouse.Mouse)
    {
    //根据输入类型检测对应的输入,传入输入消息设置的键位
    switch (nowInputInfo.inputType)
    {
    case InputInfo.E_InputType.Down:
    if (Input.GetMouseButtonDown(nowInputInfo.mouseID))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    case InputInfo.E_InputType.Up:
    if (Input.GetMouseButtonUp(nowInputInfo.mouseID))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    case InputInfo.E_InputType.Always:
    if (Input.GetMouseButton(nowInputInfo.mouseID))
    EventCenter.Instance.EventTrigger(eventType);
    break;
    }
    }
    }

    EventCenter.Instance.EventTrigger(E_EventType.E_Input_Horizontal, Input.GetAxis("Horizontal"));
    EventCenter.Instance.EventTrigger(E_EventType.E_Input_Vertical, Input.GetAxis("Vertical"));
    }
  5. 移除事件的输入监听方法

    同样我们需要向外部提供一个能够移除某个事件的输入监听的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private Dictionary<E_EventType, InputInfo> inputDic = new Dictionary<E_EventType, InputInfo>();

    /// <summary>
    /// 移除某个事件的输入监听
    /// </summary>
    /// <param name="eventType">要移除输入监听的事件</param>
    public void RemoveInputInfo(E_EventType eventType)
    {
    if (inputDic.ContainsKey(eventType))
    inputDic.Remove(eventType);
    }

使用示例

我们为三个事件分别添加不同输入的监听,然后监听这三个事件是否被输入模块触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main : MonoBehaviour
{
void Start()
{
InputManager.Instance.StartOrCloseInputManager(true);
//设置各个事件的监听输入的消息
InputManager.Instance.ChangeKeyboardInfo(E_EventType.E_Input_Skill1, KeyCode.J, InputInfo.E_InputType.Down);
InputManager.Instance.ChangeKeyboardInfo(E_EventType.E_Input_Skill2, KeyCode.K, InputInfo.E_InputType.Up);
InputManager.Instance.ChangeMouseInfo(E_EventType.E_Input_Skill3, 0, InputInfo.E_InputType.Down);
//添加事件监听
EventCenter.Instance.AddEventListener(E_EventType.E_Input_Skill1, Skill1);
EventCenter.Instance.AddEventListener(E_EventType.E_Input_Skill2, Skill2);
EventCenter.Instance.AddEventListener(E_EventType.E_Input_Skill3, Skill3);
}

private void Skill1()
{
print("技能1触发");
}

private void Skill2()
{
print("技能1触发");
}

private void Skill3()
{
print("技能1触发");
}
}

按下J键,鼠标左键,按下K键抬起时分别输出:image

总结

我们只需要使用数据持久化、UI相关知识点,再结合这节课的输入管理器 就可以做出完整的改键功能了

遗留问题:在真正制作改建功能时,我们应该如何获取到任意键盘或任意鼠标输入来记录对应的输入信息