Unity内のステージで広い草原であれば考えなくてもいい事なんですが、ゲームステージに建物や山のような地形があった場合、カメラとキャラクターの間に何らかの障害物が映り、キャラクターが見えなくなってしまう事があります。
こうなるとキャラクターが今どこを動いているのかわからなくなってしまいます。
そこで今回は障害物がカメラと主人公の間に入った時にカメラを移動させ、常に主人公が写るようにしていきます。
キャラクターが壁のせいで見えなくなる時の対処
障害物が邪魔で主人公が見えなくなった時はカメラが自動で位置を変えキャラクターを表示するか、またはキャラクターが狭い建物に入った時(入口のOnTriggerEnterで判定)にあらかじめ設定してあった別のカメラの表示に切り替えて、キャラクターが見えなくならないようにするという方法があります。
カメラを切り替える方法はメインカメラで表示していたものを固定のカメラに切り替え、メインカメラをOffにするという機能を追加すればいいんですが、
カメラの切り替えに関しては
を参照してください。
今回は自動でカメラの位置を変える機能を追加してみます。
↑のような感じで主人公とカメラの間に壁や敵が入った場合はその位置にカメラを移動させています。
狭い部屋などを主人公キャラクターが移動する事がある時は便利ですね。(^^)/
今回のサンプルでは壁だけでなく敵も障害物と同じ対象にしてますが、対象はレイヤーの指定をするかしないかだけなのですぐ変更出来ます。
キャラクターとカメラの間に障害物があるかを調べるスクリプトの作成
カメラとキャラクターの間に障害物があるかどうか判定し障害物があった時はカメラの位置を移動します。
障害物があるかどうか調べる為にPhysics.Linecastを使います。
Linecastは始点から終点にレイを飛ばして、他のゲームオブジェクトと衝突するかどうかを調べる事が出来ます。
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 41 42 43 44 | using UnityEngine; using System.Collections; using System.Collections.Generic; public class AutoRotateCamera : MonoBehaviour { // キャラクターのTransform [SerializeField] private Transform charaLookAtPosition; // カメラの移動スピード [SerializeField] private float cameraMoveSpeed = 2f; // カメラの回転スピード [SerializeField] private float cameraRotateSpeed = 90f; // カメラのキャラクターからの相対値を指定 [SerializeField] private Vector3 basePos = new Vector3(0f, 0f, 2f); // 障害物とするレイヤー [SerializeField] private LayerMask obstacleLayer; void Update() { // 通常のカメラ位置を計算 var cameraPos = charaLookAtPosition.position + (-charaLookAtPosition.forward * basePos.z) + (Vector3.up * basePos.y); // カメラの位置をキャラクターの後ろ側に移動させる transform.position = Vector3.Lerp(transform.position, cameraPos, cameraMoveSpeed * Time.deltaTime); RaycastHit hit; // キャラクターとカメラの間に障害物があったら障害物の位置にカメラを移動させる if (Physics.Linecast(charaLookAtPosition.position, transform.position, out hit, obstacleLayer)) { transform.position = hit.point; } // レイを視覚的に確認 Debug.DrawLine(charaLookAtPosition.position, transform.position, Color.red, 0f, false); // スピードを考慮しない場合はLookAtで出来る //transform.LookAt(charaTra.position); // スピードを考慮する場合 transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(charaLookAtPosition.position - transform.position), cameraRotateSpeed * Time.deltaTime); } } |
charaLookAtPositionは主人公の子要素に配置したカメラが見るポイントをインスペクタで設定します。
障害物と判定するゲームオブジェクトのレイヤーはインスペクタで設定出来ます。
UpdateメソッドではPhysics.Linecastを使ってcharaLookAtPositionからカメラまでレイを飛ばして、指定したレイヤーと衝突しているかどうかを調べます。
衝突した位置はhit.pointで得られますが、この位置はワールド空間の位置なのでカメラ位置もtransform.positionの位置に設定します。
いきなりカメラが移動するとパッと切り替わってしまう為、Vector3.Lerpを使って滑らかに移動するようにします。
障害物と接触していた場合に滑らかに移動させると障害物の中身が見えてしまうので、障害物と接触していた時は一気にぶつかったポイントにカメラを移動させています。
レイが衝突していなければカメラのローカル位置を元の位置に滑らかに移動するようにします。
カメラの回転ではQuaternion.Slerpを使って『現在の角度』から『カメラからキャラクターのカメラが見る位置の方向の角度』へと徐々に回転させています。
Debug.DrawLineで視覚的にレイを確認
Debug.DrawLineを使用するとシーンビューで視覚的にレイの様子を確認する事が出来ます。
1 2 3 | Debug.DrawLine(charaLookAtPosition.position, transform.position, Color.red, 0f, false); |
↑の場合は主人公の子要素にカメラが見るポイントを作成し、その位置からカメラ位置にレイを表示します。
レイの色を赤色、レイを表示する間隔を0(0なので常に表示)、最後の条件はレイの前に他のゲームオブジェクトがあった場合に隠されているレイを表示するかどうかです。
上のような感じで通常表示されるカメラ位置とは違う位置で表示され、壁でキャラクターが見えなくなる事はなくなりました。
これで自動でカメラの位置を変更する事が出来るようになりました。
初期のバイオみたいに、ある部屋はこの視点で、ある通路はこの視点で、みたいのもレトロな感じで面白そうですけどね。
カメラが自動でキャラクターを映す機能を作り終えて
カメラがうまくキャラクターを表示してくれず色々やっていた記憶がありますが、単純に処理を間違えていました・・・(^_^;)
レイを飛ばす方向を
『RayCastゲームオブジェクトからキャラクター』へ飛ばしていましたが、実際に壁が間に入ったかどうかは
『キャラクターからRayCastゲームオブジェクト』へ飛ばす必要があります。
なぜならRayCastからレイを飛ばすとRayCast側の壁を接触場所にしてしまって結局キャラクターを映せないという事になります。
キャラクターからRayCastの方にレイを飛ばせばキャラクター側の壁が接触場所になるので必ずキャラクターを映してくれます。
これでちゃんとキャラクターを映してくれると思います。
以前書いていた不要な処理は混乱を招くので削除しました。