U3D之inputSystem_项目练习胡闹厨房

引言


这个游戏主要是练手之用,全程跟的siki的一个课程,游戏使用最新的unity6制作。

项目使用的技术主要有:unity物理引擎,射线检测,inputSystem,animation、animator组件,UGUI系统,shader Graph,场景转换等。


架构设计

场景设计


游戏主要只有三个场景——开始界面、加载界面、游戏界面。


代码组织架构


  • Counter (各种柜台的功能实现)

  • play (玩家的各种控制)

  • UI (ui界面控制,和按键控制)

  • Manager (各种控制器,如音乐,音效控制器)

游戏流程

  • 开始界面点击开始

  • 进入加载界面

  • 进入游戏场景 (整个游戏场景处了游戏结束阶段,都能随时暂停)

  • 进入等待阶段

  • 进入倒计时阶段

  • 开始游戏 (游戏过程中存在游戏倒计时)

  • 游戏结束 (弹出游戏结束ui,点击菜单按钮返回开始界面)

代码实现

输入控制

inputSystem


使用unity新版输入系统inputSystem。需要先在场景中创建一个空物体Gameinput用于挂载输入脚本。创建GameInput脚本,把他添加到场景中的空物体GameInput上。


在Projec栏右键Create –> Input Actions

Createinput.f44786352d2bd5b2.png

创建后可自行命名,事实上unity的模版已经自带了一个input Acions。可以删掉或自行处理。

然后我们双击打开,看到以下界面:

inputSystem.png.c425172cfd094292.png

然后我们开始添加Action Map,点击加号即可:

inputSystem1.d374b3f543ea8d37.png

接下来添加Actions,同样点击加号即可,我们可以命名为Move:

inputSystem2.b98bc179fa54fb90.png

最后添加绑定,还是点击加号,这里添加的是二维移动的绑定:

ActionVector2.f1806caa45d3b197.png

添加后是这样的:

Vector2bin.9b668fcc88726cd8.png

创建后可以对每个Binding进行绑定:

Binding.7d7b8608ee2a50c4.png

可以用Listen监听按键绑定,也可以搜索。

GameInput.cs 的实现

代码实现如下:

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
using System;
using UnityEngine;
using UnityEngine.InputSystem;

public class GameInput : MonoBehaviour
{
// 做成单例模式便于全局调用
public static GameInput instance { get; private set; }

// 常量字段作为存储绑定按键时用的key
private const string GAMEINPUT_BINDING = "gameinput_bingding";

// 以下是注册的事件
// 交互事件
public event EventHandler OnIneractAction;
// 操作事件
public event EventHandler OnOperateAction;
// 游戏暂停事件
public event EventHandler OnStopGameAction;
// 持有游戏输入系统InputSystem的引用(我这里是叫GameControl是我自行定义的)
private GameControl gameControl;

// 定义绑定的类型
public enum BindingType
{
Up,
Down,
Left,
Right,
Interact,
Operate,
Stop
}
// 初始
private void Awake()
{
instance = this;// 单例模式
gameControl = new GameControl();// 创建InputSystem

// 这里是从本地获取游戏按键绑定的数据
if (PlayerPrefs.HasKey(GAMEINPUT_BINDING))
{// 如果存在存储的按键绑定信息
// 加载绑定的信息到InputSystem
gameControl
.LoadBindingOverridesFromJson(
PlayerPrefs
.GetString(GAMEINPUT_BINDING)
);
}
// 启动玩家控制
gameControl.Player.Enable();
// 注册玩家交互事件
gameControl.Player.Interact.performed += Interact_Performed;
// 注册玩家操作事件
gameControl.Player.Operate.performed += Operate_Performed;
// 注册玩家暂停游戏事件
gameControl.Player.Stop.performed += Stop_performed;
}

// 这是MonoBehaviour的类似析构函数的函数
// 在游戏对象消除时会自动调用
private void OnDestroy()
{// 取消事件绑定,避免出现重复绑定的问题
gameControl.Player.Interact.performed -= Interact_Performed;
gameControl.Player.Operate.performed -= Operate_Performed;
gameControl.Player.Stop.performed -= Stop_performed;
// 释放资源,避免存在多个gameControl
gameControl.Dispose();
}

// 这是获取玩家移动单位向量的函数
public Vector3 GetMovementDirectionNormlized()
{
// 从gameControl中获取玩家移动向量
Vector2 inputVector2 = gameControl.Player.Move.ReadValue<Vector2>();

//将移动方向改为三维向量
Vector3 moveDir = new Vector3(inputVector2.x, 0f, inputVector2.y);

//将向量归一为单位向量
moveDir.Normalize();
return moveDir;
}

// 按键重新绑定的函数
public void ReBinding(BindingType bindingType, Action onCompete)
{// 传入的参数有绑定的类型和一个回调函数

// 初始化InputAction
InputAction inputAction = null;

// 初始化标记
int index = -1;

// 停用原本的玩家输入,这一步是必须的
// 必须先停用才能进行重新绑定
gameControl.Player.Disable();

// 用switch实现多分支
switch (bindingType)
{
case BindingType.Up:
// 设置inputAction
inputAction = gameControl.Player.Move;
// 设置index
index = 1;
break;
case BindingType.Down:
inputAction = gameControl.Player.Move;
index = 2;
break;
case BindingType.Left:
inputAction = gameControl.Player.Move;
index = 3;
break;
case BindingType.Right:
inputAction = gameControl.Player.Move;
index = 4;
break;
case BindingType.Interact:
inputAction = gameControl.Player.Interact;
index = 0;
break;
case BindingType.Operate:
inputAction = gameControl.Player.Operate;
index = 0;
break;
case BindingType.Stop:
inputAction = gameControl.Player.Stop;
index = 0;
break;
default:
break;
}

// 这里执行重新监听绑定
// 通过inputAction重新设定下标对应的绑定
inputAction.PerformInteractiveRebinding(index)
// 是一个链式调用方法,用于注册一个回调函数。该函数在绑定结束后被调用
.OnComplete(
// callback表示冲洗绑定操作的回调上下文
// 提供了重新绑定操作的相关信息和方法
callback =>
{
// 释放资源
callback.Dispose();

// 启用玩家游戏控制
gameControl.Player.Enable();

// 执行回调函数,用于可视化操作
// 这时本函数传入的参数之一
onCompete?.Invoke();

// 储存按键绑定信息
PlayerPrefs.SetString(GAMEINPUT_BINDING, gameControl.SaveBindingOverridesAsJson());
// 储存
PlayerPrefs.Save();
}).Start();
}

// 加载存储的绑定信息的函数
public string GetBindingDisplayString(BindingType bindingType)
{// 获取绑定信息
switch (bindingType)
{
case BindingType.Up:
return gameControl.Player.Move.bindings[1].ToDisplayString();
case BindingType.Down:
return gameControl.Player.Move.bindings[2].ToDisplayString();
case BindingType.Left:
return gameControl.Player.Move.bindings[3].ToDisplayString();
case BindingType.Right:
return gameControl.Player.Move.bindings[4].ToDisplayString();
case BindingType.Interact:
return gameControl.Player.Interact.bindings[0].ToDisplayString();
case BindingType.Operate:
return gameControl.Player.Operate.bindings[0].ToDisplayString();
case BindingType.Stop:
return gameControl.Player.Stop.bindings[0].ToDisplayString();
default:
return "";
}

}

// 以下是注册的事件
private void Stop_performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
{
OnStopGameAction?.Invoke(this, EventArgs.Empty);
}

private void Operate_Performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
{
OnOperateAction?.Invoke(this, EventArgs.Empty);
}

private void Interact_Performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
{
OnIneractAction?.Invoke(this, EventArgs.Empty);
}
}