首批拥有虚拟化身,即可以早期访问 Decentraland。
X
你好,请选择
语言
关闭

在 9 月 16 日的 Game Jam 游戏开发活动开始前,我们邀请了六月份黑客马拉松的一些参与者在博客中公开他们的的场景设计开发秘密。本周的嘉宾是社区成员 Tak, 可以使用 @Tak 在 Discord 或 Slack 上跟他联系。

嗨,我是 Tak-John Cheung,一名 3D 艺术家。我想我就是所谓的业界通才,因为我几乎涉足了计算机图形学的所有领域。

实际上,我是从一个朋友那里了解到的 Decentraland,他建议我去看看,因为我可能会感兴趣。结果证明他是对的。从那以后,Decentraland 就成为我的新游乐场。

在 6 月份的黑客松活动中,我创建了一个名为 Kabloom Farm 的项目,在小区分类中排名第二。我将会参加 9 月 16 日的 Game Jam 游戏开发活动,并且这次我将力争一等奖。

介绍

The Builder 场景编辑器提供了设计场景简单的方法,从而让所有人都可以设计场景,不必为创建自己的素材而担心,但其真正的优势在于可以导出场景利用 Decentraland SDK 中提供的工具和实用程序进一步开发。

让我们从使用编辑器创建一个简单的花园场景开始。花园有一个固定的门,这意味着玩家可以不必跳过栅栏进入花园。通过使用新的 Utils 库,我们可以响应用户的点击来快速打开和关闭门,希望教程结束时,任何人都可以轻松创建一个简单的交互式场景。

本教程的最终代码请看这里

安装

在终端中运行以下命令,确保安装了最新版本的 Decentraland SDK:

npm install -g decentraland

您还需要从此链接下载 Builder 场景文件 。下载后,将它们解压缩到自己的文件夹中。

可以使用自己的 Builder 场景进行后续操作。从 Builder 下载场景,将会得到一个类似的 zip 文件。可以将以下步骤作为通用指南。

运行场景

在解压缩 Builder 场景后目录下,在终端中运行以下命令:

dcl start

此命令会安装所有没有安装的依赖库并自动打开默认浏览器预览场景。可能需要几秒钟加载,加载完成后,就可以看到有个亭子坐落在一个四周围绕着木制栅栏的小花园中。

正如前面提到的,玩家要能在不跳过栅栏的情况下,进入花园,所以我们的目标是让大门在用户单击它时打开和关闭。

Transform 转换

首先我们要把门处理一下,让它能填满围栏间的空隙。目前在 Builder 编辑器中不支持缩放,但是可以使用代码实现。为此,我们将执行以下操作修改门的 Transform 组件:

  • 打开 /src 目录下的 game.ts 文件
  • 向下滚动,直到找到 fencePicketDoor_01 或使用搜索
  • 修改门的 Transform 组件

提示:Builder 中的代码可能非常长,因为场景中的每个项都会生成一个新的代码块。为了更容易地在代码中定位物体,您可以返回到编辑器并将鼠标悬停在 “Item Catalog” 中要搜索的物体上,会弹出物体名称的提示,该名称与代码中使用的相似,因此您可以使用它来搜索.

/// --- Adding Basic Interactivity to gate ---
const fencePicketDoor_01 = new Entity()
fencePicketDoor_01.setParent(scene)
const gltfShape_16 = new GLTFShape('models/FencePicketDoor_01/FencePicketDoor_01.glb')
fencePicketDoor_01.addComponentOrReplace(gltfShape_16)
 
// Scale and position the gate
const transform_31 = new Transform({
  position: new Vector3(6.65, 0, 0.5),
  rotation: Quaternion.Euler(0, -90, 0),
  scale: new Vector3(1, 1, 1.5)
})
fencePicketDoor_01.addComponentOrReplace(transform_31)
engine.addEntity(fencePicketDoor_01)

您可以在上面的示例中看到我们将 scale 调整为合适的值。如果你正在处理自己的场景,你可能需要多花点时间调整。简而言之,调整门的位置和比例以使之与围栏间的空间匹配。

我们还将四元数旋转改为等效的欧拉值,这一点在以后讲到门的旋转时很重要,至少在某种意义上我们可以更好地理解它的概念。

提示:可以使用这个在线工具quaternions.online转换度量值。

Utils 库

现在是时候向场景添加功能了,为此,我们将使用新的 utils 库,这其中包括一些预构建的工具,可以简化为场景添加常用功能的过程。

如果尚未退出预览,请在安装 Utils 库之前退出预览,可以在场景的项目文件夹下运行以下终端命令完成:

npm install -g decentraland-ecs-utils

game.ts 文件的开头添加此行,将库导入场景代码。

import utils from ".../node_modules/decentraland-ecs-utils/index"

这里只使用了此库中的一些功能,但如果您有兴趣了解更多信息,可以查看此链接

切换状态

此 Utils 库我们第一个使用的组件是 ToggleComponent,可以让我们在两种可能状态之间切换实体,执行一个函数。状态由 ToggleState指定,可以是 OnOff。在这里,当 ToggleStateOn 时我们将打开门,当 ToggleStateOff 时关闭门,所以每次状态改变时,门将根据这个状态旋转到对应的位置。

ToggleComponent 有两个参数:

  • startingState:切换的开始状态( OnOff
  • onValueChangedCallback:每次切换状态改变时调用的函数

门初始是关闭的,我们将 startingState 设置 ToggleState.Off。对于我们对切换的第一次测试,在第二个参数上我们将添加一个非常简单的函数,每当状态改变时,该函数只会将一条消息发送到控制台,描述我们想要执行的操作。这样我们可以在执行打开和关闭门的操作之前检查切换是否按预期工作。

注意:每当你看到 utils 时。这表明我们正在使用 Utils 库中的功能。

// Toggle gate to its open / close positions
fencePicketDoor_01.addComponent(
  new utils.ToggleComponent(utils.ToggleState.Off, value => {
    if (value == utils.ToggleState.On) {
      log("Open")
    } else {
      log("Close")
    }
  })
)

点击

到目前为止,点击门时都没有任何反应,想让它在点击时切换状态。我们将向门添加一个 OnClick 组件,其功能是在每次单击时切换状态。

// Listen for click on the gate and toggle its state
fencePicketDoor_01.addComponent(new OnClick(event => {
  fencePicketDoor_01.getComponent(utils.ToggleComponent).toggle()
}))

OnClick 组件执行的函数在 OnOff 之间来回改变 ToggleState。尽管你看不到场景中发生的任何变化,但是如果打开浏览器的控制台,每次单击门时都应该看到 OpenClose 的消息。

提示:访问浏览器的控制台因浏览器而异。对于 Chrome,您可以使用键盘快捷键 CTRL + SHIFT + J( 在 Windows 上)或 CMD + OPTION + J(在 Mac 上)。您也可以通过菜单 View > Developer > JavaScript Console 打开

旋转门

值得注意的是,在 Builder 编辑器内,门在 y 轴上已经旋转了 -90 度。之前我们将旋转的 四元数 值转换成了 欧拉 值,这个值现在成为旋转初始值。最终,我们需要旋转另一个 -90 度,使其向内打开,最终的 y 轴值为 -180 度。让我们定义几个变量来反映这些值,我们将它们称为 startRotendRot

// Define start and end rotations for the gate
let startRot = Quaternion.Euler(0, -90, 0)
let endRot = Quaternion.Euler(0, -180, 0)

为了让门旋转,我们将使用 Utils 库中名为 RotateTransformComponent 的另一个组件,它能让我们在一段时间内将实体从一个方向旋转到另一个方向。

`RotateTransform组件’有三个参数:

  • startQuaternion 用于开始旋转
  • endQuaternion 用于结束旋转
  • duration:旋转的持续时间(以秒为单位)

我们现在可以删除用作占位符的 log 语句,每次更改状态时,让 ToggleComponent 将相应的 RotateTransformComponent 添加到门。当门打开时,我们用 startRotendRot 按预期设置开始和结束旋转参数,但是当门关闭时,刚好相反, endRot 是是起始旋转而 startRot 是结束值。

// Toggle gate to its open / closed positions
fencePicketDoor_01.addComponent(
  new utils.ToggleComponent(utils.ToggleState.Off, value => {
    if (value == utils.ToggleState.On) {
      fencePicketDoor_01.addComponentOrReplace(
        new utils.RotateTransformComponent(startRot, endRot, 0.5)
      )
    } else {
      fencePicketDoor_01.addComponentOrReplace(
        new utils.RotateTransformComponent(endRot, startRot, 0.5)
      )
    }
  })
)

在这里我们以大约 0.5 秒的时间开启和关闭门,这感觉相当快,但我鼓励你尝试这些值,从而使用感觉不错的值。

收尾工作

当在旋转完前尝试再次单击门时,您可能会发现一个小问题。它不是在打开和关闭状态之间平滑过渡,而是快速回到其起始位置并再次开始旋转。一个简单的解决方法是只关注完成旋转后门的点击。可以通过比较门的 y 轴旋转来确定它是否与 startRotendRot 匹配。

提示:添加中间变量有时很有用。虽然没有必要,但它可以帮助缩短代码并使其更具可读性.

// Listen for click on the gate and toggle its state
fencePicketDoor_01.addComponent(
  new OnClick(event => {
    // Adding an intermediate variable
    let doorRotY = fencePicketDoor_01.getComponent(Transform).rotation.y
 
    // Check if gate is at its start or end positions before toggling
    if (doorRotY == startRot.y || doorRotY == endRot.y)
      fencePicketDoor_01.getComponent(utils.ToggleComponent).toggle()
  })
)

结语

尝试运行场景。一切应该能按预期工作。如果你只是在阅读,现在想要探索场景,那么你可以这里找到它。

使用 Builder 编辑器在场景中导航和定位物体比通过代码要更为直观。希望本指南能向您展示编辑器和 SDK 一起使用时的可能性,以及即使是增加了很小的交互性也可以大大增加场景的趣味性。

在不久的将来,编辑器内的素材将进行优化,可以在 Decentraland 内更快加载,这为希望与 Builder 一起开发场景的用户提供了另一个好处。

开始游戏吧

Decentraland Game Jam 活动是一个使用 SDK 的绝佳机会。参加过 6 月份的黑客马拉松后,我可以向您保证,这是学习的最佳方法之一,不仅仅是因为您将要设计场景,而且因为你可以和其他社区成员一起做这件事。

我已经报名参加 9 月 16 日的大赛,期待看到你们的场景。

虚拟世界见!

参考文献

在虚拟世界中展示您的特色
首批拥有虚拟化身,即可先期进入 Decentraland 世界。
了解更多