原文:
zh.annas-archive.org/md5/BB76013B3798515A13405091AD7CB582
译者:飞龙
第十一章:走在野生的一边
到目前为止,在前面的章节中,我们已经建立了一些真实但小型的世界。
然而,有一些东西一直缺失。一开始,我谈到了 VR 作为一种可以互动的东西——一种现实,即使它看起来并不真实。到目前为止,我们所做的大部分是看和观察事物,但我们无法四处移动。
在本章中,我们将做到这一点。
你将学习以下主题:
使用 NPM 添加组件
凝视按钮
使用凝视按钮触发事件
添加 JavaScript 文件
将 JavaScript 文件转换为动态构建几何图形
在我们创建的世界中移动观点
移动使事物看起来更真实
更多关于 VR 控制器的信息
发疯了——VR 运动方式
我小时候晕车。VR 也会让你晕车——之前在介绍 VR 时已经讨论了这些原因,但这是一个非常重要的话题,所以值得重复。
如果你移动一个观点,独立于用户的行为(用户代理),大脑会知道它没有移动。然而,大脑也会看到世界通过你(VR)的眼睛移动。然后,大脑依赖于一个非常古老、重要的生存特征——你会认为自己中毒了。
当你中毒时,你的身体非常擅长呕吐。用不太临床的术语来说,你会呕吐。你的身体认为有什么东西试图杀死你,所以它只是想尽快摆脱胃里的任何东西,作为一种恐慌反应。
那么,在 VR 中如何移动?如何在不让人发疯的情况下启用 VR 运动方式?
VR 运动方式的类型
讨论 VR 运动方式,至少要稍微讨论一下 VR 控制器。你手中拿着的东西,脚下的东西,支撑你的东西,或者让你在周围滚动的东西显然会产生巨大的影响。
我们正在讨论 WebVR,虽然对人们来说非常容易上手,但这可能意味着你的用户可能没有各种类型的 VR 装备。如果你确实有装备,你可能会发现对于你的应用程序,更简单的运动方式更好,而且编码速度肯定更快。
在讨论设备时,人们讨论自由度(DOF)。这实际上与严格考虑自由度有关,但主要是关于被跟踪的内容。
如果您有手持设备,您可能只有3DOF;这意味着电子设备可以跟踪您是否围绕其中心旋转。6DOF控制器是这样跟踪的,但它也可以检测自己是否在移动,换句话说,是在进行平移。通常,每个都有 3 个度。
6DOF 控制器更加逼真;您可以伸手触摸物体。然而,它们需要某种形式的跟踪,对于目前的行业状态来说,通常意味着外部跟踪器,比如 Vive 灯塔或 Oculus 摄像头。
还有一种称为内部跟踪的跟踪方式,这意味着头戴设备本身可以看到控制器并确定它们的位置。它们确实使用摄像头,只是不是散布在房间周围的外部摄像头。
很难将运动类型归类为无控制器的运动方式;它也可能与控制器(传送)一起很好地工作。
我不会真的包括四处移动头部(或四处移动鼠标),尽管那也是移动;没有这个,VR 系统实际上不是真正的 VR(按我的定义)。然而,确实有一些 VR 头戴设备不包括这个功能,或者做得不好。这是高端手机(三星 Gear VR 和 Google Daydream)和 PC 头戴设备 Vive 和 Rift 的真正突破。
考虑以下类型的 VR 运动:
凝视检测:您看着某物,它会激活一个效果,一个眨眼,或者让您移动
车辆/驾驶舱运动:您的视野显示墙壁或驾驶舱的细节
可以通过凝视检测移动
带有控制器(游戏手柄等)
定时/人工(按下按钮或在一段时间后移动玩家)
只有轻微的生病几率
房间规模:
四处走动(直到边界)
生病的几率非常低
需要硬件
传送或眨眼:
通常使用凝视或 3DOF 或 6DOF 控制器
传送也可以分成小步骤进行——消除运动(视觉加速);这会让您感觉自己在移动,但不会让您感到恶心
跑步机:
一种您站在上面并移动脚,它会检测您的移动方式
还有滑翔伞模拟器和飞行模拟器,您可以躺下或坐下,通过移动身体重量来飞行
所有这些都很大且昂贵,通常只限于 VR 游乐场
跟踪的 6DOF 控制器运动范式:
Vive/Rift 通常使用传送,而 6DOF 控制器使其变得容易
有许多其他使用 6DOF 控制器的移动方式;一个好的列表可以在
bit.ly/VRLoco
找到人工运动/轨道:
一旦你使用 UI 指示要做什么,VR 系统就会沿着一条路径将你移动。
凝视/头部控制的转向属于这一类。
很容易让人感到恶心。
如果你的头转动,它可能会很烦人;只需改变你的移动方式;即使你不会感到恶心,你也会感觉自己被带走了。不过,通过谨慎的实施,它也可以起作用。
围绕移动的方式当然受到你拥有多少硬件的限制。另一个限制是你想要多大的受众群体。如果你设计你的 VR 应用程序为房间规模(自然四处走动),你就排除了每个手机用户。然而,同样地,如果你决定使用凝视瞬移系统,那些拥有房间规模 VR 的人会感到沮丧,因为他们不能四处走动。
WebVR 目前更多地针对移动 VR,房间规模是一个很大的编程挑战。这是可能的,但在 React-VR 和 WebVR 中并没有内置。从硬件可用性的角度来看:
无需设备(Google Cardboard):
自然运动(平移/倾斜)- 仅限少量
凝视检测
通过定时器或凝视检测的人工运动('轨道’运动,就像你在轨道上)
带控制器的 VR 头盔(Gear VR,Daydream 等):
现在我们有更好的方法,但仍然可以做所有以前的方法:
自然运动(平移/倾斜)- 仅限少量
凝视检测
通过定时器或凝视检测的人工运动('轨道’运动,就像你在轨道上)
驾驶舱运动
通过控制器进行瞬间移动
操纵杆/控制器
PC VR–Vive/Rift:
现在我们有更好的方法,但仍然可以做所有以前的方法:
自然运动(平移/倾斜)- 仅限少量
凝视检测
通过定时器或凝视检测的人工运动('轨道’运动,就像你在轨道上)
驾驶舱运动
通过控制器进行瞬间移动
操纵杆/控制器(在被跟踪的 6DOF 控制器上)
被跟踪的 6DOF 控制器运动范式
房间规模行走
高端设备:
全景虚拟跑步机或其他跑步机
避免幽灵效应
还有另一个原因,为什么我们希望人们能够在没有某种用户代理的情况下四处移动;没有移动,它真的不是虚拟现实。实际上,我们都在四处移动;猫在潜行时会侧着头。如果你感到好奇,你会歪着头。在 360 度视频中,一个挑战是你只能四处看看;你不能移动。歪着头真的没什么用。
360 度视频会发生什么,尽管它可能非常详细,但你会感觉自己像一个游荡的幽灵。你不能往下看到自己(尽管你可能会看到摄像机支架),你不能四处移动,也不能伸手触摸东西,也不能改变你的视角。如果你歪着头,或者左右移动,就没有视差效果。
我真的很喜欢 360 度视频,但我也觉得它并不真正是虚拟现实,因为最终你会感到游离,本质上是一个被束缚的幽灵。当然,视频可能会移动,但你无法改变它的移动方式;你只是随波逐流。
我对 WebVR 非常印象深刻的一个微妙之处是,如果你歪着头,VR 视图会稍微移动,就好像你在侧头。这是一个微妙的效果;它不是室内尺度的 VR,你不能四处走动,但它是一种 VR。你不会感觉自己像一个游荡的幽灵。
让人们探索他们的环境是很重要的;没有这一点,你真的会感觉自己像一个幽灵。在我们的例子中,我们将使用传送移动的隐喻,让人们探索一个迷宫。
没有与世界互动和移动的能力,你会感觉自己像一个游荡的幽灵。虽然我们几乎用了整本书的篇幅才达到这一点,但与环境和世界互动的能力是虚拟现实中最重要的事情之一。
在这一章中,你将能够使用任何 WebVR 客户端来做到这一点。如果我们知道每个人都有 HTC Vive 或室内尺度的 Oculus Rift,我们可以向你展示在迷宫中四处走动的代码,尽管这会带来一些有趣的用户界面问题——如果有人走过篱笆会怎么样?在我们获得全身触觉套装之前,你可以穿过虚拟墙。有一些使用用户界面来抵消这一点的方法,比如将屏幕短暂地变黑,然后将用户传送回起点,只是允许他们作弊(不好),或者其他有趣的方法来解决这个问题。
现在,我们将简单地允许用户移动到迷宫中的下一个单元格/开放位置,并且仅限于该位置。我们将使用凝视选择,这意味着当您盯着一个 UI 元素时,我们会知道您已经点击它。这将适用于市场上所有的 VR 设备,这真的是开始的最佳地点。更复杂的 UI 元素需要检查用户拥有的 VR 控制器和跟踪类型,并根据需要启用适当的移动。这超出了本书的范围。
在讨论如何在我们的世界中移动之前,我们需要有一些有趣的东西可以四处走动。例如,也许我们在森林中漫步,发现迷宫挡住了我们的去路,或者是清晨,我们想去一个小湖看清晨的雾。
让我们来建造那个迷宫。
建造迷宫
我们可以建造迷宫的几种方式。最直接的方法是启动我们的 3D 建模软件(比如 Blender)并用多边形创建一个迷宫。这样做效果很好,也可以非常详细。
然而,这也会很无聊。为什么?第一次通过迷宫会很激动,但几次尝试之后,你会知道通往目的地的路。当我们构建 VR 体验时,通常希望人们经常访问并每次都有愉快的时光。
建模的迷宫会很无聊。生命太短暂,没有时间做无聊的事情。
因此,我们希望随机生成一个“迷宫”。这样,您可以每次都改变“迷宫”,使其保持新鲜和不同。为了做到这一点,我们需要通过随机数来确保“迷宫”不会围绕我们移动,所以我们实际上希望用伪随机数来实现。要开始做到这一点,我们需要创建一个基本的应用程序。请转到您的 VR 目录并创建一个名为“WalkInAMaze”的应用程序:
react-vr init WalkInAMaze
几乎随机-伪随机数生成器
为了有机会重播价值或能够比较不同人之间的分数,我们真的需要一个伪随机数生成器。基本的 JavaScript Math.random()
不是伪随机生成器;它每次都会给你一个完全随机的数字。我们需要一个带有种子值的伪随机数生成器。如果你给随机数生成器相同的种子,它将生成相同的随机数序列。(它们并不是完全随机的,但非常接近。)随机数生成器是一个复杂的话题;例如,它们被用于密码学,如果你的随机数生成器不是完全随机的,有人可能会破解你的代码。
我们不太担心这一点,我们只是想要可重复性。尽管这方面的用户界面可能超出了本书的范围,但以一种点击刷新不会生成完全不同的Maze
的方式创建Maze
真的是一件好事,会避免用户的沮丧。这也将允许两个用户比较分数;我们可以为Maze
持续一个板号,并显示这个。这可能超出了我们书的范围;然而,拥有可预测的Maze
在开发过程中将会极大地帮助。如果没有这一点,你可能会在工作中迷失方向。(好吧,可能不会,但这样测试会更容易。)
包含来自其他项目的库代码
到目前为止,我已经向你展示了如何在 React VR(或 React)中创建组件。有趣的是,JavaScript 在include
方面有一个历史问题。在 C++、Java 或 C#中,你可以在另一个文件中include
一个文件或在项目中引用一个文件。在那之后,那些其他文件中的所有内容,比如函数、类和全局属性(变量),都可以从发出include
语句的文件中使用。
在浏览器中,“包含”JavaScript 的概念有点不同。在 Node.js 中,我们使用package.json
来指示我们需要哪些包。要将这些包引入我们的代码中,我们将在.js 文件中使用以下语法:
var MersenneTwister = require('mersenne-twister');
然后,我们将创建一个新的随机数生成器并传递一个种子,而不是使用Math.random()
。
var rng = new MersenneTwister(this.props.Seed);
从这一点开始,你只需要调用rng.random()
而不是Math.random()
。
目前,我们只需使用 npm install <package>
和 require
语句来正确格式化包。在下一章中,我们将讨论升级并修改 package.json
,以确保代码正确地发布和更新。执行 npm
命令可以为您完成其中的大部分工作:
npm install mersenne-twister --save
记住,–save 命令用于更新项目中的清单。在此期间,我们还可以安装另一个以后会用到的包:
npm install react-vr-gaze-button --save
现在我们有一个很好的随机数生成器,让我们用它来复杂化我们的世界。
迷宫渲染()
我们如何构建一个 Maze
?我想开发一些动态生成 Maze
的代码;任何人都可以在一个包中对其进行建模,但 VR 世界应该是活生生的。拥有能够动态构建 Maze
的代码(在一定程度上)将允许您重复玩您的世界。
有许多用于打印迷宫的 JavaScript 包。我选择了一个似乎无处不在的、公共领域的 GitHub 上的包,并对其进行了 HTML 修改。这个应用程序由两部分组成:Maze.html
和 makeMaze.JS
。它们都不是 React,而是 JavaScript。它运行得相当不错,尽管数字并不真正代表宽度。
首先,我确保只有一个 x
在垂直和水平方向上显示。这样打印效果可能不好(行通常比列高),但我们正在构建一个虚拟的 Maze
,而不是纸质的 Maze
。
我们使用 Maze.html
(localhost:8081/vr/maze.html
)和 JavaScript 文件 makeMaze.js
生成的 Maze
现在看起来是这样的:
x1xxxxxxx
x x x
xxx x x x
x x x x
x xxxxx x
x x x x
x x x x x
x x 2
xxxxxxxxx
这有点难以阅读,但你可以数一下方块和 x
的数量。别担心,它会看起来更加花哨。现在我们已经让 HTML 版本的 Maze
工作了,我们将开始建造树篱。
这段代码比我预期的要长一些,所以我把它分成了几部分,并将 Maze
对象加载到 GitHub 上,而不是在这里粘贴整个代码,因为它太长了。您可以在以下链接找到源代码:bit.ly/VR_Chap11
添加地板和类型检查
正如我们之前讨论过的,360 全景背景的一个奇怪之处是,你似乎可以“漂浮”在地面上。除了修复原始图像之外,另一个解决方法就是简单地添加一个地板。这就是我们在太空画廊中所做的,看起来相当不错,因为我们假设我们在太空中漂浮。
对于这个版本,让我们import
一个地面方块。我们可以使用一个大方块来包含整个Maze
;然后如果Maze
的大小发生变化,我们就必须调整它的大小。我决定使用一个较小的立方体,并对其进行修改,使其“位于”Maze
的每个单元格下方。这将使我们在将来有一些余地,可以旋转方块以制作磨损的路径、水陷阱或其他东西。
为了制作地板,我们将使用一个简单的立方体对象,我稍微修改了它,并进行了 UV 映射。我用 Blender 做的这个。我们还import
了一个Hedge
模型和一个Gem
,它将代表我们可以传送到的地方。在Maze.js
内部,我们添加了以下代码:
import Hedge from './Hedge.js';
import Floor from './Hedge.js';
import Gem from './Gem.js';
然后,在Maze.js
内部,我们可以用以下代码实例化我们的地板:
<Floor X={-2} Y={-4}/>
注意,当我们进行导入时,我们不使用’vr/components/Hedge.js
’;我们在 Maze.js 内部。然而,在 index.vr.js 中包含 Maze 时,我们确实需要:
import Maze from './vr/components/Maze.js';
然而,情况稍微复杂一些。在我们的代码中,当属性发生变化时,迷宫会构建数据结构;在移动时,如果迷宫需要重新渲染,它会简单地遍历数据结构并构建一个包含所有地板、传送目标和树篱的集合(mazeHedges)。鉴于此,要创建地板,在Maze.js
中的代码实际上是:
mazeHedges.push(<Floor {...cellLoc} />);
在这里,我遇到了两个大问题,我会告诉你发生了什么,这样你就可以避免这些问题。最初,我一直在试图弄清楚为什么我的地板看起来像树篱。这个问题很容易——我们从Hedge.js
文件中导入了Floor
。地板看起来像树篱(你在我的前面的代码中注意到了吗?如果是的话,我是故意这样做的,作为一个学习经验。诚实地说)。
这是一个简单的修复。确保你的代码中有import Floor from './floor.js';
注意Floor
没有经过类型检查。(毕竟,这是 JavaScript。)我觉得这很奇怪,因为hedge.js
文件导出了一个Hedge
对象,而不是一个Floor
对象,但请注意,你可以在import
它们时重命名对象。
我遇到的第二个问题更像是一个简单的失误,如果你没有真正思考 React,很容易发生。你可能也会遇到这个问题。JavaScript 是一种可爱的语言,但有时我会想念一种强类型的语言。这是我做的:
<Maze SizeX='4' SizeZ='4' CellSpacing='2.1' Seed='7' />
在maze.js
文件中,我有这样的代码:
for (var j = 0; j < this.props.SizeX + 2; j++) {
经过一些调试,我发现j
的值从0
变成了42
。为什么会变成42
而不是6
呢?原因很简单。我们需要充分理解 JavaScript 才能编写复杂的应用程序。错误在于将 SizeX 初始化为'4'
;这使它成为一个字符串变量。当从0
(一个整数)计算j
时,React/JavaScript 会取2
,将其加到一个字符串'4'
上,得到字符串42
,然后将其转换为整数并赋给j
。
当这样做时,非常奇怪的事情发生了。
当我们构建 Space Gallery 时,我们可以轻松地使用'5.1'
的值作为输入到框中:
<Pedestal MyX='0.0' MyZ='-5.1'/>
然后,在类中使用下面的转换语句:
transform: [ { translate: [ this.props.MyX, -1.7, this.props.MyZ] } ]
React/JavaScript 会将字符串值放入This.Props.MyX
,然后意识到它需要一个整数,然后悄悄地进行转换。然而,当你得到更复杂的对象,比如我们的Maze
生成时,你就逃不过这一点。
记住,你的代码并不是“真正”的 JavaScript。它是经过处理的。在本质上,这种处理是相当简单的,但其影响可能是致命的。
注意你所编写的代码。在 JavaScript 这样的弱类型语言中,再加上 React,你所犯的任何错误都会悄悄地转换成你意想不到的结果。
你是程序员。要正确编程。
所以,回到Maze
。Hedge
和Floor
基本上是初始Gem
代码的副本。让我们来看看我们的起始Gem
,尽管请注意它后来变得更加复杂(以及在你的源文件中):
import React, { Component } from 'react';
import {
asset,
Box,
Model,
Text,
View
} from 'react-vr';
export default class Gem extends Component {
constructor() {
super();
this.state = {
Height: -3 };
}
render() {
return (
<Model
source={{
gltf2: asset('TeleportGem.gltf'),
}}
style={{
transform: [{ translate: [this.props.X, this.state.Height, this.props.Z] }]
}}
/>
);
}
}
Hedge
和Floor
本质上是相同的东西。(我们本可以让一个 prop 成为加载的文件,但我们希望Gem
有不同的行为,所以我们将大幅编辑这个文件。)
要运行这个示例,首先,我们应该像之前一样创建一个名为WalkInAMaze
的目录。一旦你这样做了,从本章的 Git 源下载文件(bit.ly/VR_Chap11
)。一旦你创建了应用程序,复制了文件并启动了它(进入WalkInAMaze
目录并输入npm start
),你应该看到类似这样的东西一旦你四处看看——除了有一个 bug。这就是迷宫应该看起来的样子(如果你在Hedge.js
中使用文件'MazeHedges2DoubleSided.gltf'
,在<Model>
语句中):
那么,我们是如何在游戏中得到那些看起来整洁的树篱的呢?(好吧,它们的多边形确实很低,但仍然可以。)Web 标准改进的速度之一是它们的新功能。现在,React VR 不仅支持.obj 文件格式,还可以加载 glTF 文件。
使用 glTF 文件格式进行建模
glTF 文件是一种新的文件格式,与 WebGL 非常自然地配合。有许多不同的 CAD 软件的导出器。我喜欢 glTF 文件的原因是,获得正确的导出相当简单。Lightwave OBJ 文件是行业标准,但在 React 的情况下,并非所有选项都被导入。一个主要的问题是透明度。OBJ 文件格式允许这样做,但在撰写本书时,这并不是一个选项。许多其他现代硬件可以处理的图形着色器无法用 OBJ 文件格式描述。
这就是为什么 glTF 文件是 WebVR 的下一个最佳选择。这是一种现代和不断发展的格式,正在努力增强功能,并在 WebGL 可以显示的内容和 glTF 可以导出的内容之间取得相当好的匹配。
然而,这是一章关于与世界互动的内容,所以我会简要提及如何导出 glTF 文件并提供对象,特别是Hedge
,作为 glTF 模型。
从建模方面来看,glTF 的好处是,如果您使用它们的材质规范,例如 Blender,那么您就不必担心导出不够准确。今天的基于物理的渲染(PBR)倾向于使用金属/粗糙模型,这些比尝试将 PBR 材质转换为 OBJ 文件的镜面光照模型更容易导入。这是我用作凝视点的看起来金属质的Gem
:
使用 glTF 金属粗糙模型,我们可以分配纹理贴图,例如 Substance Designer 等程序计算并轻松导入。结果看起来金属的地方看起来金属,油漆仍然保持的地方看起来暗淡。
我在这里没有使用环境遮挡,因为这是一个非常凸起的模型;表面凹陷更多的东西会与环境遮挡搭配得很棒。例如,对于建筑模型和家具,也会看起来很棒。
要转换您的模型,可以在bit.ly/glTFExporting
找到用户文档。您需要下载并安装 Blender glTF 导出器。或者,您可以直接下载我已经转换过的文件。如果您要进行导出,简而言之,您需要执行以下步骤:
从
bit.ly/gLTFFiles
下载文件。您将需要gltf2_Principled.blend
文件,假设您使用的是 Blender 的较新版本。在 Blender 中,打开您的文件,然后链接到新的材质。转到文件->链接,然后选择
gltf2_Principled.blend
文件。一旦您这样做了,进入“NodeTree”,然后选择 glTF 金属粗糙度(用于金属)或其他材质的 glTF 高光光泽。选择要导出的对象;确保选择 Cycles 渲染器。
- 在窗口中打开节点编辑器(就像您在之前的章节中处理图像时所做的那样)。向下滚动到节点编辑器窗口的底部,并确保“使用节点”框被选中。
通过节点菜单添加节点,添加->组->glTF 高光光泽或金属粗糙度。
添加节点后,转到添加->纹理->图像纹理。添加与图像地图数量相同的图像纹理,然后将它们连接起来。您应该得到类似于这个图表的东西。
- 要导出模型,我建议您禁用相机导出并合并缓冲区,除非您认为将要导出共享几何图形或材质的多个模型。我使用的导出选项如下:
现在,要包含导出的 glTF 对象,使用<Model>
组件,就像使用 OBJ 文件一样,只是没有 MTL 文件。所有材质都在.glTF 文件中描述。要包含导出的 glTF 对象,只需将文件名作为<Model
中的 gltf2 属性:
<Model
source={{ gltf2: asset('TeleportGem2.gltf'),}}
...
要了解更多关于这些选项和流程的信息,您可以访问 glTF 导出网站:bit.ly/WebGLTF
。该网站还包括主要 CAD 软件的教程以及非常重要的 glTF 着色器(例如,我之前展示的 Blender 模型)。
我已经加载了几个.OBJ 文件和.glTF 文件,您可以在bit.ly/VR_Chap11
上尝试不同的低多边形和透明度的组合。当在 React VR 版本 2.0.0 中添加了 glTF 支持时,我感到非常兴奋,因为透明度贴图对于许多 VR 模型非常重要,特别是植被;就像我们的树篱一样。然而,事实证明在 WebGL 或 three.js 中存在一个 bug,无法正确渲染透明度。因此,我在 GitHub 网站上的文件中选择了低多边形版本;上面的图片是使用Hedges.js
文件中的MazeHedges2DoubleSided.gltf
文件(在 vr/components 中)。
如果您遇到 404 错误,请检查 glTF 文件中的路径。这取决于您使用的导出器——如果您使用的是 Blender,Khronos 组的 gltf2 导出器会正确计算路径,但 Kupoman 的导出器有选项,您可能会导出错误的路径。
动画 — VR 按钮
好了!我们想要做一些动画。为了做到这一点,我们将使用 VRButton。当发生以下情况之一时,它会激活:
XBox 游戏手柄上的 A 按钮
键盘上的空格键
用鼠标左键单击
屏幕上的触摸
不幸的是,我们的“最低公共分母”是 Google Cardboard,可能有,也可能没有按钮。您不想不得不把手指伸进去尝试触摸屏幕。(说了这些之后,更新的 VR 头盔有一个小杠杆可以戳屏幕,即使是在实际的硬纸板版本中)。我们将使用凝视按钮。当鼠标指针或屏幕中心(由一个小点标记)悬停在您的对象上时,事件将被调用,我们的代码将处理这个问题。
凝视按钮也被打包成了npm
生态系统中的一个漂亮的<GazeButton>
对象。请参考网页:bit.ly/GazeButton
。要使用它,我们需要了解它的功能,以及如何让视图知道一个Gem
已经被“触摸”(或者被观察了两秒)。我们在本章的前面已经安装了它;如果你到目前为止还没有安装,我们可以通过使用 Node.js 命令提示符并输入以下命令来安装它:
npm install react-vr-gaze-button
我们可以使用 VR 按钮,但那样我们就必须处理进入对象、离开对象、倒计时等等。GazeButton
会为我们处理所有这些。请注意,它对子元素的期望方式与我们到目前为止所习惯的方式有些不同。
现在,您的Gem.js
代码(注意大写)应该如下所示:
import GazeButton from 'react-vr-gaze-button'
export default class Gem extends Component {
constructor() {
super();
this.state = {
Height: -3,
buttonIsClicked: false
};
}
onGemClicked() {
this.setState({ buttonIsClicked: true });
console.log("Clicked on gem " + this.props.X + " x " + this.props.Z);
}
render() {
const { buttonIsClicked } = this.state
return (
<GazeButton onClick={() => this.onGemClicked()}
duration={2000}>
{time => (
<Model
source={{
gltf2: asset('TeleportGem.gltf'),
}}
style={{
transform: [{ translate: [0, -1, 0] }]
}}
style={{
transform: [{ translate:
[this.props.X, this.state.Height, this.props.Z] }]
}}
/>
)}
</GazeButton>
);
}
}
现在,当我们在桌面上尝试这样做时,似乎可以工作,但在手机上(我尝试了三星 GearVR),没有光标,也没有可以点击的东西。我们需要实现一个射线投射器(即使没有控制)。
正如我们在本章开头简要讨论的那样,有许多不同类型的 VR 控制系统,默认情况下是“没有”VR 输入设备,包括屏幕中心光标。
适当的控制系统的实施在我们手中。
当您使用桌面浏览器进行初始开发时,您会得到一个鼠标光标(包括在跟踪组件上时的手光标),这可能意味着内置了注视光标;实际上并没有。只需意识到这是有合理理由的。
射线投射器
射线投射器向世界发射一条射线并计算它触及了什么。通常您会看到这些作为 VR 控制器发出的发光线。没有控制器时,射线投射器将从屏幕中心发射一条射线;这正是我们需要实现我们的注视按钮的地方。
在这种情况下,就像我们对按钮所做的那样,已经有一个simple-raycaster
。如果您还没有安装它,您需要通过以下命令从npm
安装它:
npm install --save simple-raycaster
在尝试使用软件包时,您可能希望跳过--save
; 如果您这样做,请记得手动更新您的package.json
文件,或者通过适当的工具进行更新。
实现simple-raycaster
非常容易。在client.js
中,在现有的import
行(VRInstance)下面,添加以下import
语句:
import * as SimpleRaycaster from "simple-raycaster";
在“//在此处添加自定义选项”处,插入以下行:
raycasters: [
SimpleRaycaster // Add SimpleRaycaster to the options
],
cursorVisibility: "auto", // Add cursorVisibility
在您的 PC 上,此时情况会有点奇怪——屏幕中心会激活(并丢弃)宝石,即使您没有点击。这正是整个重点。
如果我们有更多的页面,当你的目光进入宝石时,我们会让宝石旋转。但现在,我们将把这个练习留给读者。
您将希望在onClick
处理程序中开始动画。
到目前为止,我们已经展示了当注视宝石时如何获得事件。这很好,我们可以使用事件来触发移动,但我们如何移动呢?
有一件有点奇怪的事情是,React VR 没有像许多图形系统那样移动摄像头的方法。要移动当前的视角,你需要在index.vr.js
的开头将<View>
向相反方向进行平移;这会使世界中的一切朝相反方向移动,看起来就像你在向前移动。要移动视角,我们需要将点击事件从Gem
传递给其父级的父级(顶级 View)。
Props,state 和 events
React,以及 React VR,在其核心,以可预测、确定的方式处理 props、事件和状态,这就是使 React 应用程序保持一致、清晰且易于维护的原因。
当对象声明时创建 props,并且在对象的生命周期内不应更改。如果对象需要更改,例如我们的传送门宝石,那么应将这些值分配给state
。这强制实现了自顶向下的单向数据流。如果组件在不同区域需要相同的状态,那么该state
应该被提升到最高级的父级。
这会引发有趣的问题,如果你想让一个子组件告诉父组件有关事件的信息,或者根据较低级别的事件改变其状态。
处理这个问题有几种方法;在 React 世界中,这可能是一个复杂的主题。React VR 在处理状态、props 和事件方面与 React Native 或 React 没有区别。一个很好的起点是 React 文档中的State and Lifecycle。
基本上,在 React 应用程序中,应该有一个用于变化的单一真相来源。如果父级不关心,例如Gem
是更高还是更低(被踩或未被踩),那么你不需要让父级跟踪其子级的高度。将state
保持在尽可能低的级别是正确的决定。高度可以从“我们是否踩了Gem
”中计算出来,因此不应该是传递下来的 prop。(尽管在很多书籍文件中,出于简洁起见,我们已经硬编码了值,但你可能会考虑起始高度作为一个 prop;良好的编程规范说不要硬编码值。)
在我们的迷宫世界中,我们遇到了一个困境。我们通过改变世界树顶部的<View>
节点来移动视角。然而,当我们点击每个<Gem>
时,我们希望视图发生变化。
我们可以用上下文来处理这个问题;许多库,比如 Redux 或 MobX,在内部使用上下文。还有一些使用上下文和其他功能的事件库。然而,上下文是一个有点高级的概念,对于我们正在做的事情来说有点过度。
在这种特殊情况下,我们将简单地将一个“回调”函数传递到子树中。我们这样做的原因如下:
此时,从层次结构的角度来看,我们的应用程序相当小,只有三个级别。
“迷宫”本身可能需要知道用户何时到达终点(例如,显示烟花或更新高分)。“宝石”不知道这一点。如果“宝石”直接向视图发送通知,那么“迷宫”将永远不知道。
我们可以引入额外的库,但这是一个简单的项目,在开源世界中太多外部依赖可能会导致问题。通常这不是一个大问题,如果出了问题,那就是开源的,去修复它。
如果在寻找外部包时出现问题,你需要卸载有问题的包,然后通过运行以下命令重新启动你的 Node.js 服务器:
npm start -- --reset-cache
npm cache clean --force
不会执行此缓存重置。如果你忘记了,你得到的错误消息应该指出这一点。
使更新向上流动
尽管更新会传播下来,但我们需要传递信息。我们怎么做?很简单,用一个功能性的“回调”。
在index.vr.js
中,创建一些例行程序,并将这些例行程序与WalkInAMaze
组件进行重要的绑定。在这一点上,我只展示了更改的行:
constructor(props) {
super(props);
this.state = {
// ... existing member state initialization
}
this.handleClickGem = this.handleClickGem.bind(this);
};
onClickFloor(X, Z) {
this.setState({ newX: X, newZ: Z });
}
handleClickGem(X, Z) {
this.setState({ newX: X, newZ: Z });
};
在我们的Gem.js
中,我们已经有一个onClick
方法。我们只需要添加一些新的props
:
onGemClicked() {
this.setState({ buttonIsClicked: true });
//send it to the parent
this.props.onClickGem(this.props.X, this.props.Z);
}
现在,这个this.props.onClickGem
是什么?这是一个从父级传递的函数 prop。在我们创建“宝石”时,我们只需插入以下 prop(加粗的插入行,注意源代码不能加粗):
...
mazeHedges.push(<Gem {...cellLoc}
onClickGem={this.handleClickGem}
/>);
好的,我们从哪里得到this.handleClickGem
?在这个(简单)情况下,“迷宫”不会对事件做任何处理,只是将其传递。在Maze.js
中,我们将添加一个处理程序:
constructor(props) {
super(props);
// existing code here doesn't change
// at the bottom:
this.handleClickGem = this.handleClickGem.bind(this);
}
handleClickGem(X, Z) {
this.props.onClickGem(X, Z);
}
现在,我们注意到这里还有另一个 prop。这当然是由迷宫的父级传递给我们的;所以,在index.vr.js
中,添加(加粗)这一行:
<Maze sizeX={this.state.sizeX} sizeZ={this.state.sizeZ}
cellSpacing={this.state.cellSpacing} seed={this.state.seed}
onClickGem={this.handleClickGem} />
基本上就是这样。当Gem
的 VR 注视按钮检测到点击时会发生什么?它调用了一个函数作为 prop。这会导致迷宫的handleClickGem
被调用;它反过来调用了index.vr.js
中的handleClickGem()
。这个例程(双关语)然后设置内部状态。这个状态导致视图重新渲染:
handleClickGem(X, Z) {
this.setState({ startX: -X, startZ: -Z });
};
就是这样。请注意,你不仅仅通过this.startX = -X
来设置state
,你需要像前面的代码中所示调用this.setState()
。然后这个例程将处理render()
更新的传递。
这些都是大文件,我们刚刚做了很多改变。我在上面标出了重要的行,但我强烈建议你从bit.ly/VR_Chap11
下载源文件并看看我们做了什么。在其中,我建立了一个 4x4 的迷宫,应该在大多数 PC 和移动设备上有合理的帧率。你可以尝试一些其他版本的各种对象(看起来像树篱或低多边形树篱)。
接下来怎么办?
这是一个相当基础的游戏,但你可以做很多事情。我们之前讨论过的一些东西,很容易包括在内,比如:
我们的传送有点突然。我们应该有声音,或者甚至通过改变
HandleClickGem()
例程进行两次更新,以添加一个简短的动画或两步传送。请注意,通常不建议平滑地动画化视图本身;这会让人们感到不适,因为他们的眼睛说他们在移动,但他们的身体说没有。点击的宝石数量可以成为得分。这使我们有优势可以慢慢前进,一步一步地点击所有的宝石。
你可以计时到达出口所需的时间,较短的时间可以增加你的得分。这使得快速前进并跳过传送宝石有优势。这两个目标是互斥的,通过平衡可以使游戏变得有趣。
你可以在迷宫前面加入按钮来增加/减少大小或生成不同的随机数(并显示它们)。
得分和随机数可以加载到高分 API 中。
事件传递库,比如
eventing-bus
,使传递props
变得更容易。我的目标是向你展示 React VR 的做法。
总结
在本章中,我们学习了构建完整的网络应用程序和游戏的最后一部分;再加上我们之前学到的知识,我们的旅程几乎已经完成;这实际上只是将现实带到网络的第一步。我们讨论了如何在 VR 世界中移动,包括基本的传送机制。我们讨论了凝视按钮和使用射线投射来实现它们。我们讨论了props
,state
和事件的重要机制。为了实现这些流程,我们复习了重要的 React 哲学,即将state
向上传递并处理下游事件。我们还讨论了使用伪随机数生成器来确保我们的props
和state
不会发生混乱性变化。总的来说,我们现在知道如何创建、在其中移动,并使世界对我们做出反应。
在下一章中,我们将讨论接下来该怎么做,如何升级 React VR,以及如何在互联网上发布你的虚拟世界。
第十二章:发布您的应用程序,以及接下来要做什么
在家中开发和体验虚拟世界是很有趣的。但最终,您希望世界能看到您的世界。为了做到这一点,我们需要打包和发布我们的应用程序。在开发过程中,可能会出现对 React 的升级;在发布之前,您需要决定是否需要“冻结代码”并使用稳定版本进行发布,或者升级到新版本。这是一个设计决策。
在某个时候,您将需要升级,并且您将需要发布。本章将解释如何同时做到这两点,以及如何组织您的代码并检查您的干衣机和代码是否有 lint。我们将在这里涵盖以下主题:
升级类型:Rip and Replace 或“Facelift”升级,或“现场升级”
如何确保您的组件的正确版本存在
开发与非开发版本、组件和库
分发许可证
链接和嵌入 VR 内容
发布到常见的 Web 主机和内容交付网络。
未来 5 年 VR 的发展方向
升级 React VR
其中一个很棒的事情,尽管可能会令人沮丧,是 Web 项目经常会更新。在编写本书时,React VR 已经更新。有几种不同的升级方式:
您可以安装/创建一个同名的新应用程序
然后您将转到您的旧应用程序并复制所有内容
这是一种facelift升级或Rip and Replace
进行更新。主要是对
package.json
进行更新,然后删除node_modules
并重新构建。这是一种现场升级。
您可以选择使用哪种方法,但主要区别在于现场升级有点更容易——无需修改和复制源代码——但可能有效也可能无效。Facelift 升级还取决于您是否使用了正确的react-vr-cli
。每当您从命令提示符运行 React VR 时,都会有一个通知告诉您它是否过时:
当您从命令提示符运行 React VR 时,关于升级的错误或警告可能会很快地飞过。运行需要一段时间,所以您可能会离开一会儿去喝杯咖啡。
认真关注红线。
要进行就地升级,通常会从 Git 那里收到更新通知,如果您已经订阅了该项目。如果没有,您应该转到:bit.ly/ReactVR
,创建一个帐户(如果您还没有),然后单击眼睛图标加入观察列表。然后,每次有升级时,您都会收到一封电子邮件。我们将介绍最简单的升级方式——就地升级,首先。
就地升级
您如何知道您安装了哪个版本的 React?从 Node.js 提示符中,键入以下内容:
npm list react-vr
还要检查react-vr-web
的版本:
npm list react-vr-web
检查react-vr-cli
(命令行界面,实际上只用于创建hello world应用程序)的版本。
npm list react-vr-cli
检查ovrui
(open VR 的用户界面)的版本:
npm list ovrui
您可以根据文档中的版本进行检查。如果您已经在 GitHub 上订阅了 React VR(您应该这样做!),那么您将收到一封电子邮件告诉您有升级。请注意,CLI 还会告诉您是否过时,尽管这仅适用于创建新应用程序(文件夹/网站)时。
发布说明在:bit.ly/VRReleases
。在那里,您将找到升级的说明。升级说明通常要求您执行以下操作:
删除您的
node_modules
目录。打开您的
package.json
文件。将
react-vr
,react-vr-web
和ovrui
更新为"新版本号",例如 2.0.0。将
react
更新为"a.b.c"。将
react-native
更新为"~d.e.f"。将
three
更新为"^g.h.k"。运行
npm
install 或 yarn。
请注意和^符号;版本表示大致等于版本,^版本表示与版本兼容。这是一个帮助,因为您可能有其他包可能需要其他版本的react-native
和three
。要获取{a…k}的值,请参考发布说明。
我还发现您可能需要在package.json
的devDependencies
部分中包含这些模块:
"react-devtools": "².5.2",
"react-test-renderer": "16.0.0",
您可能会看到此错误:
module.js:529
throw err;
^
Error: Cannot find module './node_modules/react-native/packager/blacklist'
如果您这样做,请在项目的根目录中进行以下更改
rncli.config.js
文件。
将var blacklist = require('./node_modules/react-native/packager/blacklist');
行替换为var blacklist = require('./node_modules/metro-bundler/src/blacklist');
。
第三方依赖
如果你一直在尝试实验并使用npm install <something>
添加模块,你可能会发现,在升级后,事情不起作用。package.json
文件也需要知道你在实验过程中安装的所有额外包。这是项目方式(npm 方式)确保 Node.js 知道我们需要特定的软件。如果你遇到这个问题,你需要使用--save
参数重复install
,或者编辑你的package.json
文件中的dependencies
部分。例如(我加粗的那行),在上一章中我们尝试随机数时,我们可以手动添加这行:
{
"name": "WalkInAMaze",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node -e \"console.log('open browser at http://localhost:8081/vr/\\n\\n');\" && node node_modules/react-native/local-cli/cli.js start",
"bundle": "node node_modules/react-vr/scripts/bundle.js",
"open": "node -e \"require('xopen')('http://localhost:8081/vr/')\"",
"devtools": "react-devtools",
"test": "jest"
},
"dependencies": {
"ovrui": "~2.0.0",
"react": "16.0.0",
"react-native": "~0.48.0",
"three": "⁰.87.0",
"react-vr": "~2.0.0",
"react-vr-web": "~2.0.0",
"mersenne-twister": "¹.1.0"
},
"devDependencies": {
"babel-jest": "¹⁹.0.0",
"babel-preset-react-native": "¹.9.1",
"jest": "¹⁹.0.2",
"react-devtools": "².5.2",
"react-test-renderer": "16.0.0",
"xopen": "1.0.0"
},
"jest": {
"preset": "react-vr"
}
}
再次强调,这是手动的方法;更好的方法是使用npm install <package> -save
。
-s
限定符将你安装的新包保存在package.json
中。手动编辑可以确保你得到正确的版本,如果出现版本不匹配的情况。
如果你尝试安装和移除足够多的包,最终会搞乱你的模块。即使删除了node_modules
后仍然出现错误,可以使用以下命令:
npm cache clean --force
npm start -- --reset-cache
单独的缓存清理不起作用;你需要reset-cache
,否则问题包仍然会被保存,即使它们实际上不存在!
真的坏掉的升级 - 拆除和替换
然而,即使经过所有这些工作,你的升级仍然不起作用,也不是没有希望。我们可以进行拆除和替换升级。请注意,这有点像“最后的手段”,但它确实效果相当不错。按照以下步骤操作:
- 确保你的
react-vr-cli
包是全局最新的:
[F:\ReactVR]npm install react-vr-cli -g
C:\Users\John\AppData\Roaming\npm\react-vr -> C:\Users\John\AppData\Roaming\npm\node_modules\react-vr-cli\index.js
+ react-vr-cli@0.3.6
updated 8 packages in 2.83s
这很重要,因为当 React 有新版本时,你可能没有最新的react-vr-cli
。它会在你使用它时告诉你有新版本发布了,但那行经常会滚动过去;如果你感到无聊并且没有注意到,你可能会花费很长时间尝试安装更新的版本,但却毫无结果。
npm 会生成大量的文字,但重要的是要阅读它说了什么,尤其是红色格式的行。
确保所有 CLI(DOS)窗口、编辑会话、Node.js 运行的 CLI 等都已关闭。(但是你不需要重新启动;只需关闭使用旧目录的所有内容)。
将旧代码重命名为
MyAppName140
(在旧的react-vr
目录末尾添加版本号)。使用
react-vr init MyAppName
创建应用程序,换句话说,就是原始应用程序名称。下一步最容易使用差异程序(参考
bit.ly/WinDiff
)。我使用 Beyond Compare,但也有其他选择。选择一个并安装它,如果需要的话。比较两个目录,
.\MyAppName
(新)和.\MyAppName140
,看看哪些文件已更改。从旧应用中移动任何新文件,包括资产(您可能可以复制整个 static_assets 文件夹)。
合并任何已更改的文件,除了
package.json
。通常,您需要合并这些文件:
index.vr.js
client.js
(如果您已更改)
对于
package.json
,查看已添加了哪些行,并通过 npminstall <missed package> --save
在新应用程序中安装这些包,或者启动应用程序并查看缺少了什么。删除hello world应用程序种子的任何文件,例如
chess-world.jpg
(除非您当然正在使用该背景)。通常情况下,您不会更改
rn-cli.config.js
文件(除非您修改了种子版本)。
大多数代码将直接移动过来。确保如果更改了目录名称,则更改应用程序名称,但根据前述的指示,您不必这样做。
如果 React VR 有大规模的更改,前述的升级步骤可能会稍微容易一些;这将需要一些对源文件的挑选。源代码非常直接,所以在实践中应该很容易。
我发现如果自动升级没有起作用,这些技术将效果最佳。
进行升级的最佳时间
如前所述,进行重大升级的时间可能不是在发布应用程序之前,除非有一些新功能是必需的。您希望充分测试您的应用程序,以确保没有任何错误。
我在这里包括升级步骤,但不是因为您应该在发布之前立即执行。
准备好发布您的代码
老实说,您永远不应该推迟整理衣服,直到,哦,等等,我们在谈论代码。您不应该推迟整理代码,直到您想要发布的那天晚上。即使您认为是临时的代码,也可能最终投入生产。从一开始就学习良好的编码习惯和风格。
良好的代码组织
从一开始就良好的代码对许多原因非常重要:
如果您的代码缩进混乱,那么阅读起来会更加困难。许多代码编辑器,如 Visual Studio Code,Atom 和 Webstorm,都会为您格式化代码,但不要依赖这些工具。
糟糕的命名约定可能会隐藏问题。
变量的不正确用法可能会隐藏问题,比如使用
this.State
而不是this.state
。编码的大部分时间,高达 80%,都是在维护。如果你看不懂代码,就无法维护它。当你作为一名初学者程序员时,你经常会认为自己永远能读懂自己的代码,但当几年后你拿起一段代码并说“谁写的这破玩意?”然后意识到是你自己时,你就会停止使用像 a、b、c、d 这样的变量名。
大多数软件在某个时候都会被其他人维护、阅读、复制或使用,而不是作者本人。
大多数程序员认为代码标准是给“别人”准备的,但却抱怨自己必须写好代码。那么是谁来做呢?
大多数程序员会立刻要求代码文档,并在找不到时翻白眼。我通常会要求看他们上一个项目的文档。我雇佣的每个程序员通常都会露出茫然的表情。这就是为什么我通常要求代码中有良好的注释。
一个好的注释不是这样的:
//count from 99 to 1
for (i=99; i>0; i--)
...
-
- 一个好的注释是这样的:
//we are counting bottles of beer
for (i=99; i>0; i--)
...
清理绒毛滤网(检查代码标准)
当你洗衣服时,绒毛会堆积,最终会堵塞你的洗衣机或烘干机,或者引发火灾。在 PC 世界中,旧代码、打字不好的名称等也会堆积起来。
重构是清理代码的一种方法。我强烈建议你使用某种版本控制,比如 Git 或 bitbucket 来检查你的代码;在重构时,完全有可能彻底搞砸你的代码,如果你不使用版本控制,可能会丢失很多工作。
在发布之前,对你的工作进行代码审查的一个很好的方法是使用linter。Linter 会检查你的代码并指出问题(垃圾)、不正确的语法,可能与你意图不同的东西,并且通常会尽力整理你的房间,就像你妈妈一样。虽然你可能不喜欢你妈妈这样做,但这些工具是非常宝贵的。毕竟,计算机非常挑剔,为什么不让它们互相对抗呢?
让软件检查你的 JavaScript 代码的最常见方法之一是一个名为ESLint的程序。你可以在这里了解更多信息:bit.ly/JSLinter
。要安装 ESLint,你可以像大多数软件包一样通过 npm 来安装——npm install eslint --save-dev
。
--save-dev
选项在你开发项目时会对你的项目有要求。一旦你发布了你的应用程序,你就不需要将 ESLint 信息与你的项目一起打包了!
还有一些其他事情你需要做才能让 ESLint 正常工作;阅读配置页面并完成教程。很大程度上取决于你使用的 IDE。例如,你可以在 Visual Studio 中使用 ESLint。
安装了 ESLint 之后,你需要配置一个本地配置文件。使用以下命令完成:
eslint --init
。
--init
命令将显示一个提示,询问你如何配置它将遵循的规则。它会问一系列问题,并询问要使用什么样的风格。AirBNB 是相当常见的,尽管你可以使用其他的;没有错误的选择。如果你在公司工作,他们可能已经有标准,所以请向管理层确认。其中一个提示将询问你是否需要 React。
React VR 编码风格
编码风格几乎可以说是宗教,但在 JavaScript 和 React 世界中,一些标准是非常普遍的。AirBNB 在这里有一个很好的、相当受推崇的样式指南:bit.ly/JStyle
。
对于 React VR,一些要考虑的样式选项如下:
变量名的第一个字母使用小写。换句话说,使用 this.props.currentX,而不是 this.props.CurrentX,并且不要使用下划线(这被称为驼峰命名法)。
只有在命名构造函数或类时才使用帕斯卡命名法。
由于你在文件中使用帕斯卡命名法,所以文件名要与类名匹配,因此
导入MyClass from './MyClass'
。
小心 0 与{0}。一般来说,学习 JavaScript 和 React。
始终使用
const
或let
声明变量,以避免污染全局命名空间。避免使用++和–。这对我来说很难,因为我是一个 C++程序员。希望在你读到这篇文章时,我已经在源代码示例中修复了它。如果没有,那就照我说的做,而不是照我做的做!
学习和=之间的区别,并正确使用它们,这对于 C++和 C#程序员来说是新的。
总的来说,我强烈建议你仔细研究这些编码风格,并在编写代码时使用一个代码检查工具:
第三方依赖
为了使您发布的网站/应用程序真正可靠地工作,我们还需要更新package.json
;这是一种“项目”方式,确保 Node.js 知道我们需要特定的软件。我们将编辑"dependencies"
部分以添加最后一行(我加粗的部分,加粗在文本编辑器中不会显示出来,显然!):
{
"name": "WalkInAMaze",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node -e \"console.log('open browser at http://localhost:8081/vr/\\n\\n');\" && node node_modules/react-native/local-cli/cli.js start",
"bundle": "node node_modules/react-vr/scripts/bundle.js",
"open": "node -e \"require('xopen')('http://localhost:8081/vr/')\"",
"devtools": "react-devtools",
"test": "jest"
},
"dependencies": {
"ovrui": "~2.0.0",
"react": "16.0.0",
"react-native": "~0.48.0",
"three": "⁰.87.0",
"react-vr": "~2.0.0",
"react-vr-web": "~2.0.0",
"mersenne-twister": "¹.1.0"
},
"devDependencies": {
"babel-jest": "¹⁹.0.0",
"babel-preset-react-native": "¹.9.1",
"jest": "¹⁹.0.2",
"react-devtools": "².5.2",
"react-test-renderer": "16.0.0",
"xopen": "1.0.0"
},
"jest": {
"preset": "react-vr"
}
}
这是手动的方法;更好的方法是使用npm install <package> -s
。
-s
限定符将安装的新软件包保存在package.json
中。手动编辑可以确保您拥有正确的版本,如果出现版本不匹配的情况。
如果您尝试安装和删除足够多的软件包,最终会搞乱您的模块。如果删除node_modules
后仍然出现错误,请发出以下命令:
npm start -- --reset-cache
npm cache clean --force
仅进行缓存清理是不够的;您需要重置缓存,否则问题软件包仍将被保存,即使它们在物理上不存在!
在网络上发布捆绑
假设您已经正确设置了项目依赖项,以便通过 ISP 或服务提供商从 web 服务器运行项目,您需要“捆绑”它。React VR 有一个脚本,可以将所有内容打包成几个文件。
当然,您的台式机也算是“web 服务器”,尽管我不建议您将开发机暴露在网络上。让其他人体验您的新虚拟现实的更好方法是将其捆绑并放在商业网络服务上。
为在网站上发布 React VR 进行打包
使用 React VR 提供的脚本,基本流程很容易:
- 转到通常运行
npm start
的 VR 目录,并运行npm run bundle
命令:
然后,您将以与通常上传文件相同的方式转到您的网站,并创建一个名为
vr
的目录。在您的项目目录中,在我们的案例中是
f:\ReactVR\WalkInAMaze
,在.\VR\Build
中找到以下文件:
client.bundle.js
index.bundle.js
将它们复制到您的网站上。
创建一个名为
static_assets
的目录。将您的所有文件(您的应用程序使用的文件)从
AppName\static_assets
复制到新的static_assets
文件夹。确保您为所有内容设置了 MIME 映射;特别是,.obj、.mtl 和.gltf 文件可能需要新的映射。请查阅您的 web 服务器文档:
对于 gltf 文件,使用
model/gltf-binary
。任何被 gltf 使用的.bin 文件应为
application/octet-stream
对于.obj 文件,我使用了
application/octet-stream
官方列表位于
bit.ly/MimeTypes
非常一般地说,
application/octet-stream
将以服务器上的文件的“原样”发送,因此这在某种程度上是一种通用的“捕获所有”
将
index.html
从应用程序的根目录复制到您发布应用程序的网站目录中;在我们的情况下,它将是vr
目录,因此该文件与两个.js 文件并列。修改
index.html
的以下行(请注意更改为./index.vr
):
<html>
<head>
<title>WalkInAMaze</title>
<style>body { margin: 0; }</style>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js -->
<script src="./client.bundle?platform=vr"></script>
<script>
// Initialize the React VR application
ReactVR.init(
// When you're ready to deploy your app, update this line to point to
// your compiled index.bundle.js
'./index.vr.bundle?platform=vr&dev=false',
// Attach it to the body tag
document.body
);
</script>
</body>
</html>
请注意,对于生产发布,这意味着如果您指向静态 Web 服务器上的预构建捆绑包而不是 React Native 捆绑器,则开发和平台标志实际上不会起作用,因此dev=true
,dev=false
或甚至dev=foobar
之间没有区别。
获取发布和归属
如果您从网络上的任何地方使用了任何资产,请确保您拥有适当的发布。例如,许多 Daz3D 或 Poser 模型不包括发布几何信息的权利;将这些信息包含在您的网站上作为 OBJ 或 glTF 文件可能会违反协议。某人可以轻松地下载模型,或者几乎所有的几何信息,然后将其用于其他用途。
我不是律师;您应该与您获取模型的地方核实您是否有权限,并在必要时正确归属。
归属许可在 VR 世界中有点困难,除非您将归属信息嵌入到某个图形中;正如我们所见,添加文本有时可能会分散注意力,并且您总会遇到比例问题。如果您在页面中嵌入了一个带有<iframe>
的 VR 世界,您可以在 HTML 端始终给予适当的归属。但是,这并不是真正的 VR。
检查图像大小并使用内容交付站点
您使用的一些图像,特别是<pano>
语句中的图像,可能相当大。您可能需要优化这些图像以获得适当的网页速度和响应性。
这是一个相当一般的话题,但可以帮助的一件事是内容交付网络(CDN),特别是如果您的世界将是高流量的。
将 CDN 添加到您的 Web 服务器很容易。您可以从一个单独的位置托管您的资产文件,并在ReactVR.init()
调用时将根目录作为 assetRoot 传递。例如,如果您的文件托管在https://cdn.example.com/vr_assets/
,您可以更改index.html
中的方法调用以包括以下第三个参数:
ReactVR.init(
'./index.bundle.js?platform=vr&dev=false',
document.body,
{ assetRoot: 'https://cdn.example.com/vr_assets/' }
);
优化您的模型
在第十章中,引入真实世界,我们建立了一个迷宫,迷宫的每个方块都重复使用了相同的灌木模型。如果您观察了 Web 控制台,您可能会注意到这个模型一遍又一遍地被加载。这并不一定是最有效的方法。考虑其他技术,比如将模型作为属性传递给各种子组件。
多边形减少是另一种在优化 Web 和 VR 模型中非常有价值的技术。使用 glTF 文件格式,您可以使用“法线贴图”,仍然使低多边形模型看起来像高分辨率模型。这些技术在游戏开发领域得到了很好的记录。这些技术确实非常有效。
您还应该优化模型以不显示看不见的几何图形。例如,如果您显示一个有黑色窗户的汽车模型,就没有必要加载发动机细节和内部细节(除非窗户是透明的)。这听起来很明显,尽管我发现我用来说明照明示例的灯几乎比需要的多了两倍的多边形数量;玻璃灯罩内外都有多边形在模型内部。
既然我们已经发布了,接下来呢?
一旦我们建立了我们的现实,我们该怎么办?我们该去哪里?
如果您没有某种 VR 体验的想法,您就不会读这本书。我鼓励您去玩耍和尝试。您甚至可以尝试一些您听说过的不好的东西(比如移动视角)。
当 VR 再次爆发时,我曾经感到有些不满;我在之前的 VR 时代所做的大部分事情似乎已经被遗忘了;人们认为“VR 用户界面是新的!”。而与此同时,VR 学术文献实际上已经有几十年的历史,讨论了感知效果和 VR 用户界面,仅仅是 VR 的一个领域。
然而,这并不是一件坏事。如果涌入 VR 的人们能够提出一些新鲜的想法,也许那将是每个人都在寻找的“杀手级应用程序”。
也许你可以成为这样的人!我希望你能。
React VR 是一个轻量级的、支持 VR 的渲染系统。仍然有很多东西可以添加。
物理学-使世界相互作用
现实世界中有物体移动并相互作用。编写这种交互可能会变得乏味;这就是一个好的物理包可以胜任的地方。
简而言之,如果您的物体具有真实世界的物理特性,它们看起来会更加真实。
我看到的许多物理演示都是弹跳球类型的演示,显示物体飞来飞去并撞击物体。我认为对物理的更微妙的方法,尽管准确(基于物理引擎),将在 VR 世界中给人一种逼真感,这是不应该被忽视的。
有几个 JavaScript 物理引擎:Cannon.JS和Oimo.js。
Cannon.JS 是一个刚体物理引擎,包括简单的碰撞检测、各种物体形状、接触、摩擦和约束。源代码和文档在:bit.ly/CannonJS
。
碰撞检测算法本身就足以使用这些包之一,即使您不预见编写保龄球游戏或向砖墙射击球体。例如,碰撞检测可用于确定虚拟化身是否可以在 VR 世界中导航到特定位置。
涵盖 React VR 和 Cannon.js 的博客文章在:bit.ly/ReactPhysics
。
Oimo.js 是一个类似的刚体物理引擎;可以在:bit.ly/OmioPhysics
找到。
请注意,Oimo.js 的示例显示本机的 three.js 单位,比 React VR(通常为 10 到 1,000)小。在 React VR 中,单位更多或少为 1=1 米,因此 Oimo.js 将集成得相当顺畅。
游戏引擎-让您与其他人互动
有一些网站可以打包 TCP/IP 代码以启用多人游戏。只要它们使用 JavaScript,React VR 就可以很好地集成到其中。其中一个引擎是Lance.gg,位于:bit.ly/Lance_gg
。
这是一个基于节点的游戏服务器。它是为了让 JavaScript 开发人员能够构建实时在线多人游戏而创建的,而不必担心实现网络同步代码。它致力于为开发人员和玩家提供流畅的体验,而不受延迟的影响。它具有以下功能:
Lance 负责网络代码,因此我们可以专注于 VR 部分
可以支持任何类型的游戏或流派
优化网络
通过 Websockets 的 TCP
通信被打包并序列化为二进制
网络尖峰的自动处理和步骤校正
用于处理延迟的智能同步策略
外推(客户端预测)与步骤重演
用于最佳对象运动的插值
用于调试和跟踪的工具
货币化 VR
有几种方法可以从 VR 中赚钱。这是一种非常新的艺术形式,许多应用程序仍在被发现。像任何新领域一样,那里有很多实验或资助的亏损项目。主要的硬件制造商甚至通过资助开发人员来建立软件生态系统。
目前是“硬件先行还是杀手级应用程序先行”的情况。头戴式显示器的销售额强劲,尽管移动头戴式显示器绝对领先,三星 GearVR、Google Daydream 和 Cardboard 头戴式显示器的销量几乎是 Rift 和 Vive 等高端型号的近十倍。PSVR 是一个不错的系统,但对于不属于游戏工作室的开发人员来说,可能很难获得访问权限。
但是,不要忘记,您甚至不需要 VR 头盔就可以欣赏 React VR。您所编写的大部分代码将在浏览器中查看,而无需进入完整的 VR 模式,许多观众可能也会选择这样做。即使没有 VR 设备,将 VR 窗口嵌入网站仍将为人们带来引人入胜的体验。
在利用 VR 进行货币化方面,React VR 可能具有优势,至少在广告方面是这样。
赚取 VR 的方式如下:
销售应用程序:要在网站上实现这一点,您可能需要实施某种付费观看系统。大多数人不喜欢网站周围的付费墙,因此这可能最适合完整的 VR 游戏,例如使用 Unity 和 Unreal 构建的游戏。但是,让我们不要忽视这一点。
被付费构建 VR 体验:大多数搜索 VR 广告的网络搜索结果将显示出这些类型的应用程序。它们几乎总是免费下载,并且通常非常引人入胜。然而,在我看来,这不是 VR 生产者的增长模式;您可以获得相当不错的报酬来开发 VR 应用程序,但一旦完成,您的收入流也就结束了。我认为,发布与电影相关内容的 VR 网站的工作室版本基本上是相同的概念,我认为 React VR 非常适合这个领域。
在你的世界中嵌入 VR 广告:React VR 的一个优势是它可以访问 React 本身的所有布局可能性,因此对于 React VR 来说,这可能比任何其他 VR 系统更容易和更直接。一个挑战是,沉浸在 VR 世界中的人不喜欢分心,因此弹出广告可能会产生相反的效果。不过,在你的世界中进行产品放置或者广告牌可能会非常有效,特别是如果你正在构建某种虚拟城市。
从 VR 世界内部链接到产品或网站:这对于常规网站来说效果很好,尽管对于 VR 来说,挑战在于你可能需要摘下 VR 头盔才能真正与你在 VR 世界中刚刚点击的内容进行交互。一些广告公司已经讨论过在 VR 广告世界中构建链接,你可以从 VR 世界中跳转到这些链接,尽管到目前为止,大多数都是针对传统游戏引擎的。不过,我们可以期待未来在这一领域的大量发展。
出售元数据:在这种模式下,应用本身将是免费的,但 VR 开发者将启用凝视跟踪。VR 世界内的热点区域可以以与更平面的 HTML 模型相同的方式出售给广告商,就像点击或印象一样。这是另一个新兴标准领域。
从演示版本到完整版本:你可以使用从完整游戏引擎中获取的模型和资产构建一个 React VR 世界,然后将其放在网页上作为联系或免费赠品,以吸引人们对你的完整 VR 应用感兴趣。拥有行业标准格式,如 OBJ 和 glTF,将有助于实现这一点,尽管很多逻辑将不得不从头开始开发。一个可能更好的方法是在付费墙后面拥有一个免费的网页 URL 和一个付费的 URL。
应用内购买:这将是游戏中的事物触发游戏外购买的一种方式。例如,视频播放器可以请求支付特定的视频,然后 React VR 代码将播放该视频。由于 React VR 具有直接集成 JavaScript 的简单方式,应用内购买将会相当简单。
VR 在未来五年将会走向何方
如果我确切地知道 VR 在未来五年会走向何方,那么五年后我会变得富有,如果你投资我的预测,你也会。让我知道结果如何。
我希望你通过开发令人惊叹的世界来投资 VR,即使不是通过 React VR。我真的相信 VR,并希望这一次它能取得成功。
这就提出了我所说的“这个时候”的意思。我至少经历过一次 VR 的浪潮;那时,每个人都认为 VR 是未来的浪潮。关于 VR 的所有伟大事物,我以前都听过。我大约在 1995 年到 2000 年左右开始接触 VR。VR 遭遇了严重的崩溃。当时有人在制造头戴式显示器、数据手套和整个虚拟世界。
大多数人说当时的图形太粗糙了。这有一些道理,但是那些还没有尝试过 VR 的人,仍然说“我会等到图形更真实”而没有意识到,正如我们已经讨论和看到的,VR 并不需要出色的图形才能显得“真实”。
我们所谓的 VR 也有一些不同。那时,似乎很久以前,在 PC 上的任何 3D 程序都被宽泛地称为 VR。我甚至在 VRML 语言被发明时(现在是 X3D),预测我们需要把所有这些搞对,否则人们会走到来自世界各地的陌生人面前,在美丽的 3D 环境中(虚拟地)杀死他们。
在某种程度上,我是对的;例如,在魔兽世界中,这正是发生的事情。现代 VR 之所以拥有一切,都是因为电脑游戏。所有那些击杀游戏已经创造了对廉价、高质量视频卡的需求。你可能认为高端视频卡并不便宜,但与最初的 VR 硬件相比,它们非常便宜。
屏幕上的 3D 电脑游戏不是我们今天所说的 VR,这是真的。无论电脑游戏看起来多么出色,都不如基于 HMD 的 VR 体验那样沉浸。
我担心 VR 可能会再次崩溃;然而,我不认为这次会发生。这次的不同之处在于,我们都在口袋里拥有高性能的 VR 设备——我们的手机。现代手机和 Silicon Graphics 的 10 万美元的 Reality Engine 一样强大。我们可以把手机放入一个简单、便宜的 VR 头盔中看 VR。它可能不像带有跟踪控制器的设备那样互动,但它可以工作。蓝牙控制器不会消失,手机也不会消失。
因此,VR 是来留下的,但它将走向何方呢?
不要等待明年的技术
很久以前,当我买我的第一个音响时,我 20 岁,从大学暑假工作中赚了一大笔钱,我真的很想要一个音响。我花了几个星期研究规格,听力测试,比较功能,总的来说,我对选项有点犹豫不决。
我带着一大堆钱到立体声商店,想买点东西。
我去了一家商店,问销售员他有什么立体声系统。
“哦,我有明年的型号,”他说。
什么?我想。是不是真的有什么很棒的东西要出来?
“哦,那你在等什么?”我问。“有什么要出来的吗?”
“哦,明年我也会等待明年,”他说。“你看,明年总是明年,所以我从来没有过时的立体声,”他说。他根本就没有买过立体声。
我理解他的观点。你可能会想,等它变得更好。
我想说的是,在过去的 30 年里,我听过的音乐比那个销售员听过的要多得多。
你不应该等待购买下一件伟大的 VR 设备,现在就开始。如果有新的东西出来,你会比等待更长时间更享受 VR,并且你会更了解自己喜欢什么。现在没有任何错误的决定。
更好的 HMD
我们今天拥有的 HMD,比如 HTC Vive、Oculus Rift、PS VR,以及像三星 Gear VR、Google Daydream 和 Google Cardboard 这样的基于移动设备的 VR 设备,都相当不错,但它们可以做得更好。
人眼实际上无法与像素显示进行比较,尽管我们确切知道我们看到的细节比任何现有的 HMD 都要多。我们还有更宽的视野。
当前的显示屏大致相似;1,080 x 1,200 像素,视野约为 110 度。这给我们大约每度 10 到 15 像素。人眼在十英寸处可以看到从 500 到约 1,000 像素每英寸;这给我们大约 90 到 177 像素每度。这将意味着一个近 20,000 或 40,000 像素的显示屏。我们谈论的不是百万像素,而是十亿像素。
我们能在 5 年内达到那个水平吗?这是一个非常高的分辨率,但我认为我们的分辨率至少会比现在好 4 到 16 倍。
HMD 可能会更舒适。由于光学问题,我不确定它们会变得更小,尽管在 10 到 15 年内,我预计它们的大小将与隐形眼镜或眼镜相同。这只是一个非常粗略的猜测。
更好和更逼真的图形
显卡的速度和价格都在不断提高。在五年内,我们将需要每一点处理能力来生成与现在相同视觉复杂度的显示屏,不是更好看的,因为像素增加了。换句话说,随着显示屏像素的增加,使得显示更好更宽,驱动这些显示所需的处理能力也会增加,因此视觉保真度的增加并不像你期望的那样多。这个隧道的尽头有光吗?还是隧道尽头有一列朝着另一个方向行驶的火车?有好消息,那就是凹凸渲染。为此,我们需要眼球追踪,我们稍后会讨论。
无论是眼球追踪和凹凸渲染,我们会看到更加逼真的渲染和更加逼真的人物形象。会有显著的改善吗?会看起来真实吗?如果你还记得我们书的开头,图形不需要看起来像现实生活才能显得真实,所以虽然我期待更好的图形,但我不认为 VR 依赖于它们。
更容易的内容创作和更高端的内容
到目前为止,我们只讨论了 VR 中的技术变化。我认为还会发生很多事情,这些事情将对 VR 产生更大的影响。无论图像是如何生成的,声音、外观甚至感觉,我们都需要所谓的“杀手级应用”。现在,大部分赚钱的 VR 都是游戏。一旦更大的游戏开发工作室看到 VR 的未来,他们将开发更多 AAA 游戏(AAA 游戏意味着预算庞大,通常是数十亿或数亿美元,团队庞大,包括艺术家、开发人员、设计师、作者和项目经理)。在这本书出版后不久,将有三款 AAA 游戏推出:《毁灭战士 VR》、《辐射 4 VR》和《上古卷轴 VR》。一旦我们有了数十甚至数百小时的大型游戏,如果它引人入胜(我相信它会非常引人入胜),我们将看到 VR 腾飞。
在接下来的五年里,我认为这肯定会进一步发展。我期待看到大型持久世界,大型多人在线角色扮演游戏(MMORPGS)成为 VR 的主打。你不再是在《魔兽世界》中玩游戏,而是在《魔兽世界》中。哇!(双关语)
在内容方面,我们还将看到更多高端的创作软件也在 VR 中。目前,诸如 Max、Maya 和 Blender 等更复杂的创作工具制作了我们在 VR 中看到的最好的模型,但它们本身是传统的应用程序。今天,我们看到一些应用程序,比如Tilt-brush,让我们在 VR 中设计物体。我预计更高端的 CAD 也将拥有普遍的 VR 模式。
HMD 在一段时间后可能会变得炎热、沉重和令人疲劳(主要是由于之前讨论的聚散调节冲突)。我并不认为所有的工作都会在 VR 中完成,但它将在初始阶段以及检查模型时有所帮助。
哪种更快:在屏幕上看两个物体并用鼠标拖动它们,还是直接用手抓住它们并移动?想象一下用魔法泥塑塑造物体,而且还有一个“撤销”命令。你无法在真实的黏土上做到这一点,但在 VR 中你将能够做到。
眼动追踪
我之前提到的眼动追踪是什么?它是 HMD 内部的传感器,用来追踪你的眼球注视的位置。这种扫描有一些优势,主要是社交和渲染方面。
在我看来,社交优势是巨大的。当你看着别人的头像时,这是另一个人在游戏中的代表,他们可能看起来非常僵硬和没有表情。人类从眼睛中感知到大量的情感;通过眼动追踪,你的头像可以展现出一些这些表情。一个简单的测试显示,通过简单地放置卡通眼球——只是一个白色球中的黑点——并让这些眼睛随着用户的注视而移动,可以显著改善。即使是卡通形象,头像看起来更真实。
眼动追踪的渲染改进,如之前简要提到的,是一种称为凹凸渲染的渲染类型。通过眼动追踪,我们可以只显示你正在注视的部分高清细节,而不是填充每个像素都是高细节对象。眼睛的其余部分通常没有那么多细节。我们眼睛中的视杆和锥体在中心密集分布,在眼睛的外缘密度要低得多。凹凸渲染利用这一点。
这真的很有效,可以显著加快图形处理速度;你不需要计算你不显示的部分。这是在 2016 年的年度图形专业兴趣小组(SIGGRAPH)会议上首次展示的。
音频改进
大多数人在谈到 VR 音频时,担心头戴设备的音效效果如何,或者当你坐在沙发上而不摘下头戴设备时,是否会屏蔽掉猫尖叫的声音。(希望不会!)然而,VR 音频还有很多其他方面;我们的耳朵可以用两个传感器惊人地确定声音来自何处(通常,需要三个传感器才能检测到任何 3D 声源的距离和方向)。我们的耳朵是如何做到的呢?这是通过一种叫做 HRTF 的东西完成的。目前,我们确实有技术可以实时计算 HRTF。然而,确定要计算哪个HRTF 并不那么容易。这并不奇怪;人们是独一无二的。
每个人的 HRTF 都是不同的;如果我们播放使用我的 HRTF 生成的声音,我闭上眼睛会听到声音好像直接来自设计者打算的位置。如果你用同样的头戴设备听到同样的声音,你可能会觉得它听起来是假的,或者来自不同的方向。这只是我们处理声音的现实。
目前有一些解决方案——要么在专门设计用于计算这一点的房间里测量你的 HRTF,要么可能使用头戴设备中的额外扬声器。时间会告诉我们这些技术中哪一种会是最好的;这仍然是一个开放的领域。
除了用 HRTF 计算来定位(确定)声音,我们需要更多更好的软件方式来定位那个声音。
我认为在五年内,我们用于虚拟世界的物理系统可能能够产生适当的音效,而不仅仅是播放预先录制的声音。例如,如果你撞到了一堵混凝土墙,也许软件可以生成沉闷的撞击声,位于正确的位置并发出正确的声音,考虑到墙有多厚以及其他参数。目前,我们只是在正确的位置播放预先录制的声音(这仍然很惊人,但可以有所改进)。这是我想要看到的东西。
控制 VR
当今的控制器通常由两个手持设备组成,带有多个按钮。大多数都有一种简单的机械方式通过软件来振动。创新的可能性非常巨大。VR 行业已经在几十年里致力于研发复杂的设备,可以实现更好的触觉反馈、更小、更精确的控制器和数据手套。当你可以伸手去抓东西时,为什么还要使用控制器呢?
触觉是涉及触摸的任何互动。这不仅意味着触摸或握住鼠标;它可以是任何感觉到当你移动控制器时的点击或震动(目前的技术水平),但它也可以是一种在你移动时产生阻力的设备。这些已经被演示多年,也有商用现成产品可以做到让你感受到表面。这些将变得更常见,成为更大规模生产的消费级设备。
数据手套是您戴在手上的设备,可以跟踪 VR 中每个手指关节和动作。它们已经问世几十年了。甚至有一些系统(Leap Motion 设备)可以让你用真实的手伸出来,并在虚拟世界中进行互动。我相信这是未来几年潜力巨大的领域。
全身控制器将允许您的整个虚拟身体在 VR 世界中被视觉呈现、准确地跟踪。这是另一个潜力巨大的领域。甚至有一些原型套装可以提供全身触觉。想象一下穿上一套衣服,能够感受和触摸世界,而不仅仅是在图像中挥舞魔杖。
社会和法律问题及解决方案
如果你可以把一个虚拟雕像放在公共空间中,那算不算破坏公物?在 AR 或 VR 中在商业建筑的一侧绘画呢?起初,你可能会认为这是完全可以的;我自己可能会想看到一个科幻模型在公园中央,人们可以与之互动。如果那个模型是一个赞成奴隶制的南方战争英雄呢?如果不满的邻居在虚拟空间中用诽谤性的指控标记你的房子呢?
在社交 VR 中,我们可以以一种在现实生活中不可能的方式骚扰人。如果有人进入您的空间,您可以把他们推开,但在 VR 中,不良行为可能会非常容易。VR 环境将需要考虑这些问题。
如果我们有防止虚拟亵渎的保护措施,我们如何决定什么是对的,什么是错的,尤其是在公共空间?VR 可能类似于现实世界——诽谤就是诽谤,但 VR 也提供了一些独特的可能性。如果你是一个南方绅士,你可能想看到叛军雕像,你可以。你旁边的年轻人可能会看到乔治·华盛顿·卡弗。我们都可以和睦相处;或者我们可以吗?如果你旁边的人想展示化学尾迹沉积呢?
我确实认为持久的虚拟世界将蓬勃发展,并且我们将针对先前的问题提出创新和有趣的解决方案。只要记住,许多不喜欢技术或普遍害怕技术的人并不理解它。如果我们可以创造任何我们想要的世界,我们只需要想要创造有价值的世界。
请务必!
总结
在本章中,我们学习了如何将我们的现实释放到互联网上。具体来说,我们讨论了如何进行版本升级,以及如果需要的话,如何进行rip and replace升级。我们讨论了何时进行升级。我们真的应该更早讨论编码标准,因为现在开始永远不会太早,但首先我们必须掌握一些 React VR 语法,所以在我们将代码发布到世界之前我们先进行了讨论。我们进一步讨论了如何使用 ESLint 和其他代码检查工具来帮助您编写良好的代码。一旦您有了良好的干净代码,我们还讨论了如何将您的开发 React VR 内容打包到网络上,以及如何通过优化和内容交付网络(CDN)使其快速。
我们讨论了如何通过物理学进一步使您的网站真实(通过物理学),玩游戏以及如何实现盈利。
您现在已经知道了在网络上使用 React VR 所需学习的一切。我期待着看到您创造出什么!