レゴで作った迷路をWebVRで遊べる『まよいの墓』技術解説(Hack Day 2017 最優秀賞作品) #hackdayjp

hackday2017-hackdayjp-1-1024

 

こんにちは。スタジオ・アルカナのエンジニアの吉田です。

2017年2月4日(土)~2月5日(日)にかけて開催された日本最大級のハッカソンイベント『Yahoo! JAPAN Hack Day 2017』に参加してきまして、最優秀賞を受賞した『まよいの墓』という作品について、技術的な視点からマネジメントな視点まで、ざざっとご紹介したいと思います。

当日のお話については、ひかる(@hico00)氏がブログ記事を書いてくれていたので、ご興味があれば、そちらもご覧くださいませ。

HackDay2017でWebVR迷路ゲームを開発して最優秀賞をいただいた話 #hackdayjp

 

『まよいの墓』とは?

whatis

 

今回のハッカソンで制作した『まよいの墓』ですが、どういったものかをやんわり2行でご紹介すると、

  • レゴブロックで作った迷路をカメラで映して解析し、その迷路をVR空間の中で巨大迷路として探索することができる
  • VRの外の人はリアルタイムに迷路の形を変えることができるので、VRの中にいる人にちょっかいを出して弄ぶことができる

という感じのもので、

「とあるイタズラ好きなオバケたちが、プレイヤーであるアナタを迷路に閉じ込めてしまいます。 脱出条件は、迷路のどこかにいる親玉オバケを成仏させること。オバケたちからの数々のイタズラを乗り越えつつ、脱出を目指すゲームです。」

というストーリーがございます。

 

なお、登場してくるオバケに関しては、ひかる(@hico00)氏が趣味程度に空いた時間を見つけては無尽蔵に生成しているキャラクターでして、弊社の社内勉強会や外部の勉強会などで、需要に対して供給が過多になっている程度には量産されているキャラクターでございます。

過去のブログを振り返ると、幾度となく主張されていることを再認識いたしました。

Milkcocoa Meetup vol5 に参加してきました。 #mlkcca

はじめての Blender!【圧縮版】- dots girls Advent Calendar 2015 #dotsgirls

つくってみた!第1弾(後半戦)- beeplay.js, cannon.js, D3.js, LeapMotion – // 第12回社内勉強会 #sa_study

ロイヤリティフリーな素材ということなので、自由に使って頂けますと、本人も喜んでくれるのではないでしょうか!?

 

TL;DR

本文がちょっと長いので、技術要素の要点だけ書いておくと、こんな感じです。

  • レゴ画像はMacに繋いだWebcam経由でChromeのgetUserMediaで取得してピクセル走査し、JavaScriptごり押し解析で2値化しWebSocketで送信
  • サーバーはnode.jsで稼働していてSocket.IOとExpress使ってWebSocketで受信したデータを全ノードへブロードキャスト送信
  • VRはA-Frameを使ってWebVRで実装していて、迷路データの配列をWebSocket経由で受信してA-Frameの<a-box>を配置して生成
  • VR内のオバケはBlenderの3Dモデルを.objと.mltファイルに書き出してA-Frameの<a-entity>で生成し、<a-animation>で動きを付与
  • コントローラはMOCUTE-032という小さいGamePadをBluetoothでMacに接続して、キーイベントをWebSocket経由でWebVRに送信

Webが得意なメンツで挑んでいるので、基本的にWebの技術だけで構成し、シンプルな技術の組み合わせでできています。

 

\枯れた技術の水平思考!/

 

 

HackDayでのマネジメントのおはなし

Hack Dayでは、開発に割くことのできる時間が24時間しかありません。予算は、24時間(8時間換算で3人日)×4名=12人日の工数ということです(でも眠いから稼働率は70%くらいでしょうか…)。

なので、いかに効率よく最短のクリティカルパスを考えて作業分担して滞りなく開発を進めていく事ができるのか(スケジュール管理)、予定通りに進まなかった場合にどういった対策を講じていくのか(リスク管理)、というマネジメント要素も求められるのではないかと個人的に思っております。

とにかく時間がないので、何においても判断基準は「時間がかからずに実現できそうな最短の手段」を追求するという雰囲気になってきますので、こういった状況では『リーダブルコード』といった指南書の内容は記憶から抹消して、どんなコードであっても動くことを優先させなければなりません。

「あぁ……、わたしは………なんという……汚いコードに………手を……染めてしまって………いるのだろう…か……」という葛藤との戦いもHackDayの面白いところでございます。普段のエンジニアの考え方とは反してしまうような、細かいことは気にしないメンタルも試されているような気がいたします。

 

開発体制と開発スコープ

今回のHackDayは4名で参加したので、下記のような分担で開発を進めました。

 

(よしだ)
 1-1. 通信データ構造の設計
 1-2. サーバーサイドの実装
 1-3. コントローラーの実装
 1-4. 迷路デバッガの実装

(かつみ)
 2-1. レゴブロックの画像解析

(えんどー)
 3-1. VRプログラムの実装

(ひかる)
 4-1. レゴ撮影用の環境作り
 4-2. プレゼン資料の作成

 

【1-1. 通信データ構造の設計】に関しては、他タスクの先行タスクとなっていたので、このインターフェイス設計作業だけは、\ハックターイム!/の始まりの合図のあと、最初の段階で済ませておきました。インターフェイスの定義さえできていれば、あとはダミーデータなんかを使用して各々がパラレルで開発を進められるようになるためです。

とにかく、時間がないもんですから、待ち時間や手ぶら時間は可能な限り削減できるようにがんばるのです。そう、がんばるのです…!

 

全体的なアーキテクチャのおはなし

全体的なアーキテクチャの構成を紹介します。

 

architecture1

 

当日のスライド内の手書きの説明図だと上記のようなイメージですが、実際に使用した機材などを盛り込むとこんな感じです。

 

architecture2

 

おおまかにシステム構成を分割すると、下記の6つで構成されていて、それぞれサーバーを介してリアルタイムにデータ通信をしています。

 

【1】レゴ解析

20170220_195054

 

レゴブロックを撮影し画像解析してデジタル化するプログラム。

 

【2】コントローラー

20170220_195609

 

VR空間内で前進やジャンプ操作をするためのコントローラ(MOCUTE-032という製品)。

 

【3】サーバー

socketio

 

【レゴ解析】、【VRスマホ】、【コントローラー】、【VRマップ】間のデータ通信を仲介するためのサーバー。

 

【4】VRスマホ

20170220_195509

vr-view

 

迷路データを受信し、VR空間をレンダリングするためのプログラムとVRゴーグル

 

【5】VRマップ

overview

 

迷路データとVRスマホの現在地を受信し、VR空間を上から俯瞰してみることのできるプログラム。

 

【6】迷路デバッガ

maze-debugger

 

迷路データをリアルタイムで操作するためのプログラム。

こちらは先ほどの全体図には載せていないのですが、リアルタイムに迷路を変更できるデバッグツールとして作りました。(レゴの画像解析のプログラム実装が、時間内で間に合うか間に合わないかの瀬戸際だったので、リスク回避用として準備)

 

 

通信方式と通信データ構造のおはなし

 

【レゴ解析】、【VRスマホ】、【VRマップ】、【コントローラー】は、それぞれがお互いにリアルタイムでデータの通信をする必要があったため、通信プロトコルは WebSocket を採用しています。なので、今回のシステムは、基本的にブラウザで動いています。サーバーサイドはnode.jsで、socket.io + express です。

 

【レゴ解析】→【サーバー】→【VRスマホ】の通信

 

レゴの迷路を画像解析し、その迷路の形状をVRスマホへ送る必要があるため、データ構造の設計を行います。

迷路のマスの数は、事前に 16 × 16 ということを決めていて、あとは「道」か「壁」かの判定ができれば十分です。

なので、実際に通信する際のデータ構造は、1 と 0 で構成されたシンプルな二次元配列で行っています。

 

○迷路のデータ構造

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.min.js"></script>
<script>
 var socket = io('http://<websocket-server>');
 socket.emit('maze_update', [
 [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
 [ 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0 ],
 [ 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1 ],
 [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ],
 [ 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0 ],
 [ 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0 ],
 [ 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0 ],
 [ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0 ],
 [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ],
 [ 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0 ],
 [ 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1 ],
 [ 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0 ],
 [ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1 ],
 [ 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 ],
 [ 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1 ],
 [ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0 ]
 ]);
</script>

 

【VRスマホ】では、このデータを受け取って、VR空間に迷路のブロックを配置しています。このサンプルのデータの場合、0 が「道」、1 が「壁」になっています。スタートとゴールについては位置を事前に決めておき、左上がスタート地点、右下がゴール地点、としています。

 

【コントローラー】→【サーバー】→【VRスマホ】の通信

 

今回の『まよいの墓』のVR空間での操作は、「前進」と「ジャンプ」の2種類があります。コントローラーでスティックを操作した場合は「前進」、ボタンを押した場合は「ジャンプ」と定義付けをしています。実際の通信のデータ構造は、下記のようにとてもシンプルです。シンプルじゃないと間に合わないんだよもん。

 

○操作のデータ構造

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.min.js"></script>
<script>
 var socket = io('http://<websocket-server>');
 socket.emit('controller', {
 operation: 'forward'
 });
</script>

‘operation’ の値が ‘forward’ の場合は「前進」、’jump’ の場合は「ジャンプ」を示します。

 

【VRスマホ】→【サーバー】→【VRマップ】の通信

 

VRの中で探索している人が、いま迷路のどの位置にいるのかを知るために、VR内の座標データをリアルタイムに送受信する仕組みを入れています。このデータ構造もシンプルで、ユーザーのx座標、y座標、z座標と、どの方向を向いているかという角度の情報だけのやりとりです。

 

○ユーザー位置のデータ構造

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.min.js"></script>
<script>
 var socket = io('http://<websocket-server>');
 socket.emit('user_position', {
 x: 12.34
 y: 23.45
 z: 1.23
 rotate: 180.3
 });
</script>

 

 

レゴ撮影して画像解析して配列に落とし込むおはなし

20170220_195054

 

レゴブロックは、16×16のマスで構成されています。

なんでこのサイズかというと、レゴの基礎板を探してみたところ結構大きいサイズ(ベージュとグリーンは38×38、グレーは48×48)のものしか無くって、もっと小さいサイズの基礎板はないのかしらと思っていたら『レゴ (LEGO) クラシック 黄色のアイデアボックス<スペシャル> 』というセット品に、グリーンの16×16のサイズが含まれていることがわかったので、なんとなくそのサイズにしています。

実際に16×16で迷路をつくってみたところ、それなりに迷える程度の迷路ができたので、このサイズに決めました。

レゴブロックの色は、画像の判定をしやすくるために2色だけを使用しています。床の色がホワイトで、壁の色がレッドです。本当は壁の色をブラックにしたかったんですが、持っているレゴブロックの数が足りなかったので、レッドにすることにしました。

レゴブロックの解析をするにあたっては、環境光などの反射によって撮るたびに色合いが結構変わってきたり、レゴをまっすぐ撮影できずに傾いてしまったりで、かなり難航してしまいます。ここは最後まで精度をなかなか上げられずに時間切れとなってしまったのですが、8割くらいの精度は出せてたんじゃないかと思います。

なので、撮るたびに壁がちょっと消えてたり、通路が壁にすり変わっていたり、ということがありました。ご愛嬌ということで…。

とりあえず、ゲームなので運要素もあっていいんじゃないでしょうか!?(と、勝手にポジティブな気持ちで考えている。)

この部分は、かつみ氏が実装していたのですが、最後まで納得のいく精度に至らなかったことを悔やみ続け、ちょうど2月の頭にGitLabのデータベースをロストさせた人物が「Database (removal) Specialist at GitLab」と名乗っていたことをモジって「Image Recognition (failure) Specialist at Studio Arcana」という自称を使い始めたので喜ばしい限りです。

  1.  GitLabの人為的なミスによるデータ損失と復旧<http://www.fortunerinn.org/blog/2017/02/11619>
  2.  GitLab.com Database Incident<https://about.gitlab.com/2017/02/01/gitlab-dot-com-database-incident/>

 

カメラの映像取得と解析

 

カメラの映像の取得と解析は、すべてブラウザとJavaSriptで行っています。navigator.getUserMediaでカメラの映像を取得して、ピクセルを走査して2値化している感じです。

 

○カメラのストリームを取得

 var video = document.getElementById('camera');
 var localMediaStream = null;
 navigator.getUserMedia({video: true}, function(stream) {
 video.src = window.URL.createObjectURL(stream);
 localMediaStream = stream;
 }, onFail);

○カメラの画像を解析して2値化

var canvas = document.getElementById('canvas');
 var img = document.getElementById('img');
 var ctx = canvas.getContext('2d');
 ctx.drawImage(video, 0, 0, width, height);
 img.src = canvas.toDataURL('image/png');

var c1Canvas = document.getElementById("c1");
 img.onload = function() {
 ctx.drawImage(img, 0, 0);
 var src = ctx.getImageData(0, 0, c1Canvas.width, c1Canvas.height);
 var dst = ctx.createImageData(c1Canvas.width, c1Canvas.height);

// rgba
 for (var i = 0; i < src.data.length; i=i+4) {
 var y = ~~(0.299 * src.data[i] + 0.587 * src.data[i + 1] + 0.114 * src.data[i + 2]);
 var ret = (y > THRESHOLD) ? 255 : 0;
 dst.data[i] = dst.data[i+1] = dst.data[i+2] = ret;
 dst.data[i+3] = src.data[i+3];
 }
 ctx.putImageData(dst, 0, 0);
 };

コードの途中にあるこの部分は、YUVやYCbCr色空間とRGB色空間の変換をしている部分になります。

    var y = ~~(0.299 * src.data[i] + 0.587 * src.data[i + 1] + 0.114 * src.data[i + 2]);

 

RGBから輝度信号Yへの変換は、ITU-R BT.601 という規格の中で、

  Y = 0.299 × R + 0.587 × G + 0.114 × B

という式で示されているので、この変換をかけてグレースケールさせて、閾値で2値化している感じですね。

  1. ITU-R BT.601 について<http://www.marumo.ne.jp/bt601/>

 

当日のソースコードはこちらに晒されているようなので、ご興味があればご覧ください。
<https://github.com/s-arcana/hackday2017-mystic-grave/tree/master/src/server/static/webcamera>

 

 

コントローラーのおはなし

20170220_195609

 

コントローラーは、VRゴーグルについてきたMOCUTE-032とかいう中国製の小さいGamePadを使いました。ただ、このGamePadがクセモノで、説明書が雑すぎて使い方がぜんぜんわからない。仕方ないので、一つずつボタン押してキーコード調べ、どのキーが押されたのかを解析してどうにかすることに。

 

○MOCUTE 032 gamemode の場合のキーバインディング

 // button
 // a -> 27 <esc> (85<u>, 70<f>も押される) u<esc>f
 // △ -> 189 <->  (74<j>, 78<n>も押される) j-n
 // x -> 8 <backspace> (72<h>, 82<r>も押される) h<bs>r
 // ios -> 32 <space> (89<y>, 84<t>も押される) y t
 // 
 // stick
 // up -> 220 <¥> (87<w>, 69<e>も押される) w¥e
 // left -> 222 <hat> (65<a>, 81<q>も押される) a'q
 // down -> 186 <colon> (88<x>, 90<z>も押される) x;z
 // right -> 220 <¥> (68<d>, 67<c>も押される) d¥c

 

○押されたキーによって、ジャンプ/前進を判断する

 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
 <script>
 $(document).keydown(function(e){
 if ( _.includes([27, 189, 8, 32], e.keyCode) ) {
 jump();
 }
 if ( _.includes([220, 222, 186], e.keyCode) ) {
 forward();
 }
 });
 </script>

 

○ジャンプと前進(データ量を抑制するためにthrottleで間引いています)

 <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.min.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
 <script>
 var jump = _.throttle(function(){
 socket.emit('controller', {
 operation: 'jump'
 });
 }, 1000);

 var forward = _.throttle(function() {
 socket.emit('controller', {
 operation: 'forward'
 });
 }, 250);
 </script>

 

当日のソースコードはこちらに晒されているようなので、ご興味があればご覧ください。
<https://github.com/s-arcana/hackday2017-mystic-grave/blob/master/src/server/controller.html>

 

余談

事前の企画段階では、コクヨS&Tさんから発売されているフィンガープレゼンター『黒曜石』で操作できるようにする予定だったんですが、キーのバインディングを解析していると、どうもボタンを長押しした際にF5キーが発動する仕様になっていて、ブラウザのリロードが発生してしまったので、これは諦めることになりました。前々回のOpen Hackday 3のときに『黒曜石』で操作する作品をつくっていたので、もう一度使ってみたかったんですけどね。ざんねん!

 

 

サーバーのおはなし

socketio

 

サーバーはsocket.ioを使ってWebSocketを受け付けるようにしています。WebSocketの接続後、特定のイベントが届いた場合に、接続してる全てのクライアントへブロードキャストしている程度の実装です。また、コントローラーのHTMLと、迷路デバッガのHTMLを静的なコンテンツとして提供できるようにもしています。

 

○サーバーのコード

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

server.listen(80);

app.get('/maze', function (req, res) {
 res.sendfile(__dirname + '/maze-debugger.html');
});
app.get('/controller', function (req, res) {
 res.sendfile(__dirname + '/controller.html');
});

io.on('connection', function (socket) {
 // maze_update
 socket.on('maze_update', function (data) {
 io.sockets.emit('maze_update', data);
 });
 // controller
 socket.on('controller', function (data) {
 io.sockets.emit('controller', data);
 });
 // user_position
 socket.on('user_position', function (data) {
 io.sockets.emit('user_position', data);
 });
});

 

当日のソースコードはこちらに晒されているようなので、ご興味があればご覧ください。
<https://github.com/s-arcana/hackday2017-mystic-grave/blob/master/src/server/server.js>

 

 

スマホのWebVRのおはなし

vr-view

 

スマホのブラウザで表示しているVR部分のプログラムはWebVRで実現をしていて、以下のような感じの処理になっています。

  • サーバから16×16の配列を受け取って迷路を生成
  • サーバからコントローラの情報を受け取ってカメラを移動、またはジャンプ
  • カメラが移動したらサーバに位置情報を送信

WebVRを実現するにあたっては、A-FrameというWebVR用のフレームワークを使用しています。A-FrameはHTMLタグでWebVRのコンテンツを記述することができ、DOMをいじって3Dオブジェクトのプロパティを変更することができます。

『まよいの墓』では、サーバから送られてきた16×16の配列データをもとに <a-box> という要素を追加していくことで、迷路の生成を行っています。

VR内のオバケは、Blenderで作成されていた3Dモデルから .objファイル と .mltファイル(とテクスチャのファイル) に書き出して、A-Frameの <a-entity> で生成し、アニメーションなどは <a-animation> を使ってトゥイーンアニメーションを設定することで実装しています。

 

ちなみに、この部分はえんどー氏が実装していましたが、明け方になって疲れた頃にお化けのアニメーションをいじったりして、気を紛らわせていたそうです。眠いもんね。わかる。

 

  1. A-Frame – Make WebVR <https://aframe.io/>

 

○A-FrameのHTMLタグで生成しているWebVR空間のコンテンツ

<a-scene id="myScene">
  <a-plane id='myStage' rotation="-90 0 0" width="16" height="16" color="#242E12"></a-plane>
  <a-sky color="#260045"></a-sky>

  <!-- さまよいおばけ -->
  <a-entity obj-loader="src: url(./model/obake/obake3d.obj);
           mtl: url(./model/obake/obake3d.mtl);"
       scale="0.1 0.1 0.1"
       position="0 0.5 0.0">
    <a-animation
        dur = "5000"
        attribute="position"
        from="-8.0 0.3 0.0"
        to="8.0 0.3 0.0"
        direction="alternate-reverse"
        repeat="indefinite"
        easing="ease-out">
    </a-animation>
    <a-animation
        dur = "5000"
        attribute="rotation"
        from="0 0 0"
        to="0 360 0"
        repeat="indefinite"
        easing="linear">
    </a-animation>
  </a-entity>

  <!-- さまよいおばけ -->
  <a-entity obj-loader="src: url(./model/obake/obake3d.obj);
           mtl: url(./model/obake/obake3d.mtl);"
       scale="0.1 0.1 0.1"
       position="0 0.5 0.0">
    <a-animation
        dur = "3000"
        attribute="position"
        from="0.0 0.3 -8.0"
        to="0.0 0.3 8.0"
        direction="alternate-reverse"
        repeat="indefinite"
        easing="ease-out">
    </a-animation>
  </a-entity>

  <!-- ゴールおばけ -->
  <a-entity obj-loader="src: url(./model/obake/obake3d.obj);
           mtl: url(./model/obake/obake3d.mtl);"
       scale="0.1 0.1 0.1"
       position="7.5 0.5 7.5">
    <a-animation
      dur = "200"
      attribute="position"
      from="7.5 0.25 7.5"
      to="7.5 0.35 7.5"
      direction="alternate-reverse"
      repeat="indefinite"
      easing="ease-out">
    </a-animation>
    <a-animation
        dur = "1000"
        attribute="rotation"
        from="0 180 0"
        to="0 270 0"
        direction="alternate-reverse"
        repeat="indefinite"
        easing="linear">
    </a-animation>
  </a-entity>

  <a-camera id="myCamera"
       position="-7.5 -1 -7.5"
       rotation="0 225 0"
       raycaster
       crawling-cursor="target: #cursor">
    <a-cursor></a-cursor>
  </a-camera>
  
  <a-box position="8.5 1.5 0" depth="18" height="3" color="#222222"></a-box>
  <a-box position="-8.5 1.5 0" depth="18" height="3" color="#222222"></a-box>
  <a-box position="0 1.5 8.5" width="16" height="3" color="#222222"></a-box>
  <a-box position="0 1.5 -8.5" width="16" height="3" color="#222222"></a-box>
</a-scene>

 

当日のソースコードはこちらに晒されているようなので、眺めて頂けると雰囲気がわかるかもしれません。
<https://github.com/s-arcana/hackday2017-mystic-grave/tree/master/src/server/static/app/main>
※ index.html と js/main.js がメインのプログラムです。

 

 

上から映して現在地表示するところのおはなし

overview

 

『まよいの墓』はVRを体験している人も、そうじゃない人も楽しめるようにしたかったので、迷路を俯瞰した視点をPCに表示してモニタリングできるようにしました。

サーバーからVR内のユーザーの位置情報を受信して、その位置に赤色の円錐を横向きで描画し、円錐のとがっている方向がユーザーの向いている方向を示すようにしています。

ちなみにこのソースコードは、【スマホVR】側のソースコードとほとんど同じで、カメラの位置を迷路の上空にして、通信の処理を少し書き直した程度です。

Hack Dayが終わってから振り返ってみると、この辺の汎用性の高さもWebVRの良さなんじゃないかなと思います。

 

ほぼ【スマホVR】同様なのでコード掲載は割愛しますが、こちらに晒されているようなので、眺めて頂けると雰囲気がわかるかもしれません。
<https://github.com/s-arcana/hackday2017-mystic-grave/tree/master/src/server/static/app/overlook>
※ overview.html と js/main.js がメインのプログラムです。

 

 

タブレットで迷路いじるところのおはなし

maze-debugger

 

今回はタブレットで迷路をいじれる仕組みを用意したのですが、これは事前の企画段階では考えていないものでした。

本来であれば、レゴを操作してリアルタイムにVR内の迷路データを更新する予定だったのですが、Hack Dayが始まってから、12時間くらい経ってからでしょうか、レゴの画像解析の精度がヤッベェこれマジ終わんないかもしれんという空気をレゴ担当のかつみ氏が上手に醸し出してきていたため、これはちょっと最悪レゴの解析がダメだった場合のリスク対策をしておかねばならん、というその場の判断で、急きょ開発することになったものです。

こういう想定外のことが発生することもHack Dayの面白さですし、その場で、「じゃあこうしようぜ」と、軽いノリでとにかくゴールを目指していく一体感のようなものも、とても面白いですよね!

 

迷路をいじれる仕組みについては、16×16 の要素を画面に描画して、タップするたびにその要素の data-type 属性の “1” と “0” を変更し、CSSで色を変えて画面に表示しています。

 

○要素のHTML

  <li class="column-item"><div class="cell" data-type="0"></div></li>

○要素のCSS

  [data-type='0'] {
   background-color: #FFF7F6;
  }
  [data-type='1'] {
   background-color: #B28F8C;
  }

○要素のクリックイベント

  $('.cell').click(function(e){
   var currentType = Number( $(e.target).attr('data-type') );
   nextType = currentType >= 1 ? 0 : currentType + 1;
   $(e.target).attr('data-type', nextType);
  }

という感じでdata-type属性を変えているだけです。

迷路をつくり終わったら「この迷路で更新する」を押すことで、サーバーに迷路のデータが2次元配列で送信されます。

 

当日のソースコードはこちらに晒されているようです。
<https://github.com/s-arcana/hackday2017-mystic-grave/blob/master/src/server/maze-debugger.html>

 

 

おわりに

 

当日のブースでの声

 

VRコンテンツの特性上どうしても回転率が悪いため、多くの方には体験してもらう事ができなかったのですが、体験して頂いた方からは「へー!」とか「はー!」とか「おもろい!」というポジティブな反応が多くあり、楽しんで頂けたようで本当に良かったです。

 

  \つくるってたのしいね!/

 

あと、わたしはあまり知らなかったのですが、VRボードゲーム「アニュビスの仮面」というゲームがあって、どういうゲームかというと、VRを付けた人がVRの中の迷路の様子を周りの人に言葉で説明して伝えて、それを聞いた周りの人がVRの中の迷路がどういう形になっているかをパーツを組み合わせて作っていく、という感じのゲームらしいのですね。

VRを付けている人と周りの人が一緒に遊べるというところで「アニュビスの仮面っぽいですね!」っていうコメントを頂いて、「へー!」という新しい発見があったのも良かったです。(えんどーくんは、前から知ってたぽいので私が疎いだけなんですが。)

  1. VRボードゲーム「アニュビスの仮面」<http://anyubis.com/>

 

 

感想

かつみ氏とえんどー氏から感想をいただいたので、そのまんま転載しておきたいと思います。Slackから勝手にコピペしちゃうオジサン。

 

○かつみ氏

担当している実装がうまくいかず、ぶっ続けで作業してたので、今年のHack Dayはすんごい疲れました。
展示が終わり授賞式中は、「授賞式早く終わんないかな〜」くらいに思ってました。
最優秀賞の発表でチーム名呼ばれたときは眠気が吹っ飛びました。
今までの人生で一番うれしい瞬間だったと思います。

あと、去年の参加者で夜中にニャンちゅうのモノマネをした方がいたのですが、今年はいませんでした。。がっかり。

 

○えんどー氏

色々なことがありすぎて感想書き出したらきりがないので、箇条書きにしました。
なので、ここでは素直に嬉しかったですと書かせてもらいます。
– Hack Day初参加
– 当日までインフルエンザ疑惑
– おさまらない腹痛
– 病院に行って17時に秋葉原到着
– 普段着のGAP1969トレーナーで参加
– 思いがけない受賞

 

とのことです!

わたしも受賞するとはまったく思っていなかったで、びっくりなのでした!嬉しゅうございましたね!!

 

 

Hack Day Exhibition 2017

HackDayExhibition2017

 

2017/3/20(月)に『Hack Day Exhibition 2017』というイベントが開催されます。僕らのチームも参加して『まよいの墓』を展示する予定なので、ご興味のある方は、ぜひ足を運んでみてください!

Yahoo!さんのオープンコラボレーションスペース「LODGE」で開催されるようです。

 

  • 日時: 2017/3/20(月) 13:00 ~ 18:00
  • 場所: 東京ガーデンテラス紀尾井町 LODGE(赤坂見附駅/永田町駅)
  • 受付: http://hackday.jp/ からチケットのサイトに飛ぶことができます。

 

『Hack Day Exhibition 2017』までに、レゴの画像解析精度を高めたり、VR空間にテクスチャつけてお墓っぽさを演出したりと、ちょっとだけですが、Hack Day当日の時間内で実装しきれなかった心残りな部分のアップデートを進めているようです。

 

 

関連サイト

Hack Day 2017 に関連するメディア記事や参加者のブログなど、ざざーっと調べてみたので、リンクを掲載しておきます。

運営のみなさま、参加者のみなさま、本当におつかれさまでした!

 

【公式/メディア】

▼Hack Day / Yahoo! JAPAN – Facebook
 https://www.facebook.com/hackday.jp/
 https://www.facebook.com/yahoojapan/posts/1051886214957115

▼Hack Day Japan / Yahoo! JAPAN – Twitter
 https://twitter.com/hackdayjp
 https://twitter.com/Yahoo_JAPAN_PR/status/828194241671163904

▼Hack Day 2017 – 受賞作品ダイジェスト #hackdayjp – YouTube
 https://www.youtube.com/watch?v=P8EX-VGDm7E

▼クリエイターフェス Hack Day 2017 – ステージ全編 #hackdayjp – YouTube
 https://www.youtube.com/watch?v=oJKTYEJkasU

▼オールナイト24時間ぶっ通しで開発して、90秒でプレゼン!「Yahoo! JAPAN Hack Day 2017」の舞台裏 – linotice* | Yahoo! JAPAN RECRUITMENT
 http://linotice.tumblr.com/post/158457392939/201703162 (3/16追記)

▼日本最大級のハッカソン「Yahoo! JAPAN Hack Day 2017」、最優秀賞はレゴブロックで作った迷路がVRでゲーム体験できるチーム名:アルカナラボの「まよいの墓」に決定! – PR TIMES
 https://prtimes.jp/main/html/rd/p/000000034.000014803.html

▼日本最大級のハッカソン「Yahoo! JAPAN Hack Day 2017」に出場した東海圏のエンジニア・デザイナー学生に迫る! – Nagoya Startup News
 http://nagoyastartupnews.jp/report-yahoo-jp-hack-day-2017/

▼「いらすとや」のフリー素材を使って入力した文章を画像に変換する「いらすとか」 – Gigazine
 http://gigazine.net/news/20170206-irasutoya-irasutoka/

▼合ってるような違うような…… 入力した文章を「いらすとや」の素材でシュールに表現してくれるWebサービス登場 – ねとらぼ
 http://nlab.itmedia.co.jp/nl/articles/1702/07/news092.html

▼固定電話でAmazonで買い物ができるようになるプロダクトがおもしろスゴイ! – ASCII.jp
 http://ascii.jp/limit/group/ida/elem/000/001/432/1432457/

▼日本国内のVRニュース、イベント情報ピックアップ #48 – Mogura VR
 http://www.moguravr.com/vrnews48/

▼レゴの迷路をVR体験できる「まよいの墓」、日本最大級ハッカソンで最優秀賞受賞! – SUBETE360
 http://www.subete360.com/2017/02/08/yahoo-japan-hack-day-2017/

▼日本最大級のハッカソンイベント「Yahoo! JAPAN Hack Day 2017」最優秀賞に輝いたのは? – SeleQt【セレキュト】
 http://www.seleqt.net/programming/yahoo-japan-hack-day-2017-best-award/

▼「Yahoo! JAPAN Hack Day 2017」、最優秀賞はレゴブロックで作った迷路がVRでゲーム体験できる「まよいの墓」 – IoTニュース:IoT NEWS
 https://iotnews.jp/archives/47923

 

【フリック!ニュース】

▼「ハッカソンが進化してる!」Yahoo! HACKDAY 2017開催!(前編) #hackdayjp
 http://blog.sideriver.com/flick/2017/02/yahoo-hackday-2-0661.html

▼「ハッカソンが進化してる!」Yahoo! HACKDAY 2017開催!(中編-1) #hackdayjp
 http://blog.sideriver.com/flick/2017/02/yahoo-hackday-2-364c.html

▼「ハッカソンが進化してる!」Yahoo! HACKDAY 2017開催!(中編-2) #hackdayjp
 http://blog.sideriver.com/flick/2017/02/yahoo-hackday-2-f26f.html

▼「シロウトが客観的に見てどうだったか?」Yahoo! HACKDAY 2017開催!(完結編) #hackdayjp
 http://blog.sideriver.com/flick/2017/02/yahoo-hackday-2-a326.html

 

【ブログ/その他】

▼HackDay 2017 に出場して受賞してきた話 #hackdayjp – kdenologue
 http://kden.hatenablog.com/entry/2017/02/07/201431

▼HackDay 2017に参加した話 #hackdayjp – いまにいたる
 http://budoucha.hatenablog.com/entry/2017/02/07/195514

▼HackDay2017に行った – きんめも
 http://kinmemodoki.hatenablog.com/entry/2017/02/08/001105

▼Hack Dayで勉強時間を計測するブックカバーを作成しました – いろいろ作るよ
 http://www.tatetate55.com/entry/2017/02/09/010443

▼Hack Day 2017でRecipe Mixerを作って、Fun賞を受賞しました! #hackdayjp – inajob's blog
 http://inajob.hatenablog.jp/entry/20170208/1486562555

▼ハッカソン”HACKDAY 2017”が激オモロだった【イベントレポ, エンジニア】 – ふらふら。なう
 http://furafura-nau.hatenablog.com/entry/2017/02/06/093221

▼Hack Day 2017 参加レポート(1日目) – SYSKEN ONLINE
 http://sysken.org/blog/3915/

▼Hack Day 2017 参加レポート(2日目) – SYSKEN ONLINE
 http://sysken.org/blog/3930/

▼Hack Day 2017に参加しました – Personal Factory
 http://blog.personal-factory.com/2017/02/06/hack-day-2017-report/
 ※Tech賞を受賞されたチーム

▼Kyobashi.swift x AKIBA.swift 合同勉強会で登壇させていただきました! #kyoakiswift – Developers.IO
 http://dev.classmethod.jp/smartphone/iphone/talk-in-kyobashi-swift-x-akiba-swift/

▼脳波でスカートをめくるP-WAVEを開発した話 – Qiita
 http://qiita.com/hikarut/items/c8087fe178ceb94f3ce9

▼Hack Day 2017に参加しました – しびら
 http://yamada.daiji.ro/blog/?p=1005

▼Yahoo Hack Day 2017に参加してきました! #hackdayjp – 田中、仙台に生きる
 http://tanakalivesinsendai.hatenablog.com/entry/2017/02/06/201930

▼HackDay2017 – saitetutan’s diary
 http://saitetutan.hatenablog.jp/entry/2017/02/15/172357

▼わたしが勝手にあこがれている坪倉さんが、HackDayについて言及されているツイート – Twitter
 https://twitter.com/kohack_v/status/829477699131355136
 https://twitter.com/kohack_v/status/827869189255737345

▼作品一覧 – hackdayjp2017 – Scrapbox
 https://scrapbox.io/hackdayjp2017/%E4%BD%9C%E5%93%81%E4%B8%80%E8%A6%A7

 すべての作品の技術メモ。これは素晴らしいまとめではないでしょうか。Hack Day 2017の参加者が目を向けている領域の技術を俯瞰して把握できそうです。

 キーワードだけピックアップしてみたものを、ずらずらとメモっておきたいと思います。

様々なセンサを用いたハードウェア、ChainerやTensorFlowなどのDeepLearning、IBM WatsonなどのAI、プロジェクションマッピング、Kinect、Unity、LeapMotion、自然言語の自動翻訳、位置情報連動、LINEなどのChatBot、ARマーカー、画像合成、Amazon Dash Button、ラズパイ、BLE、HomeKit、Heroku、PIC、ZigBee、脳波検知、ビールサーバー、VR、音声認識、音声合成、全天球映像、OpenCV、IchigoJam、ハンガー、カーテン、アナログ電話機、モールス信号、お地蔵さん、Apple Watch、Google Speech API、Arduino、Julius、AWS Lambda、IoT、iOS、Android、冷蔵庫、扇風機、Amazon Alexa Voice Service、Google Map API、Google Cloud Vision API、Slack API、nodejs、IFTTT、Google Cloud Natural Language API、ロボホン、顔認識、駅データ.jp API、WebVR、など。

▼be together – yahoo hack day 2017 (Hack ID: 30) – Speaker Deck
 https://speakerdeck.com/taka66/be-together-yahoo-hack-day-2017

▼【Yahoo! JAPAN Hack Day 2017】Hack作品プレゼンテーション&表彰式 まとめ – Togetterまとめ
 https://togetter.com/li/1078542

▼【情報学研究科】Yahoo!JAPAN HackDay 2017——院生参加のチームが最優秀賞受賞 – 明星大学
 http://www.meisei-u.ac.jp/2017/2017021003.html

▼学生有志チーム UEC InSilicoが Hack Day 2017において学生部門最優秀賞を受賞 – 電気通信大学
 http://www.uec.ac.jp/news/prize/2016/20170209-4.html

▼津田塾大学 – Tsuda College – Facebook
 https://www.facebook.com/TsudaCollege/posts/1917698178459176

▼Hack Day 2017:テクノロジーで遊ぼう! – art and program
 http://artandprogram.com/news/?news_id=38

 

他にもあったら、#hackdayjp をつけてツイートしてもらえると勝手にウォッチしているのでリンク追加したいと思います。

 

デワデワ。