UnityにおいてColliderが衝突した時に呼び出されるOnCollisionEnterを利用して、ボールが綺麗に跳ね返る、あるいは反射する壁を作ってみました。ここでのボールとはRigidbodyコンポーネントを持ち、物理挙動をするボールのことです。
単純にボールがバウンドするような壁はPhysic Materialでも作れないことは無いと思いますが、今回はそれとは違う、よりゲーム的に都合が良い結果が出せるであろうアプローチを取っていきます。
ボールの向きを反射する
シーンにcubeを追加して、スケールを変更し横長の壁を作ります。そしてBallReflectionWallというC#スクリプトを作って追加します。
このスクリプトの中で行われる処理の流れとしては、OnCollisionEnterに入力されるCollisionからContactPointをGetContactメソッドで取得し、接触面のnormalを利用してボールのvelocityを反転(Reflect)させて再入力する、ということになります。
また衝突がボールによって起きたのかを検知するためにCompareTagメソッドを使いますが、"Ball"タグを作成してボールに設定しておく必要があります。
言葉で説明するとややこしい感じがしますが、上画像にあるようにコードとしては十行ほどで意外と短く収まります。
Rigidbodyの進む方向とスピードは、その方向と長さを掛け合わせた三次元ベクトルの形で、velocityというVector3変数に格納されており、逆にそこに代入すれば思うがままにRigidbodyを動かすことが出来ます。いきなりその速度になるので、徐々に加速するという表現には合いませんが、弾の発射などを再現するには十分だと思います。(もちろんAddForceメソッドでも同様な挙動は再現できますが)
relativeVelocityとは
このようなボールを跳ね返す壁を作る際には、このvelocityをそのまま反転させて再入力することを考えますが、OnCollisinEnterが呼び出されるのは衝突の後なので、そこからボールのRigidbodyに対してvelocityを取得しても当然止まってしまっています。
なので、Collisionインスタンスに含まれるrelativeVelocityを利用します。これは衝突した2つのオブジェクトの相対的な速度ですが、ここから衝突してきたボールのvelocityを取得することができ、これをVector3.Reflectで反転させることで、跳ね返ったボールの向きとスピードを求めることが出来ます。
相対的、ということは壁は当然止まっているので、必然的に壁に突っ込んできたボールのスピードがここから取得できるということです。
また、このコード内でvelocityに1.0以上の数値を掛け合わせると衝突時よりも速いスピードでボールを跳ね返すことができ、ピンボールにおけるバンパーのようなゲームとして面白いギミックが作れそうです。反転させたvelocityに掛ける新たな変数を追加するだけで、この機能を追加できます。
まとめ
以上で、ボールを綺麗に反射する壁を作ることが出来ました。ここに機能を追加していけば、跳ね返す時に音を出したり壁が動くなどのアニメーション的表現も可能になりますが、今回はひとまずC#スクリプトを作成して基本的動作を作った感じです。
物理演算はコントロールが難しいですが、やはり面白い挙動が作れますね。物理演算を上手く利用したゲーム、あるいは機能が作れるように頑張りたい!