翻譯 | 使用A-Frame打造WebVR版《我的世界》

iKcamp發表於2017-08-14

我是 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打造WebVR版《我的世界》

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打造WebVR版《我的世界》
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>
複製程式碼

在 CodePen 中開啟

就是這樣!只用使用一行 HTML()即可搞定 3D 和 VR 樣板程式碼搭建,包括:canvas、場景、渲染器、渲染迴圈、攝像機以及 raycaster。然後,我們可以通過使用新增子元素的方式來為場景新增物件。無需構建,就只是一個簡單的、可隨意拷貝貼上的 HTML 檔案。


翻譯 | 使用A-Frame打造WebVR版《我的世界》


我們還可以動態查詢和操作 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');
});
複製程式碼

翻譯 | 使用A-Frame打造WebVR版《我的世界》


而且,因為這些只是 HTML 和 JavaScript,因此 A-Frame 和許多現存的框架和庫相容良好:


翻譯 | 使用A-Frame打造WebVR版《我的世界》
相容 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>
複製程式碼

在 CodePen 中開啟

預載入資源

通過 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>
複製程式碼

在 CodePen 中開啟

新增背景

讓我們使用 <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>
複製程式碼

在 CodePen 中開啟

新增體素

在我們的 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>
複製程式碼

在 CodePen 中開啟

元件可以插入到任何實體中,但並不需要像在傳統繼承模式中那樣建立或擴充套件類。如果我們想在類似 <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>
複製程式碼

在 CodePen 中開啟

隨後我們使用 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 也提供了諸如 mouseentermouseleave 這樣的事件。事件包含了使用者互動的詳細資訊。

這賦予了我們點選的能力,但我們還得寫一些響應點選事件處理生成磚塊的邏輯。可以使用事件監聽器及 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>
複製程式碼

試試看

在 GitHub 上檢視原始碼

我們的 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>
複製程式碼

在 CodePen 中開啟

如果你有 VR 頭盔(例如 HTC Vive、Oculus Rift + Touch),那麼可以找一個支援 WebVR 的瀏覽器並開啟示例。

如果你想使用桌面或移動裝置觀看 VR 是什麼樣的,可以檢視錄製好的 VR 動作捕捉和手勢演示

翻譯 | 使用A-Frame打造WebVR版《我的世界》

翻譯 | 使用A-Frame打造WebVR版《我的世界》

iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。


翻譯 | 使用A-Frame打造WebVR版《我的世界》

2019年,iKcamp原創新書《Koa與Node.js開發實戰》已在京東、天貓、亞馬遜、噹噹開售啦!

相關文章