古い Unity* ゲームを VR 向けに更新する

この記事は、インテル® デベロッパー・ゾーンに公開されている「Updating Older Unity* Games for VR」の日本語参考訳です。


この記事の PDF 版はこちらからご利用になれます。

多くの開発者によって、これまでに PC およびモバイルデバイス向けの素晴らしい Unity* ゲームが作成されてきました。Unity* 3D に VR 機能が統合されたことで、VR 体験に対応するため古いゲームの更新を検討する良い機会となりました。この記事では、既存の Unity* ゲームを HTC* Vive* VR ハードウェアに対応させる変更方法を紹介します。

  • ターゲット VR カテゴリー: プレミアム VR
  • ターゲット VR ハードウェア: HTC* Vive*
  • ターゲット Unity* バージョン: Unity* 5.0

この記事では、次のトピックをカバーします。

  • 新しい VR 体験向けに古いゲームを再設計する
  • SteamVR Plugin の統合に関するヒント
  • コントローラー・スクリプトの使用
  • 新しい C# から古い JavaScript* へのソリューション
  • 2D GUI から 3D UI への移行

新しい VR 体験の設計

既存の Unity* ゲームは VR 向けに設計されていませんが、Unity* は現実のグラフィックスや物理特性をシミュレーションする 3D 環境であるため、VR 体験に必要な作業の大部分はすでに完了していると言えるでしょう。そのため、コントローラー・スクリプトを追加する前に行うべきことは、新しい VR 体験として古いゲームを見直すことです。以下は、古いゲームに合った VR 体験を設計するためのヒントです。

乗り物酔いのような状態にならないための設計: 標準の Unity* ゲームやアプリケーションは、素晴らしい体験を提供しますが、プレーヤーの感覚を欺くものではありません。これを認識することが重要です。素晴らしいレーシングゲームを作成して、ただ単に VR に移植しても、脳に正しくない情報を送り、プレーヤーの反射神経を混乱させるだけです。脳は一人称の動作を VR で見たときに、目にした動きとのバランスをとるため足や胴体に信号を送ります。人によってはこれを面白いと感じるかもしれませんが、多くの人にとってこれは不快な体験です。特に、このときプレーヤーが立った状態であれば、倒れる可能性もあります。座った状態であれば、それほど問題にはならないでしょう。ゲームに最適な体験を評価し、プレイ時は座るべきか立つべきかを推奨して、プレーヤーがバランスを崩すことなくシーンを移動できるように動きを調整します。

立った状態でプレイする場合、乗り物酔いのような状態にならないようにするには、移動を完全に排除して、テレポート機能によりゲーム内のある場所から別の場所へ移動します。テレポート機能を使用することで、立った状態で任意の方向へ数フィート (1 フィートは 30.48cm) 以内に体験を制限できます。そして、プレーヤーがコントローラーでゲーム内の別の場所をポイントしたらそこへテレポートします。例えば、FPS シューターの動きは前後数歩に制限し、それよりも遠い場所への移動にはテレポートを使用すると良いでしょう。

私のサンプルゲームは元々、一人称の宇宙をドライブする体験として設計しました。そのため、部屋全体を使うのではなく、座った状態でプレイすることを前提としています。これは、複雑な脳信号に対してプレーヤーがバランスと取ろうとして下肢静止不能症候群 (別名: むずむず脚症候群) のようになるのを回避し、より自然にシーンを動き回れるようにします。また、多くの動きを減衰することで、動きが緩やかになるようにしました。このゲームでは、宇宙に浮かんでいるため、急旋回したり、急激な動きがないため、この方法でうまく対応することができました。座った状態で体験するため、多少の動きを感じることはありますが、脳からバランスをとるための信号が送られることはなく、より制御されています。(この記事で VR に移植したオリジナルのゲームについては、Ultrabook™ を使用して Unity* ゲームを作成するこちらの記事 (英語) を参照してください。)

一人称向けの設計: VR の別の課題は、主に一人称体験であることです。古いゲームが一人称で設計されていない場合、三人称から魅力的な一人称体験に変更する方法を検討すると良いでしょう。

サンプルゲームは、ハイブリッドの一人称ゲームですが、アクションを追跡するカメラがあります。しかし、これではあまりにも受動的であり、間接的な一人称体験であると感じたため、スクリプトを利用して、間接的に追跡するのではなく、プレーヤーを直接アクションに固定しました。

自然でインタラクティブな UI とコントロール: インタラクティブな UI とコントロールも VR の課題の 1 つです。カメラの位置が固定されていないため、通常のようにオンスクリーン・メニューを表示することができません。カメラの位置は、プレーヤーが向いている方向です。そのため、自然なコントロール、メニュー、対話になるようにする必要があります。コンソールゲームの場合、問題なく対応できるでしょう。ほとんどのコンソールゲームは、画面ではなくゲームパッドに表示されるからです。タッチ・コントロール、複雑なキーやマウス操作可能な画面コントロールを使用するモバイルゲームの場合、コントロールのスキームを見直す必要があります。

サンプルゲームには、左折、右折、前進、発射の 4 つのコントロールがあります。このゲームのモバイルバージョンには、オンスクリーンのタッチ・コントロールがあり、PC バージョンではキーコマンドを使用します。VR バージョンでは、戦車のようなコントロールを設計して、左のコントローラーは時計回りに回転し、右は反時計回りに回転するようにしました。これは良いアイデアのように思われました。しかし、テストしてみると、左の親指で両方の回転を操作したい衝動に駆られました。私の手がゲームパッドのドライビング・ゲームを覚えており、無意識のうちにそうなってしまうのです。HTC* Vive* コントローラーは、ゲームパッドのように手で持つタイプで、指は自由に使うことができるため、設計し直すことにしました。左のコントローラー・パッドで操縦し、右のトリガー (ゲームパッドの A ボタンに相当) で前進し、右の親指 (ゲームパッドの X ボタンに相当) でレーザーを発射するようにしました。

360 度の活用: VR 体験では 360 度を見渡すことができるため、前進のみの体験ではせっかくの機能を無駄にすることになります。実際に、これは通常のゲームで見られる問題です。オリジナルのサンプルゲームでは、プレーヤーが左右や背後から近づく敵を見つけられるように、すべての敵の mini-HUD ディスプレイを設計しました。VR バージョンでは、ディスプレイがなくても、左右を確認するだけで敵を見つけることができます。この 360 度の VR 体験をフル活用できるように、前方視野外にハザードを意図的に配置していく予定です。

サンプルゲームの VR 向け再設計の決定事項:

  • 乗り物酔いや下肢静止不能症候群のような状態を回避するため、座った状態でプレイする
  • 追跡カメラを無効にして、カメラを前方に固定する
  • バランスの問題を回避するため回転や動きを緩やかにする
  • コントロールを HTC* Vive* コントローラーにマップする (ゲームパッドのコントロールと同様のレイアウト)
  • パノラマ体験によってもたらされる視野とアクションを活用する機能を追加する

SteamVR Plugin の追加

これは通常簡単ですが、場合によっては困難な場合もあります。コードがどれぐらい古いか、および SteamVR Toolkit がどの程度動作するかに依存します。ここでは、通常の追加方法とそれがうまくいかなかった場合にすべきことを説明します。

最新の Unity* ビルド向けにゲームをクリーンアップする: 最初に、オリジナルゲームのコピーを作成します。Unity* の最新バージョンをダウンロードして、そのバージョンでゲームを開きます。メッセージが表示された場合は、API を更新します。ゲームを再生して、更新が必要な古い API がないかコンソールを確認します。

SteamVR Plugin をインストールする: 最新バージョンの Unity* でゲームが想定どおりに動作する場合は、SteamVR Plugin をダウンロードしてインポートします。Unity* Store に移動して、SteamVR Plugin を検索します。Steam* ロゴの付いたものを選択します。[Import] をクリックします。

[Select All] を選択してすべての機能、モジュール、スクリプトをインポートします。すべて SteamVR フォルダー以下に配置されるため、スクリプトとアセットがどのプラグインのものかがすぐに分かります。

サンプルシーンをテストする: 次のステップに進む前に、HTC* Vive* ハードウェアが接続済みで、動作していることを確認します。Unity* で [Project] タブを表示し、SteamVR フォルダーを開き、Scenes フォルダーを選択して Example シーンを選択します。さまざまなボックスと SteamVR のスクリーンショットから成るシーンが表示されます。シーンを再生して、ヘッドセットを装着します。シーンと HTC* Vive* コントローラーが表示されていれば、SteamVR Plugin は正常に動作していると見なせます。

表示されない場合 – 新しいプロジェクトで試してみる: コントローラーが表示されない場合、または SteamVR のサンプルがヘッドセットにロードされない場合、空のプロジェクトを新規作成します。そして、SteamVR Toolkit をインポートして、サンプルシーンをテストします。うまく表示できない場合は、Unity* バージョンまたは Stream* ハードウェアに問題がある可能性があります。

新しいプロジェクトでは動作するが、古いプロジェクトでは動作しない場合: 新しいプロジェクトでは問題なく動作するが、オリジナルゲームが SteamVR Plugin で動作しない場合、再度空のプロジェクトを作成し、SteamVR Toolkit をインストールして、テストします。これで動作した場合は、この新しいプロジェクトにオリジナルゲームを移動します。最初にプロジェクトを閉じます。そして、クリーンアップ済みの Unity* プロジェクトの Assets フォルダー以下をすべてコピーして、新しいプロジェクトの Asset フォルダー以下にペーストします。Unity* プロジェクトを開いて VR をテストします。先程動作したので、動作するはずだと思われるでしょうが、このままでは動作しません。プロジェクト・コンポーネントからゲームアセット (モデル、サウンドファイルなど) へのリンクを含む、多くのアセットのプロジェクト設定が失われているためです。各アセットと各コンポーネントでゲーム・オブジェクトとサウンドファイルが正しくリンクされていることを確認します。これは面倒な作業です。私も 2、3 回行いましたが、面倒ですが、単純な作業です。

カメラを変更する: 先に進む前に、HTC* Vive* ヘッドセットを装着して、通常どおりにゲームを実行し、頭 (カメラ) を動かして周囲を見渡せることを確認します。しかし、HTC* Vive* コントローラーがなく、VR でカメラを使用するために必要なものがまだありません。この統合で最も重要なステップが次のステップです。これは非常に簡単です。アプリケーションで VR を動作させるには、シーンの Main Camera を SteamVR フォルダーにある Prefab カメラに置換する必要があります。ただそれだけです。しかし、既存のカメラには大量のスクリプトや設定が含まれているかもしれません。そのため、直接カメラを置換するのではなく、既存のカメラの子として Prefab カメラをドロップすることを推奨します。

次に、既存のカメラの [Properties] パネルで、カメラとそのすべてのコンポーネントをオフにします。[Properties] パネルでカメラの横のチェックマークがオフになったことを確認します。

そして、カメラのコンポーネントと設定を Prefab 内の Camera Head または Camera Eye にコピーします。設定を移動、コピー、調整して、HTC* Vive* ヘッドセットでスカイボックスなどが想定どおりに動作するようにします。

これで、ゲームが VR 対応になりました。シーンをプレイできるはずです。VR ヘッドセットを装着して、ゲームを実行すると、手の中にコントローラーが見えるでしょう。後は、コントローラーとスクリプトを既存のゲームロジックと連携させるだけです。

コントローラー・スクリプト

私のように JavaScript* でオリジナルのゲームを開発した場合、少し面倒ではあるものの、コントローラー・スクリプトをゲームと連携させるのはさほど難しいことではありません。ゲームで JavaScript* を使用しており、コントローラーとスクリプトを連携させる必要がある場合、次のセクションを参照してください。シーンの左と右のコントローラーで次のことを行います。コントローラーは、Main Camera 以下の Prefab カメラの子アセットとして追加されています。Extra フォルダーにある Tracked Controller Script を Camera Prefab 内の Controller (Left) にドラッグします。Controller (Right) に対しても同様の操作を行います。

この操作を行うと、[Inspector] に [Trigger Pressed] とその他の項目が表示され、ゲームでの使用状況に応じてチェックがオン/オフに切り替わります。

コントローラーの使用は、イベント・リスナー・メソッドに似ています。引き金を引いたり、タッチパッドをタッチするなどのイベントをキャプチャーするには、C# スクリプトを作成する必要があります。次のスクリプトは、多くのフォーラムで見られる手法です。私のサンプルゲームでの、引き金を引く、引き金を戻す、コントローラー・パッドをタッチする、コントローラー・パッドから指を離すというイベントトリガーを作成する方法を示します。

引き金を引いたときまたはタッチパッドをタッチしたときにイベントをトリガーする C# スクリプト

using UnityEngine; 
using System.Collections; 

public class EasyController : MonoBehaviour {
    private SteamVR_TrackedController device; 
    void Start () {
        device.TriggerClicked += Trigger
        device.TriggerUnclicked += UnTrigger ; 

        device.PadTouched += PadTouch; 
        device.PadUntouched += PadLift; 
    }

    void Trigger(object sender, ClickedEventArgs e)     
        {
//        引き金を引いたときのコードをここに記述
        }
    void UnTrigger(object sender, ClickedEventArgs e)
    {
 //        引き金を離したときのコードをここに記述
            }

    void PadTouch(object sender, ClickedEventArgs e)     
    { 
//        パッドをタッチしたときのコードをここに記述
      }
    void  PadLift(object sender, ClickedEventArgs e) 
    {
 //        パッドから指を離したときのコードをここに記述
    }
}

コントローラーに関するヒント: VR 体験の開発では、左のコントローラーか、右のコントローラーかを区別することができません。そのため、いずれかのコントローラーに光や球体などの 3D アセットを追加して、区別できるようにすることを推奨します。これは開発のための措置です。ユーザーにコントローラーの使い方を示すアイコンやラベルの作成方法については、UI セクションで後述します。

さらに、タッチパッドには、タッチする/指を離す以外の使用法もあります。例えば、スクリプトでタッチパッドのタップした位置に応じてゲームを制御することができます。次のスクリプトは、タッチパッドの左右どちら側をタップしたかに応じてアクティビティーをトリガーします。

VR タッチパッドの X 値または Y 値を使用する C# スクリプト

using UnityEngine; 
using System.Collections; 
using Valve.VR; 

public class myTouchpad : MonoBehaviour
{
    SteamVR_Controller.Device device; 
    SteamVR_TrackedObject controller; 

    void Start()
    {
        controller = gameObject.GetComponent<SteamVR_TrackedObject>(); 
    }

    void Update()
    {
        device = SteamVR_Controller.Input((int)controller.index); 
        // タッチパッドに指が触れている場合

        if (device.GetTouch(SteamVR_Controller.ButtonMask.Touchpad))
        {
            // タッチパッドの値を読み取る
            touchpad = device.GetAxis(EVRButtonId.k_EButton_SteamVR_Touchpad); 
            touchstate = device.GetPress(EVRButtonId.k_EButton_SteamVR_Touchpad); 

        if (touchpad.x < 0)
            {
                // コントローラーの左側がタップされたときのコードを追加
            }

        if (touchpad.x > 0)
            {
                // コントローラーの右側がタップされたときのコードを追加
            }
       }
        
     else
       {
            // パッドに指が触れていないときのコードを追加
        }

    }

}

C# と JavaScript* の問題点

SteamVR スクリプトは C# なので、HTC* Vive* コントローラーと既存の JavaScript* のゲームロジックを連携させることは困難または不可能に思えるかもしれません。次の単純な手法は、JavaScript* から C# への変換を行わずに、この問題に対応します。しかし、最終的には JavaScript* を C# に移行することを強く推奨します。

JavaScript* と C# 間の変数の受け渡し: Unity* には、スクリプトのコンパイル順があります。C# 変数を JavaScript* に渡したり、その逆も可能ですが、そのためには、スクリプトを Standard Assets フォルダーに移動する必要があります。Standard Assets フォルダーは最初にコンパイルされるため、変数が利用可能になります。SteamVR 値の確認には JavaScript* が必要なため、Standard Assets フォルダーに Steam* スクリプトを配置し、最初にコンパイルする必要がありますが、移動すると SteamVR プラグインが利用できなくなり、C# 変数を JavaScript* に渡せなくなります。

しかし、別の方法があります。

単純な回避策: C# と JavaScript* はどちらも、ゲーム・オブジェクトの値を GET および SET することができます。例えば、どちらのスクリプトも、シーンに含まれるゲーム・オブジェクトのタグ値を取得・定義できます。ゲーム・オブジェクト・タグ自体は、スクリプト間で受け渡し可能な変数です。初期設定でシーンの LaserCannon が “notfired” とタグ付けされている場合、C# で LaserCannon.tag を “fired” に設定するタッチパッド・イベントを作成できます。既存の JavaScript* は、各フレームのオブジェクトのタグ値を参照できるので、(C# スクリプトによって) LaserCannon.tag = “fired” になったら、JavaScript* でレーザー砲を発射する関数を実行します。これにより、C# から JavaScript* へイベントや値を渡したり、その逆が可能になります。

前述の C# サンプルコードを使用して、JavaScript* と変数を共有する方法を示します。タッチパッドの左側または右側をタップすると、適切なパーティクル・エミッターのタグ値が C# によって変更されます。パーティクル・エミッター、サウンド再生、衝突検出、関連ポイントをオンにするコードはすべて、左右のパーティクル・エミッターの JavaScript* にあります。そのため、最初に C# でこれらのパーティクル・エミッターを識別する必要があります。’rthrust’ と ‘lthrust’ を GameObjects として宣言して、Start セクションで ‘lthrust’ をシーンの左のパーティクル・エミッター、’rthrust’ を右のパーティクル・エミッターとして定義します。

ゲーム・オブジェクトを追加する C# スクリプト

public class myTouchpad : MonoBehaviour
{
    public GameObject rthrust; 
    public GameObject lthrust; 

    SteamVR_Controller.Device device; 
    SteamVR_TrackedObject controller; 


    void Start()
    {
        controller = gameObject.GetComponent<SteamVR_TrackedObject>(); 
        rthrust = GameObject.Find("PartSysEngineRight"); 
        lthrust = GameObject.Find("PartSysEngineLeft"); 
    }

次に、タッチパッドのどちら側がタップされたか決定する if 文内に、’lthrust’ と ‘rthrust’ のタグ名を変更するコードを追加します。(左右逆のように思われるかもしれませんが、宇宙で右折するには、左のスラスターを噴射する必要があります。)

パッドをタッチしたときにオブジェクトのタグ値を変更する C# スクリプト

if (touchpad.x < 0)
            {
                lthrust.tag = "nolthrust"; 
                rthrust.tag = "rthrust"; 
            }

 if (touchpad.x > 0)
            {
                rthrust.tag = "northrust"; 
                lthrust.tag = "lthrust"; 
            }

最後に、各パーティクル・エミッターの JavaScript* で、タグが C# スクリプトによって設定された値と等しいかどうかチェックする追加の条件 “|| this.gameObject.tag=’rthrust’” を if 文の最後に追加します。

C# によって更新されたタグに応じてゲームロジックを実行する JavaScript*

if (Input.GetKey ("left") || this.gameObject.tag=="rthrust"){
         GetComponent.<ParticleSystem>().enableEmission= true; 
         GetComponent.<AudioSource>().PlayOneShot(thrustersright); 
    }    

これで、C# から JavaScript* へイベントや値を渡せるようになりました。同じ方法で、JavaScript* から C# へイベントや値を渡すことができます。これは、コントローラーが動作するようにし、2 つの言語およびスクリプト間で基本的なゲーム体験を構築するための単純な回避策です。時間のあるときに、JavaScript* から C# へ移行することを推奨します。

C# に関するヒント: C# に不慣れな方は、次のヒントを参考にすると良いでしょう。加算、乗算、除算の結果が小数を含む場合、変数を float として宣言する必要があります。加算、減算、乗算、除算する数値も float として設定する必要があります。

  • 宣言の例: public float: myVar ;
  • 計算の例: myVar = (float)otherVar +1f

2D GUI から 3D UI へ

オリジナルの Unity* ゲームは、GUI メニュー、ボタン、その他のオンスクリーン・コントロールや UI 要素を使用しているかもしれません。私の古いゲームはタブレットと PC 向けに設計されたためオンスクリーン・コントロールと GUI の両方を使用していました。スタートアップ・メニュー、タイトルのグラフィック、オンスクリーン・スコア、アラートに GUI を使用していました。これらはすべてヘッドセット内では無効にし、PC 画面上では表示可能にしました。Google* で調査した結果、UI はゲーム内の 3D 空間に配置して VR で表示や対話ができるのに対し、GUI ではこれが不可能であるため、GUI を UI に変換する必要があることが分かりました。

VR 向けの UI に関するヒント: UI を設定するには、[Hierarchy] パネルで [UI] を右クリックして、[Canvas] を選択します。デフォルトでは、[Canvas] は [Screen Space] に設定されているため、[Inspector] ではすべての次元属性と位置属性がグレーで表示されていますが、[Inspector] の [Canvas] モジュールで [Screen View] から [World Space] に変更できます。

これで、UI 機能をシーンの任意の位置に、任意のサイズで配置できます。[Canvas] には、ボタン、イメージ、テキスト項目などの特定の UI 項目を追加できます。

テストするには、Text UI 要素を [Canvas] 項目の子として追加します (下の図の左側を参照)。そして、[Inspector] で [Text] 入力フィールドに “Hello World” と入力します。このテキストがシーンに表示されない場合は、[Text] の [Inspector] に移動し、[Paragraph] 以下の [Horizontal Overflow] と [Vertical Overflow] を [Overflow] に変更します。[Overflow] に設定することで、デフォルトのサイズが大きすぎてもテキストを確認することができます。必要に応じて、[Inspector] の [Scale] を調整してキャンバスまたはテキストのスケールを調整します。最適な設定になるように、さまざまなスケーリングとフォントを試してみると良いでしょう。フォントが粗い場合は、[Scale] を縮小して、[Font Size] を大きくします。

コントローラー UI: コントローラーの使い方を示すのに UI が役立ちます。左右のコントローラーを区別できるようにラベルを付けたり、コントローラーの使い方を示すアイコンを配置できます。左のスクリーンショットは、私のゲームのスタートアップ・シーンです。各コントローラーに UI を追加して、それぞれのコントローラーをどちらの手で持つかを示しています。メインのゲームシーンでは、グラフィック UI を使ってコントローラーの操作方法を示しています。

終わってみると、このゲームの更新はそんなに難しいことではありませんでした。また、オリジナルの設計よりも直感的で没入型の体験を提供するためにはどうしたらよいか考えることができました。この経験を通して、オリジナルの VR ゲームの取り組み方に関する多くの知識が得られました。以下のビデオで、VR で動作するゲームをご覧いただけます。

この記事に関するフィードバックは、Twitter @bobduffy (英語) までお寄せください。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

関連記事