如何构建实时多人虚拟现实游戏(第 1 部分)

已发表: 2022-03-10
快速总结 ↬虚拟现实是一种用于探索内容的全新沉浸式媒体,无论该内容是电影 ( Life of Pi )、游戏 ( Beat Saber ) 还是社交体验(如Ready Player One中所述)。 尽管它很新颖,但 VR 不需要完全不同的工具集来设计——我们用于网页游戏开发、3D 建模和其他工具的相同工具仍然适用。 本教程利用您对 Web 开发的熟悉程度来开始 VR 开发。

在本教程系列中,我们将构建一个基于网络的多人虚拟现实游戏,玩家需要在其中协作来解决难题。 我们将使用 A-Frame 进行 VR 建模,使用 MirrorVR 进行跨设备实时同步,使用 A-Frame Low Poly 进行低多边形​​美学。 在本教程结束时,您将拥有一个功能齐全的在线演示,任何人都可以玩。

每对玩家都获得一个圆环。 目标是“打开”所有球体,如果球体升高且明亮,则该球体“打开”。 如果球体较低且昏暗,则该球体“关闭”。 然而,某些“主导”球体会影响它们的邻居:如果它切换状态,它的邻居也会切换状态。 只有玩家 2 可以控制主导球体,而只有玩家 1 可以控制非主导球体。 这迫使双方玩家合作解决难题。 在教程的第一部分,我们将为我们的 VR 游戏构建环境并添加设计元素。

本教程中的七个步骤分为三个部分:

  1. 设置场景(步骤 1-2)
  2. 创建球体(步骤 3-5)
  3. 使球体互动(步骤 6-7)
跳跃后更多! 继续往下看↓

第一部分将以打开和关闭的可点击球体结束(如下图所示)。 您将使用 A-Frame VR 和几个 A-Frame 扩展。

(大预览)

设置场景

1. 让我们来看一个基本场景

首先,让我们看看如何设置一个带地面的简单场景:

创建一个简单的场景
创建一个简单的场景(大预览)

下面的前三个说明摘自我之前的文章。 您将首先建立一个具有单个静态 HTML 页面的网站。 这允许您从桌面编写代码并自动部署到 Web。 然后可以将部署的网站加载到您的手机上并放置在 VR 耳机中。 或者,部署的网站可以由独立的 VR 耳机加载。

通过导航到 glitch.com 开始。 然后,执行以下操作:

  1. 点击右上角的“新建项目”,
  2. 点击下拉菜单中的“hello-webpage”,
  3. 接下来,单击左侧边栏中的index.html 。 我们将其称为您的“编辑器”。

您现在应该会看到以下带有默认 HTML 文件的故障屏幕。

故障项目:index.html 文件
故障项目:index.html 文件(大预览)

与上面的链接教程一样,首先删除当前index.html文件中的所有现有代码。 然后,使用 A-Frame VR 为基本的 webVR 项目键入以下内容。 这将使用 A-Frame 的默认照明和相机创建一个空场景。

 <!DOCTYPE html> <html> <head> <title>Lightful</title> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> </head> <body> <a-scene> </a-scene> </body> </html>

将相机升高到站立高度。 根据 A-Frame VR 建议(Github 问题),用新实体包装相机并移动父实体而不是直接移动相机。 在第 8 行和第 9 行的a-scene标签之间,添加以下内容。

 <!-- Camera! --> <a-entity position="0 3 0"> <a-camera wasd-controls look-controls></a-camera> </a-entity>

接下来,使用a-box添加一个大框来表示地面。 按照前面的说明将其直接放在相机下方。

 <!-- Action! --> <a-box shadow width="75" height="0.1" depth="75" position="0 -1 0" color="#222"></a-box>

您的index.html文件现在应该与以下内容完全匹配。 你可以在 Github 上找到完整的源代码。

 <html> <head> <title>Lightful</title> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> </head> <body> <a-scene> <!-- Camera! --> <a-entity position="0 3 0"> <a-camera wasd-controls look-controls></a-camera> </a-entity> <!-- Action! --> <a-box shadow width="75" height="0.1" depth="75" position="0 -1 0" color="#222"></a-box> </a-scene> </body> </html>

设置到此结束。 接下来,我们将定制灯光,营造更神秘的氛围。

2.添加气氛

在这一步中,我们将设置雾和自定义照明。

一个带有黑暗情绪的简单场景的预览
一个带有黑暗情绪的简单场景的预览(大预览)

添加雾,它会模糊我们远处的物体。 修改第 8 行的a-scene标签。在​​这里,我们将添加一个快速遮盖地面边缘的黑雾,营造出遥远地平线的效果。

 <a-scene fog="type: linear; color: #111; near:10; far:15"></a-scene>

深灰色#111从 10 到 15 的距离线性淡入。所有超过 15 个单位的物体都完全被遮蔽,所有小于 10 个单位的物体都完全可见。 中间的任何物体都被部分遮挡。

添加一个环境光以照亮游戏中的对象,并添加一个单向光以突出您稍后将添加的反射表面。 将其直接放在您在上一条指令中修改的a-scene标记之后。

 <!-- Lights! --> <a-light type="directional" castshadow="true" intensity="0.5" color="#FFF" position="2 5 0"></a-light> <a-light intensity="0.1" type="ambient" position="1 1 1" color="#FFF"></a-light>

在上一个指令中的灯光正下方,添加一个黑暗的天空。 请注意深灰色#111与远处雾的颜色相匹配。

 <a-sky color="#111"></a-sky>

这结束了对情绪的基本修改以及更广泛的场景设置。 检查您的代码是否与 Github 上第 2 步的源代码完全匹配。 接下来,我们将添加一个低多边形球体并开始自定义球体的美学。

创建球体

3. 创建一个低聚球体

在这一步中,我们将创建一个旋转的反射球,如下图所示。 该球体由两个程式化的低多边形球体组成,并带有一些提示反射材料的技巧。

旋转反射球
(大预览)

首先在你的head标签中导入低多边形库。 在第 4 行和第 5 行之间插入以下内容。

 <script src="https://cdn.jsdelivr.net/gh/alvinwan/[email protected]/dist/aframe-low-poly.min.js"></script>

创建一个轮播、包装器和球体容器。 carousel将包含多个球体, wrapper将允许我们围绕中心轴旋转所有球体,而无需单独旋转每个球体,并且container (顾名思义)将包含所有球体组件。

 <a-entity> <a-entity rotation="0 90 0" class="wrapper" position="0 0 0"> <a-entity class="container" position="8 3 0" scale="1 1 1"> <!-- place orb here --> </a-entity> </a-entity> </a-entity>

在球体容器内,添加球体本身:一个球体略微半透明并偏移,另一个球体完全实心。 这两个组合模拟反射表面。

 <a-entity class="orb" data-> <lp-sphere seed="0" shadow max-amplitude="1 1 1" position="-0.5 0 -0.5"></lp-sphere> <lp-sphere seed="0" shadow max-amplitude="1 1 1" rotation="0 45 45" opacity="0.5" position="-0.5 0 -0.5"></lp-sphere> </a-entity>

最后,通过在最后一条指令中的.orb实体内的lp-sphere之后立即添加以下a-animation标签,无限期地旋转球体。

 <a-animation attribute="rotation" repeat="indefinite" from="0 0 0" to="0 360 0" dur="5000"></a-animation>

您的 orb 包装器和 orb 本身的源代码应与以下内容完全匹配。

 <a-entity> <a-entity rotation="0 90 0" class="wrapper" position="0 0 0"> <a-entity class="container" position="8 3 0" scale="1 1 1"> <a-entity class="orb" data-> <lp-sphere seed="0" shadow max-amplitude="1 1 1" position="-0.5 0 -0.5"></lp-sphere> <lp-sphere seed="0" shadow max-amplitude="1 1 1" rotation="0 45 45" opacity="0.5" position="-0.5 0 -0.5"></lp-sphere> <a-animation attribute="rotation" repeat="indefinite" from="0 0 0" to="0 360 0" dur="5000"></a-animation> </a-entity> </a-entity> </a-entity> </a-entity>

检查您的源代码是否与 Github 上第 3 步的完整源代码匹配。 您的预览现在应该与以下内容匹配。

旋转反射球
(大预览)

接下来,我们将为球体添加更多照明以获得金色。

4.点亮球体

在这一步中,我们将添加两盏灯,一盏彩色灯和一盏白色灯。 这会产生以下效果。

用点光源点亮的球体
(大预览)

首先添加白光以从下方照亮对象。 我们将使用点光源。 直接在#orb0之前但在#container-orb0内,添加以下偏移点光源。

 <a-entity position="-2 -1 0"> <a-light distance="8" type="point" color="#FFF" intensity="0.8"></a-light> </a-entity>

在您的预览中,您将看到以下内容。

用白点光源点亮的球体
(大预览)

默认情况下,灯光不会随距离衰减。 通过添加distance="8" ,我们确保光以 8 个单位的距离完全衰减,以防止点光照亮整个场景。 接下来,添加金光。 在最后一个灯的正上方添加以下内容。

 <a-light class="light-orb" distance="8" type="point" color="#f90" intensity="1"></a-light>

检查您的代码是否与第 4 步的源代码完全匹配。 您的预览现在将与以下内容匹配。

用点光源点亮的球体
(大预览)

接下来,您将对球体进行最终的美学修改并添加旋转环。

5.添加戒指

在此步骤中,您将生成最终的球体,如下图所示。

带有多个环的金色球体
(大预览)

在 #orb0 之前直接在#orb0 #container-orb0中添加一个环。

 <a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="1.9" radius-outer="2" opacity="0.25"></a-ring>

请注意,环本身不包含颜色,因为颜色将被上一步中的点光充满。 此外, material="side:double"很重要,因为没有它,戒指的背面就不会被渲染; 这意味着环会在其旋转的一半时间内消失。

但是,仅包含上述代码的预览看起来并没有什么不同。 这是因为环当前垂直于屏幕。 因此,只有环的“侧面”(厚度为 0)是可见的。 将以下动画放置在上一条指令中的a-ring标签之间。

 <a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 0 0" to="0 360 0" dur="8000"></a-animation>

您的预览现在应该与以下内容匹配:

带环的金球
(大预览)

创建具有不同旋转轴、速度和大小的可变数量的环。 您可以使用以下示例环。 任何新的环都应该放在最后a-ring下面。

 <a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="2.4" radius-outer="2.5" opacity="0.25"> <a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 45 0" to="360 45 0" dur="8000"></a-animation> </a-ring> <a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="1.4" radius-outer="1.5" opacity="0.25"> <a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 -60 0" to="-360 -60 0" dur="3000"></a-animation> </a-ring>

您的预览现在将与以下内容匹配。

带有多个环的金色球体
(大预览)

检查您的代码是否与 Github 上第 5 步的源代码匹配。 球体的装饰到此结束。 完成球体后,我们接下来将向球体添加交互性。 在下一步中,我们将专门添加一个可见光标,当指向可点击对象时带有点击动画。

使球体互动

6.添加光标

在这一步中,我们将添加一个可以触发可点击对象的白色光标。 光标如下图所示。

点击球体
(大预览)

在您a-camera标签中,添加以下实体。 fuse属性允许该实体触发点击事件。 raycaster属性决定了检查可点击对象的频率和距离。 objects属性接受一个选择器来确定哪些实体是可点击的。 在这种情况下,所有clickable点击类的对象都是可点击的。

 <a-entity cursor="fuse: true; fuseTimeout: 250" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.03; radiusOuter: 0.04" material="color: white; shader: flat; opacity: 0.5" scale="0.5 0.5 0.5" raycaster="far: 20; interval: 1000; objects: .clickable"> <!-- Place cursor animation here --> </a-entity>

接下来,为美观添加光标动画和额外的环。 将以下内容放在上面的实体光标对象中。 这将动画添加到光标对象,以便点击可见。

 <a-circle radius="0.01" color="#FFF" opacity="0.5" material="shader: flat"></a-circle> <a-animation begin="fusing" easing="ease-in" attribute="scale" fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>

接下来,将clickable类添加到#orb0以匹配以下内容。

 <a-entity class="orb clickable" data->

检查您的代码是否与 Github 上第 6 步的源代码匹配。 在您的预览中,将光标从它们上拖到球体上,以查看实际的点击动画。 如下图所示。

点击球体
(大预览)

请注意,可点击属性已添加到球体本身,而不是球体容器。 这是为了防止环变成可点击的对象。 这样,用户必须单击构成球体本身的球体。

在这部分的最后一步中,您将添加动画来控制球体的开启和关闭状态。

7.添加球体状态

在此步骤中,您将在单击时将球体设置为动画进入和退出关闭状态。 如下图所示。

响应点击的交互式球体
(大预览)

首先,您将缩小球体并将其降低到地面。 在 #orb0 之后将a-animation标签添加到#container-orb0 #orb0 。 两个动画都是通过单击触发的,并共享相同的缓动函数ease-elastic以实现轻微反弹。

 <a-animation class="animation-scale" easing="ease-elastic" begin="click" attribute="scale" from="0.5 0.5 0.5" to="1 1 1" direction="alternate" dur="2000"></a-animation> <a-animation class="animation-position" easing="ease-elastic" begin="click" attribute="position" from="8 0.5 0" to="8 3 0" direction="alternate" dur="2000"></a-animation>

为了进一步强调关闭状态,我们将在球体关闭时移除黄金点光源。 但是,球体的灯光放置在球体对象之外。 因此,当点击球体时,点击事件不会传递给灯光。 为了规避这个问题,我们将使用一些轻量级的 Javascript 将点击事件传递给灯光。 将以下动画标签放在#light-orb0中。 灯光由自定义switch事件触发。

 <a-animation class="animation-intensity" begin="switch" attribute="intensity" from="0" to="1" direction="alternate"></a-animation>

接下来,将以下单击事件侦听器添加到#container-orb0 。 这会将点击中继到球灯。

 <a-entity ...>

检查您的代码是否与 Github 上第 7 步的源代码匹配。 最后,拉起您的预览,然后将光标移开和移出球体以在关闭和打开状态之间切换。 如下图所示。

响应点击的交互式球体
(大预览)

这结束了球体的交互性。 玩家现在可以随意打开和关闭球体,具有不言自明的打开和关闭状态。

结论

在本教程中,您构建了一个具有开启和关闭状态的简单球体,可以通过 VR 耳机友好的光标单击来切换。 通过多种不同的照明技术和动画,您可以区分这两种状态。 球体的虚拟现实设计元素到此结束。 在本教程的下一部分中,我们将动态填充球体,添加游戏机制,并设置一对玩家之间的通信协议。