InputSystem

InputSystem 是 Unity 自带的一个上层输入系统(隔离了不同的输入设备),可以让我们更方便地处理输入事件,当然同时也不可避免的会失去一些精细的调控。

要想实现本地多人游戏光有 InputSystem 可不够,还需要用 PlayerInput 对输入进行二次绑定以及 PlayerInputManager 来管理多个PlayerInput。

PlayerInput

设置

大部分设置保留默认就行。

  1. Camera:这里由于我是 2D 游戏,所以没有设置 Camera。
  2. Behaviour:这里我没有用默认的 SendMessages,是因为我希望能更精准的控制这些 Callback,所以我选择了 InvokeUnityEvents。

脚本

需要编写一个脚本来处理所有你需要的输入事件,然后在 PlayerInput 组件上注册函数。

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
private int playerIndex = 0;

private Vector2 moveInput = Vector2.zero;

public void OnDeviceLost(PlayerInput playerInput)
{
Debug.Log("OnDeviceLost: " + playerInput.playerIndex);

playerIndex = playerInput.playerIndex;
}

public void OnDeviceRegained(PlayerInput playerInput)
{
Debug.Log("OnDeviceRegained: " + playerInput.playerIndex);

playerIndex = playerInput.playerIndex;
}

public void OnControlsChanged(PlayerInput playerInput)
{
Debug.Log("OnControlsChanged: " + playerInput.playerIndex);

playerIndex = playerInput.playerIndex;
}

// Runs even if inactive
// Initializing variables, setting references
private void Awake()
{
Debug.Log("Awake: " + playerIndex);
}

// Start is called before the first frame update
// Runs only if active
// Gameplay logic, dependent object initialization
private void Start()
{
Debug.Log("Start: " + playerIndex);

if (playerIndex == 0)
{
this.transform.position = new Vector3(-5.0f, 0.0f, 0.0f);
}
else if (playerIndex == 1)
{
this.transform.position = new Vector3(5.0f, 0.0f, 0.0f);
}
}

public void OnMove(InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}

public void OnJump(InputAction.CallbackContext context)
{
if (context.performed)
{
this.transform.position += Vector3.up * 1.0f;
}
}

// Update is called once per frame
// Handling input, animations, UI updates, non-physics movement
// Faster (reacts instantly, runs as often as possible)
private void Update()
{
transform.position += new Vector3(moveInput.x, 0, moveInput.y) * Time.deltaTime * 5f;
}

// Physics calculations, applying forces (Rigidbody.AddForce()), and physics-based movement
// Slower(runs less frequently, only at fixed physics steps)
private void FixedUpdate()
{
}
}

这里目前就是按需实现了两个输入事件,分别是 OnMove 和 OnJump。

PlayerInputManager

PlayerInputManager 设置起来比较简单,而且只有两个 Event。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine.InputSystem;

public class PlayerInputManager : MonoBehaviour
{
public void OnPlayerJoined(PlayerInput playerInput)
{
Debug.Log("OnPlayerJoined: " + playerInput.playerIndex);
}

public void OnPlayerLeft(PlayerInput playerInput)
{
Debug.Log("OnPlayerLeft: " + playerInput.playerIndex);
}
}

测试

因为我懒得再增加一个 Map,从而实现一个键盘可以 Join 两个 PlayerInput,所以只测了一个 PlayerInput(输出如下),两个的也没问题(补测)。

可以发现玩家首次连接的时候会在 PlayerInput 先触发 OnControlsChanged 事件,然后 PlayerInputManager 才会响应 OnPlayerJoined 事件,这一点说实话我也不是很明白,涉及到底层实现了。

PlayerIndex

由于有些本地多人游戏里,每个玩家控制的角色是固定不同的(一般表现为合作游戏),所以需要通过 PlayerIndex 来区分不同的玩家,从而异化角色对相同的输入事件的响应。