Unity + SteamVR SDKチュートリアル Part4

引き続きSteamVRのチュートリアルを進めていきます。
今回は「Using The Controllers With Physics Objects」のパートから開始します。
https://www.raywenderlich.com/9189-htc-vive-tutorial-for-unity

コライダーの設定

今回は物をつかんで投げるという動作を作成します。その準備として、まずはコントローラにコライダーを設定します。

まずは両方のコントローラを選択し、RigidBobyコンポーネントを追加します。
追加したRigidBodyコンポーネントはUse GravityをOFF、Is KinematicをONに設定します。

次にBox Colliderコンポーネントを追加します。
Box Colliderの位置を以下のように修正します。
・Center X=0, Y=-0.04, Z=0.02
・Size X=0.14, Y=0.07, Z=0.05
・Is Triggerにチェックを入れる

スクリプトの記述

下準備が終わったので、実際にモノを掴むというスクリプトを作成します。

RW > Scripts フォルダ配下に「ControllerGrabObject」という名前でスクリプトを作成します
スクリプト全体像は以下の通りになりますので、参考にしてみてください

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR; // SteamVRの機能を利用するので、using文を追加

public class ControllerGrabObject : MonoBehaviour
{
    public SteamVR_Input_Sources handType;          // コントローラ入力元
    public SteamVR_Behaviour_Pose controllerPose;   // コントローラの位置情報
    public SteamVR_Action_Boolean grabAction;       // コントローラの「つかむ」アクション

    private GameObject collidingObject;             // コライダーが接触しているオブジェクトを保存(接触しているコライダーが存在するか)
    private GameObject objectInHand;                // 現在オブジェクトを掴んでいるか

    // Update is called once per frame
    void Update()
    {
        if (grabAction.GetLastStateDown(handType))
        {
            if (collidingObject)
            {
                GrabObject();
            }
        }

        if (grabAction.GetLastStateUp(handType))
        {
            if (objectInHand)
            {
                ReleaseObject();
            }
        }        
    }

    // コライダーが接触したとき
    public void OnTriggerEnter(Collider other)
    {
        SetCollideingObject(other);
    }
    // コライダーが触れているとき
    public void OnTriggerStay(Collider other)
    {
        SetCollideingObject(other);
    }
    // コライダーがはずれたとき
    public void OnTriggerExit(Collider other)
    {
        if (!collidingObject)
        {
            return;
        }

        collidingObject = null;
    }

    private void SetCollideingObject(Collider col)
    {
        // すでに何かをつかんでいる場合、もしくは対象にRigidBodyコンポーネントが存在しない場合
        // 処理しない
        if (collidingObject || !col.GetComponent<Rigidbody>())
        {
            return;
        }

        // コライダーが接触したオブジェクトを保存
        collidingObject = col.gameObject;
    }

    // オブジェクトを掴む
    // FixedJointコンポーネントをコントローラオブジェクトに追加し、
    // つかんでいるオブジェクトのRigidBodyを接続することで
    // つかむという動作を実現している
    private void GrabObject()
    {
        objectInHand = collidingObject;
        collidingObject = null;

        var joint = AddFixedJoint();
        joint.connectedBody = objectInHand.GetComponent<Rigidbody>();
    }

    // FixedJointコンポーネントを動的に追加する
    // 勝手に外れないように、breakForceとbreakTorqueに大きな値を入れている
    private FixedJoint AddFixedJoint()
    {
        FixedJoint fx = gameObject.AddComponent<FixedJoint>();
        fx.breakForce = 20000;
        fx.breakTorque = 20000;

        return fx;
    }

    // オブジェクトを放す
    // 追加したFixedjointを破棄し、
    // 放したときのコントローラのVelocityをオブジェクトにコピーすることで
    // 「投げる」という挙動を再現している
    private void ReleaseObject()
    {
        if (GetComponent<FixedJoint>())
        {
            GetComponent<FixedJoint>().connectedBody = null;
            Destroy(GetComponent<FixedJoint>());

            objectInHand.GetComponent<Rigidbody>().velocity = controllerPose.GetVelocity();
            objectInHand.GetComponent<Rigidbody>().angularVelocity = controllerPose.GetAngularVelocity();
        }

        objectInHand = null;
    }
}

作成したスクリプトをコントローラに適用していきます。スクリプトを適用した後は「HandType」「Controller Pose」「Grab Action」の3つを設定します

動作確認

以上の設定でボールや箱がつかめるようになっているはずです。

ものがつかめるようになったことで、VRで表現できる幅が一気に広がりますね

今回は以上です

次回からはテレポーテーションの作成準備に入っていきます