今回はアクションゲームでよくある主人公が乗るとその重みで落下する床をUnityで作成したいと思います。
仕様としては床ごとに主人公が乗っている時間を保持し、その時間を超えたら落下するようにします。
時間はインスペクタ上で設定出来るようにします。
また床がそのまま残っていると邪魔になるので、地面と接触したら床自体を消してしまいます。
落ちる床のゲームオブジェクトを作成
まずは坂と落ちる床を作っていきます。
落ちる床はFallFloor1とFallFloor2になります。Slopeも含めてすべて3D Object→Cubeで作成します。
FallFoor1とFallFloor2のLayerにFallFloorを新しく作り設定しておきます。
FallFloor1とFallFloor2にはFallFloorという新しいスクリプトを作成し追加します。
Rigidbodyも取り付けておきます。
色々視界に入り見えづらいですが・・・、坂と落ちる床を繋げて上のようにしました。
落ちる床が主人公キャラの背の高さと同じぐらいでぶつかる位置にある場合は注意が必要です。
主人公キャラが持っているコライダが床と接触している時間で床を落とすので、床の下や横に主人公がぶつかっている時にも計測されてしまいます。
そこで接触をコライダだけでなくキャラクターから真下にレイを飛ばし接触判定をして主人公が床の上にいるかどうかを判定します。
主人公キャラが落ちる床に乗った時の処理スクリプトAddForceFloor
次に主人公キャラが床に乗った時に床自体に乗っている時間を計測させますが、主人公キャラが床に乗っている
事を知らせるスクリプトAddForceFloorを作成し、主人公キャラに追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using UnityEngine; using System.Collections; public class AddForceFloor : MonoBehaviour { void OnControllerColliderHit(ControllerColliderHit col) { // FallFloorが設定されたゲームオブジェクトと接触 if (col.gameObject.layer == LayerMask.NameToLayer ("FallFloor")) { // キャラクターの下方向にレイを飛ばしFallFloorと接触したら床に接触している事を知らせる Debug.DrawLine (transform.position + Vector3.up * 0.1f, transform.position + Vector3.up * -0.2f, Color.red); if (Physics.Linecast (transform.position + Vector3.up * 0.1f, transform.position + Vector3.up * -0.2f, LayerMask.GetMask ("FallFloor"))) { col.gameObject.GetComponent<FallFloor> ().ReceiveForce (); } } } } |
主人公キャラをCharacterControllerで動かしている場合のコライダの接触判定はOnControllerColliderHitメソッドを使います。
床自体にIs Triggerにチェックを入れてるコライダがあればOnTriggerEnterで主人公キャラの進入を検知出来ますが、今回のように主人公キャラが床に乗らなければいけない時(衝突を使う)は主人公側から相手側を検知します。
主人公が接触した相手がFallFloorで、主人公の下にレイを飛ばし床があった場合はFallFloorスクリプトを取得し、その中に定義されているReceiveForceメソッドを呼び出します。
transform.positionから下側にレイを飛ばすと判定がされない事があるので、補正値を加えています。
これで主人公が床に乗っている間ReceiveForceメソッドが呼ばれる事になります。
落ちる床を実現するスクリプトFallFloorを作成
次に床に設定したFallFloorスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | using UnityEngine; using System.Collections; public class FallFloor : MonoBehaviour { // 床が落下するまでの時間 [SerializeField] private float timeToFall = 5f; // 主人公が床に乗っていたトータル時間 private float totalTime = 0f; private Rigidbody rigid; void Start () { rigid = GetComponent<Rigidbody>(); rigid.isKinematic = true; } void Update () { // 床が落下する時間を超えたらリジッドボディのisKinematicをfalseに // isKinematicをfalseにしたことで重力が働く if(totalTime >= timeToFall) { rigid.isKinematic = false; } } // 主人公が床に乗っている時に呼び出す public void ReceiveForce() { // 床に乗っている時間を足していく totalTime += Time.deltaTime; } void OnCollisionEnter(Collision col) { // 床が落下し、衝突したゲームオブジェクトのレイヤーがFieldだった時床を消去 if(col.gameObject.layer == LayerMask.NameToLayer("Field")) { Destroy(this.gameObject, 1f); } } } |
FallFloorのインスペクタで床が落ちるまでの時間を設定出来るようにします。
主人公キャラが床に乗っている場合にReceiveForceメソッドを呼び出します。
載っている間はtotalTimeに時間を足していきます。
Rigidbodyは初期値としてUse Gravityにチェックが入っているので特にスクリプトから変更はしません。
isKinematicがtrueである時はスクリプトからしか影響が与えられないようになるので、重力が働かないようになります。
Updateメソッド内で床に乗っている時間が制限時間を超えた時にRigidbodyのisKinematicをfalseに設定します。
isKinematicがfalseになったので、Use Gravityの影響が出て床は落下していきます。
OnCollisionEnterを使って、床がFieldレイヤーに設定されているゲームオブジェクトとぶつかった時に床を1秒後に削除します。
ぶつかった相手のレイヤーを調べるには
1 2 3 | col.gameObject.layer |
を使います。
1 2 3 | LayerMask.NameToLayer("Field") |
を使えばFieldという名前のレイヤーの番号が取得出来ます。
これで落ちる床の作成と設定が出来ました。
床のインスペクタでFallFloorのtimeToFallを設定してください。
それではUnityの実行ボタンを押して確認してみましょう。
これで主人公が乗ると数秒後に落下する床を作成してみる事が出来ました。
床が落ちるトラップを作成する場面で重宝しそうですね。