Godot 游戏引擎指南/输入
在每个游戏中,你都可以按下按钮或点击屏幕来执行操作。这被称为对输入做出反应。有两种方法可以做到:使用输入映射和手动方式。
转到你的项目设置,在项目 -> 项目设置... 中点击输入映射选项卡。你会看到一个输入动作列表,例如 "ui_select"。这些是默认的动作,可以编辑,但不能删除。
但是,你可以 - 也应该 - 创建你自己的输入动作。在显示 "动作:" 的地方,输入一个动作名称,例如 "射击" 或 "前进"。按下它旁边的 "添加" 按钮。这将创建你的动作。
你会注意到它没有关联的输入事件。按下你新建动作旁边的 "+" 按钮。你会看到以下选项:
- 键:允许你链接任何键盘按钮。
- 摇杆按钮:允许你链接手柄按钮。
- 摇杆轴:允许你链接手柄摇杆运动。
- 鼠标按钮:允许你链接鼠标按钮。
对于触摸屏和鼠标移动,请参阅下面提到的手动输入检测部分。
有几种方法可以检测它们是否被按下,你选择的方法取决于动作的用途。下面的函数返回一个布尔值,除非另有说明,否则应放在 "if" 或 "while" 语句中。
函数 | 用途 |
---|---|
is_action_pressed | 可以按住的动作,例如移动或射击自动武器 |
is_action_just_pressed | 仅在按钮按下时运行的动作,并且在动作释放之前无法再次运行。 |
get_action_strength | 移动,仅适用于摇杆(用于精确控制)。返回一个浮点数,1 = 摇杆完全倾斜或按下,0 = 摇杆未倾斜或未按下。 |
is_action_just_released | 仅在释放时运行的动作(例如,按钮未按下) |
以上方法都调用Input单例。它们都接受一个字符串参数。
if Input.is_action_just_pressed("shoot"): pass # Add shooting code here
字符串参数必须是输入映射中定义的输入动作的名称。
假设你想要检测鼠标移动或触摸屏事件,或者输入映射无法工作,因为你需要对无法保证用户(或你自己)会创建输入事件的工具进行输入。
这时就需要手动输入检测。这就像使用服务器。手动输入检测是输入映射的低级等效项,它更复杂,但提供更多功能。
以下代码在移动鼠标时移动一个 2D 精灵,并忽略所有其他输入。
extends Sprite2D func _input(event): if event is InputEventMouseMotion: position += event.relative
以下代码创建了一个简单的按钮。_gui_input()
仅适用于 Control 派生节点,并在以下情况下运行:如果该节点处于焦点状态,并且你按下了键,你使用鼠标按下键或鼠标悬停在其上。
extends Control signal button_up signal button_down var pressed = false func _gui_input(event): if event is InputEventMouseButton and event.button_index == BUTTON_LEFT: if event.pressed: get_tree().set_input_as_handled() emit_signal("button_down") pressed = true elif not event.pressed: if pressed: emit_signal("button_up") pressed = false
get_tree().set_input_as_handled()
确保不会调用其他 _gui_input()
和 _unhandled_input()
。
存在许多输入事件。它们如下所示:
- InputEventMouse
- InputEventMouseButton:在你点击鼠标按钮时发出。使用以下方法读取:button_index(BUTTON_* 常量),pressed。
- InputEventMouseMotion:在你移动鼠标时发出。使用以下方法读取:relative(自上次调用输入以来鼠标移动了多少),position(鼠标相对于游戏窗口左上角的新位置 - 或 "_gui_input" 调用中的节点位置)。
- InputEventKey:在你按下键盘上的按钮时发出。使用以下方法读取:button_index(KEY_* 常量),pressed,echo(如果为真,表示按钮正在被按下)。
- InputEventScreen
- InputEventScreenTouch:在你点击屏幕时发出。使用以下方法读取:position(你在屏幕上点击的位置),pressed(如果为假,表示你正在松开屏幕)。
- InputEventScreenDrag:在你拖动屏幕时发出。使用以下方法读取:position(新位置),relative(自上次输入调用以来手指移动了多少),fingers。
- InputEventJoy
- InputEventJoyButton:对手柄按钮按下发出。使用以下方法读取:pressed,button_index(JOY_* 常量)。
- InputEventJoyMotion:对手柄摇杆移动发出。使用以下方法读取:position,relative。
更多信息请参见:InputEvent 教程.
因此,读取输入非常有趣。但是,还有几种更常用的输入方法。在你继续阅读之前,尝试思考一下它们是什么?
除了使用输入单例之外,还有更多方法,本节将深入解释可以实现的功能以及如何实现。
动作控制存在于许多流行游戏中,特别是 VR 和/或 AR 游戏中,用于环顾四周。
func _process(): # Assume camera_pivot is a Spatial with a child Camera3D node. # The gyroscope only works when exported to Android. $camera_pivot.rotation += Input.get_gyroscope() # The accelerometer is only available when exported to mobile. # This makes you move the camera when you move the device (Not when you rotate it) $camera_pivot.rotation += Input.get_accelerometer()
这仅适用于 "旋转" 动作检测。
许多流行游戏都包含振动。你可以在玩家受到攻击时调用以下代码:
Input.vibrate_handheld(100) Input.start_joy_vibration(0, .5, .4, 1.0)
这将使手柄或移动设备振动 1 秒。
InputMap 允许通过代码创建输入动作。这在玩家可以更改控制方案的游戏中很有用。
有关 InputMap 的详细介绍,请参见此处.
以下代码创建了一个射击动作,它对应于按下空格键。
extends Node func _ready(): InputMap.add_action("shoot") InputMap.action_add_event("shoot", create_key_action(KEY_SPACE)) func create_key_action(key) -> InputEventKey: var input = InputEventKey.new() input.pressed = true input.scancode = key return input
它可以是任何输入事件,不一定是 InputEventKey。
有关创建 InputEvents 的更多信息,请参见下面的 "伪造" 输入部分。
其他有用方法
- load_from_globals(): 将动作重置为项目设置中设置的动作。
- has_action(action: String): 如果动作存在,则返回 true。
- action_erase_events(action: String): 清除动作,使其不再有任何关联事件。
- erase_action(action: String): 删除动作。
与往常一样,这并非列出所有方法。只是一些方法。
要录制音频,你需要执行两项操作:
- 按下屏幕底部的 "音频" 按钮,然后按下 "新建总线"。将其重命名为 "麦克风",然后点击 "添加效果"。在出现的下拉菜单中按下 "录制"。
- 为你的场景创建一个新的 AudioStreamPlayer 节点。在检查器中点击流值。从出现的下拉菜单中选择新建 AudioStreamMicrophone。将 "总线" 属性设置为 "麦克风"。每当你想在游戏中录制音频时,只需将 "播放" 值设置为 true 即可。
读取音频以进行语音识别等操作可以实现,但并不容易,超出了本书(以及作者知识)的范围。
所以,你决定要在运行时更改 InputMap?但是该怎么做呢?
Godot 有几个强大的类,它们使输入检测更容易,并且可以伪造输入。
更多信息请参见:InputEvent 教程.
InputEventKey 用于检测键盘按键的按下和释放。
以下代码允许你使用 A 键射击
extends Node func shoot_with_a(): var input = InputEventKey.new() input.pressed = true input.scancode = KEY_A InputMap.action_erase_events("shoot") InputMap.action_add_event("shoot", input)
“Input.event_erase_actions” 清除为指定事件的所有操作。在这种情况下,空格键将不再让你射击。
可以指定任何键,尽管可能不清楚如何做到这一点。
常量 | 关于 |
---|---|
KEY_<letter> | 字母键 |
KEY_<number> | 数字键 |
KEY_KP_MULTIPLY | 小键盘上的 * 键 |
KEY_KP_DIVIDE | 小键盘上的 / 键 |
KEY_KP_SUBTRACT | 小键盘上的 - 键 |
KEY_KP_ADD | 小键盘上的 + 键 |
KEY_KP_ENTER | 小键盘上的 Enter 键 |
KEY_KP_PERIOD | 小键盘上的 . 键 |
KEY_SPACE | 空格 键 |
KEY_ENTER | 回车 键 |
KEY_PLUS | + 键 |
KEY_MINUS | - 键 |
KEY_EQUALS | = 键 |
KEY_QUESTION | ? 键 |
KEY_EXCLAIM | ! 键 |
KEY_QUOTEDBL | " 键 |
KEY_NUMBERSIGN | # 键 |
KEY_DOLLAR | $ 键 |
KEY_PERCENT | % 键 |
KEY_AMPERSAND | & 键 |
KEY_APOSTROPHE | ' 键 |
KEY_PARENLEFT | ( 键 |
KEY_PARENRIGHT | ) 键 |
KEY_AT | @ 键 |
KEY_COLON | : 键 |
KEY_SEMICOLON | ; 键 |
KEY_GREATER | > 键 |
KEY_LESS | < 键 |
KEY_BRACKETLEFT | [ 键 |
KEY_BRACKETRIGHT | ] 键 |
KEY_BACKSLASH | \ 键 |
KEY_ASCIICIRCUM | ^ 键 |
KEY_UNDERSCORE | _ 键 |
KEY_QUOTELEFT | ` 键 |
KEY_BRACELEFT | { 键 |
KEY_BRACERIGHT | } 键 |
KEY_BAR | | 键 |
KEY_ASCIITIDLE | ~ 键 |
KEY_STERLING | £ 键 |
KEY_CENT | ¢ 键 |
KEY_YEN | ¥ 键 |
KEY_COPYRIGHT | © 键 |
KEY_REGISTERED | ® 键 |
KEY_UP | 向上 箭头键 |
KEY_DOWN | 向下 箭头键 |
KEY_LEFT | 向左 箭头键 |
KEY_RIGHT | 向右 箭头键 |
KEY_TAB | Tab 键 |
KEY_BACKTAB | Shift + Tab 键同时按下 |
KEY_ESCAPE | Esc 键 |
KEY_DELETE | 删除 键 |
KEY_INSERT | 插入 键 |
KEY_BACK | 退格 键 |
KEY_SHIFT | Shift 键 |
KEY_ALT | Alt 键 |
KEY_CONTROL | Ctrl 键(仅限 Windows) |
KEY_META | Meta 键(仅限 Linux) |
KEY_F<number 1 to 12> | 键盘上的 F1 等键 |
KEY_COMMA | , 键 |
KEY_PERIOD | . 键 |
KEY_ASTERISK | * 键 |
KEY_SLASH | / 键 |
KEY_HOME | Home 键 |
KEY_PAUSE | 暂停 键 |
KEY_PRINT | Print Screen 键 |
KEY_CLEAR | 清除 键 |
KEY_END | End 键 |
KEY_SYSREQ | 系统请求 键 |
KEY_PAGEUP | Page Up 键 |
KEY_PAGEDOWN | Page Down 键 |
KEY_NUMLOCK | Num Lock 键 |
KEY_SCROLLLOCK | Scroll Lock 键 |
KEY_CAPSLOCK | Caps Lock 键 |
KEY_MENU | 上下文菜单 键 |
KEY_HELP | 帮助 键 |
KEY_BACK | 媒体后退 键。不要与 Android 设备上的后退按钮混淆 |
KEY_FORWARD | 媒体前进 键 |
KEY_STOP | 媒体停止 键 |
KEY_MEDIAPLAY | 媒体播放 键 |
如果你想检测某些修饰符是否也被按下,你可以访问这些变量
- alt: 如果必须按下键盘上的“Alt”,则设置为 true。
- ctrl: 如果必须按下键盘上的“Ctrl”(仅限 Windows),则设置为 true。
- meta: 如果必须按下键盘上的“Meta”(仅限 Linux),则设置为 true。
- command: 如果必须按下键盘上的“Ctrl”(在 Windows 上)或“Meta”(在 Linux 上),则设置为 true。
- shift: 如果必须按下键盘上的“shift”,则设置为 true。
你可以将其中多个设置为 true。这将使你需要按下所有设置的修饰符。
如果你想检测释放键而不是按下键,不要将“pressed”设置为 true。如果将其添加到 InputMap,它将在其键未按下时始终保持按下状态,但只有在它们第一次释放后才会发生。那么如何解决这个问题呢?实际上,这很简单!
试试 Input.event_<press/release>("shoot")
。它工作得很好,但是如果你需要在 _input 的调用中使用它,你应该改用 Input.parse_input_event(event)
。
检测鼠标输入或模拟鼠标输入可以用两种方式解释:鼠标按钮和鼠标移动。
另请参阅:鼠标和输入坐标教程.
点击鼠标是一个常见的操作,但模拟它并不常见。它是最简单的阅读和模拟的操作,只需要 2 个值。
extends Node func shoot_on_click(): var input = InputEventMouseButton.new() input.pressed = true input.button_index = BUTTON_LEFT InputMap.action_erase_events("shoot") InputMap.action_add_event("shoot",input)
变量 | 用途 |
---|---|
doubleclick | 如果为 true ,则该按钮被双击。 |
factor | 事件的系数(或增量),以浮点数表示。在用于高精度滚动事件时,这表示滚动量。仅在某些平台上受支持。如果当前平台不支持,则可能为 0 。 |
常量 | 含义 |
---|---|
BUTTON_LEFT | 左键单击 |
BUTTON_RIGHT | 右键单击 |
BUTTON_MIDDLE | 滚动按钮按下 |
BUTTON_WHEEL_UP | 向上滚动滚轮。 |
BUTTON_WHEEL_DOWN | 向下滚动滚轮。 |
模拟鼠标移动可能更难,但有时非常有用。如果你真的想移动鼠标,请改用 Input.set_mouse_position(<Vector2>)
。这也会生成一个 InputEventMouseMotion 事件以触发。
变量 | 用途 |
---|---|
position | 新的鼠标位置,相对于节点的视口。如果在 _gui_input 中调用,它相对于 Control 节点。 |
relative | 相对于上次调用中的鼠标位置的新位置。 |
speed | 鼠标速度,以像素/秒为单位 |
默认情况下,此事件最多每渲染一帧只发出一次。如果你需要更精确的输入报告,请考虑使用 Input.set_use_accumulated_input(false)
以尽可能频繁地发出事件。如果你需要这样做来绘制徒手线条,请考虑使用 Bresenham's 线算法 以及避免在快速移动鼠标时出现间隙。
(仅在移动设备上可用)
读取和写入屏幕拖动比 InputEventMouseMotion 更难,仅仅因为你无法通过代码强制用户移动他们的手指。好吧,这是谎言。但是要做到这一点,你需要有心灵控制或催眠方面的学位,这远远超出了本书的范围!
变量 | 用途 |
---|---|
position | 手指相对于节点视口的新位置。如果在 _gui_input 中使用,它相对于 Control 节点。 |
relative | 手指的新位置,相对于其旧位置。 |
speed | 拖动速度,以像素/秒为单位。 |
(仅在移动设备上可用)
此事件用于轻触(或取消轻触)屏幕。
变量 | 用途 |
---|---|
position | 手指相对于节点视口的位置。如果在 _gui_input 中使用,它相对于 Control。 |
pressed | 如果为 true ,则用户正在将手指放在屏幕上。否则,他们正在将手指从屏幕上拿开。 |
此 InputEvent 用于检测手柄按钮(即:连接到计算机或控制台的控制器上的按钮)。
变量 | 关于 |
---|---|
button_index | 按下的按钮。 |
如果为“true”,则按钮被按下。否则按钮被释放。 |
此事件用于一次移动一个轴的摇杆。此 InputEvent 可能令人困惑。
变量 | 关于 |
---|---|
轴 | 一个常量,具有六个值之一:JOY_AXIS_*,其中 "*" 可以是 0:水平轴上的左摇杆 1:垂直轴上的左摇杆 2:水平轴上的右摇杆 3:垂直轴上的右摇杆 6:左扳机模拟轴 7:右扳机模拟轴 其他四个目前没有功能,被描述为“通用游戏手柄轴”,只是用作空白。 |
轴值 | 从 -1.0 到 1.0 的数字。 |
重要的是要知道,此 InputEvent 没有可以读取的 Vector2 值。以下脚本将此 InputEvent 转换为 Vector3,x 和 y 用于位置,z 用于移动的摇杆。
func convert_from_joypad_motion(event: InputEventJoypadMotion) -> Vector3: match event.axis: JOY_AXIS_0:# Left stick horizontal return Vector3(event.axis_value, 0, 0) JOY_AXIS_1:# Left stich vertical return Vector3(0, event.axis_value, 0) JOY_AXIS_2:# Right stick horizontal return Vector3(event.axis_value, 0, 1) JOY_AXIS_3:# Right stick vertical return Vector3(0, event.axis_value, 1) # An error occurred printerr("Invalid axis: %s in call to convert_from_joypad_motion" % [event.axis]) print_stack() # To help with debugging, print the call stack. return Vector3(0, 0, 0)