Manuals
Manuals




This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English

输入

所有用户输入都由引擎捕获,并作为操作分派给已获取输入焦点并实现了on_input()函数的游戏对象中的脚本和GUI脚本组件。本手册解释了如何设置绑定以捕获输入,以及如何创建响应输入的代码。

输入系统使用一组简单而强大的概念,允许您按照适合游戏的方式管理输入。

Input bindings

设备
输入设备是计算机或移动设备的一部分或插入其中的设备,为Defold运行时提供原始系统级输入。支持以下设备类型:
  1. 键盘 (包括按键输入和文本输入)
  2. 鼠标 (位置, 按键, 滚轮输入)
  3. 单点/多点触摸屏 (iOS, Android 设备和 HTML5 手机端)
  4. 游戏手柄 (操作系统负责将其输入发送给游戏然后映射给脚本. 详见 游戏手柄配置文件)
输入绑定
在输入发送到脚本之前,来自设备的原始输入通过输入绑定表转换为有意义的操作
操作
操作通过您在输入绑定文件中列出的(哈希)名称来标识。每个操作还包含有关输入的相关数据:按钮是按下还是释放,鼠标和触摸的坐标等。
输入监听器
任何脚本组件或GUI脚本都可以通过获取输入焦点来接收输入操作。可以同时激活多个监听器。
输入栈
输入监听器列表,第一个获取焦点的组件在栈底,最后一个获取焦点的组件在栈顶。
消耗输入
脚本可以选择消耗接收到的输入,阻止栈中更下方的监听器接收它。

设置输入绑定

输入绑定是项目范围的表,允许您指定设备输入在分派到脚本组件和GUI脚本之前如何转换为命名的操作。您可以创建一个新的输入绑定文件,在Assets视图中右键点击一个位置并选择New... ▸ Input Binding。要使引擎使用新文件,请更改game.project中的Game Binding条目。

Input binding setting

所有新项目模板都会自动创建一个默认的输入绑定文件,因此通常不需要创建新的绑定文件。默认文件名为”game.input_binding”,可以在项目根目录的”input”文件夹中找到。双击该文件在编辑器中打开:

Input set bindings

要创建新绑定,点击相关触发类型部分底部的+按钮。每个条目有两个字段:

输入
要监听的原始输入,从可用输入的滚动列表中选择。
操作
给输入操作指定的名称,当它们被创建并分派到您的脚本时使用。可以将相同的操作名称分配给多个输入。例如,您可以将空格键和游戏手柄”A”按钮绑定到操作jump。请注意,存在一个已知错误,即触摸输入不幸不能与其他输入具有相同的操作名称。

触发器类型

您可以创建五种设备特定的触发器类型:

键触发器
单键键盘输入。每个键分别映射到相应的操作。在键和文本输入手册中了解更多。
文本触发器
文本触发器用于读取任意文本输入。在键和文本输入手册中了解更多。
鼠标触发器
来自鼠标按钮和滚轮的输入。在鼠标和触摸输入手册中了解更多。
触摸触发器
单点和多点触摸类型触发器在iOS和Android设备上的原生应用程序和HTML5包中可用。在鼠标和触摸手册中了解更多。
游戏手柄触发器
游戏手柄触发器允许您将标准游戏手柄输入绑定到游戏功能。在游戏手柄手册中了解更多。

加速度计输入

除了上面列出的五种不同触发器类型外,Defold还在原生Android和iOS应用程序中支持加速度计输入。在game.project文件的Input部分中勾选Use Accelerometer框。

function on_input(self, action_id, action)
    if action.acc_x and action.acc_y and action.acc_z then
        -- 对加速度计数据做出反应
    end
end

输入焦点

要在脚本组件或GUI脚本中监听输入操作,应将acquire_input_focus消息发送到持有该组件的游戏对象:

-- 告诉当前游戏对象(".")获取输入焦点
msg.post(".", "acquire_input_focus")

此消息指示引擎将游戏对象中具有输入功能的组件(脚本组件、GUI组件和集合代理)添加到输入栈中。游戏对象组件被放在输入栈的顶部;最后添加的组件将位于栈顶。请注意,如果游戏对象包含多个具有输入功能的组件,所有组件都将被添加到栈中:

Input stack

如果已经获取输入焦点的游戏对象再次这样做,其组件将被移动到栈顶。

输入分派和 on_input()

输入操作根据输入栈进行分派,从顶部到底部。

Action dispatch

栈上任何包含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

输入焦点和集合代理组件

每个通过集合代理动态加载的游戏世界都有自己的输入栈。为了使操作分派到达加载世界的输入栈,代理组件必须位于主世界的输入栈上。加载世界栈上的所有组件都在分派继续向下主栈之前被处理:

Action dispatch to proxies

忘记发送acquire_input_focus给持有集合代理组件的游戏对象是一个常见错误。跳过这一步将阻止输入到达加载世界输入栈上的任何组件。

释放输入

要停止监听输入操作,请将release_input_focus消息发送到游戏对象。此消息将从输入栈中移除游戏对象的任何组件:

-- 告诉当前游戏对象(".")释放输入焦点。
msg.post(".", "release_input_focus")

消耗输入

组件的on_input()可以主动控制操作是否应该进一步传递到栈的下方:

  • 如果on_input()返回false,或者省略返回值(这暗示了一个nil返回,在Lua中是假值),输入操作将被传递到输入栈上的下一个组件。
  • 如果on_input()返回true,输入被消耗。输入栈中更下方的任何组件都不会接收到输入。请注意,这适用于所有输入栈。代理加载世界栈上的组件可以消耗输入,阻止主栈上的组件接收输入:

consuming input

有许多很好的用例,其中输入消耗提供了一种简单而强大的方式,在游戏的不同部分之间转移输入。例如,如果您需要一个弹出菜单,暂时是游戏中唯一监听输入的部分:

consuming input

暂停菜单最初是隐藏的(禁用的),当玩家触摸”PAUSE” HUD项时,它被启用:

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

pause menu

暂停菜单GUI获取输入焦点并消耗输入,防止除弹出菜单相关之外的任何输入:

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