roman-tech

テクノロジーはロマンだ。

超お手軽VR開発フレームワーク A-FRAME入門:第7回 ネオンカラーを作成してみよう

今回は男子なら誰でも憧れるネオンカラーをA-FRAMEで再現してみたいと思います!ネオンカラーの代表格といえばTRON

f:id:roman-tech:20160718133309j:plain:w400

やっぱいつの時代もネオンカラーは男心をくすぐるパワーがありますね!

目的

  • TRONのようなネオンカラーオブジェクトを作成する

本記事で学べること

  • 透過処理について学ぶ
  • 視点方向にオブジェクトを固定

開発編

早速開発にはいりましょう。
ネオンを作成するにあたりまずは実現方法を考えましょう。思いついたのは以下2つの手法です。

  • 1.透過処理でそれっぽく見せる(簡単そう)
  • 2.webGLを内部で使用しているためシェーダーで実装する(難しそう)
    今回は短時間でかつ簡単に実装したいため、ひとまず1の手法でやってみます。
    まずは白色のシリンダーを描画します。
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>neon</title>
    <script src="../lib/aframe.js"></script>
</head>

<body>
    <a-scene>
        <a-camera position="0 0 0"></a-camera>
        <a-assets>
            <a-mixin id="layer1" material="opacity: 1; color: #ffffff"></a-mixin>
        </a-assets>
        <a-cylinder mixin="layer1" mixin="common" position="0 0 -5" height="5" radius="0.1"></a-cylinder>
        <a-sky color="#000"></a-sky>
    </a-scene>
</body>
</html>

うん。。まぁこんなもんですよね。

f:id:roman-tech:20160718134336j:plain:w300

次に違う色のシリンダーを半径と透過率だけ変えて2つ描画してみましょう。

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>neon</title>
    <script src="../lib/aframe.js"></script>
</head>
<body>
    <a-scene>
        <a-camera position="0 0 0"></a-camera>
        <a-assets>
            <a-mixin id="layer1" material="opacity: 1; color: #ffffff"></a-mixin>
            <a-mixin id="layer2" material="opacity: 0.3; color: #0000ff"></a-mixin>
            <a-mixin id="layer3" material="opacity: 0.1; color: #0000ff"></a-mixin>
        </a-assets>
        <a-cylinder mixin="layer1" mixin="common" position="0 0 -5" height="5" radius="0.1"></a-cylinder>
        <a-cylinder mixin="layer2" mixin="common" position="0 0 -5" height="5" radius="0.2"></a-cylinder>
        <a-cylinder mixin="layer3" mixin="common" position="0 0 -5" height="5" radius="0.3"></a-cylinder>
        <a-sky color="#000"></a-sky>
    </a-scene>
</body>
</html>

ここで重要なのは以下の記述です。opacityは透過率を意味し、0-1の値をとります。0に近くなるにつれて透明度が高くなります。ここでは白色のシリンダーを透明度1,、青色のシリンダー(半径0.2)を透明度0.3、青色のシリンダー(半径0.3)を透明度0.1で描画しています。

material="opacity: 0.3; 

では結果をみてみましょう!

f:id:roman-tech:20160718134907j:plain:w500

やっつけではありますがそれっぽくなりましたね!!
しかし、この形状、何かに似ている。。。
ライトセーバー!!
ということで急遽ライトセーバーっぽくしていきます。
とりあえず、ライトセーバーの取っ手をつけてみます。

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>force</title>
    <script src="../lib/aframe.js"></script>
</head>
<body>
    <a-scene>
        <a-camera position="0 0 0">
            <a-entity rotation="0 0 0" position="0 0 -5">
                <a-cylinder mixin="layer1" mixin="common" height="5" radius="0.1"></a-cylinder>
                <a-cylinder mixin="layer2" mixin="common" height="5" radius="0.2"></a-cylinder>
                <a-cylinder mixin="layer3" mixin="common" height="5" radius="0.3"></a-cylinder>
                <a-cylinder height="0.5" color="#888888" radius="0.2" position="0 -2.75 0"></a-cylinder>
                <a-cylinder height="0.5" color="#444444" radius="0.2" position="0 -3.25 0"></a-cylinder>
            </a-entity>
        </a-camera>
        <a-assets>
            <a-mixin id="common" position="0 0 -5"></a-mixin>
            <a-mixin id="layer1" material="opacity: 1; color: #ffffff"></a-mixin>
            <a-mixin id="layer2" material="opacity: 0.3; color: #ff0000"></a-mixin>
            <a-mixin id="layer3" material="opacity: 0.1; color: #ff0000"></a-mixin>
        </a-assets>
        <a-sky color="#000"></a-sky>[f:id:roman-tech:20160718141021j:plain]
    </a-scene>
</body>
</html>

新しく二つのシリンダーを加えて取っ手を表現しました。また、ネオンの色も赤に変えました。

<a-cylinder height="0.5" color="#888888" radius="0.2" position="0 -2.75 0"></a-cylinder>
<a-cylinder height="0.5" color="#444444" radius="0.2" position="0 -3.25 0"></a-cylinder>
f:id:roman-tech:20160718135732j:plain:w400

なんかチープですがまぁいいでしょう。次に背景を加えてそれっぽくします。
が、ここで問題が!
ライトセーバーは3D空間上に配置されたオブジェクトと見なされているため、視線に追従できずライトセーバーが置き去りになっています。

f:id:roman-tech:20160718141021j:plain:w400

これを解決するのが以下のコードです。

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>force</title>
    <script src="../lib/aframe.js"></script>
</head>
<body>
    <a-scene>
        <a-camera position="0 0 0">
            <a-entity rotation="-30 0 0" position="0 0 -5">
                <a-cylinder mixin="layer1" mixin="common" height="5" radius="0.1"></a-cylinder>
                <a-cylinder mixin="layer2" mixin="common" height="5" radius="0.2"></a-cylinder>
                <a-cylinder mixin="layer3" mixin="common" height="5" radius="0.3"></a-cylinder>
                <a-cylinder height="0.5" color="#888888" radius="0.2" position="0 -2.75 0"></a-cylinder>
                <a-cylinder height="0.5" color="#444444" radius="0.2" position="0 -3.25 0"></a-cylinder>
            </a-entity>
        </a-camera>
        <a-assets>
            <a-mixin id="common" position="0 0 -5"></a-mixin>
            <a-mixin id="layer1" material="opacity: 1; color: #ffffff"></a-mixin>
            <a-mixin id="layer2" material="opacity: 0.3; color: #ff0000"></a-mixin>
            <a-mixin id="layer3" material="opacity: 0.1; color: #ff0000"></a-mixin>
        </a-assets>
        <a-sky src="back.jpg" rotation="0 200 0"></a-sky>
    </a-scene>
</body>
</html>

ポイントはカメラオブジェクトの中に視線と共に移動させたいオブジェクトを記載することです。以下の部分ですね。a-cameraタグの中にシリンダーを描画しています。

<a-camera position="0 0 0">
            <a-entity rotation="-30 0 0" position="0 0 -5">
                <a-cylinder mixin="layer1" mixin="common" height="5" radius="0.1"></a-cylinder>
                <a-cylinder mixin="layer2" mixin="common" height="5" radius="0.2"></a-cylinder>
                <a-cylinder mixin="layer3" mixin="common" height="5" radius="0.3"></a-cylinder>
                <a-cylinder height="0.5" color="#888888" radius="0.2" position="0 -2.75 0"></a-cylinder>
                <a-cylinder height="0.5" color="#444444" radius="0.2" position="0 -3.25 0"></a-cylinder>
            </a-entity>
</a-camera>

結果をみてみましょう!

f:id:roman-tech:20160718141600j:plain:w500

フォースを感じる!!
以下にアップロードしましたのでぜひみなさんも試めしてみてください。
force


ただし、僕のiphone6だと描画性能が弱いのか、ライトセーバーが結構チラついて表示されます。微妙ですね。。。

まとめ

なんとなくネオンカラーのオブジェクトを作成して、それを応用してジェダイの騎士の気分を味わいました。ネオンカラーはサイバー感があるのでもっと活用したいですね!もしこの手法以外でネオンカラーを実現できる方法をご存知の方は教えてください!それでは!

以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:番外編第2回 開発環境設定 ローカルサーバを立てよう

これまで数回に渡ってA-FRAMEを使ったVR開発を行ってきました。しかし、リソース(画像ファイル、音楽ファイルなど)読み込む必要があるプログラムはローカル環境では動作させることができません。基本的にはなんらかのレンタルサーバを借りてそこにアップロードして動作を確認する必要があります。
しかし、レンタルサーバを借りるのもタダでは無いため、ローカル環境上でデバッグ/動作確認できるように簡単にローカルサーバを立てる方法を紹介します。
というかこの方法を最初に解説すべきだったような。。。

では以下に手順を記載します。

目的

  • 自分のPC上にローカルサーバを立て、ローカル環境上でA-FRAMEの動作確認を行う

本記事で学べること

  • ローカルサーバの立て方

ローカル環境の問題点

ここでは第一回目で使ったthetaの画像を読み込んでVR表示するプログラムを例に説明します。
ソースは以下です。

[index.html]
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>sample2 theta</title>
    <script src="../lib/aframe.js"></script>
</head>
    <a-scene>
        <a-sky src="img.jpg" rotation="0 -90 0"></a-sky>
    </a-scene>
</body>
</html>

ソース中で使用しているimg.jpgは以下です。

f:id:roman-tech:20160626225356j:plain:w400

普通にindex.htmlをブラウザで表示してください。おそらく真っ白な画面が表示されると思います。これはローカル環境ではリソースを読み込め無いという制限により画像のロードに失敗しているためです。これの問題を解決します。僕はMac使いであるため、主にMacについて解説します。

Mac

【手順1】
Macには元々ローカルサーバを立てる機能が備わっています。まずはメニューからターミナルを立ち上げましょう。

f:id:roman-tech:20160713234829j:plain:w300

【手順2】
ここからはコマンドライン上での操作です。この時点でUNIX系のOSに慣れてい無い人は拒否反応が出てくるかもしれませんが、とっても簡単なのでこの機会にチャレンジしてみましょう!
index.htmlを格納しているフォルダまで「cd」コマンドで移動します。cdコマンドについては解説しませんが、ググればたくさん情報が出てくるため問題は無いでしょう。そして上記フォルダまで辿りついたら以下コマンドを実行します。

python -m SimpleHTTPServer 8888
f:id:roman-tech:20160713235612j:plain:w300

そして以下のようにブラウザのアドレスバーに入力しましょう。
sample2-theta_imageの箇所は移動したフォルダ名に置き換えてください。

http://localhost:8888/sample2-theta_image/index.html
f:id:roman-tech:20160714000036j:plain:w400

無事画像が表示されました!
超簡単ですね!!

Windows

僕はWindowsユーザでは無いため詳細は他のサイトを参考にしてみてください。
Windows ローカルサーバ」などでググるとたくさんやり方が出てきます。みた感じだと以下のサイトがお手軽で良さそうです。

zipダウンロードだけでローカルサーバーを作る方法 | 株式会社パーソンリンク

まとめ

本日はローカル環境上でA-FRAMEを使った動作確認環境の構築を行いました。これで今までサーバを持っていなかった人も開発ができますし、デバッグ効率も100倍です。ぜひお試しください!


では!

以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:第6回 コスモ(小宇宙)を感じようpart2

前回は星の画像のテクスチャを自分を中心とした球面上に貼り付ける作業を行いました。本日は続きの作業を行います。

目的

  • いよいよコスモを感じる

本記事で学べること

  • A-FRAMEのLook-At機能を使って画像のローテーションを制御する

開発編

早速開発にはいりましょう。
まず前回までのおさらいをしましょう。
【前回の記事】

roman-tech.hatenablog.jp

画像を球面上に貼ろうとして以下のように失敗したのでした。

f:id:roman-tech:20160710135931j:plain:w300

まずは自分を取り囲むように星の画像を貼り付ける作業を行います。そこで使うのがA-FRAMEの隠れ玉Look-At機能です。正直公式に置いてあるデモを見ても何に使うのかいまいちピンとこなかったこの機能ですが、ここでは凄まじい威力を発揮します。
簡単にいうとLook-Atは、ターゲットを作成することで各オブジェクトをターゲットの方向に向かせる機能です。今回は、ターゲットを自分のカメラの位置に作成し、すべての星の画像を自分の方に向けるようにします。以下にコードを示します。

[index.html]
<html>
<head>
    <meta charset="utf-8">
    <title>universe</title>
    <script src="../lib/aframe.js"></script>
    <script src="sub.js"></script>
    <script src="Utils.js"></script>
</head>

<body onload="init()">
    <a-scene>
        <a-assets> <img id="star2" src="star2.png"></a-assets>
        <a-camera position="0 0 0"></a-camera>
        <a-sphere id="target" color="#000" radius="0.1"></a-sphere>
        <a-sky color="#000">
    </a-scene>
    </a-scene>
</body>
</html>

以下のコードで座標系の中心(つまり自分の位置)にtargetを作成しています。

<a-sphere id="target" color="#000" radius="0.1"></a-sphere>

次にJavascript側です。

[sub.js]
function genStar(imgName, sizex, sizey, r_max, r_min, num)
{
    var theta1;
    var theta2;
    var radius1;
    var radius2;
    var tmp;
    // generate star
    var scene = document.querySelector('a-scene');
    for (var i = 0; i < num; i++) {    
        theta1 = random(0, 360);
        theta2 = random(0, 360);
        
        var star = document.createElement('a-image');
        star.setAttribute('src', imgName);
        star.setAttribute('width', sizex);
        star.setAttribute('height', sizey);
        star.setAttribute('look-at', '#target');
        
        radius1 = r_min;
        var y = radius1 * Math.sin(deg2rad(theta1));
        radius2 = radius1 * Math.cos(deg2rad(theta1));
        var x = radius2 * Math.cos(deg2rad(theta2));
        var z = radius2 * Math.sin(deg2rad(theta2));
        var str = String(x) + " " + String(y) + " " + String(z);
        star.setAttribute('position', str);
        scene.appendChild(star);
    }
    
}

function setup() {
    genStar('#star2', '50', '50', 700, 1000, 500);
}

function init() {
    setup();
}

以下の箇所で星の画像がtargetの方を向くように設定しています。

star.setAttribute('look-at', '#target');

さぁ結果を見てみましょう!

f:id:roman-tech:20160711234450j:plain:w300

たった2行の追加ですべての画像が私の方を見ています!!!
正直これは凄い。。。A-FRAMEで地味に一番凄い機能だと思いました。画像では分かりにくいため以下にデモをアップしました。

universe

そして見栄えを良くするために以下の対応を行いました。 説明をすると長くなるため、詳細は割愛しますが詳細を知りたい方は説明のための記事を作成するので気軽にコメントください。

  • 星の画像サイズと距離を調整
  • 星の画像のバリエーションを3種類に
  • 単純な乱数(一様乱数)を使うと星の位置に味が無いためパーリンノイズを使用し、ある程度規則を持ったノイズを生成し星の位置を調整する。(パーリンノイズはジェネラティブアートと呼ばれるプログラミング界隈で良く使われる手法です)
  • 背景画像に宇宙っぽいテクスチャを貼る
  • 惑星を幾つか配置する。惑星画像は以下サイトからダウンロードさせていただきました。ありがとうございます。

壁紙宇宙館【フリーCG宇宙画像1000枚以上収録】

さぁ色々調整した結果を見てみましょう!

f:id:roman-tech:20160711235417j:plain:w500

コスモを感じる!!!
中々綺麗な感じに仕上がりましたね!以下にアップロードしましたのでぜひ宇宙に没入してコスモを感じてください!!

http://action-code.xyz/a-frame-project/universe/



二回にわたってお届けしてきました、コスモを感じよう!特集、いかがだったでしょうか。A-FRAMEの力を借りてかなり簡単にコーディングできました。
次はまた新しいテーマに取り組んでみたいと思います!
それでは!

以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:第5回 コスモ(小宇宙)を感じようpart1

こんにちは。先日7/7は七夕でしたが、みなさんゆっくり夜空を眺めることができたでしょうか?僕は七夕のことはすっかり忘れていて見逃してしましました。七夕を見逃した人たちのために、本日はVRでコスモ(小宇宙)を感じることができるプロトタイプを作成したいと思います。ちょっと長くなりそうなので二回に分けてお送りします。

目的

  • 宇宙のようなVR空間を構築する
  • イメージとしては360VR空間内に星を無数に配置するような感じ

本記事で学べること

  • 三角関数を応用する
  • 動的にオブジェクトを作成する

開発編

前提として星の数は膨大でありHTMLに記載するのはかなり面倒くさいです。また、HTMLだけだと動的にオブジェクトを生成したり、オブジェクトの座標を変えたりするのが難しいため、今回から積極的にJavascriptを使用します。
さぁ、早速開発に入りましょう。

星の配置

星の描画には以下の画像を使います。

f:id:roman-tech:20160710125022p:plain:w300

この画像を自分を中心とした球上に貼り付けていきます。イメージとしては以下のような感じで、赤色の部分が星の画像になります。

f:id:roman-tech:20160710125628j:plain:w300

ここで問題となるのが、どうやって星の画像を配置する座標を決めるのか?です。
これを解決するのが三角関数(sin, cos)です。

学生のころ
三角関数なんか何に使うんや。。。
と思っていた方々!朗報です!今から使います!むしろ三角関数が無いと何もできません。

では早速球面上に星の画像を配置する方法を検討しましょう。
かなり今更ですがA-FRAMEの座標系は以下のようになっています。赤字で書いた部分がカメラになっており、起動時にカメラはZ軸のマイナス方向を向いています。

f:id:roman-tech:20160710131019j:plain:w300

次に画像を貼り付けるx, y, z座標の計算方法について考えます。色々な手法がありますが、今回はy座標を決めて、そこからx,z座標を求める方法について記載します。

y座標を求める

まずは、以下のようにVR空間をx, y軸平面に水平な方向から見ます。

f:id:roman-tech:20160710132129j:plain:w300

ここからy座標は以下の式で求めることができます。

y = radius1 × sin(theta1)

ここまでは簡単ですね。

x, z座標を求める

ここからがちょっとわかりにくいのですが、次にx, z座標を求めるためにVR空間をx, z軸平面に水平な方向から見ます。

f:id:roman-tech:20160710133018j:plain:w300

x, z座標を求めるにはradius2が必要になってきます。radius2はy座標を求める際にcosを使うことで以下のように求められます。

radius2 = radius1 × cos(theta1)

そしてradius2を使ってx, z座標は以下のように求められます。

x = radius2 * cos(theta2)
z = radius2 * sin(theta2)

そしてこれらをソースコードに落とし込んだのが以下です。

[index.html]
<html>
<head>
    <meta charset="utf-8">
    <title>universe</title>
    <script src="../lib/aframe.js"></script>
    <script src="sub.js"></script>
    <script src="Utils.js"></script>
</head>

<body onload="init()">
    <a-scene>
        <a-assets> <img id="star2" src="star2.png"></a-assets>
        <a-camera position="0 0 0"></a-camera>
        <a-sky color="#000">
    </a-scene>
    </a-scene>
</body>
</html>

以下では星の画像をidを付けてロードしています。

<a-assets> <img id="star2" src="star2.png"></a-assets>

そしてJavacriptの中で画像を動的にVR空間に配置していきます。このとき、貼り付ける場所をランダムにするようにrandom関数を使ってtheta1, theta2を初期化しています。

[sub.js]
/* 
画像の貼り付け関数
imgNameで指定した画像をsizex, sizeyのサイズでr_minからr_maxの半径の間にnum枚貼り付ける。

imgName:画像のID名
sizex:画像の幅
sizey:画像の高さ
r_min:radiusの最小値
r_max:radiusの最大値
num:画像を貼り付ける数
 */
function genStar(imgName, sizex, sizey, r_max, r_min, num)
{
    var theta1;
    var theta2;
    var radius1;
    var radius2;
    var tmp;
    // generate star
    var scene = document.querySelector('a-scene');
    for (var i = 0; i < num; i++) {    
        theta1 = random(0, 360);
        theta2 = random(0, 360);
        
        var star = document.createElement('a-image');
        star.setAttribute('src', imgName);
        star.setAttribute('width', sizex);
        star.setAttribute('height', sizey);
        
        radius1 = r_min;
        var y = radius1 * Math.sin(deg2rad(theta1));
        radius2 = radius1 * Math.cos(deg2rad(theta1));
        var x = radius2 * Math.cos(deg2rad(theta2));
        var z = radius2 * Math.sin(deg2rad(theta2));
        var str = String(x) + " " + String(y) + " " + String(z);
        star.setAttribute('position', str);
        scene.appendChild(star);
    }
    
}

/* ロード時に呼ばれる初期化関数 */
function setup() {
    genStar('#star2', '50', '50', 700, 1000, 500);
}

function init() {
    setup();
}
[Utils.js]
/* 度数法(0-360)からradianへの変換関数 */
function deg2rad( deg ) {
    return (deg * Math.PI / 180);
}

/* minからmaxまでの範囲の乱数を生成 */
function random(min, max)
{
    return Math.random()*(max - min) + min;
}

結果を見てみましょう!!
正面の方は

f:id:roman-tech:20160710135822j:plain:w500

お!なんだかいい感じに星画像が散らばってますね!
それでは横を向いてみましょう!!

f:id:roman-tech:20160710135931j:plain:w500

なんかちがくね?
それもそのはず。今は画像のローテーション設定ができていないため以下のように画像がすべて横向き状態になっています。

f:id:roman-tech:20160710140554j:plain:w300

まだまだ改善の余地がありますが、サンプルをとりあえずあげておきました。ご覧ください。超イマイチです。
universe



みなさんとりあえず、三角関数のリハビリはできましたでしょうか?次は画像のローテーション設定をA-FRAMEの機能を使って修正してプロトタイプを作りあげます。

お楽しみに!!



以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:番外編第1回 iPhoneアドレスバー問題

本日は開発には直接関係無いですが個人的にかなりハマってしまった問題について取り上げます。はじめはA-FRAMEのバグかと思ってライブラリをイジイジしていたのですが、結局iPhoneの設定の問題でした。。。

問題

  • iPhone6でA-FRAMEを使ったサイトを横向きモードで見るとアドレスバーが表示されてしまう。

現象

以下の画面をご覧ください。

f:id:roman-tech:20160704225559p:plain:w500

折角の美しいパノラマ画像の上下にアドレスバーとタスクバーが表示されています。これでは台無しですね。。。なんと原因はiPhoneの意外な設定でした。

原因

僕はiPhone6に機種変更してから文字が小さくて見にくかったので拡大表示モードを使っており、実はこれが原因でした。
同じ問題に悩まされる方のために設定変更方法を記載します。

1.まずは設定アプリを開き以下の画面を表示し、「画面表示と明るさ」メニューをタップします。

f:id:roman-tech:20160704230019p:plain:w200

2.以下の画面が表示されるため、「表示」メニューをタップします。

f:id:roman-tech:20160704230215p:plain:w200

3:以下の画面にて「標準」を選択し、右上の「設定」ボタンをタップします。

f:id:roman-tech:20160704230317p:plain:w200



これで以下のように上下のバーが消えます。やったぜ!!つーかどういう原理なんだよ。。。

f:id:roman-tech:20160704230455p:plain:w500



そもそも周りに聞いてみたら拡大モードで使っている人がほとんどいなかったため、かなりレアケースですがもし困っている人がいたらお試しください!簡単なtipsでした!それでは!



以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:第4回 簡単なゲームを作ってみよう

前回はVR空間上に配置したジュウシマツの3Dモデルにアニメーションをつけてマサイの踊りをさせてみました。今回はそれを応用して、VRならではの視点を使った簡単なゲームを作ってみたいと思います。

目的

  • 視点を使った簡単なゲームを作成する。

本記事で学べること

  • 視点を使ったUIについて学ぶ
  • 視点を使った場合のイベントハンドリング方法について学ぶ
  • Javascriptと連携して、動的にオブジェクトを作成する

開発編

早速開発にはいりましょう。

視点カーソルの追加

視点の位置を示すために、カーソルを描画しましょう。また、A-FRAMEにおけるカーソルはただの目印という意味だけではなく、カーソルとオブジェクトが重なった場合の衝突判定もしてくれるスグレモノです。VRにおける視点カーソルでの衝突判定はメニュー選択やゲームでの当たり判定などに用いられるため、必須の機能です。是非習得しましょう。
それではカーソルを描画するためにカメラに関する箇所を以下のように修正します。

<a-camera position="0 3 0">
<a-entity id="myCursor" cursor="fuse:true; maxDistance:30; timeout:500;" scale="0.2 0.2 0.2" position="0 0 -5" geometry="primitive: ring" material="color: red; shader: flat; opacity:0.5">
</a-entity>
</a-camera>
各プロパティの意味
  • fuse:本値をtrueにするとカーソルがオブジェクトに当たった場合にクリックイベントを発生させることができます
  • maxDistance:カーソルの最大有効判定距離
  • timeout:カーソルがオブジェクトに当たってからクリックイベントが発生するまでの時間
  • scale:カーソルのサイズ
  • position:カーソルの表示位置
  • geometry:カーソルの形状
  • material:カーソルのマテリアル


上記を設定した結果です。

f:id:roman-tech:20160703205029j:plain:w400

画面中央に赤い円が表紙されていますね!これが視点カーソルです。

イベントの取得

カーソルが表示できたら、今度はカーソルとオブジェクトが衝突した際のイベントの取得を行います。ここからはhtmlだけだと限界があるため、javascriptも使っていきます。まずはhtmlが入っているソース内にjavascriptのファイルを作成しましょう。今回はsub.jsとしました。
そして、htmlの先頭でjavascriptを読み込み、後に作成するjavascriptの関数を呼ぶためにbody onload="init()"を記載します。

[index.html]
<head>
    <meta charset="utf-8">
    <title>sample4</title>
    <script src="../lib/aframe.js"></script>
    <script src="sub.js"></script>
</head>

<body onload="init()">

sub.jsの中身は以下です。

[sub.js]
/* ジュウシマツの数 */
var objnum = 8;

function clickHandler(e) {
    var me = document.getElementById(e.target.id);
    me.parentNode.removeChild(me);
    objnum--;
    if (objnum <= 0) {    
        /* クリア画面を動的に生成する */    
        var scene = document.querySelector('a-scene');
        var img = document.createElement('a-image');
        img.setAttribute('src', '#end_img');
        img.setAttribute('rotation', '0 0 0');
        img.setAttribute('scale', '4 2.5 4');
        img.setAttribute('position', '0 4 -3');
        scene.appendChild(img);
    }
}

function init() {
    /* クリックイベントハンドラの登録 */
    var c = document.getElementsByClassName('obj');
    for (i = 0; i < c.length; i++) {
        document.getElementById('obj' + i).addEventListener('click', clickHandler, false);
    }
}

init関数内ではobjという名前がついたクラスをhtmlからすべて抜き出してそれぞれにクリックされた時のハンドラ(clickHandler)を設定しています。html側ではすべてのジュウシマツのオブジェクトにclass=obj, それぞれのジュウシマツのオブジェクトにobj*(0-7)とユニークなidをつけ各ジュウシマツを区別できるようにしています。こうすることでジュウシマツというカテゴリを一括で扱え、しかも個別にも判断できるようになります。

[index.html]
<a-entity position="0 0 0">
<a-collada-model id="obj0" class="obj" src="#jyushimatsu" rotation="0 -90 0" position="0 3 -10"></a-collada-model>
<a-animation attribute="position" direction="alternate" dur="1000" to="0 5 0" repeat="indefinite"></a-animation>
</a-entity>

ここまで設定すれば、視点カーソルとジュウシマツが重なった時にsub.jsで定義しているclickHandlerが呼ばれるようになります。あとはclickHandlerの中に好きな処理を書けばよいだけです。今回は、ジュウシマツをすべて倒した際にクリア画面を動的に生成する処理を入れました。

ゲームの概要

  • 最初にミッションを示す画像を出す
  • 視点とジュウシマツが重なったらジュウシマツを削除する
  • すべてのジュウシマツが削除できればゲームクリア。クリア画面を表示する

早速遊んでみよう!!

注1)ハコスコのようなVRスコープを使って遊んだほうが100倍楽しいです。というかVRスコープが無いとクリアがかなり困難です。 注2)ジュウシマツを駆逐するにはある程度の時間同じジュウシマツを視線で追う必要があります。気合いで追いかけてください。

進撃のジュウシマツ

あらすじ

遺伝子組み換えにより動物が高度な知能を持った西暦2500年。その動物の頂点に君臨するのがジュウシマツであった。彼らは驚異的な跳躍力を武器に人間に襲いかかってくる。既に地上の人間の99%がジュウシマツの餌食となってしまった。そしてあなたを含む人類とジュウシマツの最後の戦いが始まった。


ワクワク!早速ハコスコを装備!

f:id:roman-tech:20160703212531j:plain:w500
f:id:roman-tech:20160703212056j:plain:w500





。。。とんでもねぇクソゲー なんてもんを作っちまったんだ。とりあえず以下に公開しているので皆さんジュウシマツとバトルを楽しんでみてください。

進撃のジュウシマツ

まとめ

クソゲーとはいえ、わずか100行程度で簡単なVRゲームができてしまいました。恐るべしA-FRAME。非常に手軽に始められる素晴らしいフレームワークですが、やりたいと思うこともちゃんと実装できるのでこれは意外とおもしろいですね!!是非みなさんもお試しあれ〜

以下VR開発にオススメです!

超お手軽VR開発フレームワーク A-FRAME入門:第3回 モデルに動きをつけてみよう

前回は、簡単なVR空間を作成しジュウシマツの3Dモデルを静的に配置しました。今回は配置した3Dモデルに動きを与えてみようと思います。

目的

  • 3Dモデルにアニメーションをつける

本記事で学べること

  • アニメーションのつけ方

開発編

さっそく開発に入りましょう。今回は前回作成したジュウシマツのプロジェクトを流用します。ジュウシマツの数が4匹から8匹に増えております。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>sample2-3</title>
    <script src="../lib/aframe.js"></script>
  </head>
  <body>
    <a-scene fog="type: linear; color: #000; far: 25; near: 0">
      <a-assets>
          <img id="tile" src="tile.png">
            <a-asset-item id="jyushimatsu" src="jyushimatsu1.dae">
      </a-assets>
        <a-camera position="0 3 0"></a-camera>
        <a-image src="#tile" rotation="90 0 0" scale="50 50 50" position="0 0 0"></a-image>
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 -90 0"  position="0 3 -10"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="1000" to="0 5 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 -135 0"  position="7.07 3 -7.07"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="1400" to="0 10 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 -180 0"  position="10 3   0"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="200" to="0 3 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 -225 0"  position="7.07 3 7.07"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="1200" to="0 5 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 90 0"  position="0 3 10"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="2000" to="0 10 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 45 0"  position="-7.07 3 7.07"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="600" to="0 5 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 0 0"  position="-10 3   0"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="800" to="0 7 0" repeat="indefinite"></a-animation>
          </a-entity>
          
          <a-entity position="0 0 0">
            <a-collada-model src="#jyushimatsu" rotation="0 -45 0"  position="-7.07 3 -7.07"></a-collada-model>
            <a-animation attribute="position" direction="alternate" dur="1300" to="0 15 0" repeat="indefinite"></a-animation>
          </a-entity>
    <a-sky color="#000"></a-sky>
    </a-scene>
  </body>
</html>

今回追加したアニメーション部分は以下です。

<a-entity position="0 0 0">
<a-collada-model src="#jyushimatsu" rotation="0 -90 0"  position="0 3 -10"></a-collada-model>
<a-animation attribute="position" direction="alternate" dur="1000" to="0 5 0" repeat="indefinite"></a-animation>
</a-entity>

今回はそれぞれのジュウシマツの描画部分をa-entityタグで囲いその中でa-animationタグを使用しています。これで、ジュウシマツに動きを与えることができます。a-animationタグの主なプロパティは以下です。

  • attribute:アニメーションのカテゴリを指定します。今回はpositionなので並行移動に関する移動について記述します。他にも回転移動に関する記述であるrotationなどがあります。
  • to:行き先の座標を指定します。今回の記述では最初にジュウシマツがセットされている箇所から(0, 5, 0)の場所まで移動することを意味しています。
  • direction:アニメーションの方向を指定します。今回はalternate(交互に)を指定しており、ヨーヨーのように初期位置とtoで指定した座標の行ったり来たりを繰り返す動作となります。他にもtoまでいったら即初期位置まで戻ってまたtoまで移動するnormalなどがあります。
  • dur:一回のアニメーションにかかる秒数です(ms単位)。本値が大きければゆっくりと、小さければ素早い動きとなります。
  • repeat:アニメーションの繰り返し回数です。indefiniteを指定すると無限に繰り返します。


さぁ結果を見てみましょう。

マサイ族さながらの動き!!

f:id:roman-tech:20160701182335j:plain:w500


以下にサンプルをアップロードしましたのでぜひジュウシマツのマサイダンスをご覧ください
sample3

いかがでしたでしょうか。javascriptを使わずしても動きを制御できるのはすごいですね。もうちょっと複雑な動きもできそうです。それではまた次回〜。

以下VR開発にオススメです!

© 2016 Tsukasa Horinouchi