別視点のサブ画面を出したい in A-Frame
ハンズオンで使っている題材の迷路についてちょっと改良を加えたいなと思っていろいろとアイデアをひねってます。
1シーンを多角的視点で見るのはどうだろう?
以前、同じVR世界に複数人で集まれたらいいなと思って、複数人参加型のコンテンツをnode.jsで作ったことはありますが、逆に1つのシーンを複数視点で見れるかな?と考えました。
カメラから見る一人称視点以外にも、頭の後ろくらいから見下ろす感じの3人称視点でみれたら面白いかな~
ただA-Frameでは1シーンに配置できる(アクティブにできる)カメラは1つのみのはずだし、どうしよう。
iFrameって使えるんじゃね?
ぱっと思いついたのが、iFrameでサブウインドウを表示する方法。
様々なBlogで小窓的にA-Frameサンプルを表示しているものを、A-Frameの上に重ねられるんじゃないかと。
でも、違うシーンになるため2つのシーンの同期を考えないといけない。
WebSocketか、あるいはWebRTCか。
面倒くさいな。
で他に楽な方法はないかといろいろ調べてみると、ありました。 同一ドメイン配下であれば、iFrameもDOMとして扱えるようです。 もちろん、iFrame側から親DOM(?)もDOM操作できそう。 これは便利だ。
で作ってみたサンプルがこちら。
メイン画面はいつもの一人称ですが、サブ画面は後ろ斜め上45度から見た3人称視点となっています。 もちろん、メイン画面で動けばサブ画面も連動します。
ソース
htmlが2ファイルと、cssが1ファイルになります。
(ハテブのMarkdownのコード表記って、色とか行番号とかつかないんだな。。。)
まずはメインとなるindex.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, World! with subwindow</title> <meta name="description" content="Hello, World! - A-Frame"> <script src="https://aframe.io/releases/0.3.2/aframe.min.js"></script> <link rel="stylesheet" type="text/css" href="./css/style.css"></link> <script> // サブWindow内のユーザ情報。変数の取り回しがわからないためグローバルに持たせる。改善したい。 var user; // カメラエンティティの位置情報をサブWindowのユーザと同期させるコンポーネント // コンポーネント名がキャメル形式だと動いてくれない不思議。 AFRAME.registerComponent('sync-user', { schema: { userId: {default: "userPosition"}, }, init: function() { this.camera = this.el; var userId = this.data.userId; document.getElementById("iframe").onload = function(e) { var doc = this.contentDocument; // TODO:このuser変数をAFRAME.registerComponent内のスコープにできないか? user = doc.getElementById(userId); } }, tick: function(e) { if ((typeof user !== "undefined") && (user != null)) { var pos = this.camera.getAttribute("position"); var rot = this.camera.getAttribute("rotation"); user.setAttribute("position", pos); user.setAttribute("rotation", {"x":0, "y":rot.y, "z": 0}); } } }); </script> </head> <body> <a-scene id="sceneId" class="aframe-scene"> <a-entity id="camera" camera="userHeight: 1.6;" look-controls wasd-controls sync-user="userId: userPosition"></a-entity> <a-box id="box" position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box> <a-sphere id="sphere" position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> <a-cylinder id="cylinder" position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> <a-plane id="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> <div class="subwindow"> <iframe id="iframe" src="./index2.html" /> </div> </body> </html>
次にサブ画面側のindex2.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello, World! - A-Frame</title> <meta name="description" content="Hello, World! - A-Frame"> <script src="https://aframe.io/releases/0.3.2/aframe.min.js"></script> </head> <body> <a-scene id="sceneId" class="aframe-scene" vr-mode-ui="enabled: false"> <a-sphere id="userPosition" position="0 0 0" rotation="0 0 0" radius=0.25 color="#0000FF"> <a-entity position="0 3 3" rotation="-30 0 0"> <a-entity camera="userHeight: 0"></a-entity> </a-entity> </a-sphere> <a-box id="box" position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box> <a-sphere id="sphere" position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> <a-cylinder id="cylinder" position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> <a-plane id="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> </body> </html>
最後にサブウインドウの位置などを決めるcss
@charset "utf-8"; .aframe-scene{ position: absolute; z-index: 1; } .subwindow{ position: absolute; top: 10px; left: 10px; width: 300px; height: 200px; padding: 10px; z-index: 3; }
改善したいところ
このままでも動きますが、何とか改善したいポイントが2点あります。
どなたかヒントだけでもいただけるとありがたいです。
グローバル変数の除外
iFrameの読み込みが遅いため、コンポーネントのinit内でiFrameのonload終了後にiFrameのDOMを持ってくるように実装しています。
その際、変数の受け渡しがわからないため、グローバルに変数を持たせて、tick内で利用しています。
コンポ―ネント内で閉じた変数にできれば汎用性が高まると思うのですが、変数の引き渡し方がわかりません。コンポーネントのupdateは何の契機で動く?
tickにて毎フレーム毎にサブ画面SCENEと同期をとっていますが、初めはEntityのPositionやRotationが変更された契機で動きそうなupdateで実装していました。ですがupdateだと動かなかったです。というかsetAttributeなどを使ってEntityを更新してもupdateが動くことはありませんでした。
何か条件が足りないのでしょうか?