ゼロから始めるVRアプリ/ゲーム開発の手引き

VR上のオブジェクトを「触る」「つかむ」「持ち運ぶ」仕組みを実装しよう

第3回では、Unity XR Interaction Toolkit(以下、Unity XRTKと表記)を使ってゲームオブジェクトを触ったりつかんだり運んだりするなど、VRで「手」を使ったインタラクションを実装する方法について解説します。

Unity XRTKのInteractorを知る

Unity XRTKはVRの開発をスムーズに進めるためのUnity公式パッケージで、本連載の第2回ではUnity XRTKに搭載された各種スクリプトのパラメータを調整することで、VRの移動方法の仕様が変えられることを解説しました。今回はUnity XRTKの要ともいえるInteractor、すなわち「プレイヤーによるオブジェクトへの干渉の仕方を制御する機能」の基本を押さえましょう。

プレイヤーがオブジェクトへ干渉する方法は、Unity XRTK上に用意されたComplete XR Origin Set Up -> XR Origin (XR Rig) -> Camera Offset -> Right ControllerとLeft Controller の中にある「Direct Interactror」⁠Ray Interactor」⁠Poke Interactor」の3つで実装され図1⁠、それぞれ「手元のオブジェクトを触る/つかむ」⁠遠くのオブジェクトを触る/つかむ」⁠オブジェクトをつっつく」動作を制御しています[1]

これらの操作を実現するためには、干渉されるオブジェクト側に「XR Grab Interactable」というコンポーネントを付与する必要があります。本記事では、これら「Direct Interactror」⁠Ray Interactor」⁠Poke Interactor」⁠XR Grab Interactable」の4つを説明したのち、⁠リュックサックなどに)オブジェクトを入れて持ち運ぶ操作」の実装法を紹介します。

図1 「Direct Interactror」「Ray Interactor」「Poke Interactor」の収納場所
図1

また、Interactorには「Hover」⁠Select」⁠Activate」という機能と「Entered」⁠Exited」という区分が用意されていますが、各セクションごとに正確な意味は異なるためそれぞれ説明を入れます。

近くのオブジェクトを直接触る/つかむ⁠Direct Interactor

Direct Interactor図2はプレイヤーがオブジェクトを直接つかんだり触ったりする機能であり、これなくしてVRコンテンツは開発できません。Direct Interactorはパラメータが初期状態のままでもオブジェクトをつかんだり触れたりすることが十分にできますが、さらにInteractor Eventsに用意された「手がオブジェクトに触れたとき(Hover⁠⁠」と「手がオブジェクトをつかんだ(Select⁠⁠」ときにイベントを発火させることができます。使い道としてはプレイヤーがオブジェクトをつかんだときに手の見た目を変更したり、効果音を再生したりといったイベントを仕込むことが想定されます(なお、Direct Interactorには「Activate⁠⁠:オブジェクトを使ったときのイベントが存在しません⁠⁠。また、HoverとSelectそれぞれに「Entered」「Exited」という2つの状態が標準で用意されていますが、それらの違いは後述のXR Grab Inteactableで解説します。

なお、好みの問題ですが「オブジェクトを握ったときに手元を見えなくしたい(握っているオブジェクトだけが見えるようにしたい⁠⁠」場合はHideControllerOnSelectを有効化してください。Unity XRTKの初期状態ではプレイヤーの手元のモデルがコントローラになっていますが、手元のモデルを実際の人間の手にした場合に「手がオブジェクトを握っているモーション」を作らずに省略できるといった利点があります。

図2 DirectInteractorの設定画面
図2

遠くのオブジェクトを触る/つかむ⁠Ray Interactor

Ray Interactorはコントローラ(プレイヤーの手)の先端からレイ(Ray:光線)を発射して遠くのオブジェクトに作用するためのインタラクションです。Ray Interactorにおいては「Hover」がレイを当てたとき、⁠Select」がレイを向けた対象を選択したときで、⁠Activate」は実質的にSelectと統合されており、存在しません。

Ray Interactorは、VRだとおもにプレイヤーの移動にかかる労力を省略する目的で、以下2つの用途に使います。

遠くのオブジェクトをつかむ

VRコンテンツにおいては「手を向けた方向にあるオブジェクトを、遠くからでも引き寄せてつかむことができる」実装がよく見られます。VRゲームでは2019年12月の『Boneworks』や2020年3月の『Half-Life: Alyx』ごろから実装の普及が見られました。Direct Interactorの「手の位置とそのすぐ近くにあるオブジェクトをつかむ」という仕様しか実装されていないと、床に落ちたオブジェクトを拾うときに現実でもしゃがんで床に手を伸ばさなければいけません。現実ではできないVRならではの不便の解消が「遠くにあるオブジェクトをつかむ」です。

少し困ったことに、プレイヤーがオブジェクトをつかんだときの処理がRay InteractorとDirect Interactorでやや異なります。RayとDirectのどちらも「Hover」⁠Select」の機能面においてはあまり変わりありませんが、Direct Interactorだと、つかんだオブジェクトはプレイヤーの手の座標に直にくっつきます。それに対しRay Interactorは、つかんだオブジェクトがプレイヤーの手の位置に追従する際、ちょっと遅れてついてきます(今のフレームと前のフレームの移動距離を補間する処理が入っているためです⁠⁠。これはRight/Left Controller Stabilized AttachがRay Ineractorに設定されているためで図3⁠、これによってプレイヤーがオブジェクトを直接握ったか遠くから握ったのかでオブジェクトが手を追従するときの挙動が異なってしまうのです。これが気になる場合はDirect InteractorをRay Interactorと同様に遅延するようにRight/Left Controller Stabilized Attachを設定するか、Ray Interactorの設定でこの機能を外して、Direct Interactorと挙動をそろえるかのどちらかをするとよいでしょう。

図3 デフォルトのRay Interactorでは、AttachTransformに座標を遅延させる機能が設定されている
図3

UIパネルを操作する

VRにおけるメニュー画面は、プレイヤーから見て1~2メートルほどの大きさのパネルをレーザーポインターで操作することが多いです。これをUnityで実装するときはRay Interactorを用います。

オブジェクトをつっつく⁠Poke Interactor

Pokeは日本語で「つっつく」という意味で、プレイヤーの指先でつっつかれるオブジェクトを制御する際に用います。おもにボタンを押すときに使いますが、意外にもVRではボタンを押すという実装そのものが少ないです。かつてUnity XRTKのサンプルとして使われていたシーン「DemoScene(Assets -> Samples -> XR Interaction Toolkit -> 任意のバージョン -> Starter Assets⁠⁠」にてボタンが配置されている様子が確認できます図4が、Unity最新のVRサンプルシーンでもRay Interactorと兼用のパネル操作に部分的に使われるのみとなっています。

図4 DemosceneのPoke Interactorサンプル
図4

VRゲームにおいて「つっつく」インタラクションがあまり見られなくなったのは2つの理由が見られます。はじめに、つっつく動作は先述のレーザーポインターで代用可能であることです。つっつく動作は機能面でいえばON/OFFといった2値の切り替えであり、これはほとんどRay Interactorのレーザーポインターで代替可能です。現状の技術におけるVRで近くにある小さいパネルを指でつつく図5よりも、遠くにある大きいパネルをレーザーポインターで制御するほうがはるかに楽なのです(Poke InteractorからRay Interactorに置き換わっていった歴史的経緯についてはVRエンジニアのizm氏のブログ記事がくわしいので、ぜひご一読ください⁠⁠。また、つっつく動作を実装することで「どれくらいボタンを深く押したか」を表現できるようにすることも考えられますが、VRは物理的な感触や反発力といった現実的なフィードバックが得られないため、VR上のボタン自体がアナログな入力を取得するのに向いていません。

もちろんインタラクションの楽しさや手ごたえを理由に指で押せるボタンが実装されているVRゲームも多数ありますが、それらはPoke Interactorではなく、物理演算によって制御されることが多いです。ジャンルにもよりますが、直観的には人間の指先だけでなくプレイヤーが手に持った剣やハンマーといった剛体のオブジェクトでもボタンは押せてしかるべきであり、⁠人間の指でだけ押せるボタン」という仕様をわざわざ実装する必要がありません。そのため、Unity XRTKにおいてもPoke Interactorは影の薄い機能となっています。

図5 Poke Interactorでパネルを押す
図5

オブジェクト側の制御⁠XR Grab Interactable

次はプレイヤーに干渉されるアイテム側の制御について説明します。XR Grab InteractableはVRで欠かせない「つかまれたオブジェクト」をつかさどるコンポーネントです。

まず左のHierarchyウィンドウを右クリックし、3D Objectから「Cube」「Sphere」など、適当なオブジェクトを追加してください(これが「つかまれるオブジェクト」です⁠⁠。次に、その追加したオブジェクトをクリックし、デフォルト設定では右に出てくるInspectorウィンドウの最下部、Add Componentから「XR Grab Interactable」を追加しましょう図6⁠。

図6 XR Grab Interactableの設定画面
図6

まずは「Hover」⁠Select」⁠Activate」の3つの意味から押さえましょう。Hoverはプレイヤーの手がオブジェクトの当たり判定と重なっているときです。オブジェクトに手が触れている、もしくはオブジェクトと手の位置が非常に近いものの、それでも握ったり持ったりはしていない状況を指します。

Selectはプレイヤーがオブジェクトを手に持ったときです。プレイヤーの手がオブジェクトにHoverしている状態でMeta Quest Controllerの中指のグラブボタンを押下すると、手にオブジェクトを持つことができます。

Activateはプレイヤーが手に持っているオブジェクトを使ったときです。プレイヤーがオブジェクトをSelectして手に持っている状態で、Meta Quest Controllerの人差し指のトリガーを押下すると、手に持っているオブジェクトを使うことができます(使ったときに起きるイベントが設定されていなければ、当然動きません⁠⁠。

これらを前提に、XR Grab InteractableのInteractable Events(XR Grab Inteactableで発火タイミングを制御できるイベント)を整理すると、以下のようになります図7⁠。

イベント名 説明
First Hover Entered Hoverに手が初めて入ったとき
(オブジェクトに手が初めて重なったとき)
Last Hover Exited Hoverから手が最後に抜けたとき
(オブジェクトから手が最後に離れたとき)
Hover Entered Hoverに手が入ったとき
(任意の手がオブジェクトに重なったとき)
Hover Exited Hoverから手が抜けたとき
(任意の手の位置がオブジェクトから離れたとき)
First Select Entered 手が初めてSelectしたとき
(グラブボタンを押してオブジェクトが初めてつかまれたとき)
Last Select Exited 手が最後にSelectをやめたとき
(グラブボタンを離してオブジェクトが最後に離されたとき)
Select Entered 任意の手がSelectに入ったとき
(グラブボタンを押してオブジェクトをつかまれたとき)
Select Exited 任意の手がSelectから抜けたとき
(グラブボタンを離してオブジェクトを離されたとき)
Activated 使われ始めたとき
(トリガーボタンを押したとき)
Deactivated 使われ終わったとき
(トリガーボタンを離したとき)
図7 XR Grab Interactable内、Interactable Eventsの設定画面
図7

さて、一部にある「First」「Last」の差がよくわからない方も多いと思いますが、たとえば「1つのオブジェクトに対して右手と左手の両方で触る」といった場合にこの機能を使うことができます。先に右手で触ると、そのタイミングで「FirstHoverEntered」「HoverEntered」のイベントが発火しますが、右手で触ったままさらに左手で触れると「HoverEntered」のイベントのみが発火します。

仮に「あるオブジェクトがプレイヤーに触られたら(ホバーされたら)目立つようにテクスチャを明るくする」といった仕様を実装するとして、プレイヤーが両手で触ったとしても、最初に触れたどちらかの手でのみイベントを実行すれば済む話です。

逆に「プレイヤーがあるオブジェクトに触るのをやめたら(ホバーをやめたら)テクスチャの色を元に戻す」という仕様を実装するとして、両手で触っている状態から片手だけ離したときにテクスチャの色が元に戻られては困ります。この場合は、オブジェクトをホバーしていた手(片手のみか両手かは問わない)がすべてホバーをやめた瞬間となる「LastHoverExited」にイベントを差し込むのが適切となります。

なお、先述のRay Interactorで触れた「遠くのオブジェクトをつかむ機能」ですが、Unity XRTKにおいては、このGrab Interactableのついたオブジェクトに、Add Componentからスクリプト「RayAttachModifier」を付与することで遠くにあるオブジェクトを手元に引き寄せることができます。RayAttachModifierを付与していない場合、Ray Interactorでつかまれたオブジェクトはプレイヤーの手元に引き寄せられず、その場で浮いたまま手の動きに追従します。筆者が考えうる限り、VRコンテンツにおいてはオブジェクトがその場で浮くよりも手元に引き寄せられる方が適切なことが圧倒的に多いはずです。

「放置されたオブジェクトが元の場所に戻る」仕様を実装する

ここまでプレイヤーがVRで道具に触れたり使ったりする機能について説明しました。一方で、VRにおいてプレイヤーが道具を持ち運ぶためには、プレイヤーのVR上の身体のどこかに道具を「固定」してあげる必要があります。これは一般的なゲーム開発においてインベントリと呼ばれます。

そこで、まずは前段として「元あった場所とは異なる場所に放置されたオブジェクトが元の配置に戻る」という実装をしてみましょう。これはたとえば、⁠プレイヤーがゲームオブジェクトを握っていない間はカウントを進め、カウントが限界に達したら元の座標に戻る」といった実装が考えられます。この実装は一番シンプルですが、オブジェクトがVR世界の特定の座標に固定されるため「長距離間で持ち運ぶ」ことができなくなります。本連載の第2回で紹介したルームスケ-ル(現実世界の2.0m x 2.0mに即したスケールのVR空間でのみプレイヤーが移動できる)に適した手段です。

// 時間経過で道具の位置を元に戻すスクリプトの例
using UnityEngine;

public class ItemCountDown : MonoBehaviour
{
    //// ゲームオブジェクトのRigidbodyを取得する
    //private Rigidbody m_rigidbody;
    // ゲームオブジェクトの復帰する座標(を持つゲームオブジェクト)を指定する
    [SerializeField] public GameObject m_gameObjectRestore;
    // 復活する座標を保持するための変数
    private Transform m_transtormRestore;
    // ゲームオブジェクトがすでに動いたかどうか
    private bool m_isMoved = false;
    // プレイヤーがゲームオブジェクトに触れているかどうか
    private bool m_isTouching = false;
    
    // ゲームオブジェクトの位置がリセットされるまでの時間制限
    public float m_grabItemTimeLimit = 3.0f;
    // スクリプト内のタイマーに用いる変数
    private float m_timer;

    // Start is called before the first frame update
    void Start()
    {
        m_timer = 0.0f;
    }

    // プレイヤーに握られたとき
    public void GetGrab()
    {
        m_isMoved = true;
        m_isTouching = true;
    }

    // プレイヤーに離されたとき
    public void ExitGrab()
    {
        m_isTouching = false;
    }

    // Updateは毎フレーム実行される
    void Update()
    {
        // 制限時間が0秒の場合は、位置のリセットを実行しない
        if (m_grabItemTimeLimit != 0)
        {
            // ゲームオブジェクトが動いたかどうか
            if(m_isMoved == true)
            {
                // プレイヤーが現在アイテムに触っているかどうか
                if(m_isTouching == false)
                {
                    // カウントダウンを進める
                    m_timer += Time.deltaTime;
                    //カウントダウンが制限時間を迎えたらゲームオブジェクトの位置をリセット
                    if(m_timer > m_grabItemTimeLimit)
                    {
                        // ゲームオブジェクトの速度をリセットする
                        var rigidbody = GetComponent<Rigidbody>();
                        rigidbody.velocity = Vector3.zero;
                        // ゲームオブジェクトを指定箇所に配置する
                        rigidbody.transform.position = m_gameObjectRestore.transform.position;
                        rigidbody.transform.rotation = m_gameObjectRestore.transform.rotation;
                        // ゲームオブジェクトは不動の状態に戻る
                        m_isMoved = false;
                        // カウントダウンをリセットする
                        m_timer = 0.0f;
                    }
                }
                // プレイヤーが触っている場合はカウントダウンをリセット
                else
                {
                    m_timer = 0.0f;
                }
            }
        }
    }
}

上記のスクリプトは「プレイヤーに握られたオブジェクトがプレイヤーが手から離してから3秒後に再配置される」というスクリプトですが、オブジェクトが元の位置に戻る条件は他にも「オブジェクトの速度が0のとき」⁠オブジェクトのY軸座標が0m以下になったとき」などさまざまなパターンが考えられます。

VRで道具を持ち運ぶための強力な機能「Socket Interactor」

Unity XRTKではプレイヤーが道具を持ち運ぶ操作を実現するときに「Socket Interactor」を使うことができます。Socket Interactorはコリジョン(オブジェクトとオブジェクトがぶつかったり触れたりしたことを判定するための形状、いわゆる当たり判定)に触れたオブジェクトを特定の位置座標に固定する機能で、ソケット自体は任意の場所に配置したり、他のオブジェクトに追従させたり動かしたりすることができます。VRコンテンツのインベントリにおいて強力な味方です。ソケットの配置方法にもいろいろあり、VRコンテンツの種類に合わせてさまざまな仕様が考えられます。

ただ、意図しないオブジェクトがソケットに収納されてしまうのは困ります。このためUnity XRTKにはInteraction Layer Maskという機能が用意されており、たとえばXR Grab Interactableに「GrabbableItems」という独自のレイヤーを作成・指定し、XR Socket Interactorには「GrabbableItemsのレイヤーを持つGameObjectのみソケットに挿入できるようにする」といったことを指定できます図8⁠。

図8 Interaction Layer Maskを用いた設定
図8

(1)ホルスター型

肩と腰といった身体の部位にソケットを配置する手法です。プレイヤーは、自らの肩や腰に手をやることで、そこに固定したアイテムを取り出すことができるようになります。たとえばシューター系のVRゲームであれば、両肩に大型の銃器、両腰にピストルなど小型の銃が配置されていることが多いです。現実でもショットガンは背中に背負って担ぐことが多いため、肩から取り出す実装になります。ピストルなど小型銃は腰元のホルスターに吊り下げておくのは想像しやすいでしょう。

ホルスター型のデメリットとしては、特に肩が「何のアイテムを所持していたかがわからなくなる」リスクがあることと、操作ミスによって肩のアイテムをつかみそこねるリスクがあることです。片手サイズの装備が2つに収まるのであれば両腰に吊り下げておくのがいちばんシンプルでわかりやすい実装になるでしょう。

Unityで実装する際は、プレイヤーの頭の真下にソケットを配置しつつ、プレイヤーの頭の直下に垂直な上半身がぶらさがっているとみなして、頭の垂直下の座標に合わせて両肩と両腰にUnity Socketを配置するのがシンプルな方法です図9図10⁠。とくに肩はよくテストプレイしてつかみ損ねが発生しないように調整してください。

図9 ホルスター型の階層(Hierarchy)例
図9
図10 ホルスター型ソケットの設定画面
図10

以下は、図10で実装しているスクリプト、Waist Holsters Lerpの実装例です。ソケットをプレイヤーのCameraOffset直下に配置しつつ、PlayerCameraのY軸の角度に合わせて回転しつづける処理を行っています。

// HolsterLerpの実装例

using UnityEngine;

public class WaistHolstersLerp : MonoBehaviour
{
    // XR Origin (XR Rig)直下のCameraOffsetを
    [SerializeField] Transform _cameraOffset;
    // ソケットの角度をカメラの向きに合わせる
    [SerializeField] Transform _playerHead;
    [SerializeField] private float timeRatio = 0.5f;
    private float timeCount;
    
    // Start is called before the first frame update
    void Start()
    {
        // タイマーを初期化
        timeCount = 0.0f;
    }

    // Update is called once per frame
    void Update()
    {
        // 回転の処理にはクォータニオンを使う
        transform.rotation = Quaternion.Slerp(_cameraOffset.rotation, _playerHead.rotation, timeCount * timeRatio);
        // rotationをY軸以外0にする処理
        transform.rotation = Quaternion.Euler(new Vector3(0f, transform.eulerAngles.y, 0f));
        timeCount = timeCount + Time.deltaTime;
    }
}

また、ホルスターだけでなくベストの形状になるようにソケットを配置することも可能です。特にミリタリー要素の強いVRゲームはプレイヤーが持ち運ぶオブジェクトの量が多いため(ナイフ、ピストル、ライフル、グレネード、回復アイテムなど⁠⁠、必然的にインベントリの実装に注力せざるをえません。モデルガンやBB弾などを常備するサバイバルゲームもVRのインベントリの実装の参考になるかと思います。

(2)手首型

『Half-Life: Alyx』で発明されたインベントリーの一種です。プレイヤーの手首にソケットを配置し、任意のアイテムをいつでも取り出せるようにすることができます。XR Socket InteractorにはSocket Scale Modeという便利な処理があり、ソケットに入れたオブジェクトのスケールをソケット側で指定することができます。これをStretched to Fit Sizeにすると指定したサイズ(この場合は手首に収まるぐらい)に収まるようスケールが調整されます図11図13⁠。

図11 手首型の階層(Hierarchy)例
図11
図12 手首型ソケットの設定画面
図12
図13 収納すると、スケールが自動調整される
図13

ただしオブジェクトをソケットから取り出すときにスケールをもとに戻す処理まではしてくれない(なんと不便なのでしょうか!)ので、ソケットから取り出したオブジェクトのスケールが元に戻る処理を仕込みましょう。

スケールをもとに戻す処理はいたってシンプルです。ゲームの起動時のスケールを保持しておいて、プレイヤーがソケットから取り出したタイミングで最初に保持したスケールを適用すれば問題ありません。オブジェクトのHover Exited(ソケットから取り出されたとき)のタイミングのイベントとして実行しましょう図14⁠。

using UnityEngine;

public class ItemScaleRecover : MonoBehaviour
{
    // ゲームオブジェクトのスケールを保持する変数
    private Vector3 m_defalutScale;
    
    // Start is called before the first frame update
    private void Awake()
    {
        // シーン起動時のゲームオブジェクトのスケールを保持する
        m_defalutScale = transform.localScale;
    }

    public void RecoverScale()
    {
        // 小型ソケットから取り出したときにゲームオブジェクトのスケールを元に戻す
        transform.localScale = m_defalutScale;
    }
}
図14 オブジェクトのHover Exitedに実装
図14

(3)バックパック型

VRでは現実と同じようにインベントリーがバックパックの形状をしていることが多く、プレイヤーの背中からアイテムを取り出すことがしばしばあります。この実装は、バックパックの中にソケットがいくつか用意されている場合図15と、バックパックの中に入ったアイテムをバックパックの子オブジェクトとしてバックパックに追従させる場合図16があります。

図15 ソケットを用いたバックパックの実装
図15
図16 アイテム同士を親子関係で紐づけたバックパックの実装
図16

後者の親子紐づけ型のバックパック、実はものすごくシンプルな実装です。バックパック内の空間にボックス状のコリジョンを配置し、バックパックに放り込まれた任意のオブジェクトがボックスのコリジョンにぶつかったら、そのオブジェクトをバックパックの子オブジェクトとみなして物理演算を止めるだけ。今回はオブジェクトがぶつかったコリジョンがバックパックかどうかを判別するのにタグを使っていますが、タグ以外の実装をしたい場合はコリジョンのレイヤーやInteraction Layerなど活用して改変してみてください。

このオブジェクト親子紐づけ型のインベントリーはあまり数多くのVRゲームで採用されているわけではありませんが、SteamVRやQuest StoreのベストセラーVRシューター『Into the Radius』やそのフォロワー『Ghosts of Tabor』でも採用されています。実装がかんたんであり、改変次第でさまざまな味付けが可能です。たとえばバックパックに重量制限を設けるとか、オブジェクト同士が重なってはいけないようにしてプレイヤーにバックパックの中の整理整頓を促すことができるでしょう。

using UnityEngine;

public class ItemAttachParent : MonoBehaviour
{
    // ゲームオブジェクトがぶつかったとき
    public void OnTriggerEnter(Collider other)
    {
        // ぶつかったゲームオブジェクトのコリジョンのレイヤーの種類を文字列にする
        string layerName = LayerMask.LayerToName(other.gameObject.layer);

        if (other.gameObject.tag == "Backpack")
        {
            // ぶつかったゲームオブジェクト(かばんを想定)を親ゲームオブジェクトにする
            this.transform.SetParent(other.gameObject.transform);
            // Rigidbodyを取得し、ゲームオブジェクトが物理設定で動かないようにする
            Rigidbody rb = this.transform.GetComponent<Rigidbody>();
            rb.isKinematic = true;
        }
    }

    // ゲームオブジェクトがプレイヤーに握られたとき
    public void ExitGrab()
    {
        // 親ゲームオブジェクトを空にして独立させる
        this.gameObject.transform.parent = null;
        // Rigidbodyを取得し、ゲームオブジェクトが物理設定で動くようにする
        Rigidbody rb = this.transform.GetComponent<Rigidbody>();
        rb.isKinematic = false;
    }
}

おわりに

この記事では道具を持ち運ぶ方法について解説しましたが、さらなる手段として「インベントリそのものを実装しない」という実装も考えられます。VRコンテンツによっては「プレイヤーが道具を持ち運ぶ必要性がまったくない」ことも十分に考えられるためです。もちろんそうではないケースがあるために汎用的な設計が生まれたのですが、その都度「自分の作っているVRコンテンツに適したインベントリの設計はなんだろうか?」と考えつづけることが大事です。VRは発展途上であり、あなたが「もっとも革新的なVRの仕様をいち早く発明したVRコンテンツ」を作ることもありえるのです。

おすすめ記事

記事・ニュース一覧