title | tags |
---|---|
プレハブのインスタンス数を管理するクラス |
Unity C# |
- Unity 2018.4、2020.3 で確認しました。
- addressablesを使用しています。
同じオブジェクト(GameObject)を複数作って使う場合、プレハブを使いますよね。 あらかじめシーンに配置して使うこともありますが、私はどちらかというと動的に生成したいことが多いです。
例えば、以下のような使い方で、uGUIでフライテキスト(ダメージの数値表示とかテロップとかの類)を表示するプレハブを作るものとします。
_ = FlyText.CreateAsync ("Start!");
使う側は、単にクラスの静的メソッドを呼ぶだけです。 このFlyTextクラスは、例えば以下のようになります。
using UnityEngine;
using UnityEngine.UI;
/// <summary>フライテキスト</summary>
public class FlyText : MonoBehaviour {
#region Static
public static async Task<GameObject> Prefab {
get {
if (!prefab) {
prefab = await Addressables.LoadAssetAsync<GameObject> ($"Prefabs/{typeof (FlyText).Name}.prefab").Task;
}
return prefab;
}
}
private GameObject prefab;
public static async Task<FlyText> CreateAsync (GameObject parent, string message, float end = 2.5f) {
var instance = Instantiate (await Prefab, parent.transform);
instance.init (message, end);
return instance;
}
#endregion
private void init (string message, float end) {
transform.SetAsLastSibling ();
gameObject.SetActive (true);
var text = GetComponentInChildren<Text> ();
if (text != null) { text.text = message; }
var panelRect = GetComponent<RectTransform> ();
panelRect.sizeDelta = new Vector2 (text.preferredWidth + 128, panelRect.sizeDelta.y); // 文字量に応じたサイズ
Destroy (gameObject, end);
}
}
このクラスを使う場合は、クラス名と同じ名前のプレハブをPrefabs
フォルダに用意することになります。
しかし、このままでは、タイミング次第で複数が重なって表示されることになります。
上記のフライテキストの場合なら、「既に表示中なら、表示中のものを消してから、新たに表示する」ようにしたいです。 そして、それを組み込むだけなら話は簡単です。 しかし、使い道によっては、「既に表示中なら、表示中のものを残して、新たに表示しようとしたものを破棄する」ような場合もありそうです。 他にも、モーダルダイアログのように複数重ねる可能性があって、最前だけを特別扱いしたいような場合もあるでしょう。
uGUIの例ばかりですが、私の場合は、プレハブから動的に生成して数や挙動を管理したいものが多くあります。 それぞれに同じようなことを書くのが面倒なので、共通化できないか考えた末に、以下のようなアプローチになりました。
このクラスを使うフライテキストは、以下のようになります。
ManagedInstance/Assets/Scripts/FlyText.cs
Lines 1 to 56 in d4658c9
フライテキストの基本的な使い方は変わっていません。
_ = _FlyText.CreateAsync ("Start!");
以下のような使い方が可能になります。
if (FlyText.managedInstance.OnMode) {
// フライテキスト表示中
}
// フライテキスト全削除
FlyText.managedInstance.OnMode = false;
// 生成1
await FlyText.CreateAsync (gameObject, "Ready?");
// 消滅を待つ
await TaskEx.DelayWhile (() => FlyText.managedInstance.OnMode);
// 生成2
await FlyText.CreateAsync (gameObject, "Start!");
// 消滅を待つ
await TaskEx.DelayWhile (() => FlyText.managedInstance.OnMode);
// 3秒待つ
await Task.Delay (3000);
// 生成3
await FlyText.CreateAsync (gameObject, "End");
// 消滅を待つ
await TaskEx.DelayWhile (() => FlyText.managedInstance.OnMode);
// 終了
モーダルダイアログのように必要なだけ重ねて使いたい場合は、コンストラクタへ渡す引数が省略されて、以下のようになります。
public class ModalDialog : MonoBehaviour {
#region Static
public static ManagedInstance<ModalDialog> managedInstance { get; protected set; }
static ModalDialog () {
if (managedInstance == null) { managedInstance = new ManagedInstance<ModalDialog> (); } // 数を制限しない
}
//~
そして、複数のダイアログが重なり合っていても、以下のようにして応答すべき最も手前のダイアログを判別できます。
if (managedInstance.LastInstance == this) {
// 一番手前のダイアログなら
}
最後までお読みいただき、どうもありがとうございます。 ご意見、ご感想、ご提案など、何でもいただければうれしいです。
なお、リポジトリのコードは検証済みですが、記事中のコードは未検証です。 あしからずご了承ください。
OnDestroyCallback
は、コガネブログ様の記事を使わせていただきました。
いつも、役立つ記事をありがとうございます。この場を借りてお礼申し上げます。