Unity初心者が初めてHTC ViveでVRゲームを作ってみた
-
Upload
yasutaka-kurihara -
Category
Engineering
-
view
3.218 -
download
3
Transcript of Unity初心者が初めてHTC ViveでVRゲームを作ってみた
Unity初心者が初めてHTC Viveで VRゲームを作ってみた
2016年 8月 14日(日)
栗原泰隆
元 Flasher、Web制作会社勤務、上海在住 5年目。Unity歴 1年、本格的に使うのは今回が初めて。
栗原泰隆。 1978年、栃木県出身。 2005年、その頃隆盛だった Flashエンジニアに憧れてそれまで勤めていたメーカー子会社を退職、会社が出来てまだ 1年、マンション1室の制作会社に転職、 Flashエンジニアとしての第一歩を踏み出す。その後、仕事の関係でサーバーサイドエンジニアをやったり、転職を繰り返しながら徐々に Flashエンジニアとしての実力をつけるが、 2008年に登場した iPhoneが Flash非サポートを発表したことをきっかけに急速に Flashは日陰な技術になっていく。2011年、その当時勤めていた会社が行った上海現地法人立ち上げのための社内公募に迷わず挙手、猛烈にアピールの末、上海勤務 2名のうち 1名に選ばれその翌年 2012年、上海に渡る。その後、上海の水が合ったのか、あれよあれよと 4年が経つ。去年、映像制作会社に転職したことをきっかけに VRに興味を持ち調査を開始、自分でも Unityを独学で勉強しはじめる。今年 7月、「 Unity VR Expo Akiba」に会社の支援を得て出展、自分で企画、開発した HTC Vive用ゲーム「 Space Tennis」を展示。
[email protected]://www.facebook.com/yasutaka.kuriharahttp://www.immersive.jp/
自己紹介
このスライドでは、
Unityで初めて VRゲーム「 Space Tennis」を作って VR Expo Akibaに出展してみてわかった VR開発のトピックなどをまとめてみました。
一から Unityを勉強しているため、開発については基本的な内容が中心になると思います。
VR開発に興味がある人、これから開発してみたいという人の助けになればと思います。
Space Tennisとは
今年の会社 10周年パーティーの出し物用に企画した VRゲーム。
Viveのコントローラーがテニスのラケットになっていて、前方から飛んで来るボールを打ち返すゲーム。4ステージ終わった時点での合計得点を競います。
参考記事
Mogura VR記事 : http://www.moguravr.com/unity-vr-expo-akiba-2-2/Vine動画 : https://vine.co/v/5ZKJbeWQ29Q
世界観はこんな感じ Unity Vr Expo Akibaのブースの様子 ゲームに熱中すると動作も激しくなる
メンバー
プロデューサー:外間正太( Pyramidfilm上海)ビジュアル、モデリング:宮村綾(フリー、 VJ/デザイナー)Unityオーサリング:栗原泰隆( Pyramidfilm上海)
開発環境
Cinema4D(モデル)Unity5.4ASUSの Vive Ready PC + サブとしてMacBookPro13インチ
開発期間
Oculusでの開発を予定していたが、 4末に先に HTC Viveが届いたので Viveに切り替え、すぐに検証をはじめた。開発期間は仕事の合間など作業で Akibaまで約 2 ヶ月半。ただ、 unityでの開発が初めてだったため、前半の 1 ヶ月半はほぼ unityの検証に費やした。今も絶賛、開発中。
開発概要
企画
キービジュアル作成
基本動作検証
背景モデル作成
ビジュアル検証
仕様検討
実装
仕様再考
再実装
内覧・最終チェック
メンバー全員で Viveゲームをやって何をやったら面白いのか話し合う
ゲームの方向性をテニス+シューティングと決めた時点で、実際にunityで玉を打つデモを作って、当たり判定などを検証
キービジュアルを作って世界観をメンバーで共有
C4Dで背景モデルを作成
出来たモデルを unityにインポートするがいろいろと問題が発覚したので、ライティングなどを勉強
カウントダウンで開始、時間、スコア、ライフを表示してゲームとして成立するところまで作る
ステージ制の採用を決定、ミサイル仕様の削除を決定
各ステージ、チュートリアルを実装
会社で内覧会を実施、最終チェック
時間制限を設けること、ハイスコアゲームにすることなどを決定
開発の流れ
企画( 1)本業(広告)で活かせるイベントで使える VR VRが初めての人でも楽しめる 年齢、性別を問わない 楽しい、楽しそう(ネガティブは避ける)
他の Viveゲームをやってみて Audioshieldは意外と単調でつまらない シューティングゲーム楽しい!( Space Pirate Trainer)
やってないけど参考にした事例として
NPEX Energy Rhythm ( http://panora.tokyo/8196/)
体験者の動きから楽しさが伝わってくる モチベーションの上げ方 UIの親切感
2回のブレストで見えてきたゲームの方向性
HTC Viveを使った立ったまま行うゲーム(立ちゲー) 音楽に合わせて玉を打ち返して得点を競うゲーム→音楽に合わせるは無くなった 世界観的には近未来、宇宙をイメージ イベント利用を考え難易度は低め、ルールも簡単で理解しやすく 短時間( 1分〜 1分半程度)で楽しめる→最終的には 3分弱に
どうしてテニス型にしたのか
シューティングが面白いのはわかっていた もともとのコンセプト(誰でも遊べる、ネガにならない)を考えると血生臭くなる可能性 Viveの特性を考えると立ちゲーはマスト Audioshieldのつまらなさは、やっている人の動きの地味さにある 動きを派手にするには?ボクシング?テニス?と考えて、最終的にはテニスに落ち着いた
企画( 2)
全体のイメージの共有
異なる役割の 3人(プロデューサー、デザイナー、プログラマー)で企画を行うにあたり、全員が最終イメージを共有しやすいようにプロジェクトの当初でキービジュアルを作った。
VR EXPOの出展を決めた時も、ゲームは未完成だけど KVを使って応募できた。( KV作っておいて良かった。)
VR EXPO AKIBAに応募したときのビジュアル。KVに即席で文字を入れた
宇宙イメージ+テニスという方向性が見えた時点で、デザイナーがイメージを作成。実際の背景の 3Dモデルもこのときに作成したものを使ったので、ほぼゲームの世界観はこのイメージになった。
グラッフィク検証( 1) – 暗くする
C4Dのモデルをステージに配置した直後の状態、デフォルトの Directional LightをOffにしても暗くならない。
Main Cameraの Clear Flagsを黒に、 Lightingで環境光を消して C4Dの太陽ライト位置に Directional Lightをアタッチ、完全に真っ暗にするには環境光の反射の設置も 0にする必要があるが、0にすると暗すぎたので反射だけは少し調整した。
すべてのライトを無効にしてもシーン全体が暗くならない
Main Camera-Inspector-Clear Flags:オブジェクトが配置されていない部分の塗りのデフォルトを指定するSPACE TENNISでは宇宙空間にしたかったので、 Assets Storeで星空の Sky Boxを購入した。
購入した Sky BoxSkybox Simple Nebula( https://www.assetstore.unity3d.com/jp/#!/content/8453) $1,95
Window-Lighting-Ambient Intensity:環境光の強さの設定
下記のブログ記事を参考に環境光を調整した。http://tsubakit1.hateblo.jp/entry/2015/03/26/011351
グラッフィク検証( 2) - 発光、透過、映り込み
発光飛んでくるボールを発光させるMaterial-Inspector-Emission(発光)を 1.0 以上に
参考にしたブログ記事:http://tsubakit1.hateblo.jp/entry/2015/06/24/055130
このブログ記事で紹介されている「 Bloom Pro」( $5)も使った。モバイル向けらしく軽かったので採用。
透過、映り込み
ディスク部分は Shaderを Specularにして透過、映り込みを実現、枠の部分は Emission強めで発光させた。右はディスクのMaterialの設定。
プレーヤーが立つディスクはガラスっぽい質感にしたかったので、 Standard Materialの指定で Shaderに Specularを指定、映り込みを実現。透過は Render Modeに Fadeを指定した。設定はマニュアルを見ながら実際の見た目を見ながら行ったため、この設定が本当に正しいかどうかは良く分かっていない。
参考にした Unityのマニュアルページ:
Metallic vs Specular ワークフローhttp://docs.unity3d.com/ja/current/Manual/StandardShaderMetallicVsSpecular.html
Viveコントローラーの使い方
私がよく参考にしたのは「フーレムシンセシス VR開発メモ」フーレムシンセシス VR開発メモhttps://framesynthesis.jp/tech/unity/htcvive/
RightController.csをつくり、 Controller (right)に追加 RightController.csの Startで TrackObjectを取得
trackedObj = GetComponent<SteamVR_TrackedObject> ();
Racketのモデルを Importして、 Viveの ControllerのModelと同じ階層に追加 元のModelは非 Activeに CGのボタンと実際のコントローラーのトリガー位置を同期する
button = GameObject.FindGameObjectWithTag ("Button"); var device = SteamVR_Controller.Input((int)trackedObj.index); float value = device.GetAxis (Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x; float diff = defaultTriggerValue - value; if (button != null) { Vector3 position = defaultTriggerPositionValue; position.z = defaultTriggerPositionValue.z + diff; button.transform.localPosition = position; }
これから初めて Viveで開発する場合、 Viveのセットアップやコントローラーの使い方については、すでに良くまとまっているブログ記事があるのでこれらを参考にすれば良いと思う。
手に持ったラケットでボールを打つ検証
Racketのモデルを Viveの ControllerのModelと同じ階層に追加、元のModelは非 Activeに
ボールとラケットに当たり判定用の BoxColliderを追加
ラケットとボールに張った BoxClliderに設定した Physic Materialで跳ね返りをほぼ最大( 0,99)に、 Bounce CombineもMultiplyに
それ以外の Dynamic Frictionと Static Frictionも検証したが、摩擦系は玉の跳ね返り方にそれほど影響しない(玉の質量にしそうだが検証しきれなかった)
BoxClliderはモデルより厚めにしないとラケットを早く打った時にすり抜けてしまうのでラケットの厚さの 3 倍程度まで大きく(リアリティは薄くなるがすり抜けるよりはマシ)
玉に Rigidbodyを追加、 Collision Detectionを Continuousに設定
Gravityは検証時は使ってなかったが、直線的な玉の動きは予想がしやすく面白みが無いので後ほどGravityを使うように変更した
その他、調整に使ったのは Edit-Project Settings-PhysicsManagerの Sleep Thresholdの値あたりだが、劇的に当たり判定がよくなることは無かった
最終チェックでラケットを早く振ったプレイヤーの玉が前に飛ばないで後方に飛んでしまう問題が多発して調整しようとしたが、 Expo本番までに時間がなく修正しきれなかった
Unityでボールを打つデモを作るのは簡単だったが、当たり判定と跳ね返り方の調整には実際にプレイしながらでないとわからないので時間がかかった
物理エンジン
AngleXと AngleYに HMDを起点に上下左右にずらす距離を指定。 //Viveの目線に合わせる Vector3 target = new Vector3 (Camera.main.transform.position.x, Camera.main.transform.position.y + gap, Camera.main.transform.position.z); var heading = target - this.startPositon.transform.position; var distance = heading.magnitude; var direction = heading / distance; // This is now the normalized direction. this.transform.position = direction;
//ボールを上下左右にちらす transform.Translate(Quaternion.AngleAxis(this.AngleX, Vector3.up) * this.transform.position ); transform.Translate(Quaternion.AngleAxis(this.AngleY, Vector3.right) * this.transform.position);
Vector3 force = this.transform.position;
GameObject ballObject; ballObject = Instantiate (Resources.Load ("ThrowBall", typeof(GameObject)), this.startPositon.transform.position, Quaternion.identity) as GameObject; ballObject.transform.LookAt(target); Rigidbody rigidbody = ballObject.GetComponent<Rigidbody> (); rigidbody.AddForce (force * this.Speed, ForceMode.VelocityChange);
SpaceTennisは毎回ランダムな位置にボールが飛ぶように設定した
ボールを飛ばすスクリプト
ボール GameObjectに Rigidbodyコンポネートを追加 Rigidbodyを AddForceで力を加える AddForceの第一引数に渡すベクトルはターゲット位置、投球位置、左右に散らす距離を利用して求めた
Unity – スクリプトリファレンスhttp://docs.unity3d.com/ja/current/ScriptReference/Rigidbody.AddForce.html
ボールの当たり判定OnTriggerEnterは衝突する Colliderの isTriggerフラグが立っている場合のみ発行される。
Space Tennisではラケットとの衝突判定のハンドリングには OnCollisionEnterを使い、ラケット以外ののオブジェクトとの判定は OnTriggerEnterを使った。どちらを使っても当たり判定はできるが、ラケット以外のオブジェクトでは、ラケットで触れた後しか反応させたくなかったので、一度ラケットが当たって時点で Hitフラグを立てておき、 OnTriggerEnterの中では Hitフラグを判定するということを行った。
(Ball.cs内のスクリプト)
void OnCollisionEnter(Collision hit) { if (hit.gameObject.CompareTag ("Racket") && !Hit) { timer = new Utils.Timer (); timer.FireDelegate = showPoint; timer.startTimer (.2f); Hit = true; } }
void OnTriggerEnter(Collider target) { if ((target.gameObject.CompareTag ("Hexagon") || target.gameObject.CompareTag ("LastBoss")) && Hit && !throughTarget) { throughTarget = true;
Vector3 position = transform.position; pointInstance.transform.position = position; if (target.gameObject.CompareTag ("Hexagon")) { anim.StartAnim (2); Destroy (pointInstance, 5f); } } }
Unityでのイベント処理の方法 Unityの GameObjectには SendMesssageがあるが、Flashの Eventシステムに較べて以下の問題があると思う。
SendMesssageの問題点
送信先のオブジェクトの参照を持っていないといけない メッセージを伝播する仕組みがない
Space TennisではSteamVRのソースを真似て Utils.Eventクラスを作った。
使い方
1. イベント発行時に実行したいメソッドを定義
Private void onIntroEnd(params object[] args) 2. Awakeでイベントを Listen
Utils.Event.Listen (Utils.Events.INTRO_END, onIntroEnd);
3. 適当なクラスからイベントを Send
Utils.Event.Send (Utils.Events.INTRO_END);
public class Event{
public delegate void Handler(params object[] args);
public static void Listen(string message, Handler action){
var actions = listeners[message] as Handler;
if (actions != null){
listeners[message] = actions + action;}else{
listeners[message] = action;}
}public static void Remove(string message, Handler action){
var actions = listeners[message] as Handler;
if (actions != null){
listeners[message] = actions - action;}
}
public static void Send(string message, params object[] args){
var actions = listeners[message] as Handler;
if (actions != null){
actions(args);}
}private static Hashtable listeners = new Hashtable();
}
イベント管理用の Utils.Eventクラス
2Dアニメーション2Dのアニメーションには SpriteStudio 5を利用。
SpriteStudio 5 Player for Unityhttps://www.assetstore.unity3d.com/jp/#!/content/22916
自分が元 Flasherなので SSの UIは Flashに近いので割りと馴染みやすかった。カウントダウンの文字を 512 x 512で作って Importしたら大きすぎたので Scaleを 0.01にして使った。
ひとつのプロジェクトファイル( sspj)で複数のアニメーションをさせる方法
ひとつのプロジェクトファイルで複数のアニメーションをさせることが可能。Space Tennisでは得点の表示アニメーションをひとつのファイルにまとめて行った。
ssaeに複数のアニメーションを追加StartAnimの引数に渡す番号は上からのインデックス値になる。
anim.StartAnim (0);
とした場合は、右の 0_point100のアニメーションが実行される。
VR 空間での UIの使い方 UIの基本については Amazonで買った本で学習した。
Unityゲーム UI実践ガイドhttps://www.amazon.co.jp/dp/B00U0ZUETW/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1
Space Tennisでは Canvasの下にレイヤーを 7 層重ねて、状態によって GameObjectのアクティティブ状態を切り替えて表示 /非表示を制御を行った。
InfoBoard…画面左上の TIME/SCORE/LIFEResult… 結果表示Welcom…ゲーム開始前の状態の表示Intro…ゲーム前のチュートリアル画面RemainingTime… 残り時間表示Message1…メッセージ 1Message1…メッセージ 2
Canvasの設定
Render Mode: World Space( X:0, Y:0, Z:10)Width: 1024 / Height: 768
Fontは Assetとして Projectに Importしておくと選べるようになる。
チュートリアル( Intro)ゲームをはじめる前にプレーヤー全員に体験してもらうにチュートリアル機能をつけた。はじまる前にチュートリアルで実際にボールを打ってもらい、スムーズにゲームに導入していけるようにと、イベント会場でスタッフが全員に説明しなくても良いように、チュートリアル内にゲームの説明を入れた。
目的
スムーズな導入会場スタッフの作業軽減
SPACE TENNIS へようこそ
「 SPACE TENNIS」は、ミッションをクリアしながらハイスコアを目指すゲームだよ
心の準備はできたかな?
中央の三角かボールからボールが飛んで来るよラケットで打ち返してね
それじゃ まず練習だ
トリガーを引きなら打つとボールが加速すよ今度はトリガーを引きながら打ってみてカウントダウンの後にゲーム開始だ最後まで楽しんでねカウントダウンの後にゲーム開始だ
1回目練習
2回目練習
汎用メッセージ(Message)ゲーム中に汎用的に使えるメッセージビューを作成した。
1. メッセージ表示用のMessageItem.csを作成、Messageゲームオブジェクトに追加
2. Message制御用のMessage.csを作成3. MessageItem.csの StartでMessageに自身を登録4. メッセージを表示するタイミングでMessageを通して
showMessageをコール
Message.GetView(0).showMessage (Glossary.GAME_END_COMPLETE, showMessageCompete);
第一引数に表示する文言、第二引数にコールバック用の delegateメソッドを指定。
public class MessageItem : MonoBehaviour {
public int messageNum;
void Start () { Message.SetView(this, messageNum); 〜省略〜 }}
using UnityEngine;using System.Collections;using System.Timers;
namespace Quadra{ public static class Message { public static MessageItem View0; public static MessageItem View1; public static MessageItem GetView(int number) { if(number == 0) return View0; if(number == 1) return View1; return null; } public static void SetView(MessageItem view, int number) { if(number == 0) View0 = view; if(number == 1) View1 = view; } }}
MessageItem.cs
Message.cs
3Dアニメーション
private void move() { System.Random rand = new System.Random (); int num = rand.Next(POSITIONS.Count);
float y = this.gameObject.transform.position.y; Vector3 pos = POSITIONS[num];
iTweenExtention.SerialPlay (this.gameObject ,(iTweenAction)iTween.MoveTo, iTween.Hash ("y", y+0.5f, "time", 1.5f, "easeType", iTween.EaseType.easeInOutSine) ,(iTweenAction)iTween.MoveTo, iTween.Hash ("y", y-0.5f, "time", 1.5f, "easeType", iTween.EaseType.easeInOutSine) ,(iTweenAction)iTween.MoveTo, iTween.Hash ("y", y+0.5f, "time", 1.5f, "easeType", iTween.EaseType.easeInOutSine) ,(iTweenAction)iTween.MoveTo, iTween.Hash ("y", y-0.5f, "time", 1.5f, "easeType", iTween.EaseType.easeInOutSine) ,(iTweenAction)iTween.MoveTo, iTween.Hash ( "position", pos ,"time", 1 ,"easeType", iTween.EaseType.easeInOutQuint ,"oncompletetarget", this.gameObject ,"oncomplete", "onMoveComplete") ); }
3Dオブジェクトのアニメーションは Flashの Tweenerに似ている iTween(無料)を採用。Space Tennisではラスボスの動きなどに使用した。iTweenExtention(有料 $2)の SerialPlayを使うことで連続したアニメーションが簡単に記述できた。
iTweenhttps://www.assetstore.unity3d.com/jp/#!/content/84
iTween Native Extensionhttps://www.assetstore.unity3d.com/jp/#!/content/18391
LastBoss.cs
音楽の使い方Unityでは 3Dサウンドが利用可能。利用方法は簡単で、音を発生させる GameObjectに追加した AudioSourceの Inspectorで Spatial Blendのつまみを 3Dに向けるだけ。
AudioSource-Inspector-Spatial Blend( 3D エンジンがオーディオソースに影響を与える度合い)
その他、Min DIstanceとMax Distanceなどで聞こえる距離を設定したり、 Rolloffの設置を行った。Space Tennisの場合、ボールが近づく感じを音でも迫力を持たせたかっため、 Volume RolloffをLogarithmic Rolloff(オーディオソースに近ければサウンドは大きいが、オブジェクトから遠くなるとサウンドは急速に小さくなる。)にした。
音のフェイドアウトは iTweenの onupdateを使った。下記はカウントダウン開始と同時に BGMをフェイドアウトさせるところ。
private void onCountDownStart(params object[] args) { iTween.ValueTo (gameObject, iTween.Hash ( "from", defaultVolume ,"to", 0 ,"time", .5f ,"onupdate", "SetVolume" ,"easeType", iTween.EaseType.linear ));
} private void SetVolume(float value) { audioSource.volume = value; }
その他VRの開発に慣れてきてからは、メインのゲーム開発はMacBookProで行い、当たり判定の調整など実際にHMDを被らないと出来ない場合のみ、WIndowsマシンで開発した。Unityは Packageの Export/Importが簡単なので複数のマシンで開発するのも問題ないと感じた。自宅やちょっとした移動中など、持って歩けるノート PCで開発したくても今の VR開発環境はノート PCが対応してないため諦めているということもあると思う。しかし、コード上でちょっと工夫することでノート PCでもMac環境でも開発は出来た。
Space Tennisの場合は、 Hierarchyで [CameraRig]を非アクティブにして、 [CameraRig]内のカメラと同じ設定を施したMain Cameraをアクティブ化、 Configクラスに設けた GameModeをコンソールモードを設定、 HMDに依存した処理は GameModeを見てエラーにならないようにした。
void Start () { Config.gameMode = Config.GameMode.CONSOLE;
if (Config.gameMode == Config.GameMode.CONSOLE) { racket = GameObject.Instantiate (Racket, Vector3.zero, Quaternion.Euler(new Vector3(0,0,0.5f))) as GameObject; } game = this.GetComponent<ShootGameMain> () as ShootGameMain; }
Packageを Import後、実行( Run)すると 100%、例外エラーが発生する問題が発生。原因は設定されていた Tagがなぜか外れてしまっていることで、 Tags & Layersを開き、適当な Tagを追加することで治った。
VR Expo Akiba当日 プレーヤーの向く方向の調整ができなかった→プログラムで変更できるようにしておこう!
プレイ中に壁に振った手などを当てる人が出た→ HMD起点にボールが投げられる仕様が裏目。本番は固定位置を起点にボールを投げれば自然に元の位置に戻るはず。
現場の思いつきで背面に投影するためのプロジェクターを接続したこと原因でゲームが何度もフリーズを繰り返す→現場の思いつきでシステムを変更することはやめよう。だいたい問題が起きる。
運営にプレイ時間を短めに報告していたため、ファストパスに行列→正しいプレイ時間を運営には報告しよう!
などなどありましたが、開始の 10時半から終了の 17時まで列が切れず好評で終わりました。総勢 50名ぐらいの方にプレイ頂けたと思います。
ご来場頂いた方には大変ありがとうございました。
まとめ
VRの検証には時間がかかる→しっかり計画を立ててから始める3Dグラッフィクも必要以上に勉強しなくても開発は可能→ Flashなど 2D 系の開発しかやったことのが無いエンジニアでも開発可能Unityの考え方を知れば C#のプログラムはそれほど難しくないAsset Storeのコンポーネントはそのままでも利用できる→デモと割り切ればAsset Storeで売られているコンポーネントをそのまま使うのも全然有り
プログラマーがひとりでも VRゲーム開発は可能実際にコードを書きながらゲームを作る楽しさがあるVRはまだ未知の部分が多い分野、恐れずにはじめよう