UFL9-1——输入控制模块的具体实现

输入控制模块的基本原理(以键盘为例)

  1. 制作 InputManager​ 单例模式管理器

    1
    2
    3
    4
    public class InputManager : BaseManager<InputManager>
    {
    private InputManager() { }
    }
  2. 在输入管理器中进行按键检测

    要检测哪个按键,就在帧更新方法内监听对应的方法,帧更新方法需要在添加到公共Mono模块的帧更新内

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    private InputManager()
    {
    MonoManager.Instance.AddUpdateListener(InputUpdate);
    }

    // 开启或者关闭输入模块管理的检测
    public void StartOrCloseInputManager(bool isCheckInput)
    {
    this.isCheckInput = isCheckInput;
    }

    private void InputUpdate()
    {
    if (Input.GetKeyDown(KeyCode.W))
    {
    Debug.Log("按下W键")
    }
    }
  3. 利用事件中心分发事件

    先到事件中心声明对应的事件名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /// <summary>
    /// 事件类型枚举
    /// </summary>
    public enum E_EventType
    {
    /// <summary>
    /// 键盘按下某一个键按下的事件
    /// </summary>
    E_Keyboard_Up,
    /// <summary>
    /// 键盘某一个键抬起的事件
    /// </summary>
    E_Keyboard_Down,
    /// <summary>
    /// 键盘某一个键长按的事件
    /// </summary>
    E_Keyboard,
    }

    要监听键盘的哪个键输入,就在帧更新方法内调用检测键盘按键输入时传入这个按键的枚举值
    检测到输入时,需要将监听到的按键枚举值通过事件中心传递出去,让外部识别

    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
    using UnityEngine;

    public class InputManager : BaseManager<InputManager>
    {
    private InputManager()
    {
    MonoManager.Instance.AddUpdateListener(InputUpdate);
    }

    private void InputUpdate()
    {
    CheckKeyCode(KeyCode.W);
    CheckKeyCode(KeyCode.A);
    CheckKeyCode(KeyCode.S);
    CheckKeyCode(KeyCode.D);

    CheckKeyCode(KeyCode.H);
    CheckKeyCode(KeyCode.J);
    CheckKeyCode(KeyCode.K);
    CheckKeyCode(KeyCode.L);
    }

    private void CheckKeyCode(KeyCode key)
    {
    if (Input.GetKeyDown(key))
    EventCenter.Instance.EventTrigger(E_EventType.E_Keyboard_Down, key);
    if (Input.GetKey(key))
    EventCenter.Instance.EventTrigger(E_EventType.E_Keyboard, key);
    if (Input.GetKeyUp(key))
    EventCenter.Instance.EventTrigger(E_EventType.E_Keyboard_Up, key);
    }
    }
  4. 在希望处理输入逻辑的位置监听事件

    例如要监听W键按下的事件,就需要监听事件中心上的E_EventType.E_Keyboard_Down​事件,根据传入的枚举值来判断是否是W键按下

    注:为了便于移除监听方法,请不要使用匿名函数来监听事件中心的事件!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Main : MonoBehaviour
    {
    void Start()
    {
    EventCenter.Instance.AddEventListener<KeyCode>(E_EventType.E_Keyboard_Down, CheckKeyCode);
    }

    private void CheckKeyCode(KeyCode key)
    {
    switch (key)
    {
    case KeyCode.W:
    print("W键按下");
    break;
    default:
    break;
    }
    }
    }

    但是要注意,由于输入管理器继承的单例基类是懒汉模式的,此时还没有调用过输入模块,因此,这时输入检测是不会生效的

  5. 提供输入系统检测开关

    声明一个bool​变量表示是否开启输入检测,默认是关闭的,声明一个方法来为外部提供开启或关闭输入检测的功能

    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
    private bool isCheckInput;

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

    // 开启或者关闭输入模块管理的检测
    public void StartOrCloseInputManager(bool isCheckInput)
    {
    this.isCheckInput = isCheckInput;
    }

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

    CheckKeyCode(KeyCode.W);
    CheckKeyCode(KeyCode.A);
    CheckKeyCode(KeyCode.S);
    CheckKeyCode(KeyCode.D);

    CheckKeyCode(KeyCode.H);
    CheckKeyCode(KeyCode.J);
    CheckKeyCode(KeyCode.K);
    CheckKeyCode(KeyCode.L);
    }

    然后,在外部开启这个输入检测,之后输入检测就会生效

    注:为了便于移除监听方法,请不要使用匿名函数来监听事件中心的事件!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Main : MonoBehaviour
    {
    void Start()
    {
    InputManager.Instance.StartOrCloseInputManager(true);
    EventCenter.Instance.AddEventListener<KeyCode>(E_EventType.E_Keyboard_Down, CheckKeyCode);
    }

    private void CheckKeyCode(KeyCode key)
    {
    switch (key)
    {
    case KeyCode.W:
    print("W键按下");
    break;
    default:
    break;
    }
    }
    }

    此时按下W键就会输出:image

鼠标按键的监听

先到事件中心声明对应的事件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 事件类型枚举
/// </summary>
public enum E_EventType
{
/// <summary>
/// 鼠标按下
/// </summary>
E_Mouse_Down,
/// <summary>
/// 鼠标抬起
/// </summary>
E_Mouse_Up,
/// <summary>
/// 鼠标长按
/// </summary>
E_Mouse,
}

仿照键盘输入的监听,声明对应的监听鼠标按键输入的方法,
监听到鼠标输入时需要触发事件中心对应的事件,并将监听到的按键通过事件中心传递出去,方便外部识别
要监听鼠标的哪个键的输入,就在帧更新方法内调用检测鼠标按键输入时传入这个按键的ID

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
private bool isCheckInput;

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

// 开启或者关闭输入模块管理的检测
public void StartOrCloseInputManager(bool isCheckInput)
{
this.isCheckInput = isCheckInput;
}

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

//监听鼠标的左键和右键
CheckMouse(0);
CheckMouse(1);
}

private void CheckMouse(int mouseID)
{
if (Input.GetMouseButtonDown(mouseID))
EventCenter.Instance.EventTrigger(E_EventType.E_Mouse_Down, mouseID);
if (Input.GetMouseButton(mouseID))
EventCenter.Instance.EventTrigger(E_EventType.E_Mouse, mouseID);
if (Input.GetMouseButtonUp(mouseID))
EventCenter.Instance.EventTrigger(E_EventType.E_Mouse_Up, mouseID);
}

假设要监听鼠标按键按下的事件,就需要监听事件中心上的E_EventType.E_Mouse_Down​事件,根据传入的mouseID​来识别是哪个按键的输入

注:为了便于移除监听方法,请不要使用匿名函数来监听事件中心的事件!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main : MonoBehaviour
{
void Start()
{
InputManager.Instance.StartOrCloseInputManager(true);
EventCenter.Instance.AddEventListener<int>(E_EventType.E_Mouse_Down, CheckMouse);
}

private void CheckMouse(int mouseID)
{
switch (mouseID)
{
case 0:
print("鼠标左键按下");
break;
case 1:
print("鼠标右键按下");
break;
}
}
}

鼠标左右键分别按下时,输出:image

轴的监听

直接在帧更新方法内不停的触发事件并传递某个轴的值即可,需要传递哪个轴的值就在帧更新方法内触发事件并传递出去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private InputManager()
{
MonoManager.Instance.AddUpdateListener(InputUpdate);
}

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

EventCenter.Instance.EventTrigger(E_EventType.E_Input_Horizontal, Input.GetAxis("Horizontal"));
EventCenter.Instance.EventTrigger(E_EventType.E_Input_Vertical, Input.GetAxis("Vertical"));
}

然后在外部监听对应的事件即可获取值

注:为了便于移除监听方法,请不要使用匿名函数来监听事件中心的事件!

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main : MonoBehaviour
{
void Start()
{
InputManager.Instance.StartOrCloseInputManager(true);
EventCenter.Instance.AddEventListener<float>(E_EventType.E_Input_Horizontal, CheckHorizontalAxis);
}

private void CheckHorizontalAxis(float v)
{
print(v);
}
}

按下AD键输出内容:image

抛出问题

  1. 多开关,分别控制键盘、鼠标、热键检测的开关 —— 思路:加 bool​ 变量
  2. 按下、抬起、长按3种类型的事件合3为1 —— 思路:通过参数传递当前事件类型
  3. 如何制作改键功能