Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A-Frameで始めるOculus Quest対応WebVR

386 views

Published on

6/23に開催したOculusQuestハンズオンの資料。

Published in: Technology
  • Be the first to comment

  • Be the first to like this

A-Frameで始めるOculus Quest対応WebVR

  1. 1. ARコンテンツ作成勉強会 Oculus Quest開発のはじめの一歩
  2. 2. 自己紹介 氏名:吉永崇(Takashi Yoshinaga) 所属:九州先端科学技術研究所(ISIT) 専門:ARを用いた医療支援や運動計測 コミュニティ:ARコンテンツ作成勉強会 主催
  3. 3. ARコンテンツ作成勉強会の紹介  2013年5月に勉強会をスタートし、100回以上開催  ARコンテンツの作り方をハンズオン形式で学ぶ  人数は5~10名程度の少人数で実施  参加条件はAR/VRに興味がある人(知識不要)  各地で開催 (福岡、熊本、宮崎、長崎、大分、 鹿児島、山口、広島、関東)
  4. 4. Twitterと勉強会ページで情報を発信しています #AR_Fukuoka Googleで「AR勉強会」で検索
  5. 5. 準備 もろもろのダウンロード http://arfukuoka.lolipop.jp/quest/Sample.zip
  6. 6. A-Frameの概要  Webブラウザ上でVRなどの3D表現を簡単に実現するためのライブラリ  HTMLのタグを書くだけで3Dオブジェクトを配置できる  Firefox、Chrome、Edgeなど主要なブラウザがWebVR対応を表明  Windows MRやHTC Vive、Oculus QuestなどのHMDにも対応
  7. 7. まずは体験 A-Frameのページにアクセス (https://aframe.io/)
  8. 8. まずは体験 ページの左側にサンプルがあります サンプル
  9. 9. まずは体験 基本サンプル Hello WebVRをクリック Hello WebVR
  10. 10. まずは体験 基本サンプル Hello WebVRをクリック 画面をクリック 回転:マウスでドラッグ 左右:[←][→]キー 前後:[↑][↓]キー ※前後左右は自分がどちらに動くかで考える
  11. 11. まずは体験 360°Imageをクリックして全天球コンテンツを表示 360°Image RICOH Theta
  12. 12. Oculus QuestのブラウザでA-Frameにアクセス Oculus Questでの体験 Click
  13. 13. 本日のゴール
  14. 14. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  15. 15. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  16. 16. 必要なもの  Webブラウザ → コンテンツの体験や動作確認  テキストエディタ → HTMLやjavascriptの記述  Webサーバー → コンテンツの公開 サーバーに関して今回は・・・  Glitchを利用 https://glitch.com/ ◆ FacebookかGitHubのアカウントがあればOK ◆ サーバーとエディタの両方を無料で提供 この資料ではGlitch使用を前提に説明します
  17. 17. 基本サンプルのコード Hello WebVRのコードを取得 GET STARTED
  18. 18. 基本サンプルのコードの複製 Hello WebVRに関する記述 (たったこれだけ!) Hello WebVR
  19. 19. 基本サンプルのコードの複製 Glitchユーザーはremix the starter example on Glitchをクリック ※通常はHello WebVRの記述をコピーし、自作のHTMLファイルにペースト Click
  20. 20. 基本サンプルのコードの複製 Remix your ownをクリック Click
  21. 21. 基本サンプルのコードの確認 index.htmlをクリックし、コードが表示されることを確認 Click
  22. 22. ソースの確認 <html> <head> <title>Hello, WebVR! - A-Frame</title> <meta name="description" content="Hello, WebVR! - A-Frame"> <script src="https://aframe.io/releases/0.9.2/aframe.min.js"> </script> </head> <body> <a-scene background="color: #FAFAFA"> 表示するオブジェクトや視点の設定をここに記述 </a-scene> </body> </html>  ヘッダー部でA-Frameの機能を提供するライブラリを取り込む  <a-scene>と</a-scene>の間に描画に関する記述をする
  23. 23. ソースの確認 <a-scene background="color: #ECECEC"> <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"> </a-box> <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"> </a-sphere> <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-scene>  基本図形はa-xxxタグで提供されている https://aframe.io/docs/0.9.0/primi tives/a-box.html (例:a-boxの詳細) 位置 回転 色
  24. 24. 動作確認 ①Show ②Next to The Code
  25. 25. 動作確認
  26. 26. アレンジしよう (まだやらなくてOK) タグ内の各パラメータ(Component)を編集してCGの見た目を調整  position(位置):x y zの順にスペースで区切って指定 (0 1.25 -5) 位置 回転 色x y z座標
  27. 27. アレンジしよう (まだやらなくてOK) タグ内の各パラメータ(Component)を編集してCGの見た目を調整  position(位置):x y zの順にスペースで区切って指定  rotation(傾き):各軸を中心とした回転で表現  color(色):カラーコード等で指定  他にも図形によって各種設定項目がある X Z Y (0 1.25 -5) 【設定項目の例】 radius(半径) width(幅) height(高さ) depth(奥行) src (画像など)原点
  28. 28. <a-scene background="color: #FAFAFA "> <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"> </a-box> <a-sphere position="0 1.25 -5" radius=" 1.25" color="#EF2D5E"> </a-sphere> <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-scene> HTMLの編集に慣れよう #AAAAAA position="-1 0.5 -2" rotation="0 0 0" position="0 1.25 -4" radius="0.7" position="1 0.75 -2 height="0.7" position="0 0 0" width="10" height="10"
  29. 29. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  30. 30. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  31. 31. テクスチャをつけよう
  32. 32. テクスチャをつけよう ①assets ②Add Asset → Upload
  33. 33. テクスチャをつけよう ①back.png ②開く
  34. 34. テクスチャをつけよう クリック
  35. 35. テクスチャをつけよう ①Click ②何もないところをクリック
  36. 36. ソースの書き換え index.htmlクリック
  37. 37. ソースの書き換え <a-scene background="color: #AAAAAA"> <a-box position="-1 0.5 -2" rotation="0 0 0" color="#4CC3D9"> </a-box> <a-sphere position="0 1.25 -4" radius="0.7" color="#EF2D5E"> </a-sphere> <a-cylinder position="1 0.75 -2" radius="0.5" height="0.7" color="#FFC65D"></a-cylinder> <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" color="#7BC8A4" shadow></a-plane> </a-scene>  a-sphereの色をcolor(色)ではなくsrc(画像へのリンク)に変更  srcの右辺に前操作でコピーした“画像のURL”を貼り付ける colorを削除
  38. 38. ソースの書き換え <a-scene background="color: #AAAAAA"> <a-box position="-1 0.5 -2" rotation="0 0 0" color="#4CC3D9"> </a-box> <a-sphere position="0 1.25 -4" radius="0.7" color="#EF2D5E"> </a-sphere> <a-cylinder position="1 0.75 -2" radius="0.5" height="0.7" color="#FFC65D"></a-cylinder> <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" src="画像のURL" shadow></a-plane> </a-scene>  a-sphereの色をcolor(色)ではなくsrc(画像へのリンク)に変更  srcの右辺に前操作でコピーした“画像のURL”を貼り付ける URLをペーストsrcを追加
  39. 39. ソースの書き換え <a-scene background="color: #AAAAAA"> <a-box position="-1 0.5 -2" rotation="0 0 0" color="#4CC3D9"> </a-box> <a-sphere position="0 1.25 -4" radius="0.7" color="#EF2D5E"> </a-sphere> <a-cylinder position="1 0.75 -2" radius="0.5" height="0.7" color="#FFC65D"></a-cylinder> <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" src="画像のURL" shadow></a-plane> <a-plane position="0 5 0" rotation="90 0 0" width="10" height="10" src="画像のURL" shadow></a-plane> </a-scene> Copy & Paste
  40. 40. ソースの書き換え <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" src="画像のURL"></a-plane> <a-plane position="0 5 0" rotation="90 0 0" width="10" height="10" src="画像のURL"></a-plane> <a-plane position="0 2.5 -5" rotation="0 0 0" width="10" height="5" src="画像のURL"></a-plane> <a-plane position="0 2.5 5" rotation="0 180 0" width="10" height="5" src="画像のURL"></a-plane> <a-plane position="5 2.5 0" rotation="0 -90 0" width="10" height="5" src="画像のURL"></a-plane> <a-plane position="-5 2.5 0" rotation="0 90 0" width="10" height="5" src="画像のURL"></a-plane> 同じ要領でコピー&ペーストを使って前後左右、合計4枚の壁を追加
  41. 41. URLの変更 文字列をクリック ここを書き換える
  42. 42. URLの確認 https://XXXX-XXXX.glitch.me
  43. 43. Oculus Questで動作確認 今作った空間に没入できる
  44. 44. コントローラを追加 <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" src="画像のURL" shadow> </a-plane> <a-plane position="0 5 0" rotation="90 0 0" width="10" height="10" src="画像のURL" shadow> </a-plane> <a-plane position="0 2.5 -5" rotation="0 0 0" width="10" height="5" src="画像のURL" shadow> </a-plane> <a-plane position="0 2.5 5" rotation="0 180 0" width="10" height="5" src="画像のURL" shadow> </a-plane> <a-plane position="5 2.5 0" rotation="0 -90 0" width="10" height="5" src="画像のURL" shadow> </a-plane> <a-plane position="-5 2.5 0" rotation="0 90 0" width="10" height="5" src="画像のURL" shadow> </a-plane> <a-entity laser-controls="hand: left"> </a-entity> <a-entity laser-controls="hand: right"> </a-entity> <a-xxx>で表現されるオブジェクトをEntityと呼び、自分で定義できる
  45. 45. Oculus Questで動作確認の前に 必ずリロード
  46. 46. Oculus Questで動作確認 コントローラが表示される(Oculus Touch)
  47. 47. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  48. 48. ハンズオン手順 Step1: サンプルを使ってA-Frame基本操作を覚える Step2: CG見た目の調整を行い簡易VRコンテンツを作成 Step3: スクリプトを記述してインタラクションを実現する
  49. 49. コントローラの挙動を追加 <a-plane position="0 2.5 -5" rotation="0 0 0" width="10" height="10" src="画像のURL"> </a-plane> <a-plane position="0 2.5 5" rotation="0 180 0" width="10" height="10" src="画像のURL"> </a-plane> <a-plane position="5 2.5 0" rotation="0 -90 0" width="10" height="10" src="画像のURL"> </a-plane> <a-plane position="-5 2.5 0" rotation="0 90 0" width="10" height="10" src="画像のURL"> </a-plane> <a-entity laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> コントローラのEntityに、ボタン操作時の挙動を記述したinput-listenを追加 ↓ input-listenの具体的な挙動は独自に記述する(次のページで解説)
  50. 50. コントローラの挙動を追加 <script> AFRAME.registerComponent('input-listen', { init:function () { //初期化 } , tick: function () { //毎フレーム更新 } }); </script> <a-scene background="color: #AAAAAA"> <a-box position="-1 0.5 -2" rotation="0 0 0" color="#4CC3D9" shadow></a-box> <!--中略--> <a-entity laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-scene> 1.txt
  51. 51. コントローラの挙動を追加 init:function () { //初期化 //xボタンを押し始めたときに実行される(左手のみ) this.el.addEventListener('xbuttondown', function (e) { /*Do something*/ }); //xボタンを離したときに実行される (左手のみ) this.el.addEventListener('xbuttonup', function (e) { }); //グリップボタンを押し始めたときに実行される(両手に対応) this.el.addEventListener('gripdown', function (e) { }); //グリップボタンを離したときに実行される(両手に対応) this.el.addEventListener('gripup', function (e) { }); } , 2.txt
  52. 52. コントローラの入力情報を文字で表示 <a-scene background="color: #AAAAAA"> <!--スペースの都合により省略--> <a-plane position="-5 2.5 0" rotation="0 90 0" width="10" height="5" src="画像のURL" shadow></a-plane> <a-entity camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-scene> 3.txt
  53. 53. コントローラの入力情報を表示 const txt = document.getElementById("txt"); //xボタンを押し始めたときに実行される(左手のみ) this.el.addEventListener('xbuttondown', function (e) { txt.setAttribute("value", "x pressed"); }); //xボタンを離したときに実行される (左手のみ) this.el.addEventListener('xbuttonup', function (e) { txt.setAttribute("value", "x released"); }); //グリップボタンを押し始めたときに実行される(両手に対応) this.el.addEventListener('gripdown', function (e) { txt.setAttribute("value", "grip pressed"); }); //グリップボタンを離したときに実行される(両手に対応) this.el.addEventListener('gripup', function (e) { txt.setAttribute("value", "grip released"); }); 4.txt
  54. 54. Oculus Questで動作確認の前に 必ずリロード
  55. 55. Oculus Questで動作確認 押したボタンの情報(grip,a-button)が表示される
  56. 56. 参考資料 https://aframe.io/docs/0.9.0/components/oculus-touch-controls.html ①Oculus-touch-controls ②Events 公式リファレンスでほかのボタンのイベント名を調べられます!
  57. 57. プロジェクトの複製 コンテンツ名をクリック Remix Project ここまでのコードは最も基本的なサンプルなので保存しておくと後々便利
  58. 58. URLの変更 文字列をクリック 覚えやすい名前に
  59. 59. 文字列の表示コードを削除 const txt = document.getElementById("txt"); //xボタンを押し始めたときに実行される(左手のみ) this.el.addEventListener('xbuttondown', function (e) { txt.setAttribute("value", "x pressed"); }); //xボタンを離したときに実行される (左手のみ) this.el.addEventListener('xbuttonup', function (e) { txt.setAttribute("value", "x released"); }); //グリップボタンを押し始めたときに実行される(両手に対応) this.el.addEventListener('gripdown', function (e) { txt.setAttribute("value", "grip pressed"); }); //グリップボタンを離したときに実行される(両手に対応) this.el.addEventListener('gripup', function (e) { txt.setAttribute("value", "grip released"); });
  60. 60. テレポート機能の追加 <html> <head> <meta charset="utf-8"> <title>Hello, WebVR! • A-Frame</title> <meta name="description" content="Hello, WebVR! • A-Frame"> <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script> <script src="https://rawgit.com/fernandojsg/aframe-teleport- controls/master/dist/aframe-teleport-controls.min.js"></script> </head> 5.txt
  61. 61. テレポート機能の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> <a-entity id="head" camera position="0 1.6 0"> カメラと左右のコントローラを<a-entity id="cameraRig">の子要素に することにより、これらをまとめて目的地にテレポートさせることが可能になる
  62. 62. テレポート機能の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> <a-entity id="head" camera position="0 1.6 0"> カメラと左右のコントローラを<a-entity id="cameraRig">の子要素に することにより、これらをまとめて目的地にテレポートさせることが可能になる 次はここをいじる
  63. 63. テレポート機能の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> このあと、左手用のコントローラにテレポート機能を追加する ここをいじる
  64. 64. テレポート機能の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> 先ほど導入したライブラリが提供する teleport-controls componentを追加 6.txt
  65. 65. テレポート機能の追加(補足①) <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> #headを中心に#cameraRigの子要素ごと移動するという設定
  66. 66. テレポート機能の追加(補足②) <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> teleportstart は移動先をポインティングする際に呼び出すイベント。 teleportend は指定位置にジャンプする際に呼び出すイベント。
  67. 67. テレポート機能の追加 init:function () { const txt = document.getElementById("txt"); //xボタンを押し始めたときに実行される(左手のみ) this.el.addEventListener('xbuttondown', function (e) { //テレポート先の選択を開始 this.emit('teleportstart'); }); //xボタンを離したときに実行される (左手のみ) this.el.addEventListener('xbuttonup', function (e) { //指定した場所に移動 this.emit('teleportend'); }); /*スペースの都合により省略*/ }
  68. 68. URLを確認 文字列をチェック https://XXX-XXX.glitch.me
  69. 69. Oculus Questで動作確認 テレポートできるようになる
  70. 70. このあとやりたいこと:マニピュレーション raycaster 交差判定 intersect Gripdown Manipulate!
  71. 71. 交差判定(Raycaster)の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> </a-entity> </a-scene> コントローラのEntityに注目
  72. 72. 交差判定(Raycaster)の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend“ raycaster="objects: .collidable; far:1.2;" laser-controls="hand: left" input-listen> </a-entity> <a-entity laser-controls="hand: right" input-listen> </a-entity> collidableに分類されたEntityとのに交差判定を行う。(あとで解説) 有効範囲はコントローラの前方とEntityとの距離が1.2m以内の時。
  73. 73. 交差判定(Raycaster)の追加 <a-scene background="color: #AAAAAA"> <!--中略--> <a-entity id="cameraRig"> <a-entity id="head" camera position="0 1.6 0"> <a-text id="txt" value=" " position="0 0 -1" scale="0.5 0.5 0.5" align="center" color="#FFFFFF"> </a-text> </a-entity> <a-entity teleport-controls= "cameraRig: #cameraRig; teleportOrigin:#head; startEvents: teleportstart; endEvents: teleportend" raycaster="objects: .collidable; far:1.2;" laser-controls="hand: left" input-listen> </a-entity> <a-entity raycaster="objects: .collidable; far:1.2;" laser-controls="hand: right" input-listen> </a-entity> collidableに分類されたEntityとのに交差判定を行う。(あとで解説) 有効範囲はコントローラの前方とEntityとの距離が1.2m以内の時。
  74. 74. 交差判定対象の設定 <a-scene background="color: #AAAAAA"> <a-box class="collidable" position="-1 0.5 -2" rotation="0 0 0" color="#4CC3D9" shadow> </a-box> <a-sphere class="collidable" position="0 1.25 -4" radius="0.7" color="#EF2D5E" shadow> </a-sphere> <a-cylinder class="collidable" position="1 0.75 -2" radius="0.5" height="0.6" color="#FFC65D" shadow></a-cylinder> <a-plane position="0 0 0" rotation="-90 0 0" width="10" height="10" src="画像のURL" shadow></a-plane> <!--以下省略--> </a-scene> raycasterと交差判定 の計算をする 床や壁はraycasterと 交差判定をしない
  75. 75. マニピュレーション開始と終了の条件 (1/2) init:function () { const txt = document.getElementById("txt"); this.el.grip=false; this.el.addEventListener('xbuttondown', function (e) { this.emit('teleportstart'); }); this.el.addEventListener('xbuttonup', function (e) { this.emit('teleportend'); }); this.el.addEventListener('gripdown', function (e) { this.grip=true; }); this.el.addEventListener('gripup', function (e) { this.grip=false; }); } , グリップボタンを握っているか否かを記憶
  76. 76. マニピュレーション開始と終了の条件 (2/2) init:function () { /*スペースの都合により省略*/ this.el.addEventListener('gripup', function (e) { this.grip=false; }); //raycasterが何かに交差したとき this.el.addEventListener('raycaster-intersection', function (e) { //複数選択された場合は0番目を覚えさせておく this.selectedObj = e.detail.els[0]; }); //raycasterの交差が解除されたとき this.el.addEventListener('raycaster-intersection-cleared', function (e) { //選択オブジェクトの情報をリセット this.selectedObj = null; }); } , raycasterと交差したCGが あるか否かをチェック 7.txt
  77. 77. マニピュレーションを行う init:function () { /*スペースの都合により省略*/ this.el.addEventListener('raycaster-intersection', function (e) { this.selectedObj = event.detail.els[0]; }); this.el.addEventListener('raycaster-intersection-cleared', function (e) { this.selectedObj = null; }); }, //毎フレーム実行される tick: function () { if (!this.el.selectedObj) { return; } if (!this.el.grip) { return; } //選択中のオブジェクトがあり、かつグリップ押下中であれば //そのオブジェクトをコントローラに追従させるコードを記述 }
  78. 78. マニピュレーションを行う //毎フレーム実行される tick: function () { if (!this.el.selectedObj) { return; } if (!this.el.grip) { return; } //コントローラに追加したraycasterを取得 var ray = this.el.getAttribute("raycaster").direction; //コントローラの1.2m手前の座標を計算(コントローラが原点) var p = new THREE.Vector3(ray.x, ray.y, ray.z); p.normalize(); p.multiplyScalar(1.2); //ローカル座標(コントローラ中心)をワールド座標に変換 this.el.object3D.localToWorld(p); //raycasterの先端に選択オブジェクトを追従させる this.el.selectedObj.object3D.position.set(p.x, p.y, p.z); } 8.txt
  79. 79. 動作確認 Gripでつかんで動かせる
  80. 80. 参考資料:raycaster https://github.com/aframevr/aframe/blob/master/docs/co mponents/raycaster.md GitHubでraycasterの設定項目や取得できるデータを見られます
  81. 81. このあとやりたいこと:弾(ball)の発射 トリガー押下 飛んでいく 飛んでいく
  82. 82. 物理ライブラリのインポート <head> <meta charset="utf-8"> <title>Hello, WebVR! • A-Frame</title> <meta name="description" content="Hello, WebVR! • A-Frame"> <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script> <script src="https://rawgit.com/fernandojsg/aframe-teleport- controls/master/dist/aframe-teleport-controls.min.js"></script> <script src="//cdn.rawgit.com/donmccurdy/aframe-physics- system/v3.3.0/dist/aframe-physics-system.min.js"></script> </head> <body> <script>スペースの都合により省略</script> <a-scene background="color: #AAAAAA"> /*スペースの都合により省略*/ </a-entity> </body> 9.txt gravity:0で無重力化。 restitution:0.9で高反発 physics="gravity: 0; restitution: 0.9;"
  83. 83. トリガー押下の検出 init:function () { /*スペースの都合により省略*/ //Raycaster intersected with something. this.el.addEventListener('raycaster-intersection', function (e) { this.selectedObj = event.detail.els[0]; }); //Raycaster intersection is finished. this.el.addEventListener('raycaster-intersection-cleared', function (e) { this.selectedObj = null; }); //トリガーを押した this.el.addEventListener('triggerdown', function (e) { }); }
  84. 84. 弾(ball)を出現させる this.el.addEventListener('triggerdown', function (e) { //コントローラの三次元座標を取得(thisはコントローラを参照) var point = this.object3D.getWorldPosition(); //球エンティティを生成 var ball = document.createElement('a-sphere'); ball.setAttribute('class', 'ball'); ball.setAttribute('scale', '0.2 0.2 0.2'); ball.setAttribute('position', point); //物理演算の影響をオンにする ball.setAttribute('dynamic-body', 'shape: sphere; sphereRadius:0.2; '); //Instantiate ball entity in a-scene var scene = document.querySelector('a-scene'); scene.appendChild(ball); }); 10.txt
  85. 85. 動作確認 トリガーを押すと手元に球が現れる(まだ飛ばない)
  86. 86. 弾を撃つ this.el.addEventListener('triggerdown', function (e) { /*スペースの都合により省略*/ //Instantiate ball entity in a-scene var scene = document.querySelector('a-scene'); scene.appendChild(ball); //レイキャスターの向きを取得 var dir = this.getAttribute("raycaster").direction; //ボールを飛ばす力を計算 var force = new THREE.Vector3(); force.set(dir.x, dir.y, dir.z); force.multiplyScalar(2000); //ボールにforceというプロパティを宣言して代入 ball.force = this.object3D.localToWorld(force); }); 11.txt
  87. 87. 弾を撃つ this.el.addEventListener('triggerdown', function (e) { /*スペースの都合により省略*/ ball.force = this.object3D.localToWorld(force); //物理的演算に必要な情報がballで読み込み終わったら弾を撃つ ball.addEventListener('body-loaded', function (e) { //弾(this)の位置を取得 var p = this.object3D.position; //先ほど計算した弾(this)に加える勢いを取得 var f = this.force; this.body.applyForce( new CANNON.Vec3(f.x, f.y, f.z), new CANNON.Vec3(p.x, p.y, p.z) ); }); }); 12.txt
  88. 88. 動作確認 弾が飛んでいく。ただし壁をすり抜けてしまう。
  89. 89. 衝突判定をつける <a-scene physics="debug: false; gravity: 0; restitution: 0.9; " background="color: #AAAAAA"> <a-box static-body class="collidable" --省略-- ></a-box> <a-sphere static-body class="collidable" --省略-- ></a-sphere> <a-cylinder static-body class="collidable" --省略-- ></a-cylinder> <a-plane static-body position="0 0 0" --省略-- ></a-plane> <a-plane static-body position="0 5 0" --省略-- ></a-plane> <a-plane static-body position="0 2.5 -5" --省略-- ></a-plane> <a-plane static-body position="0 2.5 5" --省略-- ></a-plane> <a-plane static-body position="5 2.5 0" --省略-- ></a-plane> <a-plane static-body position=“-5 2.5 0" --省略-- ></a-plane> <a-entity id="cameraRig">--省略--</a-entity> box, sphere, cylinder, planeにstatic-bodyを追加
  90. 90. 弾を掃除 init:function () { /*スペースの都合により省略*/ //トリガーを押した this.el.addEventListener('triggerdown', function (e) { /*スペースの都合により省略*/ }); //A-buttornを押下 this.el.addEventListener('abuttondown', function (e) { //ballというクラス名のEntity(つまり弾)を全て取得 var els = document.querySelectorAll('.ball'); //弾を一つずつ削除 for (var i = 0; i < els.length; i++) { els[i].parentNode.removeChild(els[i]); } }); } , 13.txt
  91. 91. 完成
  92. 92. おまけ:スティックの値を取得 オブジェクトをマニピュレートしている最中に スティックを前後させてオブジェクトを移動させよう
  93. 93. おまけ:スティックの値を取得 init:function () { const txt = document.getElementById(“txt”); this.el.grip=false; //スティックの縦方向の値の和 this.el.sumY=1; //スティックが動いたときに呼ばれる this.el.addEventListener('axismove',function(e){ //x,yの値が-1~1で得られるのでそれを文字で表示 txt.setAttribute("value", e.detail.axis[0].toFixed(2)+","+e.detail.axis[1].toFixed(2)); //スティックの縦方向の値を合計。-をかけるのはスティックの前後と±を一致させるため this.sumY+=(-e.detail.axis[1]*0.01); }); /*スペースの都合により省略*/ } ,
  94. 94. おまけ:スティックの値を取得 init:function () { /*スペースの都合により省略*/ //グリップボタンを押し始めたときに実行される(両手に対応) this.el.addEventListener('gripdown', function (e) { //グリップ押下時にスティックの縦方向の値の和を初期化 this.sumY=1; this.grip=true; }); //グリップボタンを離したときに実行される(両手に対応) this.el.addEventListener('gripup', function (e) { this.grip=false; }); /*スペースの都合により省略*/ } ,
  95. 95. tick: function () { if (!this.el.selectedObj) { return; } if (!this.el.grip) { return; } //コントローラに追加したraycasterを取得 var ray = this.el.getAttribute("raycaster").direction; //コントローラの1.2m手前の座標を計算(コントローラが原点) var p = new THREE.Vector3(ray.x, ray.y, ray.z); p.normalize(); //スティックの前後により選択しているオブジェクトを前後に移動 p.multiplyScalar(1.2*this.el.sumY); //ローカル座標(コントローラ中心)をワールド座標に変換 this.el.object3D.localToWorld(p); //raycasterの先端に選択オブジェクトを追従させる this.el.selectedObj.object3D.position.set(p.x, p.y, p.z); } おまけ:スティックの値を取得
  96. 96. 参考 サンプルを公開しています。 (1)各ボタンからの入力を取得 https://github.com/TakashiYoshinaga/O culus-Quest-Input-Sample (2)今日作ったコンテンツ https://github.com/TakashiYoshinaga/O culus-Quest-Interaction-Sample

×