- 原文地址:Minecraft in WebVR with HTML Using A-Frame
- 原文作者:Kevin Ngo
- 譯者:Felix
- 校對:阿希
我是 Kevin Ngo,一名就職於 Mozilla VR 團隊的 web 虛擬現實開發者,也是 A-Frame 的核心開發人員。今天,我們來看看如何使用 A-Frame 構建一個夠在 HTC Vive、Oculus Rift、Samsung GearVR、Google Cardboard、桌面裝置以及移動裝置上執行的、支援空間追蹤(room-scale)技術的 WebVR 版《我的世界》示例。該示例基於 A-Frame,且僅使用 11 個 HTML 元素!
A-Frame
幾年前,Mozilla 發明並開發了 WebVR —— 一套在瀏覽器中創造身臨其境 VR 體驗的 JavaScript API —— 並將其釋出在一個實驗版本的 Firefox 瀏覽器中。此後,WebVR 得到了 Google、Microsoft、Samsung 以及 Oculus 等其他公司的廣泛支援。而現在,WebVR 更是在短短几個月內就被內嵌在發行版的 Firefox 瀏覽器中,並被設定為預設開啟!
為什麼會誕生 WebVR?Web 為 VR 帶來了開放性;在 Web 上,內容並不由管理員所控制,使用者也不被關在高高的圍牆花園(walled garden)中。Web 也為 VR 帶來了連通性;在 Web 上,我們能夠在世界中穿梭 —— 就像我們點選超連結在頁面見穿梭一樣。隨著 WebGL 的成熟以及諸如 Web Assembly 和 Service Workers 規範的提出,WebVR 已經準備好了。
Mozilla VR 團隊創造了 A-Frame 框架來為 WebVR 生態系統拋磚引玉,該框架給予開發者構建 3D 和 VR 世界的能力。
A-Frame 官方網站首頁
A-Frame 是一個構建虛擬現實體驗設的 web 框架,它基於 HTML 和實體元件正規化(the Entity-Component pattern)。HTML 是所有計算機語言中最易理解的語言,這使得任何人都能快速上手 A-Frame。下面是一個使用 HTML 搭建的完整的 3D 和 VR 場景,它能夠在諸如桌面裝置和移動裝置等任何 VR 平臺執行:
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
<a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
複製程式碼
就是這樣!只用使用一行 HTML()即可搞定 3D 和 VR 樣板程式碼搭建,包括:canvas、場景、渲染器、渲染迴圈、攝像機以及 raycaster。然後,我們可以通過使用新增子元素的方式來為場景新增物件。無需構建,就只是一個簡單的、可隨意拷貝貼上的 HTML 檔案。
我們還可以動態查詢和操作 A-Frame 的 HTML,就像使用標準 JavaScript 和 DOM APIs (例如 querySelector、getAttribute、addEventListener、setAttribute)那樣。
// 使用 `querySelector` 查詢場景影像。
var sceneEl = document.querySelector('a-scene');
var boxEl = sceneEl.querySelector('a-box');
// 使用 `getAttribute` 獲得實體的資料。
console.log(box.getAttribute('position'));
// >> {x: -1, y: 0.5, z: -3}
// 使用 `addEventListener` 監聽事件。
box.addEventListener('click', function () {
// 使用 `setAttribute` 修改屬性。
box.setAttribute('color', 'red');
});
複製程式碼
而且,因為這些只是 HTML 和 JavaScript,因此 A-Frame 和許多現存的框架和庫相容良好:
相容 d3、Vue、React、Redux、jQuery、Angular
儘管 A-Frame 的 HTML 看起來比較簡單,但是 A-Frame 的 API 卻遠遠比簡單的 3D 宣告強大。A-Frame 是一個實體元件系統(ECS)框架,ECS 在遊戲開發中是一種流行的模式,值得注意的是 ECS 也被 Unity 引擎所使用。其概念包括:
- 在場景中,所有的物件都是實體(entities),空物件本身什麼也不能做,類似空
<div>
。A-Frame 使用 HTML 元素在 DOM 中表示實體。 - 接下來,我們在實體中插入元件(components) 來提供外觀、行為和功能。在 A-Frame 中,元件被註冊在 JavaScript 中,並且可以被用來做任何事情。它們可使用完整的 three.js 和 DOM APIs。元件註冊後,可以附加在 HTML 實體上。
ECS 的優勢在於它的可組合性;我們可以混合和搭配這些可複用的元件來構建出更復雜的 3D 物件。A-Frame 更上一層樓,將這些元件宣告化,並使其作為 DOM 的一部分,就像我們待會在《我的世界》示例中看到那樣。
示例骨架
現在來關注我們的示例。我們將搭建一個基本的 VR 立體畫素製作器(voxel builder),它主要用於支援位置追蹤(positional tracking)和追蹤控制器(tracked controllers)的空間追蹤 VR 裝置(例如 HTC Vive 及 Oculus Rift + Touch)。
我們會從 HTML 骨架開始。如果你想要快速瀏覽(完整的 11 行 HTML),點選這裡到 GitHub 檢視原始碼。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<body>
<a-scene>
</a-scene>
</body>
複製程式碼
新增地面
<a-plane>
和 <a-circle>
都是常被用作新增地面的圖元,不過我們會使用 <a-cylinder>
來更好地配合控制器完成燈光計算工作。圓柱(cylinder)的半徑為 30 米,待會我們要新增的天空將會和這個半徑值匹配起來。注意 A-Frame 中的單位是米,以匹配 WebVR API 返回的現實世界中的單位。
地面的紋理部署在 https://cdn.aframe.io/a-painter/images/floor.jpg
。我們將紋理新增進專案中,並使用該紋理製作一個扁的圓柱實體。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<a-cylinder id="ground" src="https://cdn.aframe.io/a-painter/images/floor.jpg" radius="32" height="0.1"></a-cylinder>
</a-scene>
複製程式碼
預載入資源
通過 src
屬性指定的 URL 資源將在執行時載入。
由於網路請求會對渲染的效能產生負面影響,所以我們可以預載入紋理以保證資源被下載完成前不進行渲染工作,預載入可以通過資源管理系統(asset management system)來完成。
我們將 <a-assets>
置入 <a-scene>
中,將資源(例如圖片、視訊、模型及聲音等)置入 <a-assets>
中,並通過選擇器(例如 #myTexture)將資源指向我們的實體。
讓我們將地面紋理移動到 <a-assets>
中,使用 <img>
元素來預載入它:
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
</a-assets>
<a-cylinder id="ground" src="#groundTexture" radius="32" height="0.1"></a-cylinder>
</a-scene>
複製程式碼
新增背景
讓我們使用 <a-sky>
元素為 <a-scene>
新增一個 360° 的背景。<a-sky>
是一個在內部貼上材質的巨大 3D 球體。就像普通圖片一樣,<a-sky>
可以通過 src
屬性接受圖片地址。最終我們將可以使用一行 HTML 程式碼實現身臨其境的 360° 圖片。稍後你也可以在 Flickr 球面投影圖片池(需翻牆)中選擇一些 360° 圖片來做練習。
我們可以新增普通的顏色背景(例如 <a-sky color="#333"></a-sky>
)或漸變,不過這次讓我們來新增一張背景紋理圖片。該圖片被部署在 https://cdn.aframe.io/a-painter/images/sky.jpg
。我們所使用的圖片是一張適用於半球體的圖片,所以首先我們需要將剛剛的球體使用 theta-length="90"
水平截成半球體,另外我們將球的半徑設定為 30 米以匹配地面。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
<img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
</a-assets>
<a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
<a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>
</a-scene>
複製程式碼
新增體素
在我們的 VR 應用中,體素(voxels)的寫法類似 <a-box>
,但會新增一些自定義的 A-Frame 元件。不過讓我們先大致瞭解實體-元件正規化,來看看像 <a-box>
這樣的圖元是怎樣合成的。
在這個部分,我們將會對若干 A-Frame 元件的實現做一些深入探討。在實踐中,我們經常會通過已由 A-Frame 社群開發人員編寫好的 HTML 來使用元件,而不是從頭構建它們。
實體-元件正規化
在 A-Frame 場景中的每一個物件都是 <a-entity>
,其本身什麼也不能做,就像一個空 <div>
一樣。我們將元件(不要和 Web Components 或 React Components 混淆)插入實體來給予其外觀、行為和邏輯。
對於一個盒子來說,我們會為其配置及新增 A-Frame 的基礎幾何元件和材質元件。元件使用 HTML 屬性來表示,元件屬性預設使用類似 CSS 樣式的表示方法來表示。下面是一個 <a-box>
的基礎元件拆解寫法,可以看到 <a-box>
事實上包裹了若干元件:
<a-box color="red" depth="0.5" height="0.5" shader="flat" width="0.5"></a-box>
<a-entity geometry="primitive: box; depth: 0.5; height: 0.5; width 0.5"
material="color: red; shader: standard"></a-entity>
複製程式碼
使用元件的好處是它們的具有可組合性。我們可以通過混合和搭配一堆已有的元件來構造出各種各樣的物件。
在 3D 開發中,我們可能構建出的物件型別在數量和複雜性上是無限的,因此我們需要一個簡便的、全新的、非傳統繼承式的物件定義方法。與 2D web 相比,我們不再拘泥於使用一小撮固定的 HTML 元素並將它們巢狀在很深的層次結構中。
隨機顏色元件
A-Frame 中的元件由 JavaScript 定義,它們可使用完整的 three.js 和 DOM APIs,它們可以做任何事。所有的物件都由一捆元件來定義。
現在將剛剛所描述的模式付諸實踐,通過書寫一個 A-Frame 元件,為我們的盒子設定隨機顏色。元件通過 AFRAME.registerComponent 註冊,我們可以定義 schema(元件的資料)以及生命週期方法(元件的邏輯)。對於隨機顏色元件,我們並不需要設定 schema,因為它不能被配置。但我們會定義一個 init
處理函式,該函式會在元件首次附加到它的實體時被呼叫。
AFRAME.registerComponent('random-color', {
init: function () {
// ...
}
});
複製程式碼
對於隨機顏色元件,我們的意圖是為其附加的實體設定隨機顏色。在元件的方法中,可以使用 this.el
訪問實體的引用。
為了使用 JavaScript 來改變顏色,我們使用 .setAttribute()
來設定材質元件的顏色屬性。A-Frame 只引入了少數 API,大多數 API 和原生 web 開發 API 保持一致。點此詳細瞭解如何在 A-Frame 中使用 JavaScript 和 DOM API。
我們還需要將 material
元件新增到預先初始化元件列表中,以保證材質不會被 material
元件覆蓋掉。
AFRAME.registerComponent('random-color', {
dependencies: ['material'],
init: function () {
// 將材質元件的顏色屬性設定為隨機顏色
this.el.setAttribute('material', 'color', getRandomColor());
}
});
function getRandomColor() {
const letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
複製程式碼
在元件被註冊後,我們可以直接使用 HTML 來連結該元件。A-Frame 框架中的所有程式碼都是對 HTML 的擴充套件,而且這些擴充套件可以用於其他物件和其他場景。很棒的是,開發者可以寫一個向物件新增物理元素的元件,使用這個元件的人甚至不會察覺到 JavaScript 在他的場景中加入了這個物理元素!
注意力回到剛剛的盒子實體,將 random-color
作為 HTML 屬性插入到 random-color
元件中。我們將元件儲存為一個 JS 檔案,然後在場景程式碼之前引用它:
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>
<a-scene>
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
<img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
</a-assets>
<a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>
<a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
<!-- 隨機顏色的盒子 -->
<a-entity geometry="primitive: box; depth: 0.5; height: 0.5; width 0.5"
material="shader: standard"
position="0 0.5 -2"
random-color></a-entity>
</a-scene>
複製程式碼
元件可以插入到任何實體中,但並不需要像在傳統繼承模式中那樣建立或擴充套件類。如果我們想在類似 <a-shpere>
或 <a-obj-model>
中附加元件,直接加就是了!
<!-- 在其他實體上重用並附加隨機顏色元件 -->
<a-sphere random-color></a-sphere>
<a-obj-model src="model.obj" random-color></a-obj-model>
複製程式碼
如果我們想要將這個元件分享給他人使用,也沒問題。我們可以在 A-Frame 倉庫中獲取 A-Frame 生態系統中許多便利的元件,這類似 Unity 的 Asset Store。如果我們使用元件開發應用程式,那麼就應當保證我們的程式碼在內部是模組化和可重用的!
對齊元件
我們將使用 snap
元件來將盒子對齊到網格以避免它們重疊。我們不會深入到該元件的實現原理,不過你可以看看 snap 元件的原始碼(20 行 JavaScript 程式碼)。
將 snap 元件附加到盒子實體上,讓盒子每半米對齊,同時使用 offset 來使盒子居中:
<a-entity
geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
material="shader: standard"
random-color
snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-entity>
複製程式碼
現在,我們有了一個由一捆元件構成的盒子實體,該實體可以用來描述我們場景中的所有體素(磚塊)。
Mixins
我們可以建立 mixin 來定義可複用的元件集合。
與使用 <a-entity>
為場景新增一個物件不同,我們使用 <a-mixin>
來建立可複用的體素,使用它們就像使用預設實體一樣。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/snap.js"></script>
<a-scene>
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
<img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
<a-mixin id="voxel"
geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
material="shader: standard"
random-color
snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-mixin>
</a-assets>
<a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>
<a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
<a-entity mixin="voxel" position="-1 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 1 -2">
<a-animation attribute="rotation" to="0 360 0" repeat="indefinite"></a-animation>
</a-entity>
<a-entity mixin="voxel" position="1 0 -2"></a-entity>
</a-scene>
複製程式碼
隨後我們使用 mixin 新增了若干體素:
<a-entity mixin="voxel" position="-1 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 0 -2"></a-entity>
<a-entity mixin="voxel" position="0 1 -2">
<a-animation attribute="rotation" to="0 360 0" repeat="indefinite"></a-animation>
</a-entity>
<a-entity mixin="voxel" position="1 0 -2"></a-entity>
複製程式碼
接下來,我們將通過使用追蹤控制器根據使用者互動來動態建立體素。讓我們開始向程式中新增一雙手吧。
新增手部控制器
新增 HTC Vive 或 Oculus Touch 追蹤控制器非常簡單:
<!-- Vive -->
<a-entity vive-controls="hand: left"></a-entity>
<a-entity vive-controls="hand: right"></a-entity>
<!-- Rift -->
<a-entity oculus-touch-controls="hand: left"></a-entity>
<a-entity oculus-touch-controls="hand: right"></a-entity>
複製程式碼
我們將使用抽象的 hand-controls
元件來同時相容 Vive 和 Rift 的控制,它提供基本的手模型。左手負責移動位置,右手負責放置磚塊。
<a-entity id="teleHand" hand-controls="left"></a-entity>
<a-entity id="blockHand" hand-controls="right"></a-entity>
複製程式碼
為左手新增瞬移功能
我們將為左手增加瞬移的能力,當按住左手控制器按鈕時,從控制器顯示一條弧線,鬆開手時,瞬移到弧線末端的位置。在此之前,我們已經自己寫了一個實現隨機顏色的 A-Frame 元件。
但也可以使用社群中已有的開源元件,然後直接通過 HTML 使用它們!
對於瞬移來說,有一個來自於 @fernandojsg 的瞬移控制元件。遵循 README,我們使用 <script>
標籤引入 teleport-controls
元件,並將其附加到控制器實體上。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>
<!-- ... -->
<a-entity id="teleHand" hand-controls="left" teleport-controls></a-entity>
<a-entity id="blockHand" hand-controls="right"></a-entity>
複製程式碼
隨後我們來配置 teleport-controls
元件,將瞬移的 type
設定為弧線。預設來說,teleport-controls
的瞬移只會發生在地面上,但我們也可以指定 collisionEntities
通過選擇器來允許瞬移到磚塊和地面上。這些屬性是 teleport-controls
元件建立的 API 的一部分。
<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>
複製程式碼
就是這樣!**只要一個 script 標籤和一個 HTML 屬性,我們就能瞬移了。**在 A-Frame 倉庫中可以找到更多很酷的元件。
為右手新增體素生成器功能
在 2D 應用程式中,物件內建了處理點選的能力,而在 WebVR 中物件並沒有這樣的能力,需要我們自己來提供。幸運的是,A-Frame 擁有許多處理互動的元件。VR 中用於類似游標點選的場景方法是使用 raycaster,它射出一道鐳射並返回鐳射命中的物體。然後我們通過監聽互動事件及檢視 raycaster 來獲得命中點資訊。
A-Frame 提供基於注視點的游標(注:就像 FPS 遊戲的準心那樣),可以利用此游標點選正在注視的物體,但也有可用的控制器游標元件來根據 VR 追蹤控制器的位置發射鐳射,就像剛剛使用 teleport-controls
元件那樣,我們通過 script 標籤將 controller-cursor
元件引入,然後附加到實體上。這次輪到右手了:
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>
<script src="https://unpkg.com/aframe-controller-cursor-component@0.2.x/dist/aframe-controller-cursor-component.min.js"></script>
<!-- ... -->
<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>
<a-entity id="blockHand" hand-controls="right" controller-cursor></a-entity>
複製程式碼
現在當我們按下追蹤控制器上的按鈕時,controller-cursor
元件將同時觸發控制器和互動實體的 click
事件。A-Frame 也提供了諸如 mouseenter
及 mouseleave
這樣的事件。事件包含了使用者互動的詳細資訊。
這賦予了我們點選的能力,但我們還得寫一些響應點選事件處理生成磚塊的邏輯。可以使用事件監聽器及 document.createElement
來完成:
document.querySelector('#blockHand').addEventListener(`click`, function (evt) {
// 建立一個磚塊實體
var newVoxelEl = document.createElement('a-entity');
// 使用 mixin 來將其變為體素
newVoxelEl.setAttribute('mixin', 'voxel');
// 使用命中點的資料來設定磚塊位置。
// 上文所述的 `snap` 元件是 mixin 的一部分,它將會把磚塊對齊到最近的半米
newVoxelEl.setAttribute('position', evt.detail.intersection.point);
// 使用 `appendChild` 新增到場景中
this.appendChild(newVoxelEl);
});
複製程式碼
為了概括性地處理在命中點建立實體這樣的需求,我們建立了 intersection-spawn
元件,該元件接受任何事件和屬性列表的配置。我們不會詳細討論其實現,但你可以在 GitHub 上檢視這個簡單的 intersection-spawn 元件的原始碼。我們將 intersection-spawn
的能力附加到右手上:
<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>
複製程式碼
現在當我們點選時,就可以生成體素了!
新增移動裝置和桌面裝置支援
我們通過組合元件瞭解到瞭如何構建一個自定義型別的物件(例如,一個具有點選功能和點選時生成磚塊的手部控制器)。元件的好處之一是它們可以在不同的上下文中被重用。我們將 intersection-spawn
元件和基於注視點的 cursor
元件結合起來,便可以在一點都不改變元件的情況下,實現在移動裝置和桌面裝置中生成磚塊的功能了。
<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>
<a-camera>
<a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>
</a-camera>
複製程式碼
試試看
我們的 VR 體素構建器最終使用 11 個 HTML 元素實現。我們可以在桌面或移動裝置上預覽它。在桌面裝置上,我們可以通過拖動和點選來生成磚塊;在移動裝置上,我們可以平移裝置和點選螢幕來生成磚塊。
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>
<script src="https://unpkg.com/aframe-controller-cursor-component@0.2.x/dist/aframe-controller-cursor-component.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/random-color.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/snap.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/csstricks/scenes/aincraft/components/intersection-spawn.js"></script>
<body>
<a-scene>
<a-assets>
<img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg">
<img id="skyTexture" src="https://cdn.aframe.io/a-painter/images/sky.jpg">
<a-mixin id="voxel"
geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5"
material="shader: standard"
random-color
snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"
></a-mixin>
</a-assets>
<a-cylinder id="ground" src="#groundTexture" radius="30" height="0.1"></a-cylinder>
<a-sky id="background" src="#skyTexture" theta-length="90" radius="30"></a-sky>
<!-- Hands. -->
<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>
<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>
<!-- Camera. -->
<a-camera>
<a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>
</a-camera>
</a-scene>
</body>
複製程式碼
如果你有 VR 頭盔(例如 HTC Vive、Oculus Rift + Touch),那麼可以找一個支援 WebVR 的瀏覽器並開啟示例。
如果你想使用桌面或移動裝置觀看 VR 是什麼樣的,可以檢視錄製好的 VR 動作捕捉和手勢演示。
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。
2019年,iKcamp原創新書《Koa與Node.js開發實戰》已在京東、天貓、亞馬遜、噹噹開售啦!