A-Frame 的多用户体验

自在线聊天和社交网络兴起以来,互联网上的多用户体验已经走过了漫长的道路。虚拟现实技术的进步使网络成为一个更加沉浸式和交互式的环境。尼尔·斯蒂芬森设想了一套共享的、持久性的、互联的 3D 虚拟空间,并将其称为 *元宇宙*;今天,我们正在用 HTML、JavaScript 和 A-Frame 构建它。

鉴于 HTML 的学习曲线平缓,A-Frame 是那些希望用几行 HTML 和 JavaScript 在网络上创建虚拟空间的人的完美选择。随着浏览器中 链接遍历 的实现,我们离在现实世界中无需通勤即可从一个体验传送到另一个体验又近了一步。然而,仍然缺少一个协作模型来促进元宇宙居民之间信息交换。WebRTC 在实现这种交换方面发挥着关键作用。

使用 WebRTC 进行点对点通信

WebRTC 是一个 Web API,存在于 大多数浏览器 中(包括截至今年 9 月的 Safari),它允许以点对点的方式交换信息,无需中介。使用 WebRTC,可以实现将体验集成到元宇宙所需的持久基础设施。

WebRTC 最大的复杂性来自 会话管理、对等发现和信令,所有这些都是识别浏览器之间连接所必需的。一旦建立了对等身份,该标准就可以提供用于共享媒体和数据的端到端通道。

sharedspace 组件

为了将 WebRTC 的强大功能带给 A-Frame 用户,我一直在开发 sharedspace 组件。该组件提供了一个协作模型,参与者可以在其中加入或离开命名空间,共享音频和状态,并向其他对等方发送可 JSON 序列化的对象。

该组件不直接处理 WebRTC API。相反,它使用 修改后的版本webrtc-swarm 作为包装库。我选择它是因为它的简单性、占地面积、源代码的清晰度以及易用性。

您可以体验一些 VR 聊天在 Glitch 上浏览项目 以了解有关 sharedspace 组件的更多信息。该存储库包含 丰富的文档,其中包含其他常见用例的解释和示例。
sharedspace 组件不是 A-Frame 的通用网络解决方案,它没有提供一种直接方法来在应用程序的多个实例之间共享实体。但是,发送数据的能力意味着可以基于此组件创建新的协作形式。

对于更通用的网络组件,请查看 networked-aframe 组件,作者是 Hayden Lee

兼容性

sharedspace 组件需要支持 WebRTC 的浏览器,这意味着 大多数流行的移动和桌面浏览器的任何最新版本。Chrome 还要求支持 WebRTC 的页面通过 HTTPS 提供服务,除非您从 localhost 提供服务。

它是如何工作的?

这是实现共享空间所需的最小 HTML 代码

<!-- Inside head -->
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
<script src="https://cdn.rawgit.com/delapuente/aframe-sharedspace-component/master/dist/aframe-sharedspace-component.min.js"></script>
<script src="https://rawgit.com/feiss/aframe-environment-component/master/dist/aframe-environment-component.min.js"></script>

<!-- Inside body -->
<a-scene>
  <a-entity sharedspace avatars>
    <!-- Actually, this is not needed but convenient to have some reference points -->
    <a-entity environment="preset: forest"><a-entity>
  </a-entity>
</a-scene>
<template>
  <a-sphere radius="0.1" color=”#ff0000”></a-sphere>
</template>

但是,上面的代码将始终连接到名为 room-101 的房间,并且化身表示将是红色的球体,这对人类参与者来说不是有效的表示。让我们自定义设置来更改它。

您可以按照我的步骤自定义代码,使用相同的 最小设置 在 Glitch 上:重制项目

A forest with a red sphere in the middle representing the other peer

重要事项:要测试您的进度,您至少需要两个客户端连接到同一个房间。在测试时,您会注意到对等方已连接,因为相机已重置,并且应用程序不再允许您移动化身。当发生这种情况时,寻找一个红色的球:这是另一个对等方。

单击查看连接对相机的效果。

随机生成的房间名称

sharedspace 组件将在 A-Frame 场景准备就绪后立即尝试连接到房间。一旦组件已连接,更改其属性将不起作用。要阻止组件连接服务器,请将 hold 属性设置为 true

<a-entity sharedspace="hold: true" avatars></a-entity>

准备一个脚本,以便场景加载完成后修改它。在 body 的结束标记之前添加以下 script 标记

<script>
  var scene = document.querySelector('a-scene');
      
  (function start() {
    if (!scene.hasLoaded) {
      scene.addEventListener('loaded', start);
      return;
    }
        
    // Now it’s safe to change the scene components...
  }());
</script>

用以下 JavaScript 替换注释,该 JavaScript 将检查当前 URL 以查找要连接到的房间。如果未找到房间,应用程序将生成一个新房间并替换地址栏中的 URL,以允许用户邀请他们的朋友加入该房间

var prefix = window.location.host.split('.')[0] + '-';
var currentUrl = new URL(window.location);
var roomName = currentUrl.search.substr(1);

if (!roomName) {
  roomName = prefix + Date.now();
  currentUrl.search = roomName;
  history.pushState({}, '', currentUrl.href);
}
        
var room = document.querySelector('[sharedspace]');
room.setAttribute('sharedspace', { room: roomName, hold: false });

最重要的部分是最后一行,您在其中设置房间的名称并将 hold 属性重置为 false,允许组件连接。

假设我想与朋友进行 VR 聊天。我在左侧的 Firefox 中输入裸 URL,网页会自动附加房间名称并连接。现在,我将新 URL 与我的朋友共享,他在右侧的 Chrome 中粘贴了它。

自定义化身

安装 sharedspace 组件后,avatars 组件可用。它管理 A-Frame 场景,为每个参与者提供化身表示。默认情况下,avatars 组件将搜索 template 标记并使用其内容来实例化化身。

用以下基本体替换 template 标记的内容

<template>
  <a-entity>
    <a-sphere radius="0.1" color="#ffffff"></a-sphere>
    <a-sphere position="0.05 0.03 -0.08" radius="0.02" segments-width="8" segments-height="8" color="#000000"></a-sphere>
    <a-sphere position="-0.05 0.03 -0.08" radius="0.02" segments-width="8" segments-height="8" color="#000000"></a-sphere>
    <a-sphere class="themable" position="0 -0.07 -0.1" scale="1 1 0.5" segments-width="4" segments-height="4" radius="0.02" color="#11fd3e"></a-sphere>
    <a-cone class="themable" position="0.03 -0.07 -0.1" rotation="0 0 90" scale="1 1 0.5" segments-radial="8" segments-height="1" height="0.03" radius-bottom="0.03"  color="#1cff3c"></a-cone>
    <a-cone class="themable" position="-0.03 -0.07 -0.1" rotation="0 0 -90" scale="1 1 0.5" segments-radial="8" segments-height="1" height="0.03" radius-bottom="0.03"  color="#1cff3c"></a-cone>
  </a-entity>
</template>

单击并查看在右侧选项卡上环顾四周如何在左侧选项卡上移动化身。

修复方向

不知道连接时相机指向的方向可能会很烦人。让我们解决这个问题。当参与者加入房间时,avatars 组件将实例化化身模板并在其实体上发出 avataradded 事件。这允许动态配置模板。

找到您获取房间元素的行,并在该行中添加以下代码(在将 hold 设置为 false 之前)

room.addEventListener('avataradded', function onAdded(evt) {
  
  var avatar = evt.detail.avatar;
  if (!avatar.hasLoaded) {
    avatar.addEventListener('loaded', onAdded.bind(null, evt));
    return;
  }
          
  var avatarY = avatar.getAttribute('position').y;  
  avatar.object3D.lookAt(new THREE.Vector3(0, avatarY, 0));

  var radToDeg = THREE.Math.radToDeg;
  var rotation = avatar.object3D.rotation;
  rotation.y += Math.PI;

  avatar.setAttribute('rotation', {
    x: radToDeg(rotation.x),
    y: radToDeg(rotation.y),
    z: radToDeg(rotation.z)
  });
});

请注意,avataradded 不能保证化身实体已加载。您应该等待化身完全加载,然后才能安全地更改其他组件。该代码使用底层 Three.js API 来计算化身的正确方向。

位置音频

使用 WebRTC 流式传输音频非常普遍,以至于 sharedspace 组件与 avatars 组件协作,使它变得非常简单。只需将 audio 属性设置为 true

<a-entity sharedspace="hold: true; audio: true" avatars>

下次您加载体验时,浏览器将要求您允许共享您的麦克风。

如果参与者授予权限,A-Frame 化身的位置音频将由 avatars 组件自动管理。位置音频意味着声音将根据监听器(即相机)的相对位置进行左右声场处理。佩戴耳机或耳塞可以增强这种效果。

共享位置

您可能已经注意到,表示用户的化身有特殊处理。因为它的化身“携带”相机,所以当您环顾四周时,您的方向将由其他参与者共享。默认情况下,avatars 将向用户的化身添加一些特定的组件。

您可以使用 A-Frame 混合器 控制应该向用户的化身应用哪些组件。混合器是组件容器,实体可以将 mixin 属性设置为混合器 ID 列表以 *继承* 其组件。

在带有混合器内部的 a-scene 标记之后添加一个 a-assets 标记,并将它的 id 设置为 users

<a-assets>
  <a-mixin id="user" visible="false" look-controls wasd-controls share="position, rotation"></a-mixin>
</a-assets>

share 组件(在注册 sharedspace 后也可用)指示哪些组件应该在其他对等方之间保持同步。

通过将 sharedspaceonmyself 属性设置为混合器的 id,您是在指示 avatars 向用户的化身添加该混合器。

<a-entity sharedspace="hold: true; audio: true" avatars="onmyself: user">

发送和接收消息

sharedspace 组件允许用户向其他对等方发送消息。您将使用此功能在按下空格键时强制对预设环境进行更改。

找到您向 avataradded 事件添加监听器的位置,并插入以下代码来管理环境预设

var presets = [
  'contact', 'egypt', 'checkerboard', 'forest',
  'goaland', 'yavapai', 'goldmine', 'threetowers',
  'poison', 'arches', 'tron', 'japan',
  'dream', 'volcano', 'starry', 'osiris'
];
var environment = document.querySelector('[environment]');
        
function setEnvironment(preset) {
  environment.setAttribute('environment', { preset: preset });
}
        
function getNextPreset() {
  var currentPreset = environment.getAttribute('environment').preset;
  var index = presets.indexOf(currentPreset);
  return presets[(index + 1) % presets.length];
}

// Here comes the code to send and receive message….

最后,用以下代码替换注释,用于接收和发送消息

window.addEventListener('keydown', function (evt) {
  if (evt.keyCode === 32 /* spacebar */) {
    var preset = getNextPreset();
    setEnvironment(preset);
    room.components.sharedspace.send('*', { type: 'environment', preset: preset });
  }
});
        
room.addEventListener('participantmessage', function (evt) {
  if (evt.detail.message.type === 'environment') {
    var preset = evt.detail.message.preset;
    setEnvironment(preset);
  }
});

下一步是什么?

本文展示了如何将最小的共享空间设置演变为引人入胜的多用户户外体验。但这仅仅是开始。如果您想继续改进演示,以下是一些您可以尝试的操作

组件存储库 包含 对组件 API 的详细说明Glitch 上的模板 VR 聊天项目,包括 源代码指南,可以帮助您快速熟悉这些组件。

寻找更多创意?在 Unbirthday Room 中,这里还有另一个由 sharedspace 支持的多用户应用程序。

结论

多用户应用程序不仅限于聊天:其他参与性体验也适合 sharedspace 模型。即使提出的参与模型有限,其他组件也可以在此基础上构建,以实现新的交互。

现在轮到您构建多用户体验了:安装 A-Frame 和 sharespace,进行黑客攻击,录制演示,并在 Twitter 上提及 @aframevr 来告知我们。加入活跃的 Slack 频道,并告诉我们您的协作体验。

关于 Salva

Mozilla 的前端开发人员。开放网络和 WebVR 倡导者,我喜欢编程语言、电影、音乐、电子游戏和啤酒。

更多 Salva 的文章...


3 条评论

  1. Rob Swain

    没有头像,但有视频聊天:https://matrix.org/vrdemo/

    可以结合数据通道的功能,让视频画面在空间中漂浮。:)

    2017 年 10 月 6 日 上午 08:33

  2. fabrizio

    有意思

    2017 年 10 月 6 日 下午 01:24

  3. 215

    woooooooooooooo

    2017 年 10 月 14 日 上午 07:49

此文章的评论已关闭。