如何构建TwilioQuest扩展并在游戏中加入自己的内容

205 阅读13分钟

TwilioQuest的爱好者、游戏开发者、教育工作者......欢呼吧!你现在可以建立自己的TwilioQuest(TQ)扩展。你可以设计新的关卡,编写你自己的代码挑战,并为TwilioQuest生态系统作出贡献。

在这篇文章中,你将学习开发TwilioQuest扩展的机制。你将建立一个VR培训任务,其目标是教玩家如何用Twilio发送短信。作为建立培训任务的一部分,你将:

  • 规划出任务的学习目标
  • 设计一个使用Tiled的关卡地图
  • 使用JavaScript和Node.js验证玩家对每个目标的反应。

设置你的开发环境

在进行游戏的任何步骤之前,你需要在你的电脑上为所有的TQ扩展创建一个文件夹,并下载你需要的模板文件。

创建你的扩展文件夹

从你的终端窗口或命令提示符,导航到你喜欢的位置,然后运行以下命令:

mkdir tq-extensions
cd tq-extensions

在你的浏览器中,导航到TwilioQuest扩展模板,并按照提示从这个模板创建你自己的资源库。

一旦完成,回到你的终端,运行以下命令,注意用你的信息替换占位符:

git clone https://github.com/your-username/your-repo-name.git
cd your-repo-name
npm install

这将创建一个新的目录,包含你自己的TwilioQuest扩展所需的所有基本文件。这样开始并不是必须的,但是模板包含了关卡地图的所有图像和瓦片信息,你将需要这些信息来设计自己的地图。

下一步是导航到新模板文件夹内的关卡文件夹,复制里面的例子关卡目录:

cd levels
cp -R vr_mission_template my_first_mission

这将创建一个新的关卡,供你编辑和制作你自己的关卡。本教程接下来的两个部分将指导你设置TwilioQuest和安装你的新扩展。

启动游戏

你需要做的第一件事是下载并安装TwilioQuest。一旦游戏安装完毕,打开该应用。在本出版物发表时,TQ扩展仅在预览即将发布的v3.2版本时可用,该版本应在2021年晚些时候发布。要加载v3.2的预览版,在打开应用程序后,朝启动器的底部看。

Screenshot of the TwilioQuest launch screen

点击当前版本号后的 "更多 "按钮,然后从下拉菜单中选择TQ 3.2预览版。之后,该应用程序可能需要花一些时间下载和提取游戏数据。一旦准备就绪,它就会在底部显示 "TwilioQuest已准备好启动!",然后你就可以点击PLAY TWILIOQUEST 按钮。

在TwilioQuest中启用扩展功能

点击PLAY TWILIOQUEST 按钮后,你会看到一个加载屏幕,片刻之后,会提示你按下空格键。按空格键将使你进入游戏。如果你是第一次下载和玩TwilioQuest,你首先会看到一个序幕和一个需要完成的简短任务。虽然你可以在完成介绍性任务之前启用扩展,但在完成任务之前,你将无法进入雾中猫头鹰(从而测试你的扩展)。

沿着游戏屏幕的顶部,在中心位置,你会看到四个图标。点击左起第三个图标,或按键盘上的数字3 ,以打开设置 菜单:

Screenshot showing the top menu bar of the TwilioQuest game, with the icon representing the settings button circled in red.

一旦打开设置菜单,沿着左侧寻找扩展程序选项。选择该选项,打开扩展程序设置面板:

Screenshot of the TwilioQuest extensions settings panel in the main settings menu. There is a button that says Enable Extensions.

点击 "启用扩展"的按钮。

启用扩展后,你会看到一个扩展目录的字段,上面有一个选择目录的按钮。点击这个按钮,找到并选择你之前创建的文件夹:tq-extensions

Screenshot of the extensions settings panel showing a field and corresponding button to choose the directory.

当你选择了一个目录后,你的更改将被自动保存。设置完扩展目录后,重新启动TwilioQuest。当你再次启动它时,一定要选择TQ 3.2预览版,就像你第一次启动它时那样。每次重启TwilioQuest时,你都需要做这个选择。

重新启动后,回到TQ的设置菜单。现在你应该可以看到,你的扩展模板已经被加载:

Screenshot of the extensions settings panel with text listing the loaded extensions, circled in red.

组织你的TwilioQuest VR任务

现在,TwilioQuest可以找到你的扩展,是时候开始定制和建立你的关卡了。

规划出关卡的学习目标

TwilioQuest VR培训任务要求玩家完成目标,以完成任务。一个目标是你为任务添加教育内容的地方。每个任务可以有多个目标。

提前规划出玩家在任务中应掌握的学习目标,将为你设计地图提供参考,并使你的关卡具有凝聚力。

在本教程中,你要建造的关卡将给玩家两个目标:

  • 首先,玩家必须收集他们的账户凭证(他们的Twilio账户SID和Twilio Auth Token)。
  • 第二,玩家必须使用这些凭证来发送短信。为了达到第二个目标,他们必须先通过第一个目标。

玩家在游戏中的路径将被激光障碍物所阻挡。当玩家完成第一个目标时,第一个激光屏障将打开。第二个激光屏障将在玩家完成第二个目标时打开。在后面的步骤中,你将学习如何验证玩家对目标的反应,以确定他们是否成功完成了目标。

下图描述了这个项目的关卡地图,显示了玩家的入口和出口,两个激光屏障都关闭了。你将在接下来的章节中学习如何访问这个地图。

Screenshot of level map for the extension being created in this tutorial

定制关卡元数据

现在你已经规划好了你的关卡,你可以开始对代码进行修改。

打开名为my_first_mission/level.json的文件。这是你的关卡的基本描述性数据的存放地。

在这个模板*.json*文件中,并不是所有的属性都需要被编辑。修改你的文件以反映下面的高亮行:

{
  "title": "My First VR Mission",
  "description": "A VR training mission for TwilioQuest that teaches players how to send an SMS!",
  "mission_icon": "mission_icon.png",
  "is_mission": true,
  "priority": 1,
  "backgroundMusic": "hackertheme_104771b",
  "backgroundEffect": {
    "key": "vr",
    "options": {}
  },
  "flavorTextOverrides": {
    "bookshelf": "There's nothing like a good book."
  },
  "objectives": ["get_credentials", "send_sms"]
}

你在这个*.json*文件中所做的修改是更新关卡的标题和描述,同时将占位的目标名称更新为正确的名称:get_credentialssend_sms

因为你改变了level.json文件中的目标名称,你也必须改变你的文件结构中的名称。

如果你查看my_vr_mission/objectives文件夹,你会看到名为example_objectiveexample_objective2的文件夹:

  • example_objective目录的名称改为get_credentials
  • example_objective2的名称改为send_sms

设计并验证你的关卡的目标

你的关卡有两个目标:get_credentialssend_sms 。每个目标的数据都存储在自己的文件夹中。

每个目标文件夹应该有以下文件:

  • objective.json
  • description.md
  • walkthrough.md
  • 验证器.js

在这一部分,你将从第一个目标开始:get_credentials

当玩家在TwilioQuest中打开一个目标时,他们会看到一个模态,上面有三个标签。概述目标,和帮助。每个标签的内容都是由上面列出的文件生成的。现在,我将带领你了解每一个标签。

objective.json文件和概览标签

这是玩家将看到的第一个屏幕。这个屏幕显示了目标的标题,玩家完成目标后将得到的XP奖励,以及对目标目的的简要描述。在模式的右侧,你可以看到两个任务/问题,是这个目标的一部分。获取你的账户SID获取你的授权令牌。无论玩家在哪个标签上,模态的这一部分都会持续存在。

所有这些数据都存储在objective.json文件中:

A screenshot of the objective's modal inside TwilioQuest, with the contents of the Overview tab in view.

在你的文本编辑器中打开my_vr_mission/objectives/get_credentials/objective.json文件。删除里面的任何内容,用以下内容代替:

{
  "title": "Get Your Account Credentials",
  "description": "Learn how to get your Twilio account credentials in order to send an SMS!",
  "validation_fields": [
    {
      "name": "accountSid",
      "type": "text",
      "placeholder": "Paste your Account SID here",
      "label": "Get your Account SID"
    },
    {
      "name": "authToken",
      "type": "text",
      "placeholder": "Paste your Auth Token here",
      "label": "Get your Auth Token"
    }
  ],
  "rewards": {
    "xp": 100
  }
}

在上面的JSON中,你会看到顶级的属性键。title,description,validation_fields, 和rewards

title,description, 和rewards 属性对应于玩家可以在概览 标签上看到的目标的标题、描述和奖励。

validation_fields 属性持有一个由两个对象组成的数组:每个对象对应于目标的模式右侧显示的一个问题。你可以向这个数组添加额外的问题,或者甚至让它为空。除了text ,或基于字符串的问题,你还可以创建具有字段类型 prompttextareaboolean ,或任何其他HTML输入类型的问题

description.md文件和Objective标签

保存并关闭object.json文件。打开description.md文件,该文件与你刚才处理的objective.json文件在同一文件夹中。这个文件是向玩家描述你希望他们作为这个目标的一部分做什么的地方。

删除这个文件中的任何内容,用下面的标记替换它:

# Get Your Account Credentials

<div class="aside">
<h3>To-Do List</h3>
<ul>
  <li>Get your Account SID</li>
  <li>Get your Auth Token</li>
</ul>
</div>

To find these credentials, you can login to the [Twilio Console](https://twilio.com/console). Once you log into the Console, look on your main account dashboard under the heading **Project Info**. You should see your credentials there.

这个文件的内容将被呈现在目标模式的目标标签上:

A screenshot of the objective's modal inside TwilioQuest, with the contents of the Objective tab in view.

walkthrough.md文件和帮助标签

walkthrough.md文件是你可以添加详细说明或有用信息的地方,这对正在努力完成目标的玩家有好处。这些内容将在 "帮助"选项卡上呈现。你不会为本教程编辑walkthrough.md文件。你可以随意保留里面的任何占位符内容。

帮助标签可能会在未来的某个时候从TwilioQuest中移除。TQ团队还建议,在向帮助标签添加内容时,主要是链接到外部资源,而不是编写自己的教育内容。

验证get_credentials的目标

打开get_credentials目标文件夹中的validator.js文件。删除里面的所有内容。

每个validator.js文件都是一个Node.js模块,如果需要的话,可以消费其他Node.js模块。每个validator.js 文件应该导出一个函数,接收一个helper 对象。这个helper 对象有来自游戏用户界面的重要信息,包括对目标问题的回答。

helper 对象也有两个回调函数:success()fail() 。一旦确定玩家错过了什么,你的验证代码就会调用fail() 方法。success() 回调函数将只在验证代码的最末端被调用。基本上,只有当玩家没有失败时,代码才应该进入success() 回调。

这意味着,当你设计你的验证代码时,你应该首先检查所有可能的失败。我将在你为这个目标建立validator.js文件时讲解这个概念。

首先,在validator.js中添加以下代码:

module.exports = async function (helper) {

};

这段代码是你导出的验证器函数的外壳。

在验证器函数中,添加高亮行:

module.exports = async function (helper) {
  const { accountSid, authToken } = helper.validationFields;
};

上面代码中的高亮行是访问玩家对目标中两个问题的回答。如果你再看一下目标的objective.json文件,你会发现这里使用的变量名称与分配给validation_fields 数组中每个问题的名称一致。

下一步是检查第一个潜在的失败:玩家是否将这些问题中的任何一个留空?如果他们这样做了,那么继续验证就没有意义了。要看到这个动作,请在你的验证器函数中添加高亮的行:

module.exports = async function (helper) {
  const { accountSid, authToken } = helper.validationFields;

  if (!accountSid || !authToken) {
    return helper.fail(`
      Whoops, looks like you're missing one of the answers! Check to make sure you provided both an Account SID and Auth Token
    `);
  }
};

如果玩家将其中任何一个字段留空,那么helper.fail() 回调将被返回,验证将停止执行。玩家将看到传递给fail() 回调方法的任何信息:

Screenshot showing the failed the objective with the failure message inside the TwilioQuest game

如果if-statement失败,代码将继续执行。在你确定accountSidauthToken 都有值之后,下一步是尝试使用玩家的凭证来连接到Twilio API。

在if语句的下面,添加下面的try-catch块:

module.exports = async function (helper) {
  ...

  try {
    const client = require('twilio')(accountSid, authToken);
  } catch (e) {
    return helper.fail(`Looks like something went wrong: ${e}`);
  }
};

这段代码尝试使用Twilio Node Helper Library连接到Twilio API。如果这个尝试出了问题,代码将转移到catch块,并自动返回一个包含错误的失败信息。

在真正的TwilioQuest级别中,你可能想让失败信息更加人性化,而不是仅仅返回错误信息,或者给出额外的提示。

如果连接到Twilio API的尝试是成功的,那么你就知道玩家输入了有效的、可用的凭证。这意味着他们完成了目标

在try-catch块下面,添加以下代码:

module.exports = async function (helper) {
  ...

  helper.success(`
    Hooray! You did it!
  `, [{ name: "ACCOUNT_SID", value: accountSid, concealed: true }, { name: "AUTH_TOKEN", value: authToken, concealed: true }]);
};

这段代码运行helper.success() 回调。它向回调传递了两样东西:一个玩家将看到的成功信息和一个数组。数组是可选的,但在本例中它非常重要。

因为本关的第二个目标要求玩家用他们在第一个目标中输入的账户凭证发送短信,你需要能够在第二个目标的验证代码中访问这些凭证。

否则,如果不能访问玩家在第一个目标中输入的成功凭证,就没有办法测试他们在第二个目标中的短信是否成功。唯一的选择是让他们再次输入他们的凭证,这对玩家来说是非常烦人的。

相反,每当你调用success() 回调时,你可以传递一个对象阵列作为第二个参数。这个数组中的对象将成为环境变量,你现在可以在任何其他目标的验证代码中访问这些变量。

由TQ扩展创建的环境变量是没有命名空间的--这意味着一个扩展的环境变量有可能覆盖另一个扩展的环境变量。避免这种情况的方法是在环境变量前加上你的扩展的简短标识符。

完成你的第二个目标

恭喜你,你完成了第一个目标的代码现在是时候进入第二个目标了:send_sms

objective.json文件

打开my_vr_mission/objectives/send_sms/objective.json文件。把所有的内容替换成以下内容:

{
  "title": "Send an SMS",
  "description": "Send an SMS with the Twilio API",
  "validation_fields": [
    {
      "name": "messageSid",
      "type": "text",
      "placeholder": "Type your message SID here",
      "label": "What is the SID of the message you just sent? You can find it in the response of your API call."
    }
  ],
  "rewards": {
    "xp": 100
  }
}

除了标题和描述之外,这个object.json文件和上一个目标的主要区别是validation_fields 数组。这个目标将只有一个问题。你刚刚发送的消息的SID是什么?

当你为这个目标写验证代码时,你将使用这个SID和玩家的Twilio凭证来获取消息,并将其正文与你在目标的description.md文件中列出的预期文本进行比较。

description.md文件

保存并关闭你的objective.json文件,转而打开my_vr_mission/objectives/send_sms/description.md文件。用以下内容替换所有内容:

# Send an SMS 

<div class="aside">
<h3>To-Do List</h3>
<ul>
  <li>Use the API to send an SMS that says "TwilioQuest rocks!</li>
  <li>Submit the message SID</li>
</ul>
</div>

Make an API request using the same account credentials that you entered in the previous objective to send an SMS that says "TwilioQuest rocks!" Check the response for the message's SID and submit it here.

就像之前的目标一样,请随意保留walkthrough.md文件的原样。

验证目标

下一步是验证玩家的反应。打开my_vr_mission/objectives/send_sms/validator.js文件,删除里面的所有内容。

复制以下代码并粘贴到该文件中:

const assert = require("assert");

module.exports = async function (helper) {
  const { TQ_ACCOUNT_SID:accountSid, TQ_AUTH_TOKEN: authToken } = helper.env;

  try {
    const client = require('twilio')(accountSid, authToken);
    const {messageSid} = helper.validationFields;

    const message = await client
                    .messages(messageSid)
                    .fetch();

    assert.strictEqual(message.body, 'TwilioQuest rocks!')
  } catch (e) {
    return helper.fail(`Looks like something went wrong: ${e}`);
  }

  helper.success(`
    Hooray! You did it!
  `);
};

这段代码导入了assert npm包。然后,在validator函数中,它将你在第一个目标中创建的两个环境变量保存为本地变量。这就是高亮行(第4行)上发生的事情。

注意到环境变量有一个前缀,在你创建它们时并不存在:TQ_ 。每当你访问在游戏中创建的环境变量时,你都需要使用这个前缀。

现在可以访问玩家的账户凭证了,这段代码试图连接到API并使用玩家在这个目标中提供的消息SID来获取消息。

假设消息被成功获取,该代码将比较消息的主体和预期的文本:TwilioQuest rocks!

如果消息不能被获取,将抛出一个错误,并从catch块中运行fail() 回调。

同样地,如果断言是falsefail() 回调将从catch块中运行。

如果上述检查都没有失败,那么你就知道玩家成功地完成了目标,success() 回调将运行。

创建关卡地图

构建TwilioQuest扩展的一个额外有趣的部分是设计关卡地图。TQ团队使用一个名为Tiled的应用程序来设计和构建游戏的地图。你将在你的关卡中使用同样的应用程序,而且你下载的扩展模板已经包含了你需要的所有瓷砖组,可以开始使用。

设计一个图层地图可能很复杂,Tiled应用程序有很多细微的差别。我不会在这篇文章中讨论所有这些,但TQ团队已经写了一些关于地图构建的伟大文档。另外,请继续关注即将发布的教程,它将涵盖所有这些概念!

同时,在这个演示中,我将为你提供地图文件

在你的任务文件夹,my_vr_mission,寻找地图子文件夹。在这个文件夹中,会有一个default.json文件。这是TQ团队为你制作的关卡地图,它非常酷,但与你在这个扩展中创建的目标不一致。因此,在本教程中,用我创建的default.json文件替换所含的default.json文件,可在这个gist上找到。

测试你的扩展

保存所有打开的文件,然后重新启动TQ,注意将版本设置为TQ 3.2 Preview

一旦游戏加载完毕,你会发现你的头像在雾中猫头鹰里面。

Screenshot of the TwilioQuest game, showing my TwilioQuest avatar centered inside the Fog Owl

使用方向键将你的化身向下和向右移动,进入训练室。

Screenshot of the TwilioQuest game with the avatar positioned inside the training room

走近房间中央的电脑,你会看到一个感叹号出现。在TQ中,感叹号表示你可以与之互动的对象。按空格键来启动互动。

Screenshot of TwilioQuest game with avatar positioned at the computer in the training room, with an exclamation mark over the computer indicating it can be interacted with.

在这种情况下,按空格键将打开一个模式,里面有所有可用的训练任务,包括你的。

选择你的任务,然后点击启动任务按钮。这将打开你的关卡。

Screenshot of the extension level created in this tutorial

使用方向键,导航到右边的第一个终端,然后按空格键。这将打开你的第一个目标。完成目标,你会看到一个成功的屏幕。一旦你退出模式,第一个激光屏障将打开。

Screenshot of TwilioQuest game showing objective success screen

Screenshot of TwilioQuest game showing first laser barrier open

继续前进,使用下一个终端完成第二个目标并退出本关。

恭喜你,你成功了