Unity C# ゲーム開発でインターフェースの使い所
プログラム苦手な私にとって、C#の継承やインターフェースは、最近になるまで理解できなかった要素でした。
ゲーム開発で使わざるを得なくなった状況になったので記します。
どういう状況で必要か?
1.シーン上にある多数のオブジェクトのうち、複数の特定のコンポーネントを持つものを、まとめて一つのコードで取得・管理する
2.別々のコンポーネントに同じメソッドを実行させる
3.自動生成したプレハブにシーン上の情報を取得させる
具体的に書くと、
群れをスポーンさせるシステムを作るのに必須でした。
画像はタイマーとオブジェクト切り替えで生育して収穫できる作物ですが、一定数または全部収穫すると全て収穫したメッセージが出て、時間が経つとまたスポーンするようになってます。
類似の状況として、全滅させたら第2波第3波とウェーブで襲ってくる敵、生成された大量のアイテムを全てもしくは一定数拾ったらフラグ達成などがあります。
これを実現しようとしたら、生成したキャラやアイテムの死亡・消滅フラグをスポーンシステムが把握する必要があります。
それが意外と難しいと気づいたのが、数日前でした。
どんなキャラやアイテム=異なるコンポーネントであっても、それができるようにするには、生成される側に死亡フラグやメソッドをつけたインターフェースを体力と死亡を管理するスクリプトに継承させておく方法が有効です。
public interface HitDamKillReceiver
{
}
public class Breakable : MonoBehaviour, HitDamKillReceiver
{
//敵キャラの体力とか管理するスクリプト
]
インターフェースをmonobehaviorの後に付け足したスクリプトを有すオブジェクトは、そのインターフェースをGetcomponentすればどんなコンポーネントであっても取得できます。つまりオブジェクト検索の手段として使えます。
そしてインターフェースに記したメソッドや変数は、どんなスクリプトでも共通して参照・操作できます。
インターフェースなしでは同名のメソッド・変数にしても別物として認識され、GetComponentとifで分岐してを繰り返して個別のコードで取得するはめになりますが、インターフェースつきなら一発でできます。
死亡フラグに使えるインターフェースとは
で、作るべきインターフェースの内容ですが、
public interface HitDamKillReceiver
{
//死亡フラグ
public bool SibouFrag(bool a);
//情報の送り先
public void SetIngoSendTarget(GameObject Target);
//リセットリスタートも共通でつける
public void ResetRestart(bool a);
}
死亡フラグ、情報の送り先、リセット機能の3点セットが実用的かなと思いました。
これを体力管理スクリプトに継承させて使います。
死亡フラグは、記述例はこんな感じで↓
public bool Killed;
public bool SibouFrag() { return Killed; }
インターフェース付きスクリプトを取得したスポーンシステムがSibouFrag()を常時監視して、すべてのスクリプトのSibouFrag()がtrueになったら全滅判定にできます。
情報の送り先は、
記述例はこんな感じで↓
GameObject ObjectSetSendTarget;
public void SetSendTarget(GameObject Target)
{
ObjectSetSendTarget = Target;
}
スポーンシステムがキャラを生成し、インターフェース付きスクリプトを取得したら、
SetSendTarget(this);
…つまりスポーンシステムのあるゲームオブジェクトをセットすることで、生成されたキャラ側はスポーンシステムを認識でき、死んだらスポーンシステムの持つキルカウント用変数とかにアクセスして死亡通知を書き込むなりできます。
生成物の数が4桁5桁とかあったら常時監視では重いでしょうというプランBでつけました。
また(自分のプログラム技術が拙いせいだけど)死亡フラグが何故か認識されない状況があったので、データをやりとりする手段は複数あるといいんじゃないかなと。
リセット機能は、死んだ後復活させたいときに役立ちます。復活したらキルカウントは取り消すとか。
死んだキャラや拾われたアイテムを削除せず非表示の不活性状態にしてキープ、一定時間後にリセットして再度湧かせることができます。
この3点セットは、HPやダメージのやり取り以上に汎用的に使えるのではと思いました。
インターフェースをまとめて取得する方法
プレハブを生成する時に配列やリストで生成オブジェクトを取得する、まではいいとして、
体力管理スクリプトもちがプレハブの親階層だけにあるとは限らず、スポーンシステムはたいていシーン上に複数あるもので、となると生成キャラはスポーンシステムの子階層に作り、子階層を総ざらいして別のリストで取得する、としたいですが、子階層を全検索する方法が意外と見つからずでした。
この記事を参考にしてみましたが、
【Unity】子オブジェクトを取得する4つの方法
https://zenn.dev/daichi_gamedev/articles/b901ca3a1b4391
これが最適解かどうかは不明です。
落とし穴
この死亡判定インターフェース、使えない状況も当然あり、それを意識して避ける必要があります。
まず、第2形態とか持ってる敵です。
初期装備や装甲を破壊されたら第2形態に移行するような敵キャラは、パーツにHPがあり、同じ体力管理スクリプトをつけたくなるわけですが、そうなるとパーツまでキルカウントに含まれるし、リセットすると初期形態だけでなく、非表示にしてあった第2形態用オブジェクトが最初から表示されてしまうでしょう。そういうものにはたとえ耐久値や死亡判定があっても、例外処理や別系統の復活方法が必要です。
そして、不死のキャラです。
特に当たり判定がない飾りパーツや死なない設定のキャラにもこのインターフェースをつけてたら、死亡フラグは当然つかないわけで、全滅判定が狂います。体力管理も移動もアクションも一つのスクリプトで賄おうとしてた場合に問題になります。
インターフェースは複数種類継承できるので、使う機能ごとに分けてトッピングするといいようです。全部のせは思わぬ状況で苦しむことになります。なった。
プログラムの達人でもない限り、経験と失敗を重ねないと何が最適かわからないです。
いろいろ試して実際に使って使えると思ったのでこの記事を書きました。
リセット機能は実際には調整が地獄で時間が溶け尽きたので、確実にリセットできる手段として、Destroyしてプレハブ再生成という方法にしました。こいよGCかかってこい