はじめに
Unity+VRTKで,VR空間において,モーションコントローラーに追従するラケットを振り回し,ボールを打ち返すゲームを作ったが,ラケットとボールと思うように反発せずハマったためメモした.ラケットに限らず,VR空間でバットなどを振り回すゲームにも応用できそう.
うまく反発しない原因としては.Unityにおいて,オブジェクト同士の正しい反発を計算するには,ボールとラケットのRigidbody.velocity
変数に正しく速度が設定されている必要があるが,ラケット側のそれは常にVector3.zero
であることが考えられる.
ただし,スクリプトでラケットのRigidbody
コンポーネントを直接いじるとラケットが変な動きをしそうなので,ラケット自体に速度は設定せず,ラケットと同じ位置に適切な速度が設定されたオブジェクトを配置することで,跳ね返りを実現した.
下準備
Unityプロジェクトを作成し,開発環境にあわせてVRTKが使えるようにセットアップする.
ラケットの作成
- シーンに空オブジェクトを作成し,名前を
Racket
に変更.Transform
をリセットしておく. - 子にCylinderとCubeを配置し,それぞれ名前を
Face
,Grip
等に変更.それぞれのTransform
を適当にいじりラケットの形にする, - このままではコライダーが正しく設定されないので,
Grip
オブジェクトのBox Collider
コンポーネントを削除する.Face
オブジェクトのCapsule Collider
を削除し,代わりにMesh Collider
をアタッチし,Convex
にチェックする. - 適当なPhysic Materialを作成し,コライダーのMaterialにセットする.
コントローラーの作成
- シーンに2つの空のオブジェクトを作成し,それぞれ
RightController
,LeftController
などに変更. -
Racket
オブジェクトをどちらかの子にする. -
VRTK_SDK Manager
のScript Aliases
に作成した2つのオブジェクトを登録する. - シーンを再生し,コントローラーを動かすとラケットも同様に動くことを確認する.
- グリップの位置が変な場合,
Racket
オブジェクトのTransform
を適当にいじって調整する.
ここまででこんな感じになる.マテリアルはなんでも良い.
ボールを打ってみる
- シーン中にSphereを配置し,
Rigidbody
コンポーネントをアタッチする.とりあえずはUse Gravity
のチェックを外しておく. -
Scale
を(0.1, 0.1, 0.1)
等に変更してボールっぽくする.ボールを前方 (+z方向) に配置する. - ラケットと同様に,適当なPhysic Materialを作成し,コライダーにセットする.
- 以下のようなスクリプトをアタッチして,動いてくるボールを打ってみる.
- おそらくうまく跳ね返らない.
void Start() {
GetComponent<Rigidbody>().AddForce(Vector3.back);
}
ラケットに追従するオブジェクトを作成する
ここでRigidbody
を持ったオブジェクトを作成.適切な速度を設定し,ラケットに追従するオブジェクトを作成する.これを用いてボールが跳ね返るようにする.
-
Face
オブジェクトを複製し,シーン中に適当に配置.FaceFollower
等に名前を変更する. -
Rigidbody
コンポーネントをアタッチし,Use Gravity
のチェックを外す. - 適当にわかりやすいマテリアルに変更する.
-
Face
オブジェクトからはColliderコンポーネントを削除する. -
Face
オブジェクト,FaceFollower
オブジェクトに以下のようなスクリプトをアタッチし,FaceFollower
が自身のRigidbody
コンポーネントに適切な速度を設定しながら,Face
を追従するようにする. -
FaceFollower
をプレハブ化し,シーンから削除.faceFollowerPrefab
に作成したプレハブを登録する.
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);
}
}
[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
値を調節する.うまくいけば,FaceFollower
のMesh Renderer
を無効にして完成.
参考リンク