Unity
VR
VRTK

Unity+VRTKにおいてボールと反発するラケットを作成する

はじめに

Unity+VRTKで,VR空間において,モーションコントローラーに追従するラケットを振り回し,ボールを打ち返すゲームを作ったが,ラケットとボールと思うように反発せずハマったためメモした.ラケットに限らず,VR空間でバットなどを振り回すゲームにも応用できそう.

うまく反発しない原因としては.Unityにおいて,オブジェクト同士の正しい反発を計算するには,ボールとラケットのRigidbody.velocity変数に正しく速度が設定されている必要があるが,ラケット側のそれは常にVector3.zeroであることが考えられる.

ただし,スクリプトでラケットのRigidbodyコンポーネントを直接いじるとラケットが変な動きをしそうなので,ラケット自体に速度は設定せず,ラケットと同じ位置に適切な速度が設定されたオブジェクトを配置することで,跳ね返りを実現した.

下準備

Unityプロジェクトを作成し,開発環境にあわせてVRTKが使えるようにセットアップする.

ラケットの作成

  1. シーンに空オブジェクトを作成し,名前をRacketに変更.Transformをリセットしておく.
  2. 子にCylinderとCubeを配置し,それぞれ名前をFaceGrip等に変更.それぞれのTransformを適当にいじりラケットの形にする,
  3. このままではコライダーが正しく設定されないので,GripオブジェクトのBox Colliderコンポーネントを削除する.FaceオブジェクトのCapsule Colliderを削除し,代わりにMesh Colliderをアタッチし,Convexにチェックする.
  4. 適当なPhysic Materialを作成し,コライダーのMaterialにセットする.

コントローラーの作成

  1. シーンに2つの空のオブジェクトを作成し,それぞれRightControllerLeftControllerなどに変更.
  2. Racketオブジェクトをどちらかの子にする.
  3. VRTK_SDK ManagerScript Aliasesに作成した2つのオブジェクトを登録する.
  4. シーンを再生し,コントローラーを動かすとラケットも同様に動くことを確認する.
  5. グリップの位置が変な場合,RacketオブジェクトのTransformを適当にいじって調整する.

ここまででこんな感じになる.マテリアルはなんでも良い.

画面1.PNG

ボールを打ってみる

  1. シーン中にSphereを配置し,Rigidbodyコンポーネントをアタッチする.とりあえずはUse Gravityのチェックを外しておく.
  2. Scale(0.1, 0.1, 0.1)等に変更してボールっぽくする.ボールを前方 (+z方向) に配置する.
  3. ラケットと同様に,適当なPhysic Materialを作成し,コライダーにセットする.
  4. 以下のようなスクリプトをアタッチして,動いてくるボールを打ってみる.
  5. おそらくうまく跳ね返らない.
Ball.cs
void Start() {
    GetComponent<Rigidbody>().AddForce(Vector3.back);
}

ラケットに追従するオブジェクトを作成する

ここでRigidbodyを持ったオブジェクトを作成.適切な速度を設定し,ラケットに追従するオブジェクトを作成する.これを用いてボールが跳ね返るようにする.

  1. Faceオブジェクトを複製し,シーン中に適当に配置.FaceFollower等に名前を変更する.
  2. Rigidbodyコンポーネントをアタッチし,Use Gravityのチェックを外す.
  3. 適当にわかりやすいマテリアルに変更する.
  4. FaceオブジェクトからはColliderコンポーネントを削除する.
  5. Faceオブジェクト,FaceFollowerオブジェクトに以下のようなスクリプトをアタッチし,FaceFollowerが自身のRigidbodyコンポーネントに適切な速度を設定しながら,Faceを追従するようにする.
  6. FaceFollowerをプレハブ化し,シーンから削除.faceFollowerPrefabに作成したプレハブを登録する.
RacketFace.cs
public class RacketFace : MonoBehavior {
    [SerializeField]
    GameObject faceFollowerPrefab;

    private GameObject faceFollower;

    void OnEnable(){
        faceFollower = Instantiate(faceFollowerPrefab, transform.position, transform.rotation);
        faceFollower.GetComponent<RacketFaceFollower>().SetTarget(gameObject);
    }

    void OnDisable(){
        Destroy(faceFollower);
    }
}
RacketFaceFollower.cs
[RequireComponent(typeof(Rigidbody))]
public class RacktFaceFollower : MonoBehavior{
    [SerializeField]
    float sensitivity = 50f;

    private Rigidbody rb;
    private GameObject target;
    private Vector3 velocity;

    void Awake(){
        rb = GetComponent<Rigidbody>();
    }

    public void SetTarget(GameObject target){
        this.target = target;
    }

    void FixedUpdate(){
        if(target != null){
            velocity = (target.transform.position - rb.transform.position) * sensitivity;
            rb.velocity = velocity;
            rb.transform.rotation = transform.rotation;
            transform.rotation = target.transform.rotation;
        }
    }

}

シーンを再生するとボールをうまく打てるようになっているはず.FaceFollowerが変な動きをしていたら,RacketFaceFollower.sensitivity値を調節する.うまくいけば,FaceFollowerMesh Rendererを無効にして完成.

参考リンク

https://unity3d.college/2016/04/11/baseball-bat-physics-unity/