今回はUnityおよびプログラミング上級者向けの最適化に関するニッチな話題で、タイトルの通り
をご紹介するという内容になっています。
オープンワールドゲームを作った経験のある方であれば、その最適化を行う際は主に
- 描画の最適化
- アセットの読み込み(=ロード)の最適化
という2点が重要になってくることはご存じかと思います。このうち1番目の描画に関しては比較的簡単に行える最適化手法が色々とあるのですが、2番目に関してはネット上に情報がほとんどなく個人的には全くの手探りで行うしかありませんでした。
ただ最近になってようやく
- 「八分木空間分割」により、読み込むべきアセットを超高速で判定する
- 上で必要だと判定したアセットをUnityの「Addressables」で必要な時だけ読み込む
という実用的な最適化手法が分かったので今回記事を書こうと思った次第です。ここではその手法について詳しくご紹介しますね。
オープンワールドゲームのアセットのロードに関する問題
でははじめに、そもそもオープンワールドゲームにおいて「アセットの読み込みを最適化する必要がある理由」から解説していこうと思います。
オープンワールド上の全アセットを事前にロードしておくのは非効率的
まず、その理由とは端的に言えば
です。オープンワールドゲームではシームレスなシーンに大量のオブジェクトを配置することになるので、最適化をせず全オブジェクトのアセットを事前に読み込んでおく場合、オブジェクトがたくさん存在するフィールドほど多くのメモリを消費します。
しかし当然ながらメモリは有限かつ貴重なリソースであり、足りなくなればゲームのクラッシュの原因にもなるためその使用量はなるべく減らしておくべきですよね。
そこでオープンワールドゲームの性質を考えてみると、
- あまりにも遠すぎるオブジェクトはプレイヤーが近づくまでロードする必要がない
- 小さなオブジェクトもプレイヤーが近づくまでロードする必要がない
- 屋内など、外から見て完全に隠れている部分は中に入るまでロードする必要がない
といった点が挙げられるので、全てのアセットを事前にロードしておく必要は全くないことが分かります。
では、必要なアセットをどのような方法でロードすればよいか?
このことが分かったら、次は「必要なアセットをどんな方法で適宜ロードするか?」を考える必要があります。
これは先ほどのオープンワールドゲームの性質を考えると、すごくザックリ言えば
ということになります。
ただしこの場合の距離判定は曲者です。なぜなら通常、オープンワールド上には大量のオブジェクトがあるので、プレイヤーとの距離を判定するのであれば非常に高速な判定方法を使わなければ簡単に処理落ちしてしまうからです。
またフィールド上のオブジェクトは大きさもバラバラなので、例えばプレイヤーと同じ距離だけ離れていても
- 大きなオブジェクトは遠くからでも見えるようにしたい
- 逆に小さなオブジェクトは近づくまでロードしないようにしたい
という風になってきます。
そこでオブジェクトの大きさを考慮した超高速な距離判定を行うための方法としてここでは「八分木空間分割」というアルゴリズムを利用することにしました。また、ロードが必要だと判定したオブジェクトを適宜読み込むための方法として「Addressables」を使うことにしました。
では下記でそれぞれについて簡単に説明しますね。
八分木空間分割とAddressablesによるオープンワールドゲームの最適化手法
「八分木空間分割」で読み込むアセットを高速で判定する方法
まずは読み込むべきアセットを超高速で判定できる八分木空間分割についてです。これに関しては素人の私が説明するよりも、下記の3つのページが非常に丁寧かつ詳しいのでそちらをご覧頂いたほうが早いと思います(一番目の記事は四分木の場合ですが、八分木の場合の考え方の基礎となるため紹介しています)。
要約すると
- 衝突判定等の処理においては、離れているオブジェクト同士をわざわざ判定するのは効率が悪い。そこで八分木による空間分割を行い、同じ空間上にあるオブジェクトに対してのみ処理を行う。
- あるオブジェクトを分割した空間に登録したりそこから削除したりする処理は非常に遅い。そこで「モートンオーダー」というアルゴリズムを使って超高速でその辺の処理を行えるように工夫する。
- 線形配列を使うことで空間へのアクセスを高速化する。
というような内容です。なお上記のページでは衝突判定の最適化手法として八分木空間分割を取り上げていますが、これを応用すれば「プレイヤーに近いオブジェクト」を判定するためにも使えます。
参考までに、3番目の記事のサンプルスクリプトを参考にして作った最適化システムが実際に動いている様子は次のGIFのとおりです。
青いキューブがプレイヤーで、プレイヤーが近づいたときだけ近傍の灰色のキューブがロードされていることが分かります。また、プレイヤーが離れるとキューブがアンロードされていることもお分かりいただけると思います。
Addressablesを使ってアセットを必要な時だけ読み込む方法
お次はAddlessablesを使ってアセットを必要な時だけ読み込む方法についてですが、こちらについては次の記事で解説しているアセットのロード・アンロードのやり方を使えばOKです。
これで必要なときに必要なアセットだけを読み込めるようになるので、たくさんのオブジェクトが存在するオープンワールドでも負荷を軽減することができると思います。
おわりに
以上、かなりざっくりした内容になってしまいましたが、オープンワールドゲームを最適化する手法についてご紹介しました。
オープンワールドゲームはレベルデザインだけでなく最適化も非常に重要になるので、ぜひ上記の内容を参考にしていただき最適化システムを構築して頂ければと思います。
この記事がUnityでのゲーム開発のお役に立てば幸いです。