使用 PlayCanvas 引擎构建基础演示

PlayCanvas 专门为现代浏览器打造,是一款功能齐全的 3D 游戏引擎,包含资源加载、实体和组件系统、高级图形处理、碰撞和物理引擎(使用 ammo.js 构建)、音频,以及处理来自各种设备(包括游戏手柄)的控制输入的工具。

这是一份令人印象深刻的功能列表 - 让我们看看一些实际效果。

PlayCanvas engine repository on GitHub.

我们首先尝试构建一个简单的演示 - 在屏幕上渲染一个立方体。如果你已经完成我们的 使用 Three.js 构建基础演示 文章(或者你熟悉其他 3D 库),你会注意到 PlayCanvas 基于类似的概念:摄像机、灯光和物体。

环境设置

要开始使用 PlayCanvas 开发,你不需要太多东西。你应该从以下步骤开始:

  • 确保你使用的是支持 WebGL 的现代浏览器,例如最新的 Firefox 或 Chrome。
  • 创建一个目录来存储你的实验。
  • 最新 PlayCanvas 引擎 的副本保存到你的目录中。
  • 在另一个选项卡中打开 PlayCanvas 文档 - 它很有用,可以作为参考。

HTML 结构

这是我们将使用的 HTML 结构。

html
<!doctype html>
<html lang="en-GB">
  <head>
    <meta charset="utf-8" />
    <title>MDN Games: PlayCanvas demo</title>
    <style>
      body {
        margin: 0;
        padding: 0;
      }
      canvas {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <script src="playcanvas-latest.js"></script>
    <canvas id="application-canvas"></canvas>
    <script>
      const canvas = document.getElementById("application-canvas");
      /* all our JavaScript code goes here */
    </script>
  </body>
</html>

它包含一些基本信息,例如文档 <title>,以及一些 CSS 用于将 PlayCanvas 将使用的 <canvas> 元素的宽度和高度设置为 100%,使其填充整个可用的视窗空间。第一个 <script> 元素将 PlayCanvas 库包含在页面中;我们将在第二个元素中编写示例代码。已经包含了一个辅助变量,它将存储对 <canvas> 元素的引用。

在继续阅读之前,将此代码复制到一个新的文本文件中,并将其保存到你的工作目录中,命名为 index.html

PlayCanvas 应用程序

要开始开发我们的游戏,我们首先必须创建 PlayCanvas 应用程序(使用给定的 <canvas> 元素),然后启动更新循环。将以下代码添加到你的第二个 <script> 元素的底部

js
const app = new pc.Application(canvas);
app.start();

pc 全局对象包含引擎中可用的所有 PlayCanvas 函数。

接下来,我们将设置画布以填充窗口,并自动更改其分辨率以与画布大小相同。同样,将以下几行添加到脚本的底部。

js
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

摄像机

现在,设置代码已到位,我们需要考虑实现标准场景组件:摄像机、灯光和物体。让我们从摄像机开始 - 将以下几行添加到你的代码中,位于之前的代码下方。

js
const camera = new pc.Entity();
camera.addComponent("camera", {
  clearColor: new pc.Color(0.8, 0.8, 0.8),
});

app.root.addChild(camera);
camera.setPosition(0, 0, 7);

上面的代码将创建一个新的 Entity

注意: 实体是场景中使用的任何物体 - 它可以是像盒子、圆柱体或圆锥体这样的物体,也可以是摄像机、灯光或声音源。

然后它向其添加一个 camera 组件,该组件具有浅灰色 clearColor - 该颜色将显示为背景。接下来,camera 对象将添加到应用程序的根目录,并定位在场景中心的 z 轴上 7 个单位。这使我们能够腾出一些空间来可视化我们稍后将创建的物体。

注意: 距离值(例如摄像机 z 位置)是无单位的,可以是任何你认为适合你的场景的值 - 毫米、米、英尺或英里 - 由你决定。

尝试保存文件并在你的浏览器中加载它。现在你应该看到一个灰色窗口。恭喜你!

几何体

现在场景已正确渲染,我们可以开始向其中添加 3D 形状了。为了加快开发速度,PlayCanvas 提供了一些预定义的基元,你可以使用它们在单行代码中立即创建形状。有立方体、球体、圆柱体以及更复杂的形状可用。引擎将负责绘制每个形状的所有内容,因此我们可以专注于高级编码。让我们从定义立方体形状的几何体开始 - 将以下新代码添加到你之前添加的内容下方

js
const box = new pc.Entity();
box.addComponent("model", { type: "box" });
app.root.addChild(box);
box.rotate(10, 15, 0);

它将创建一个具有 box 模型组件的 Entity,并将其添加到应用程序的根目录,即我们的场景。我们还稍微旋转了一下盒子,以表明它实际上是一个 3D 立方体,而不是正方形。

立方体是可见的,但它是完全黑色的。为了使其看起来更好,我们需要在其上照射一些灯光。

灯光

PlayCanvas 中的基本灯光类型是方向光和环境光。第一种类型是放置在场景中的方向光,而第二种类型则反射第一种类型的光,因此看起来更自然;这可以在全局范围内设置。同样,将以下新代码添加到你之前添加的内容下方。

js
const light = new pc.Entity();
light.addComponent("light");
app.root.addChild(light);
light.rotate(45, 0, 0);

它将创建一个灯光 Entity 组件,并将其添加到场景中。我们可以沿着 x 轴旋转灯光,使其照射到立方体的多个侧面。现在是添加环境灯光的时候了

js
app.scene.ambientLight = new pc.Color(0.2, 0.2, 0.2);

上面的代码为整个场景分配了深灰色环境光。盒子现在看起来好多了,但它可以添加一些颜色以看起来更棒 - 为此,我们需要为它创建材质。

材质

此示例使用了较旧的 材质,称为“Phong 材质”(PlayCanvas 引擎 v1.65.0 及更高版本支持)。要使用 PhongMaterial,请在之前的代码下方添加以下几行

js
const boxMaterial = new pc.PhongMaterial();
boxMaterial.diffuse.set(0, 0.58, 0.86);
boxMaterial.update();
box.model.model.meshInstances[0].material = boxMaterial;

通过在物体上漫射灯光,我们可以为其赋予自己的颜色 - 我们将选择一种熟悉的蓝色。

注意: 在 PlayCanvas 中,颜色通道值以 0-1 范围内的浮点数形式提供,而不是你可能习惯在 Web 上使用的 0-255 整数。

在创建材质并设置其颜色后,需要对其进行更新,以便应用我们的更改。然后,我们需要做的就是将 box 的材质设置为新创建的 boxMaterial

恭喜,你已使用 PlayCanvas 在 3D 环境中创建了你的第一个物体!比你想象的要容易,对吧?以下是它应该看起来的样子

Blue cube on a gray background rendered with PlayCanvas.

以下是我们到目前为止创建的代码

你也可以 在 GitHub 上查看它

更多形状

现在我们将向场景中添加更多形状。让我们将立方体向左移动 2 个单位,为一些朋友腾出空间 - 在之前的代码下方添加以下行

js
box.translate(-2, 0, 0);

现在让我们添加一个新的形状 - 如何添加一个圆柱体?

圆柱体

在你的 JavaScript 代码的底部添加以下几行

js
const cylinder = new pc.Entity();
cylinder.addComponent("model", { type: "cylinder" });
app.root.addChild(cylinder);
cylinder.rotate(15, 0, 0);

这看起来与用于创建立方体的代码非常相似,但我们添加的是 cylinder 组件,而不是 box 组件。它也围绕 x 轴旋转,以表明它实际上是一个 3D 形状。为了使圆柱体具有颜色,比如黄色,我们需要像之前一样为它创建材质。添加以下几行

js
const cylinderMaterial = new pc.PhongMaterial();
cylinderMaterial.diffuse.set(1, 0.58, 0);
cylinderMaterial.update();
cylinder.model.model.meshInstances[0].material = cylinderMaterial;

圆锥体

创建圆锥体及其材质的过程与创建圆柱体几乎完全相同。再次将以下代码添加到脚本的底部

js
const cone = new pc.Entity();
cone.addComponent("model", { type: "cone" });
app.root.addChild(cone);
cone.translate(2, 0, 0);

const coneMaterial = new pc.PhongMaterial();
coneMaterial.diffuse.set(0.9, 0.9, 0.9);
coneMaterial.update();
cone.model.model.meshInstances[0].material = coneMaterial;

上面的代码将创建一个新的 cone,将其添加到 app 中,并将其向右移动 2 个单位,使其不与圆柱体重叠。然后创建材质,赋予其灰色颜色,并将其分配给圆锥体 Entity

以下是它现在应该看起来的样子

Shapes: blue cube, yellow cylinder and gray cone on a light gray background rendered with PlayCanvas.

这可以工作,但它有点无聊。在游戏中,通常会发生一些事情 - 我们可以看到动画等等 - 因此,让我们尝试通过为这些形状添加动画来为它们注入一些活力。

动画

我们已经使用 translaterotate 来调整形状的位置;我们也可以使用 setPosition 直接更改它们的位置,或者对它们进行缩放。要显示实际的动画,我们需要在渲染循环中更改这些值,以便它们在每一帧上更新。有一个特殊的 update 事件,我们可以使用它来实现 - 在之前的添加内容下方添加以下代码

js
let timer = 0;
app.on("update", (deltaTime) => {
  timer += deltaTime;
  // code executed on every frame
});

回调函数以deltaTime作为参数,因此我们可以获得自上次调用此更新函数以来的相对时间。对于基于时间的动画,我们将使用一个timer变量,它会在每次更新时将deltaTime添加到其中,从而存储自应用程序启动以来的已过去时间。

旋转

旋转非常简单 - 你只需要在每一帧中向给定的旋转方向添加一个定义的值。将以下代码行添加到app.on("update")回调函数中,紧接在timer变量中添加deltaTime之后

js
box.rotate(deltaTime * 10, deltaTime * 20, deltaTime * 30);

它将在每一帧中将boxx轴上旋转deltaTime*10,在y轴上旋转deltaTime*20,在z轴上旋转deltaTime*30,从而产生平滑的动画。

缩放

我们也可以缩放给定对象 - 有一个名为setLocalScale的函数可以实现此目的。将以下代码添加到回调函数中

js
cylinder.setLocalScale(1, Math.abs(Math.sin(timer)), 1);

在这里我们使用Math.sin来循环缩放圆柱体,使其变大和变小。我们将y轴的缩放值包装在Math.abs中,以传递绝对值(大于或等于 0);sin在 -1 和 0 之间变化,对于负值,圆柱体的缩放可能会意外呈现(在这种情况下,它看起来一半时间是黑色的。)

现在开始移动部分。

移动

除了旋转和缩放之外,我们还可以移动场景中的对象。添加以下代码来实现此目的。

js
cone.setPosition(2, Math.sin(timer * 2), 0);

这将通过在每一帧中将sin值应用于y轴来使cone上下移动,并进行一些调整以使其看起来更酷。尝试更改值以查看它如何影响动画。

结论

以下是最终的代码清单,以及一个可视化的实时示例

你也可以在 GitHub 上查看,并分叉存储库,如果你想在本地玩它。现在你了解了 PlayCanvas 引擎的基本知识;祝你实验愉快!

总结

现在你可以继续阅读PlayCanvas 编辑器文章,返回使用 PlayCanvas 构建基本演示页面,或返回到主Web 上的 3D 游戏页面。