質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2792閲覧

【Unity】オブジェクトプールを使用すべき状況について

cushionA

総合スコア90

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

2クリップ

投稿2020/08/28 08:52

編集2021/01/29 08:43

質問内容

こんにちは。
少し長くなりますが質問の方に目を通していただければ幸いです。

僕は現在2Dのアクションゲームを作っています。
そして魔法を実装するにあたってオブジェクトプールについて調べていたところ、疑問が湧いたので知恵をお借りさせていただこうと思い立った次第です。

今のところ、テスト用の火の玉を飛ばす魔法をAddressables.InstantiateAsyncとAddressables.Release、それからPrefabを使って実装しています
しかしインスタンス化とDestroy(僕はReleaseを使っていますが)はとても重いとのことなのでオブジェクトプールという手法について調べてみました。

ですがオブジェクトプールの解説での例はいずれも弾幕ゲームのように二十、三十と弾丸を撒き散らすようなゲームを想定しており、さらに下記のページのようにオブジェクトプールを行わない方が効率的な場合もあると言及しているサイトもありました。

参考にしたページの一つ

そこで質問なのですが、オブジェクトプールはどのような場面で使うのかをどうか教えていただきたいです。
また、魔法に使うとしてオブジェクトプールを行うのは魔法装備後に装備してる魔法を検出して、その魔法のオブジェクトのみをプールするような運用でいいのでしょうか?
あらかじめすべての魔法のオブジェクトをプールすることはしない方がいいですか?

個人的に使えるのかなと思っている場面は

・敵キャラを配置、あるいは無限湧きの敵キャラのみ?
・魔法
・継続的にオブジェクトを吐くステージギミック
・ロードで復活する障害物

このくらいでしょうか。
しかしこうなると逆にプールしない方がいいオブジェクトが分かりません。
一度壊すと復活しない隠し通路に繋がる壁とかボスとかでしょうか?

一応下で今回はどちらがいいのかの検証をしてみますが、オブジェクトプールとはどのくらいの規模で最適となるのか諸先輩方のアドバイスをいただければ幸いです。

どうぞよろしくお願いいたします。

検証結果

オブジェクトプールはこちらのサイトのコードを使用して実装しました。

Akira_Kido_N様の 「Unityで簡単☆オブジェクトプール」

そしてこちらのメソッドはAddressables.InstantiateAsyncで生成したオブジェクトはプールできなかったのでInstantiateメソッドを使用したことを記しておきます。
また、プールの最大量は五個にしました。
実験の条件はプールする場合としない場合で断続的に五個ずつの火の玉を飛ばしてメモリの平均使用量と最高使用量を比較しました。

肝心の結果ですが、下記の画像のようにメモリの負荷はプールしない側が勝っていました。

プールしてない方
プールせず

プールした方
プールした

平均的に見てメモリ使用量は20MBほどプールしてない方が軽いです。

よって今回の場合はプールしない方がよさそうですが、もしなにか間違えていたら教えてください。
それから、プールを使用する場面について何かアドバイスがあれば教えてください。

この手の検証にはまだ不慣れなので、なにか結果の提示に不備がありましたらそちらもご指摘くださいませ。
よろしくお願いいたします。

(また、本筋とは違いますし本当に図々しいのですが、一つ気になることがあります。
プロファイラのメモリ使用量の変動を見ていてたまにGCが起きていることに気が付きました。
何故ならメモリの線グラフが増えては減るジグザグになり、Monoの使用量が2MBほど増えて減るのを繰り返していたからです。
ゲームプログラミングなら最初に全てのメモリを確保しておくのが望ましいかと思われますが、このようなジグザグになるコードは修正した方がよいでしょうか?
一応LINQの削除やコレクションの使いまわしなどはできうる限りで行っています。
どうかこちらも気が向きましたらご教授くださいませ)

補足情報(FW/ツールのバージョンなど)
Unity 2019 3,7f1

追記
AddressablesでAddressables.InstantiateAsync().Resultで生成したオブジェクトを渡すことができるようです。
大変失礼しました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エゴサしてて見つけました、「Unityで簡単☆オブジェクトプール」の記事を書いたものです。

Addressables自体そこまで使っていないのでcushionAさんのケースに当てはまるかわかりませんが、一般論的なお話をさせて頂ければと思います。

オブジェクトプールを使う理由は一つで、インスタンス化=アロケーションコストを極限まで下げる為にあります。逆に、速度を稼ぐためにメモリ使用量は犠牲になっています。

たとえばですが、1分の間に弾を100発撃つとして、常に画面上には20発しか表示されていないとします。これをオブジェクトプーリング無しで行うと、GameObject の Instantiate が 100 回呼び出されることになります。逆に、オブジェクトプーリングが適切に行われていれば、最初の20発分のみ生成されるので、Instantiate は 20 回のみになります。ですので、理論上CPU使用率が 1/5 に抑えられ、パフォーマンスが上がっていると言えます。

ただし、メモリの使用量に関してはどうしてもオブジェクトプーリングしたほうが大きくなります。例えば上記の例で、最初の10秒で20発、次の10秒で5発しか弾を発射しなかった場合、オブジェクトプーリングしていると20発分のメモリが常に確保されていますが、適宜Destroyしていれば次の10秒間は5発分のメモリしか使用しません。

とはいえ、①20発分のアロケーションができている時点でメモリに支障がない点、②ゲーム中にアロケーションやGCが走ってゲーム体験が損なわれる点を考慮するに、基本的にはプーリングしてしまってもよい、と考えてもいい気はします。逆に、プーリングしなくてもパフォーマンスに影響が無いのであれば、その最適が不要であるともいえます(コードを書くということは負債を増やすということです)。

ちなみに更新していなくて申し訳ないのですが、Qiitaに上げているオブジェクトプールはスレッドセーフでないので、UniTaskとかCoroutine等から呼び出す場合はご注意ください。

投稿2021/01/29 04:55

akiraKido

総合スコア9

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cushionA

2021/01/29 08:35 編集

回答いただきありがとうございます! しかし記事をお書きになったご本人様からまさか回答いただけるとは思ってもいませんでしたのでとても驚いております。 そして大変丁寧な解説を伺えたおかげで僕がオブジェクトプールについて思い違いをしていたことがよくわかりました。 重ねてありがとうございます。 つまり僕はなぜかメモリの使用量を減らそうとしていますが、そもそもオブジェクトプールとはメモリを多少犠牲にしてCPUの負荷を減らし助けるというものなのだと理解することができました。 変な勘違いをしてしまい失礼いたしました。 それから一つ訂正なのですが、Addressablesで生成したオブジェクトはプールできないという文は誤りでした。 Addressables.InstantiateAsync().Resultで生成したオブジェクトを渡すことができるようです。 実際問題なくプールできました。 誤りを放置してしまい申し訳ありません。 少し話がそれたのでまとめると、オブジェクトプールを使うべき場面は「これ破棄するけどまたすぐ使うな」という場合だと考えてみることにします。 勘違いだったら申し訳ないですが、大変分かりやすい説明で助かりました。 それからスレッドセーフの件ですが、注意喚起ありがとうございます。 UniTaskはまだ使ったことがないのですが、使う時は十分に気をつけます。 丁寧に教えて下さりありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問