最終更新:
orika_ex_miyako 2021年09月18日(土) 11:53:50履歴
この手順では、エフェクト機能を実装するための準備を行います。
以下の内容で実装を進めていきます。
発展10 ーエフェクト機能の準備ー
15.エフェクトを管理するマネージャークラスを作成し、エフェクト機能の運用の準備をする
新しい学習内容は、以下の通りです。
・switch 文の式形式による記述方法
ゲームには効果的な画面の演出を盛り込むことで画面が豪華になります。これらは総じてエフェクトと呼ばれています。
例えば、エネミーが破壊されたときに、エネミーを破壊するだけではなくて、その周囲にエフェクトを生成することで破壊したことを視覚的に演出します。
こういった演出があることで、エネミーが破壊されたことを伝えつつ、ユーザーに、エネミーを倒したという感覚をゲーム画面から体験してもらうことが出来ます。
エフェクトを実装する方法は何種類もあります。
最も簡単でわかりやすいものは、エフェクトを生成させたいゲームにスクリプトをアタッチしておいて、
そのスクリプト内にエフェクトのプレファブを変数内に登録して、任意のタイミングで生成する方法です。
先ほどのエネミーの破壊時のエフェクトであれば、エネミーのゲームオブジェクトにアタッチされている EnemyController スクリプト内にエフェクト用の変数を用意し、
そこにインスペクターからエフェクト用のプレファブをアサインしておいて、エネミーの破壊時にエフェクトのクローンをプレファブから生成する、という処理の流れになります。
この手法は他のサイトでも多く紹介されているため、今回は別の方法で設計し、実装を行うようにします。
設計内容としては、いままでも利用してきた、シングルトンというデザインパターンを利用し、BattleEffectManager という名前で、エフェクトを管理するためのスクリプトを1つ作成します。
そして、この中にエフェクト用のプレファブを登録するための変数を集約させます。
よって、エネミー用のエフェクトだから、エネミーのゲームオブジェクトに用意する、という手法ではなくて、
ゲームオブジェクトに関係なく、使うエフェクトはすべてこの BattleEffectManager 内に記述して登録する、という設計になります。
エフェクトを1つのスクリプト内に集約するとした場合、重要なのは、どのようにそれらのエフェクトの情報を必要なゲームオブジェクトに提供するか、です。
ここでシングルトンの機能が役に立ちます
シングルトンで作成しているクラスは、変数への代入が不要で、いずれのクラスからでも参照が可能な状態になっています。
つまり、BattleEffectManager クラスは、いずれのクラスからでも自由に参照できる状態になっているので、
あとは、適宜なタイミングで、必要となるエフェクトの情報を利用してもらい、エフェクトを生成するようにします。
エネミーの破壊時のエフェクトであれば、BattleEffectManager クラスにエフェクト用の変数を作成し、登録しておきます。
そして、EnemyController クラスにおいて、エネミーの破壊処理をする前に、BattleEffectManager クラスからエフェクトの情報を取得して、
それを Instantiate メソッドを使ってエフェクトとして生成するという流れになります。
さて、それでは、どのようにエフェクトの情報を各クラスに提供するか、その方法ですが、これも設計方法はいくつもあります。
もっとも簡単なのは、各エフェクトごとにメソッドを準備し、それを必要なクラスから実行してもらうようにするものです。
エネミーの破壊に必要なエフェクトであれば、その情報を記述したメソッドを準備するという形式です。
ただし、この方法での実装には問題点もあります。
エフェクトの数だけメソッドを準備する必要があるので、同じような内容が書いてあるメソッドが複数必要になります。
また、新しいエフェクトを作るたびに、メソッドも新しく準備する必要があります。非常に効率が悪いです。
こういったケースには、引数を利用することで、処理を簡便化することが出来ます。
複数あるエフェクトの情報に合わせて複数のメソッドを準備するのではなく、メソッドは1つだけにして引数に応じて、エフェクトの内容を変化させる、という考え方です。
今回は、enum として EffectType を作成していますので、この情報を利用して、BattleEffectManager クラス内にエフェクトの情報を取得するための GetEffect メソッドを準備しています。
GetEffect メソッドには引数として EffectType が設定してあるので、エフェクトの情報を欲しいクラスは、どの EffectType のエフェクトが欲しいのかを指定して GetEffect メソッドを実行します。
そうすると、GetEffect メソッド内において引数で届いた EffectType による switch 文の自動分岐が行われて、必要なエフェクトのプレファブの情報が取得できるような設計になっています。
この設計の便利な部分は、エフェクトの種類が増えても、Enum の列挙子と switch文の処理をそれぞれ1つずつ増やせばすむので、管理と修正がしやすいことがあげられます。
シングルトンのデザインパターンを利用し、BattleEffectManager クラスを作成します。
エフェクトの種類ごとに1つずつ変数を用意し、わかりやすい変数名を付けておきましょう。
このクラスの役割は、エフェクトを一元管理することと、管理しているエフェクトのプレファブの情報を別のクラスに提供することの2つがあります。
2つ目の役割については、設計でも説明したように、たくさんのエフェクトの数だけメソッドを作成するのではなくて、1つのメソッドを用意し、引数を活用します。
今回は GetEffect メソッドを作成して、 EffectType 型の引数を設定して準備しておきます。
GetEffect メソッド内は引数の値に合わせて switch 文により自動分岐し、対象のエフェクトの情報を GameObject 型の戻り値で戻すようになっています。
エフェクトの情報が必要になったクラスは、この GetEffect メソッドを実行します。実行する際には、どのエフェクトの情報が欲しいのか、EffectType で指定して利用します。
引数を活用した処理のイメージをつかんでいきましょう。ロジカルなメソッドを組み立てることができるようになります。
BattleEffectManager.cs
スクリプトを記述したらセーブします。
ヒエラルキーの空いている場所で右クリックをしてメニューを開き、Create Empty を選択して、新しいゲームオブジェクトを作成します。
名前を BattleEffectManager に変更し、先ほど作成した BattleEffectManager スクリプトをドラッグアンドドロップしてアタッチしてください。
インスペクターを確認すると、各エフェクトのプレファブをアサインするための変数が表示されますので、
こちらに任意のエフェクトのプレファブをアサインして登録してください。
なおエフェクトの多くはパーティクルによって作成されていますが、画面上の優先順位の関係上、ゲーム画面に見えなかったり、
キャラよりも優先順位が低くて一部が隠れてしまったりする場合があります。
その場合には、ParticleSystem コンポーネントの中の設定項目のうち、一番下にある Renderer モジュール内の Sorting Layer と Order in Layer の項目を見直して、適宜修正してください。
2Dゲームの場合には画像の重なりが付き物ですので、どの画像の優先順位を高くするかが重要になります。
これはパーティクルにも当てはまりますので、キャラなどの画像の優先順位よりもパーティクルの優先順位を高くすることで、画面の最前面にエフェクトを表示することが出来るようになります。
参考サイト
Unity 公式マニュアル
Renderer モジュール
https://docs.unity3d.com/ja/current/Manual/PartSys...
はんなりと、ゆるやかに 様
UnityのLayerとSortingLayerとOrder in Layerについて調べた
https://iucstscui.hatenablog.com/entry/2020/08/15/...
テラシュールブログ 様
UnityのSpriteとパーティクルとかモデルの描画順番について
https://tsubakit1.hateblo.jp/entry/2015/01/05/2330...
インスペクター画像

以上で設定は完了です。
現在の Unity は C# 8.0 以降に対応していますので、switch 文を式形式の記述で書くことが出来ます。
ただし、すべての switch 文に対してこの記述ができる訳ではありません。
switch 文の式形式による記述では、戻り値があることが条件ですので、戻り値のない switch 文にはこの書式を利用できません。
なお、この説明が難しいと感じる場合には、戻り値について復習をしておいてください。
<�…名錣� switch 文>
public GameObject GetEffect(EffectType effectType) {
GameObject effectPrefab = null;
switch(effectType) {
case EffectType.Destroy_Chara :
effectPrefab = destroyCharaEffectPrefab;
break;
case EffectType.Destroy_Enemy:
effectPrefab = destroyEnemyEffectPrefab;
break;
case EffectType.Hit_Enemy:
effectPrefab = hitEnemyEffectPrefab;
break;
case EffectType.Hit_DefenseBase:
effectPrefab = hitDefenseBaseEffectPrefab;
break;
default:
effectPrefab = destroyCharaEffectPrefab;
break;
};
return effectPrefab;
}
↓
<�⊆扱措阿竜�述による switch 文>
public GameObject GetEffect(EffectType effectType) {
return effectType switch {
EffectType.Destroy_Chara => destroyCharaEffectPrefab,
EffectType.Destroy_Enemy => destroyEnemyEffectPrefab,
EffectType.Hit_Enemy => hitEnemyEffectPrefab,
EffectType.Hit_DefenseBase => hitDefenseBaseEffectPrefab,
_ => destroyCharaEffectPrefab,
};
}
記述の方法は違いますが、これらはまったく同じ挙動になります。
戻り値のある switch 文であれば、case / break を省略できる、�⊆扱措阿砲茲覽�述に挑戦してみるといいでしょう。
式形式による記述の場合、 _ (アンダーバー)の部分が default に当たります。
また、通常の switch 文は default 部分がなくてもエラーは出ませんが、式形式による記述の場合には _ を記述しないとエラーが出ます。
参考サイト
++C++; 未確認飛行 C 様
C# の式と文の一覧
https://ufcpp.net/study/csharp/list_expression.htm...
++C++; 未確認飛行 C 様
is、switch の拡張(型スイッチ)
https://ufcpp.net/study/csharp/datatype/typeswitch...
以上でこの手順は終了です。
次は 発展11 ーエフェクト機能の実装− です。




コメントをかく