今回はワールドマップから戦闘シーンへと遷移する時のフェードエフェクトをシェーダースクリプトを使って作成していきます。
前回は戦闘シーンで使用する敵のキャラクターと敵のパーティーのステータスデータを作成しました。
ユニティちゃんのRPGを作ってみようの他の記事は
から見ることが出来ます。
以前作成したシーン間の遷移の機能で画像の透明度を操作してフェードする機能を作成しました。
その機能をそのまま使ってもいいんですが、今回はシェーダースクリプトを使ってフェード時のエフェクトを作成したいと思います。
シェーダースクリプトはシェーダーグラフを使うとビジュアル操作で作れますが、シェーダーグラフを使うにはHDRPかLWRP(現URP)のレンダリングパイプラインが設定されている必要があります。
今回のユニティちゃんRPGでは組み込みのレンダリング機能を使ったテンプレートでプロジェクトを作成したので新たにレンダリングパイプラインの設定を行わないとシェーダーグラフが使えません。
設定をしてもいいんですが、今回は自分でシェーダースクリプトを作ることにしてシェーダーグラフは使いません。
元々あるシェーダースクリプトに処理を追加するだけなのでそれほど難しい事は行いませんが、中身を理解しようと思うと難しくなります。(´Д`)
戦闘シーンへのフェードエフェクトの作成
それでは戦闘シーンへのフェードエフェクトの作成を行っていきます。
このフェードエフェクトに関するファイル群はAssets/RPG/フォルダに新しくFadeToBattleフォルダを作成し、その中に全部入れます。
フェードエフェクトシェーダーの作成
フェードエフェクトを行うシェーダースクリプトを作成していきます。
Assets/RPG/FadeToBattleフォルダ内で右クリックしCreate→Shader→Image Effect Shaderを選択し、名前をFadeToBattleとします。
FadeToBattleシェーダースクリプトにはデフォルトで既にスクリプトが記述されていますので、そこに処理を追加します。
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 | Shader "MyShader/FadeToBattle" { Properties { _MainTex ("Texture", 2D) = "white" {} _Amount("Amount", Range(-0.1, 0.1)) = 0 _Speed("Speed", float) = 50 } SubShader { // No culling or depth Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } sampler2D _MainTex; float _Amount; float _Speed; fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv + float2(sin(i.vertex.x * _Time[1] * _Speed) * _Amount, sin(i.vertex.y * _Time[0] * _Speed) * _Amount)); return col; } ENDCG } } } |
Shaderの後の表記をMyShader/FadeToBattleに変更します。
これでマテリアルのシェーダーを選択する時にMyShader項目にFadeToBattleシェーダーが振り分けられます。
次にマテリアルに表示する項目を追加します。
1 2 3 4 5 6 7 8 | Properties { _MainTex ("Texture", 2D) = "white" {} _Amount("Amount", Range(-0.1, 0.1)) = 0 _Speed("Speed", float) = 50 } |
_Amount、_Speedを追加します。
_Amountは表示を歪める量で-0.1~0.1の範囲を指定出来、_Speedは歪みのスピードでfloat型、を指定します。
_Amountは今回Rangeを使って-0.1~0.1の範囲にしましたが範囲を変えても構いません。
vert関数の下に変数の宣言とfrag関数内に処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 | sampler2D _MainTex; float _Amount; float _Speed; fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv + float2(sin(i.vertex.x * _Time[1] * _Speed) * _Amount, sin(i.vertex.y * _Time[0] * _Speed) * _Amount)); return col; } |
_Amount等のプロパティの値を使う時には型と宣言が必要になります。
frag関数はフラグメントの処理で画面のピクセル毎の処理を行います。
詳しい事は
を参照してください。
tex2Dでは第1引数のメインテクスチャの割り当てを第2引数のUV(Uは画面の横軸、Vは画面の縦軸)の設定で行っています。
なので第2引数のUVの数値を変更するとテクスチャがどう割り当てられるかが変わってきます。
そこでi.uvの元のUVにfloat2型の値を足すことで割り当てを変更します。
float2型の第1引数ではi.vertex(入力の頂点)のxの値に_Time[1]、_Speedをかけてsinの値(-1~1の値)を得て、それに_Amountをかけています。
第2引数はcosの値を求めていますが、やっていることは第1引数と同じです。
行っていることは時間経過で値が変わるsinとcosの値を使って描画されるピクセルの位置の割り当てを変えて一定の歪みを与えているということです。
sinの部分をcosに変えたり、i.vertex.xの部分をi.vertex.yに変えたりと色々変更してみると面白いかもしれません。
マテリアルの作成
シェーダースクリプトが出来たので、次はこのシェーダーを設定するマテリアルの作成を行います。
Assets/RPG/FadeToBattleフォルダに右クリックからCreate→Materialを選択し名前をFadeToBattleMaterialとします。
選択したらインスペクタのShaderでMyShader→FadeToBattleを選択します。
ShaderにMyShader/FadeToBattleを設定すると以下のようになります。
FadeToBattleシェーダーで作ったAmountとSpeedプロパティが表示されていますね。
何らかのゲームオブジェクトに取り付け歪みを確認してみる
シェーダーとマテリアルが出来たので、次はヒエラルキー上にCubeを設定し、そのCubeのマテリアルにFadeToBattleMaterialマテリアルを設定し歪みを確認してみます。
FadeToBattleMaterialに適当にテクスチャを設定し、Amoutを0以外の数値にします。
ヒエラルキー上で右クリックから3D Object→Cubeを選択します。
CubeのMesh RendererのElement0にFadeToBattleMaterialを設定します。
シーンビュー上で確認するとCubeに割り当てられたマテリアルのテクスチャが歪んでいるのを確認出来ます。
Cubeは確認の為に設置しただけなので削除します。
カメラに描画されるもの全てを歪ませたい!
先ほどFadeToBattleMaterialが設定されたCubeのテクスチャに歪みは生じましたが、元々の目的はワールドマップシーンから戦闘シーンへと遷移する時のフェードエフェクトを作成することなので、カメラに映る映像全てを歪ませる必要があります。
Assets/RPG/FadeToBattleフォルダに新しくFadeToBattleScriptスクリプトを作成し、WorldMapシーンのMain Cameraに取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class FadeToBattleScript : MonoBehaviour { [SerializeField] private Material material; // フェードAmountの到達値 private float destinationAmount; private void Start() { // スタート時に初期化 material.SetFloat("_Amount", 0f); } // カメラに取り付けると呼ばれる private void OnRenderImage(RenderTexture source, RenderTexture destination) { Graphics.Blit(source, destination, material); } } |
materialにはインスペクタでFadeToBattleMaterialを設定します。
Startメソッドではmaterialの_Amountプロパティの値を0にし、画面に歪みが出ないようにします。
OnRenderImageメソッドはMonoBehaviourクラスを継承して作成したスクリプトがカメラに取り付けられている時に呼ばれるメソッドです。
sourceは元のRenderTextureで、destinationは結果のRenderTexture、materialはインスペクタで設定したマテリアルでこれをdestinationに適用します。
OnRenderImageでは最終画面がsourceで受け取られるので、その画面をdestinationマテリアルのシェーダーで歪みを生じさせ、そのマテリアルをdestinationに適用する事によってカメラに映る全体に歪みが生じるという感じです。
Graphics.Blitは元のテクスチャをシェーダーでレンダリングするテクスチャにコピーします。
これでカメラに描画されたものがFadeToBattleMaterialのAmountやSpeedを操作する事によって歪まされるはずです。
Unityを実行し、FadeToBattleMaterialのプロパティ値を変更して歪みを確認してみてください。
このままだとゲーム中はマテリアルのプロパティ値は固定なので固定の歪みになります。
これは後からスクリプトで変更するようにします。
UIには歪みが適用されない?
先ほどの画像を見ると、パーティーのステータスを表示しているUIは歪んでいないのがわかります。
これはUIのCanvasのRender ModeがScreen Space -Overlayとなっている為です。
これはゲーム画面の上に表示するモードなので歪みが適用されません。
もしUIにも歪みを生じさせたい場合はCanvasのRender ModeをScreen Space -Cameraに変更し、Render CameraにMain Cameraを指定し、Plane Distanceでカメラからの距離を指定します。
Screen Space -Cameraに変更するとこのUIはRender Cameraに指定したカメラの前に描画され、その距離はPlane Distanceで指定します。
確認してみると、下のようにUIにも歪みが生じています。
カメラの設定によってUIの表示が変わってくるため今回はUI関連は全てScreen Space – Overlayにしておきます。
ここら辺の事はUnityマニュアルのCanvasのページを確認してみてください。
シーン遷移時のフェード処理を変更する
シーン遷移の処理はLoadSceneManagerスクリプトで行っているので、ここでワールドマップシーンから戦闘シーンへと遷移する時のフェード処理を変更します。
LoadSceneManagerスクリプトにフェードに使用するマテリアルを設定するフィールドを作成します。
1 2 3 4 5 | // フェードに使用するマテリアル [SerializeField] private Material material; |
FadeAndLoadSceneメソッド内でフェードアウト処理をしている部分にsceneがSceneMovementData.SceneType.WorldMapToBattleではない時は今まで通りのフェードアウト処理を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // フェードをした後にシーン読み込み IEnumerator FadeAndLoadScene(SceneMovementData.SceneType scene) { if (scene != SceneMovementData.SceneType.WorldMapToBattle) { // フェードUIのインスタンス化 fadeInstance = Instantiate<GameObject>(fadePrefab); fadeImage = fadeInstance.GetComponentInChildren<Image>(); // フェードアウト処理 yield return StartCoroutine(Fade(1f)); } else { yield return StartCoroutine(FadeWorldMapToBattle(0.1f)); } |
sceneがSceneMovementData.SceneType.WorldMapToBattleだった時はコルーチンを使ってFadeWorldMapToBattleメソッドに引数0.1を渡して実行します。
シーン遷移後のフェードイン時は今まで通りのフェードイン処理を行います。
FadeWorldMapToBattleメソッドを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 | // ワールドマップ→バトルマップでのフェード処理 IEnumerator FadeWorldMapToBattle(float destinationAmount) { var fadeAmount = material.GetFloat("_Amount"); while (Mathf.Abs(material.GetFloat("_Amount") - destinationAmount) > 0.01f) { material.SetFloat("_Amount", Mathf.Lerp(material.GetFloat("_Amount"), destinationAmount, fadeSpeed * Time.deltaTime)); yield return null; } material.SetFloat("_Amount", destinationAmount); } |
行っていることはFadeメソッドと同じで、フェードに使用するマテリアルの_Amountプロパティ値を徐々にdestinationAmoutに変更しています。
ある程度_Amount値がdestinationAmountに近づいたら終了します。
VillageシーンとWorldMapシーンのSceneManagerゲームオブジェクトのインスペクタでLoadSceneManagerのmaterialにAssets/RPG/FadeToBattle/FadeToBattleMaterilaを設定します。
敵と遭遇したら効果音を鳴らす
敵と遭遇したら遭遇した!という効果音を鳴らしたいと思います。
EncountManagerゲームオブジェクトを選択し、インスペクタのAdd ComponentからAudio→Audio Sourceを取り付けます。
アセットストアで『sound bits Free Sound FX Collection』で検索して出てくる無料の効果音をインポートし、適当な効果音をAudio SourceのSourceに設定します。
わたくしはCrash&Smash-Designed_Glass, Crash, Designed,037を設定しました。
敵と遭遇した時の効果音でフェードが終了したらすぐに戦闘シーンへと移行するので直ぐに音声が再生され、長い音でないものの方がいいかもしれません。
EncountMangerスクリプトで戦闘シーンへの遷移時に音声を再生するようにします。
フィールドの追加とStartメソッドでAudioSourceコンポーネントの取得を行います。
1 2 3 4 5 6 7 8 | private AudioSource audioSource; void Start() { audioSource = GetComponent<AudioSource>(); } |
次にUpdateメソッドで敵と遭遇した時に音を鳴らしてから戦闘シーンへの遷移を行います。
1 2 3 4 5 6 7 8 9 | if(elapsedTime >= destinationTime) { Debug.Log("遭遇"); audioSource.Play(); sceneManager.GoToNextScene(SceneMovementData.SceneType.WorldMapToBattle); elapsedTime = 0f; SetDestinationTime(); } |
音の再生位置を変更したい時はAudioSourceのtime(秒単位での再生位置)やtimeSamples(PCMサンプルでの再生位置)プロパティを変更すると出来るようです。
1 2 3 4 | audioSource.time = 0.8f; audioSource.timeSamples = 2000; |
音声の最初の方が無音に近い場合は途中から再生出来ると便利ですね。
ワールドマップから戦闘シーンへの遷移時のフェードを確認する
これで機能が出来たので、ワールドマップでユニティちゃんを動かし、戦闘シーンへと遷移する時にフェードが行われるかどうかを確認してみましょう。
上のようになりました。
終わりに
ワールドマップから戦闘シーンへと移行する時にフェードエフェクトが実行され、効果音が鳴るようになりました。
シーン遷移時のフェードエフェクト時間は通常のエフェクト時間と同じfadeSpeedを使って行っていますので戦闘に入るまでが短すぎると感じたら別のフィールドを使ってもう少し遅くするという手もあります。
ただ、プレイするユーザーの事を考えると戦闘シーンまでのフェードエフェクトに時間がかかるより早く戦闘シーンに入った方がいいですよね。
というわけで適度な時間を探してみる必要がありますね。
次回はようやく戦闘シーンであるBattleシーンの作成に取り掛かろうと思いますが、それも徐々に作る予定で、次回は味方パーティーデータ、敵パーティーデータからキャラクターのプレハブを取得してそれを地面に並べるところを作ってみたいと思います。
もう一気に作る体力がないので、一記事一記事を分けて細かく作っていこう。(´Д`)
この作品はユニティちゃんライセンス条項の元に提供されています