一次按键进入游戏引擎以后,会不会交给场景树里的所有节点?Godot官方godot-docs的master分支《Using InputEvent》给出的过程,先从窗口与视口开始,再按阶段和事件类型分配接收者。对整理交互说明的游戏开发读者,这份过程可以说明某个阶段为何有机会收到事件,以及这种机会在什么地方结束。[1]
操作系统的事件先由各平台的DisplayServer读取,送入根Window,再由窗口的Viewport处理。如果视口正在嵌入窗口,它会先按窗口管理职责解释事件,例如窗口移动或尺寸变化。接着,若某个嵌入窗口获得焦点,事件就送入该窗口的视口,处理后视为已处理;没有这样的焦点窗口时,才进入当前视口的节点顺序。这一分流使“进入根窗口”与“已经送到当前场景节点”成为两个具体位置。
节点阶段先是_input:节点需要覆盖相应回调,并且没有禁用这一处理。文档随后安排GUI接收,能收到事件的Control通过_gui_input及gui_input信号参与。普通节点或GUI都能消费事件,使它不再继续传播。因此,后面的阶段带有“到目前还无人消费”的前提,而不是每次输入都会经过的固定通知清单。
GUI之后的快捷键阶段有明确的类型范围:_shortcut_input针对InputEventKey、InputEventShortcut和InputEventJoypadButton。再往后的_unhandled_key_input只针对InputEventKey;_unhandled_input则承接仍未处理的事件。每一阶段还保留覆盖回调和未禁用处理的条件。三个名称虽然都含input,但快捷键、未处理按键和未处理输入,在这份文字里各自有不同的接收范围。
仍无人接收且对象拾取已开启时,事件才用于对象拾取。三维情形还要求视口具有Camera3D:向物理世界投出的射线若击中对象,才会调用相关CollisionObject3D的输入事件函数;二维则有对应的CollisionObject2D过程。这里的先后关系说明,对象回调位于前述阶段之后,而且带有自身条件,不能从一次输入已经产生就认定某个三维对象已经收到它。
传播到普通子节点与后代节点时,文档给出逆深度优先顺序,从场景树底部开始,最后到根节点,并把Window和SubViewport排除在这种遍历之外。但GUI不套用这个顺序:它根据事件位置或获得焦点的Control来寻找接收者。因而,解释一个界面元素的输入去向,需要保留GUI自身的定位依据,而不是把普通节点的树遍历顺序直接移过去。
GUI内部也有两种向上传播关系。鼠标事件可沿场景树向上,但只有目标Control的直接祖先参与,并受mouse_filter限制;键盘和手柄事件不沿GUI树向上传递,只由收到它的Control处理。若没有在GUI中处理,它们会作为非GUI事件继续进入_unhandled_input。这些限定把“向上”具体落在事件类型和目标祖先上,而不是泛指树中所有更高位置的节点。
视口之间还有另一道边界:Viewport不会自动把事件送到别的SubViewport。文档将SubViewportContainer描述为一种桥接对象,在_input或_gui_input之后把事件送给其子SubViewport。以上只整理本次master连续文字中的职责与条件,不把它标成stable或4.5发行版;这里也没有运行项目或从流程图补入额外行为。
资料:[1] https://github.com/godotengine/godot-docs/blob/master/tutorials/inputs/inputevent.rst
信息来源
本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。