UnityのスタンダードアセットにあるWaterを使えば、簡単に湖や海を設置出来る事がわかりました。
しかしWaterは平面なので水の下に潜ってもその下には地上にある空間と同じようにカメラに表示されてしまいます。
↑のように水は平面なので下側を写しても地上と同じに表示されます。
上の画像の場合はかなり透明な水なのでそれほど違和感はありませんが、水の色がよくわかるようになると水の平面を潜った時にかなりの違和感が生じます。
そこで今回は水中に入った時にカメラの映像にエフェクトを加えて水中にいるような表現を作ってみたいと思います。
水を設置する
まずはゲーム内に水を設置し、Scaleを調整してちょっとした湖を作成します。
水の設置については
を参照してください。
今回はAssets→StandardAssets→Environment→Water→Water→Prefabs→WaterProDaytimeを設置しました。
設置したWaterProDaytimeのインスペクタを表示しAdd ComponentからPhysics→Box Colliderを取り付けます。
Is Triggerにチェックを入れコライダをキャラクターを検知するエリアにします。
上のように水面から下にコライダのエリアを作ります。
横から見ると水面から下にコライダが伸びています。
これは水中にキャラクターがいる事を検知する為、水の中では常にコライダを使ってキャラクターを検知したい為です。
水にはWaterというタグを作り設定しておきます。
これはカメラ側から水を検知する時にこのタグを使って識別します。
カメラの設定
次にキャラクターを写すカメラの設定とスクリプトの取り付けを行います。
キャラクターの動きやキャラクターを追従するカメラの機能は
の記事で作ったラジコン操作キャラクターと徐々に追従するカメラ機能を使います。
カメラにはRigidbodyとBox Colliderコンポーネントを取り付けます。
Rigidbodyの設定
RigidbodyはIs Kinematicにチェックを入れスクリプトで移動の操作を行います。
Rigidbodyを取り付けたのは取り付けないとOnTriggerEnterやOnTriggerExitでの検知が出来ない為です。
CharacterController以外のゲームオブジェクトで移動する可能性があるものにはRigidbodyの機能を使っていなくても取り付けてIs Kinematicにチェックを入れておくといいです。
Rigidbodyが取り付けられていないゲームオブジェクトはその場から動かない物体と思われるので挙動がおかしくなったり動作しなくなる可能性があります。
Box Colliderの設定
Box Colliderのサイズを調整しカメラの描画範囲の開始位置のサイズと合わせていきます。
↑のようにカメラの開始位置からコライダで他のゲームオブジェクトを検出出来るようにサイズと位置を調整します。
↑の例ではキャラクターの目の辺りにカメラを置いて調整していますが、これはわかりやすくしただけで実際には3人称視点のカメラになります。
今回の機能はFPSにした場合でもカメラを目の位置に置けば同じように機能するのでどちらにしても問題はありません。
カメラのClipping PlaneのNearは0.1とカメラ位置から近い部分から描画するようにしておきます。
カメラが水中にいる時の処理スクリプト
カメラには水を検知するエリアを作成したので、水を検知した時にカメラの映像にエフェクトを加え水中にいるような表現をします。
今回は二通りのやり方を作ってみます。
の2つです。
Blurは両方ともに使いますが、これはカメラの映像をぼやけさせる機能です。
UIの場合はCanvasの子要素にPanelを設置しカメラが水中に入った部分に色を塗り水中の色の表現をさせます。
UIの場合は水面の高さに合わせてPanelのサイズを変えるだけなので波打っていると水中部分との隙間が出来たり、水中の平面部分が目立ちます。
Fogの場合はカメラが半分水中に入ったらFog(霧)の設定を変更し、水中にいるかのような表現をさせます。
Fogの場合はカメラが半分水中に入った時にカメラ全体に霧を発生させますので多少違和感は生じます。
どちらにしても多少は問題がありそうです。(^_^;)
それではスクリプトを作成しましょう。
カメラには新しいスクリプトUnderWaterCameraを作成し取り付けます。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityStandardAssets.ImageEffects; using UnityEditor; public class UnderWaterCamera : MonoBehaviour { private enum WaterMode { FogAndBlur, UIAndBlur } // 水中にカメラがある時のモード [SerializeField] private WaterMode mode = WaterMode.FogAndBlur; // 水ゲームオブジェクト public Transform water; // 水中表現にUIのパネルを使用する場合は設定 public GameObject waterPanel; // 水中表現にUIのパネルを使用する場合の水のRect private RectTransform waterRect; // カメラが水と接触しているかどうか private bool isInWater = false; // カメラ表示をボケさせるスクリプト private BlurOptimized blur; // ノーマルのFogの色 private Color normalFogColor; // ノーマルのFogの深さ private float normalFogDensity; // 水中のFogの色 private Color inWaterFogColor = new Color (0.5f, 0.7f, 1.0f, 0.2f); // 水中のFogの深さ private float inWaterFogDensity = 0.02f; void Start() { waterRect = waterPanel.GetComponent<RectTransform> (); blur = GetComponent<BlurOptimized> (); normalFogColor = RenderSettings.fogColor; normalFogDensity = RenderSettings.fogDensity; } void Update() { // UIとBlurを使った水中表現 if (mode == WaterMode.UIAndBlur) { if (isInWater) { // カメラの中心が水の位置より高い時 if (transform.position.y > water.position.y) { blur.enabled = false; waterRect.localScale = new Vector3 (waterRect.localScale.x, Camera.main.WorldToViewportPoint (water.position).y, waterRect.localScale.z); } else { blur.enabled = true; waterRect.localScale = new Vector3 (1f, 1f, 1f); } } // FogとBlurを使った水中表現 } else if (mode == WaterMode.FogAndBlur) { if (isInWater) { blur.enabled = true; RenderSettings.fog = true; RenderSettings.fogColor = inWaterFogColor; RenderSettings.fogDensity = inWaterFogDensity; } else { blur.enabled = false; RenderSettings.fogColor = normalFogColor; RenderSettings.fogDensity = normalFogDensity; } } } // カメラと水が接触した時 void OnTriggerEnter(Collider col) { if (col.tag == "Water") { if (mode == WaterMode.UIAndBlur) { waterPanel.SetActive (true); } isInWater = true; } } // カメラが水から出た時 void OnTriggerExit(Collider col) { if (col.tag == "Water") { if (mode == WaterMode.UIAndBlur) { waterPanel.SetActive (false); } isInWater = false; blur.enabled = false; } } } |
列挙型でUIとFogモードを選択出来るようにしています。
modeは水中表現のモード
waterは水のゲームオブジェクト
waterPanelはUIモードの時にサイズを変化させるPanel
waterRectはUIモードの時のPanelのRectTransform
isInWaterは水にカメラが接触しているかどうか
blurはカメラをぼかすスクリプト
normalFogColorはFogモードの時の標準の霧の色
normalFogDensityはFogモードの時の標準の深さ
inWaterFogColorはFogモードの時の水中の色
inWaterFogDensityはFogモードの時の霧の深さ
を表しています。
Startメソッドではコンポーネントの取得とFogの設定を取得しています。
Fogの設定はRenderSettingsから取得・変更出来ます。
Updateメソッドではモードによって処理を分けています。
UIモードの時でカメラが水に接触(厳密にはコライダとの接触)している場合、
カメラの中心が水の平面部分より上の場合Panelのサイズを水の平面部分のところまでにします。
Camera.main.WorldToViewportPoint (water.position).yで水の位置をワールド座標からビューポート(カメラの0~1の範囲に変換したもの)のY座標の位置を取得し、PanelのYのScaleをそのビューポートの位置にします。
カメラの中心が水の平面部分より下に来た場合はPanelのScaleを全て1に設定し、画面全体のサイズにします。
Fogモードの時はカメラが水に接触した時点でFogを有効にし水中用の設定に切り替えています。
水との接触がなくなったら元の設定に戻しています。
もしFogを水中以外でも使っている場合はFogのオン・オフをせず設定値を切り替えるだけの処理にします。
OnTriggerEnter、OnTriggerExitでカメラが水と接触しているかしていないかを判定し、isInWaterフラグのオン・オフやBlurの無効をしています。
UI部分は
↑のように作成しました。
Panelは最初は無効化しておきます。
PanelのSource ImageはBackgroundとなっており完全にカメラ全体に適応せず角が丸まったりしていますので、全体を覆いたい場合は別のSource Imageを設定するか、
Source ImageをNoneにしてColorだけを利用してください。
これでカメラの機能も出来ました。
カメラのインスペクタの設定は
↑のようになります。
Blurは最初は表示させたくない為に名前の横のチェックを外し無効化しておきます。
水中表現の確認をする
機能が完成したので水中を表現出来るか見てみましょう。
↑のようになりました。
水に設定したコライダのサイズによってはうまくいかない場合もありますのでどこからを水中とするか?の調整が必要になってきます。
水中に入った時にもキャラクターが普通に歩いていますが・・・・(^_^;)
水に入ったら泳いだり、移動処理を変えたりといった事も必要になってきますね。
それはまた今度記事にしたいと思います。