開発部兼採用担当の柳井です。今日は、去る3月7日に開催された合同説明会「リクナビ 就活開幕LIVE 神戸」でおこなった「VRオフィス見学」を紹介します。VRに興味のある方はもちろん、「就職説明会に何か面白いコンテンツは用意できないかな?」とお探しの採用担当者さんも参考にしていただければと思います。
開発の経緯

2016年の忘年会でVRゴーグルを入手し、気軽にVRを体験できる環境ができたので、これを何かに活かせないかと考えていました。VRはゲーム以外にも研修や施設見学にも用いられるため、オフィス見学に使えば採用活動でも使えて一石二鳥!ということでVRオフィス見学アプリに決まりました。
合同説明会の会場は、オフィスから数分で移動できる場所だったため、終了後に実際のオフィスに見学に来てもらう時間を設けていました。しかし、都合が合わず参加できない学生さんもいます。ならVRでオフィス見学をしてしまおうということです。目標は、前述の2019年卒向け、3月の合同説明会での実施。
開発の進め方
キー・ポイントには、業務時間のうち1日1時間を、普段の業務以外の研究に使える研究支援制度「チャレンジラボ」があります。1月の全体会議で発表しているのは、これです。この制度を使い、開発を進めました。開発は2017年11月ごろから開始し、期間は毎日ではありませんが3.5ヶ月ほどです。
アプリ開発は、O’Reillyの「UnityによるVRアプリケーション開発」に沿って進めました。VRでオフィスそのもの(3Dモデル)を作り、そこを自由に歩き回るという形も考えました。しかし、自分が3Dアプリは初経験で開発が間に合わない可能性と、VR酔い対策(後述)、「歩き回る」を合同説明会の会場で行うことへの懸念(安全を確保できない)があり、これは見送りました。
-
1章:すべての人にバーチャルなすべてのものを
VRの解説です。
- 2章:オブジェクトとスケール
Unityをインストール。Unityでの基本操作。カメラ操作、テクスチャなど。
- 3章:VRのビルドと実行
AndroidとiOSでのVR向けビルドの方法(Android StudioとXcode)。
- 4章:注視点ベースのコントロール
視線を使って、ものを動かしたりします。
- 5章:ワールド空間のUI
色々な情報を画面に表示する方法。
- 6章:一人称のキャラクター
カメラ(つまり自分ごと)を動かす。ここまでで2ヶ月。
-
7章:物理と環境
飛ばしました。重力を考慮したり、物と物を反発させる方法。
-
8章:ウォークスルーとレンダリング
飛ばしました。オフィスをモデリングし、歩き回れるようにするならここ。
-
9章:360度全方位の活用
いよいよオフィス体験アプリの形にしていきます。
-
10章:VR空間での社会性
飛ばしました。ネットワーク通信機能を付けて、プレイヤー同士でVRチャットができるようにします。
-
11章:次に何が起こるのか
まとめ。
残り1.5ヶ月で、サンプルコードから実際のオフィス見学アプリの形にしていきました。
VRゴーグルにセットするスマートフォンは、会社でiPhoneを3台用意してもらいました。360度画像はRICOHのTHETA SCを使って撮影しました。なお、このTHETAも、忘年会のゲームの景品でもらいました。(私のVR環境は、かなり会社頼りです(笑))

このような写真を、オフィス内の随所で撮影。これは会議室(会議スペース)、冷蔵庫、雑誌コーナーなどを写しています。三脚を使わなかったため、下部に自分の手が写っています。(あまり見栄えは良くない…)

THETAで撮った写真を、球体の内側に貼り付けます。(こちらは別の画像を貼り付けた時の様子)

この球体の中を覗き込むと、周りに冷蔵庫や雑誌コーナーがあるように見えます。これはUnityでのプレビューですが、VRゴーグルで見ると、同じように見えます。中央の半透明で黄色い円形のマークは、「今、どこを見ているか」をわかりやすくするためのマークです。上部の黄色の文字は、今どこにいるかを文字情報で提示しています。右側にある目玉のマークは、移動に使います。

開発部などの写真も同様に球体を配置し、写真を貼り付けます。部屋から部屋へは瞬間移動するので、位置関係はそれほど重要ではありません。もし移動するモーションをつけるなら、実際の位置関係や縮尺を考慮する必要があります。
部屋を用意したら、移動できるようにします。ドアやソファなど(目玉のマークがあるところ)を3秒間見続けるとそちらへ移動する、というスクリプトの例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MoveToTarget : MonoBehaviour { // AからBへ移動するためのターゲット public GameObject target_to_visitor_door_from_elevator_hall; public GameObject target_to_mocha_kona_from_guatemala; public GameObject target_to_guatemala_from_mocha_kona; // 略 public float timeToSelect = 3.0f; public Text roomText; // 部屋名表示 public GameObject MeMyselfEye; // カメラ public float countDown; void Start () { countDown = timeToSelect; roomText.text = "エレベーターホール"; } void Update () { Transform camera = Camera.main.transform; Ray ray = new Ray (camera.position, camera.rotation * Vector3.forward); RaycastHit hit; if (Physics.Raycast (ray, out hit) && (hit.collider.gameObject == target_to_visitor_door_from_elevator_hall)) { if (0.0f < countDown) { // 的中した際の処理 countDown -= Time.deltaTime; } else { // 3秒間見続けた際の処理 countDown = timeToSelect; SetPosition(1); } } else if (Physics.Raycast (ray, out hit) && (hit.collider.gameObject == target_to_mocha_kona_from_guatemala)) { if (0.0f < countDown) { countDown -= Time.deltaTime; } else { countDown = timeToSelect; SetPosition(7); } } else if (Physics.Raycast (ray, out hit) && (hit.collider.gameObject == target_to_guatemala_from_mocha_kona)) { if (0.0f < countDown) { countDown -= Time.deltaTime; } else { countDown = timeToSelect; SetPosition(5); } /* 他のターゲットも同様の処理 */ } else { // どこも見ていない場合はリセットする countDown = timeToSelect; } } void SetPosition(int target_num) { // カメラを移動。移動先はターゲットごとに変える float x = 0.0f; float z = 0.0f; if (target_num == 0) { // elevator_hall x = 0.0f; z = 0.0f; roomText.text = "エレベーターホール"; } else if (target_num == 5) { // guatemala x = 20.0f; z = 30.0f; roomText.text = "グァテマラ(小会議スペース)"; } else if (target_num == 7) { // mocha_kona x = 20.0f; z = 60.0f; roomText.text = "モカ・コナ(小会議スペース)"; } /* 他のターゲットも同様にセットする */ MeMyselfEye.transform.position = new Vector3(x, 0.0f, z); // エレベーターホールへ戻るマークを常時表示(後述) target_to_elevator_hall_from_anywhere.transform.position = new Vector3(x, -1.5f, z+0.3f); } } |
RayとRaycastHitは、カメラがある場所から視線を飛ばすようにして、「今、どこを見ているか。視線の先に何かのオブジェクトがあるか」を判定するのに使います。
部屋を追加するにあたって、Update()とSetPosition()にてひたすらif-else文を増やしていきましたが、本当なら配列やコレクションを使うべきですね。。
当日の様子

iPhoneにアプリをインストールし、VRゴーグルにセットします。このゴーグルを、学生さんに装着してもらいます。

ブースでの1回30分の説明のうち、最後の5分ほどでVRオフィス見学をおこないました。時間の都合で、実施しなかった回もありますが、学生の皆さんには「お〜〜」と感動したり「すごい!」と好評いただけました。この後、更にリアルオフィスの見学も来てくれた学生さんには、「VRで見た通りのオフィスだ」とびっくりしてもらえました。
開発・実施にあたって配慮したこと
まずはVR酔いをさせないことです。このVRオフィス見学で初めてVRに触れる学生さんもいること(VRにマイナスイメージを抱いてしまう)、そして連日の就活で体調が万全でない学生さんもいることを考えました。
VR酔いは、感覚(視覚)では動いているのに、身体は動いていないという状況が原因の一つです。逆に、歩いている感覚(速度)と映像の動き方が違っていても、VR酔いが起きます。
なので、横を向いたり振り返ったりして、周りを見渡す動きはそのまま反映し、位置を変える動きは極力カットしました。動こうとしていないときは動かない、動く場合は決まった方法で、決まった場所にのみ動く。ユーザの自由度は制限されますが、オフィス見学程度ならこれで十分です。
当日も、学生さんが気分悪そうにしていないかを見ていました。また、見知らぬ人が使ったゴーグルを装着することになるので、ウェットティッシュも用意しました。ニンジャマスクでも良いのですが、今回しか使わない可能性があるため、マスクは用意しませんでした。

他には、当日に次々と学生さんに体験してもらうための工夫として、リセット機能を付けました。足元にある出口のマークを3秒間見続けると、どこに居てもエレベーターホールに戻るようにしました。これが無くても、次に体験する学生はオフィスの様子を見られますが、自分で入り口から入るという体験をしてもらいたくて、こうしました。
開発で大変だったこと
Unityでアプリを作ったり、スクリプトを書いたり、写真を用意するといったところは楽しかったです。UnityのアプリをVRで使えるように、Google VR SDK for Unityのパッケージをインストールしたり、iOS向けにビルドするところでよく詰まりました。UnityもiPhoneアプリ開発も初めてだったため、Unityのエラーなのか、パッケージのエラーか、Xcodeでエラーが出ているのかがなかなか分からず、開発に時間がかかりました。実はUnityでの設定が原因でXcode上で動かない(Unityではエラーが出ない)、という場合は更に分かりにくくて厄介です。これは、やはり慣れが必要です。
開発中にうっかりUnityをバージョンアップし、昨日まで動いていたコードが動かなくなってしまったということもあったので、バージョンアップは慎重に。。逆に、macOS(PC)とXcode、XcodeとiOSのバージョンが合わないというパターンもありました。
終わりに
VRのアプリを作ることは、前述の通りGoogle VR SDK for Unityというパッケージがあるため、ハードルは低かったです。また、オフィスの雰囲気を体験してもらうという目的も達成できました。そのため、来年の説明会でもこれを使うか、また何か新しい企画をするつもりです(未定)。
VRのコンテンツや、VRを体験するための機材は増えてきている(入手もしやすくなっている)とは思いますが、まだまだ一部の人のものという印象です。私としては、VRはやはりゲームやエンターテインメントというイメージが強いのではないかと予想しています。しかし実際には不動産の内見や、医療分野では手術支援システムに利用されています。現状では、大きなゴーグルやOculus RiftのようなHMDが必要ですが、将来的にはスマートグラス(AR)のように常に身に付けられるくらい小型化・軽量化すれば、もっとVRが身近なものになるのではないでしょうか。