输入由引擎捕获并转化为输入行为传送到获取了输入焦点并且实现了 on_input()
函数的游戏对象脚本组件中去. 本教程介绍了捕获输入绑定行为的方法以及如何用代码对输入做出响应.
输入系统包含一些概念, 用以让开发者直观地处理游戏逻辑.
输入绑定是整个项目通用的, 记录如何把设备输入映射为带名字的 动作 以方便脚本使用的列表. 新建输入绑定文件, 在 Assets 视图中 右键点击 选择 New... ▸ Input Binding. 然后修改 game.project 里 Game Binding 项对输入绑定文件的引用.
每个新建项目都会自动生成默认输入绑定文件. 默认叫做 “game.input_binding”, 位于项目根目录下 “input” 文件夹内. 双击 即可在编辑器中打开此文件:
点击相关触发类型底部的 + 按钮, 即可新建一个绑定项. 每一项有两个部分:
jump
动作. 可是触屏输入的动作名必须是唯一值.触发器有五种类型:
除了上述五种输入触发器, Defold 还支持 Android 和 iOS 原生系统加速度计输入. 需要勾选 game.project 配置文件中输入部分里的 Use Accelerometer.
function on_input(self, action_id, action)
if action.acc_x and action.acc_y and action.acc_z then
-- 读取加速度计数据
end
end
脚本要想获得输入消息, 就要把 acquire_input_focus
消息发给其所在的游戏对象:
-- 告诉当前游戏对象 (".") 要接收输入消息了
msg.post(".", "acquire_input_focus")
此消息让引擎把可接收输入的游戏对象组件 (脚本, GUI 和集合代理) 压入 输入栈. 这些组件位于栈顶; 最后入栈的组件位于栈顶. 注意如果一个游戏对象包含多个输入组件, 所有组件都会入栈:
如果已获得输入焦点的游戏对象再次请求输入焦点, 那么其组件会被移至输入栈顶端.
输入事件在输入栈上, 从上到下传递.
每个入栈组件都有 on_input()
函数, 一帧中每个输入都调用一次该函数, 连同如下参数:
self
action_id
action
按下
状态等等. 详情请见 on_input() 函数.function on_input(self, action_id, action)
if action_id == hash("left") and action.pressed then
-- 左移
local pos = go.get_position()
pos.x = pos.x - 100
go.set_position(pos)
elseif action_id == hash("right") and action.pressed then
-- 右移
local pos = go.get_position()
pos.x = pos.x + 100
go.set_position(pos)
end
end
由集合代理动态载入的游戏世界都有自己的输入栈. 为了让被载入的游戏世界获得输入信息, 集合代理组件必须位于主游戏世界的输入栈里. 被加载的游戏世界优先于主游戏世界获得输入信息:
开发者经常会忘记发送 acquire_input_focus
来使集合代理所在的游戏对象获得输入焦点. 不这么做的话此集合代理加载的所有游戏世界都无法获得输入消息.
要取消动作监听, 发送 release_input_focus
消息给游戏对象即可. 这样该游戏对象的所有组件都会从输入栈中移除:
-- 告诉当前游戏对象 (".") 释放输入焦点.
msg.post(".", "release_input_focus")
每个 on_input()
函数都能决定当前动作是否要阻止其继续传播下去:
on_input()
返回 false
, 或者未返回值 (此时默认返回 nil
也被看作是false) 输入动作会继续传播.on_input()
返回 true
输入就此销毁. 再无组件可以接收到这个消息. 作用于 全部 输入栈. 也就是说集合代理加载的组件销毁输入那么主栈的组件就收不到这个输入消息了:输入消耗可以使游戏变得灵活, 控制性更强. 例如, 如果需要弹出菜单暂时只有部分界面可以接受点击:
菜单开始是隐藏的 (disabled) 玩家点击 “PAUSE” 组件, 菜单被激活:
function on_input(self, action_id, action)
if action_id == hash("mouse_press") and action.pressed then
-- 玩家点击了 PAUSE?
local pausenode = gui.get_node("pause")
if gui.pick_node(pausenode, action.x, action.y) then
-- 弹出暂停菜单.
msg.post("pause_menu", "show")
end
end
end
此时弹出的暂停菜单获得输入焦点并且消耗输入, 以防止点击穿透:
function on_message(self, message_id, message, sender)
if message_id == hash("show") then
-- 显示暂停菜单.
local node = gui.get_node("pause_menu")
gui.set_enabled(node, true)
-- 获得输入焦点.
msg.post(".", "acquire_input_focus")
end
end
function on_input(self, action_id, action)
if action_id == hash("mouse_press") and action.pressed then
-- 这里做其他游戏逻辑...
local resumenode = gui.get_node("resume")
if gui.pick_node(resumenode, action.x, action.y) then
-- 隐藏暂停菜单
local node = gui.get_node("pause_menu")
gui.set_enabled(node, false)
-- 释放输入焦点.
msg.post(".", "release_input_focus")
end
end
-- 消耗掉输入. 输入栈里其他组件
-- 不会得到输入, 直到脚本释放输入焦点.
return true
end
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB