你好,请选择
语言
关闭

可能您一直在使用我们的 SDK 构建交互式 Decentraland 场景,但是新的 5.0 版本已经发生很大的变化! 我们相信这些更改有着很好的理由,但您将不得不对场景中的代码进行更改。 好消息是,新的场景构建变得更加直观,也更容易上手了。

对于那些使用 静态 XML 构建场景的人,请不要担心。您基本上可以忽略本文和新的 SDK 版本。在这方面并没有做任何改动。因为我们正在努力在后端将相同的 XML 语法解释为 .ts 代码,所以这并不影响你的场景的编写方式。

这篇文章用于帮助您完成向新版本 SDK 的迁移,并使您的场景能受益于我们新 SDK 版本的实体组件系统架构。

另外,为了帮助您入门:

  • 我们已经重写了我们的文档,请查看!
  • 我们发布了一个参考说明,您可以在其中找到有关 SDK 中所有公开的组件和功能的详细信息
  • 我们发布了一些示例场景。包括使用新的 SDK 来重建大多数旧的示例场景,因此您可以看到我们如何通过这些转换获得灵感。
  • 另外,请查看教程,我们将介绍几个有关新 SDK 中更改的主要概念。并且不久还会有更多的教程出现。

在本文中,我们不会深入探讨这些概念 - 我们将主要向您展示新语法的不同之处以及如何将旧的场景移植到新的 SDK 中。对于第一部分,我们将重点关注如何定义场景的实体和组件。 第二部分将介绍更高级的主题,提供有关迁移交互式场景的技巧。

值得高兴的是,之前支持的所有 3D 模型、动画和纹理仍将受到支持:您无需更改或重新构建。事实上,对于包含多块土地的场景,我们提高了最多三角形数量限制,因此您可以在其中添加更多内容!

用于在场景中定位、旋转和缩放实体的所有值仍会产生相同的结果 - 所以不必考虑这些!

高级别差异

让我们快速回顾一下你会立即注意到的一些显而易见的事情:

  • 主文件现在是 .ts 而不是 .tsx 文件,它位于场景目录中的 src 文件夹中。
  • 场景的主代码不再包含在类定义中,并且不再有诸如 onSceneDidMount()render() 这类默认方法。 下面的示例代码可以直接放入 game.ts 文件中。
  • 您不再需要将 SDK 或任何基本组件导入 import 到文件中 - 它们已被隐式导入。只有 blockchainaudio 库才需要导入。
  • 没有实体类型:所有实体都是通用的。 它们的特征取觉于添加的组件
  • 旋转在内部存储为四元数而不是欧拉角度。
  • 场景状态对象已被删除。 现在,场景中的实体本身就包含了场景数据。
  • 实体不再有过渡属性:实体的移动现在可以通过逐帧重新定位实体来实现。
  • 实体不再需要 ID 和 keys。

有关这些差异的更深入了解,请参阅公告

您会注意到 SDK 的新版本的代码会稍微冗长点,但回报是,编程时您会意识到事情变得简单得多了,尤其是编写动态行为代码时。

以下是使用旧 SDK 编写的最简单场景的示例。 可以注意到在实际添加雕像的 3D 模型之前,需要完成几层复杂包装。

import * as DCL from 'metaverse-api'

export default class SampleScene extends DCL.ScriptableScene {
  async render() {
    return (
      <scene>
        <gltf-model src="models/statue.gltf" position={{ x:5, y:0, z:5 }} />
      </scene>
    )
  }
}

另一方面,这是使用 SDK 5.0 的场景:

let statue = new Entity()
statue.add(new GLTFShape("models/statue.gltf"))
statue.add(new Transform({
 position: new Vector3(5, 0, 5)
}))
engine.addEntity(statue)

基本形状

因为不再有实体类型,因此要创建基本形状,首先创建一个通用实体,然后向其中添加 形状(shape) 组件及其他相关组件。

下面是如何添加立方体的示例:

旧代码:

<box
  position={{ x: 5, y: 3, z: 5 }}
  rotation={{ x: 180, y: 90, z: 0 }}
  scale={0.5}
/>

新的代码:

let box = new Entity()
box.add( new BoxShape())
box.add( new Transform({
	position: new Vector3(5, 3, 5),
  	rotation: Quaternion.Euler(180, 90, 0),
  	scale: new Vector3(0.5, 0.5, 0.5)
}))
engine.addEntity(box)

可以为每个基本形状指定不同的组件类型,例如 PlaneShape (平面) 或 SphereShape(球形)。 有关更多详细信息,请参阅形体组件

请注意,实体的位置、旋转和比例保存在 Transform 组件中。 以前使用的这些字段值在 Transform 组件中仍然有效,并且效果一样。

唯一的主要区别是 rotation 现在存储为四元数。 您可以用Quaternion.Euler() 方法来使用欧拉角度(一般的 x、y 和 z 轴角度,最高 360 度) ,并以这种方式使用旧场景中的值。

和以前一样,可以通过 withCollisions 属性启用基本形状的实体的碰撞。 此属性现在属于 shape 组件。

旧代码:

<box
 withCollisions={true}
/>

新代码:

let box = new Entity()
const bShape = new BoxShape()
bShape.withCollisions = true
box.add(bShape)
engine.addEntity(box)

材质

材质的工作方式几乎与以前相同。您仍然可以将 Materials 材质和 Basic Materials 基本材质与应用它们的实体分开定义。 在原旧版本中添加到材质的所有的属性和属性名称现在仍然可用,只要使用新的语法即可。

以下是如何定义新材料的示例:

旧代码:

<material
  id="myMaterial"
  albedoTexture = "materials/wood.png"
  roughness={0.5}
/>

新代码:

let myMaterial = new Material()
myMaterial.albedoTexture = "materials/wood.png"
myMaterial.roughness= 0.5

请注意,MaterialsBasicMaterials 现在是组件; 它们不再被视为实体。通过将它们视为组件,我们可以将它们添加到实体中,并可以使用类似以前的方式继续使用它们。

以下示例展示了如何将材质应用于实体:

旧代码:

<box material = "#myMaterial" />

新代码:

let box = new Entity()
box.add(myMaterial)

3D 模型

可以以与以前相同的方式添加 .glTF 和 .glb 格式的 3D 模型。 同样,语法会发生变化,但没有明显的概念差异

旧代码:

<gltf-model
  src="models/myModel.gltf"
  position={{ x: 5, y: 3, z: 5 }}
  rotation={{ x: 180, y: 90, z: 0 }}
  scale={0.5}
/>

新代码:

let myModel = new Entity()
myModel.add(new GLTFShape("models/myModel.gltf"))
myModel.add( new Transform({
	position: new Vector3(5, 3, 5),
  	rotation: Quaternion.Euler(180, 90, 0),
  	scale: new Vector3(0.5, 0.5, 0.5)
}))
engine.addEntity(myModel)

3D模型通过 GLTFShape 组件添加。 此组件有一个必需的指向 3D 模型文件路径的参数。

与基本形状一样,实体的位置、旋转和比例保存在单独的 Transform 组件中。 同样,旧版本中的相同值仍然有效。

唯一的主要区别是 rotation 现在存储为四元数。 您可以用Quaternion.Euler() 方法来使用欧拉角度(一般的 x、y 和 z 轴角度,最高 360 度) ,并以这种方式使用旧场景中的值。

glTF 模型的碰撞器也可以像以前一样工作,它们必须作为 3D 模型的一部分导入。

OBJ objects

3D objects in .obj format can also be added in a similar way, using an OBJShape component.

也可以使用 OBJShape 组件以类似的方式添加 .obj 格式的 3D 对象。

旧代码:

<obj-model
  src="models/myModel.obj"
  position={{ x: 5, y: 3, z: 5 }}
  rotation={{ x: 180, y: 90, z: 0 }}
  scale={0.5}
>

新代码:

let myModel = new Entity()
myModel.add(new OBJShape("models/myModel.gltf"))
myModel.add( new Transform({
	position: new Vector3(5, 3, 5),
  	rotation: Quaternion.Euler(180, 90, 0),
  	scale: new Vector3(0.5, 0.5, 0.5)
}))
engine.addEntity(myModel)

动画

当在 glTF(或glb)3D 模型上使用动画时,我们现在创建一个 AnimationClip 对象并将其添加到 GLTFShape 组件。

旧代码:

<gltf-model
  src="models/shark_anim.gltf"
  skeletalAnimation={[
    {
      clip: "swim",
      playing: true
    },
    {
      clip: "bite",
      weight: 0.8,
loop: false,
      playing: false
    }
  ]}
/>

新代码:

let shark = new Entity()
shark.add(new GLTFShape("models/myModel.gltf"))
engine.addEntity(shark)

const swimClip = new AnimationClip('swim')
const biteClip = new AnimationClip('bite', { weight: 0.8, loop: false })
shark.get(GLTFShape).addClip(swimClip)
shark.get(GLTFShape).addClip(biteClip)

swimClip.play()

AnimationClip 对象可以具有在创建时添加的可选参数。

提示:您现在还可以在动画参数中设置播放动画的速度。

如果您不需要为动画设置任何属性,则可以跳过创建 clip 对象并将其添加到组件的步骤。 当您第一次尝试从 GLTFShape 获取动画 clip 时,它会自动创建(使用默认属性值):

let shark = new Entity()
shark.add(new GLTFShape("models/myModel.gltf"))
engine.addEntity(shark)
let swimClip = shark.get(GLTFShape).getClip("swim")
let biteClip = shark.get(GLTFShape).getClip("bite")
swimClip.play()

要播放或暂停动画,可以使用 play()pause() 函数,也可以将 .playing 属性设置为 true 或 false。

// using the play() function
swimClip.play()
// using the playing property
swimClip.playing = true

实体嵌套

要将实体作为其他实体的子项,可以使用 setParent()

旧代码:

  <entity position={{ x: 0, y: 0, z: 1 }} rotation={{ x: 45, y: 0, z: 0 }}>
    <box position={{ x: 10, y: 0, z: 0 }} />
    <box position={{ x: 10, y: 10, z: 0 }} />
  </entity>

新代码:

// create parent
let wrapper = new Entity()
wrapper.add(new Transform({
	position: new Vector3(0, 0, 1),
	rotation: Quaternion.Euler(45, 0, 0)
}))
engine.addEntity(wrapper)

// create first child
let box1 = new Entity()
box1.add(new BoxShape())
box1.add(new Transform({
	position: new Vector3(10, 0, 0)
}))
engine.addEntity(box1)

// create second child
let box2 = new Entity()
box2.add(new BoxShape())
box2.add(new Transform({
	position: new Vector3(10, 10, 0)
}))
engine.addEntity(box2)

// assign parent
box1.setParent(wrapper)
box2.setParent(wrapper)

当一个实体是另一个实体的子项时,它的位置、旋转和缩放比例将从父项继承。 在新版本的 SDK 中也是如此,因此在每个实体上使用相同的值应该产生与之前相同的结果。

因为不再用 <scene> 节点包裹整个场景,所以如果您的场景以前具有一个通用的位置、旋转或缩放值,那么可以将它设置为所有其他实体的父实体。

<scene position ={{ x: 5, y: 0, z: 5 }}>
  <box position={{ x: 10, y: 0, z: 0 }} />
</scene>
let sceneWrapper = new Entity()
sceneWrapper.add(new Transform({
	position: new Vector3(5, 0, 5)
}))
engine.addEntity(sceneWrapper)

let box = new Entity()
box.add(new BoxShape())
box.add(new Transform({
	position: new Vector3(10, 0, 0)
}))
box.setParent(sceneWrapper)
engine.addEntity(box)

四元数和欧拉角

正如上面已经提到的,Transform 组件的旋转值以四元数存储。 但可以使用以下语法来使用旧版 SDK 的欧拉角度

// Create a transform with a predefined rotation in Euler angles
let myTransform = new Transform({rotation: Quaternion.Euler(0, 90, 0)})

// Use the .setEuler() function to set rotation
myTransform.rotation.setEuler(0, 90, 180)

// Set the `eulerAngles` field
myTransform.rotation.eulerAngles = new Vector3(0, 90, 0)

点击

旧版 SDK 中的 onClick 字段现在改为 OnClick 组件。 此组件可以添加到任何实体,并且具有与 onClick 字段完全相同的功能。 有关详细信息,请参阅(有关点击的文档)。

旧代码:

<box
  onClick={() => console.log("Clicked!")}
/>

新代码:

let box = new Entity()
box.add(new BoxShape())
box.add(
  new OnClick(e => {
   log("Clicked!")
})
engine.addEntity(box)

后记

本文中的技巧和示例应该能让您将场景中的所有静态内容迁移到 5.0。

对于交互式和动态内容,迁移过程就不是这么简单了,需要更多的考虑。 我们很快就会发布本文的第二部分来介绍这些内容。

要查看使用新 SDK 构建的场景的完整示例,请查看文档中的示例场景页面!

开始建设!
我们的 SDK 提供了开发游戏和应用所需的一切
让我们开始吧