LoginSignup
1
1

【UE5】Max UObject count is invalidが表示され、パッケージしたアプリが起動しなくなる

Last updated at Posted at 2023-11-12

確認したバージョンは UE5.3.2です。

はじめに

プロジェクト設定を色々といじった後、アプリをパッケージして.exeを起動すると、Max UObject Count is invalid. It must be a number that is greater than 0.と表示されて.exeが終了してしまう状況に陥ってしまった。

image.png

原因はちょっと罠っぽいものだったので、備忘録として残しておく。

状況は:

  • エディタ上で実行した場合は問題なく動く。
  • そのアプリをパッケージしてできた.exeファイルを起動すると、上記のダイアログが表示される。ダイアログ右下の OKボタンを押すと、アプリが終了する。(アプリを正しく起動する手段がない)
  • 当時、Garbage Collectionに関する試行錯誤のため、プロジェクト設定をあれこれいじっていた。

原因

まずは結論から。

この Fatalエラーは、プロジェクト設定の Engine - Garbage Collectionの Optimizationカテゴリにある「Maximum number of UObjects that can exist in cooked game」(デフォルト値0)を、一度他の数値に変えてから0に戻した場合に発生する。

image.png

プロジェクト設定の各項目は「現在表示されている値と異なる値を書き込む」ことで、.iniファイルに即座に反映される。
例えば、今回問題となっている「Maximum number of UObjects that can exist in cooked game」を、0 → 1 → 0と書き換えると、.iniファイルの内容は以下のように変化する。

※当該.iniファイルはプロジェクトディレクトリの Confing/DefaultEngine.ini

1)初期状態。当該パラメータを書き換える前 (値は0のまま)

ProjectDir/Config/DefaultEngine.ini
[/Script/Engine.GarbageCollectionSettings]

2)当該パラメータを 0 → 1 に書き換えた後

ProjectDir/Config/DefaultEngine.ini
[/Script/Engine.GarbageCollectionSettings]
gc.MaxObjectsInGame=1

3)当該パラメータを 1 → 0 に戻した後

ProjectDir/Config/DefaultEngine.ini
[/Script/Engine.GarbageCollectionSettings]
gc.MaxObjectsInGame=0

つまり:

  • プロジェクト設定でいう「Maximum number of UObjects that can exist in cooked game」は、.iniファイルでは「gc.MaxObjectsInGame」である。
  • 何もしなければ DefaultEngine.iniには gc.MaxObjectsInGameに関する項目は無いはずだった。
  • いじった本人は「元の値に戻した」つもりだったのに、DefaultEngine.iniには元々無かったはずの gc.MaxObjectsInGame=0が書き込まれてしまった。
  • gc.MaxObjectsInGame=0はパッケージしたアプリ専用のパラメータのため、パッケージ版でしか問題が発現しなかった。
  • gc.MaxObjectsInGameの値は 0であってはならないため(本来、何らかの大きな数値を書くべきである)、.exe起動時に Fatalエラーダイアログが起動した。

…という現象が起きてしまったのである。

解決方法

元の状態に戻してやれば解決する。
つまり、テキストエディタで DefaultEngine.iniからgc.MaxObjectsInGame=0の行を削除すればよい。
※パッケージを作り直す必要はある。

深掘り

ここから先は、この問題について深掘りした内容を書いていく。

gc.MaxObjectsInGameのデフォルト値はいくつか?

プロジェクト設定「Maximum number of UObjects that can exist in cooked game (=gc.MaxObjectsInGame)」のデフォルト値がゼロであることから、「パッケージしたアプリではメモリが許す限り無限」だと連想してしまう。
だが、gc.MaxObjectsInGameの実際の初期値は 2 * 1024 * 1024 (=2,097,152)である。
これはエンジンソースの InstallDir\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectBase.cpp の984行目、UObjectBaseInit()関数で指定されている。
エンジンソースをコピペするのは憚られるので、ダミーコードで流れを示す。

UObjectEngine.cpp (986)
void UObjectBaseInit()
{
	int32 MaxUObjects = 2 * 1024 * 1024; // Default to ~2M UObjects

	if (/*クックされたデータ*/)
	{
		// Maximum number of UObjects in cooked game
		GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"), TEXT("gc.MaxObjectsInGame"), MaxUObjects, GEngineIni);
	}
	else
	{
		// Maximum number of UObjects in the editor
		GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"), TEXT("gc.MaxObjectsInEditor"), MaxUObjects, GEngineIni);
	}

	GUObjectArray.AllocateObjectPool(MaxUObjects);
}
  1. まずローカル変数MaxUObjectsが 2 * 1024 * 1024 (=2,097,152)で初期化される。
  2. クックされたデータが要求されている場合、つまりパッケージ版のアプリの場合は、if の中に入る。その場合 .iniファイルからgc.MaxObjectsInGameの値が取得される。
    • GConfing->GetInt()は .iniファイルにそのパラメータの記述がない場合は何もしない。初期状態では、DefaultEngine.iniをはじめとするどのEngine.iniファイルにもgc.MaxObjectsInGameの記述はない。したがって、ローカル変数MaxUObjectsは 2 * 1024 * 1024のままである。
    • もし、DefaultEngine.iniなどのどれかのEngine.iniファイルにgc.MaxObjectsInGameの記述があれば、GConfing->GetInt()はその値を取得する。今回私がハマったように、手動でゼロを書き込んでしまっていた場合、ローカル変数MaxUObjectsはゼロになり、そのあと呼び出されるGUObjectArray.AllocateObjectPool()から、冒頭に載せたスクショのような Fatalエラーのダイアログが表示され、アプリは終了する。

なお、エディタから起動した場合は、elseの中に入る。
この場合、gc.MaxObjectsInEditorの値がローカル変数MaxUObjectsに入る。
ちなみに、gc.MaxObjectsInEditorは InstallDir\Engine\Config\BaseEngine.iniにデフォルト値が定義されており、その値は 25,165,824である。

EngineInstallDir/Config/BaseEngine.ini
[/Script/Engine.GarbageCollectionSettings]
gc.MaxObjectsInEditor=25165824

この値は、プロジェクト設定の Engine - Garbage Collectionの Optimizationカテゴリにある「Maximum number of UObjects that can exist in editor」に相当する。
image.png

gc.MaxObjectsInGameもしくはgc.MaxObjectsInEditorの値を超えるとどうなるか

これを実験するのは比較的たやすい。UObjctを大量に生成(NewObject)するだけで発現させられる。
この場合、次のようなダイアログが表示され、OKボタンを押すとアプリが終了する。
image.png

つまり、gc.MaxObjectsInGamegc.MaxObjectsInEditorの値を超えるとアプリは強制終了することになる。

上記のダイアログのスクショはパッケージ版で試したものである。
ここに書かれているオブジェクトの最大数は 2,162,688個であり、上述のMaxUObjects = 2 * 1024 * 1024(=2,097,152)とは異なる。
オブジェクトはチャンク(1チャンク辺り 65,536個)で管理されており、実際のオブジェクト上限数はgc.MaxObjectsInGamegc.MaxObjectsInEditorより大きくて最も近い数で管理されている。
InstallDir\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectArray.hの464行目、PreAllocate()関数内で以下のように算出されている。

UObjectArray.h(461)
void PreAllocate(int32 InMaxElements)
{
	// NumElementsPerChunkは 64 * 1024 (=65,536)
	MaxChunks = InMaxElements / NumElementsPerChunk + 1;
	MaxElements = MaxChunks * NumElementsPerChunk;
	Objects = new FUObjectItem*[MaxChunks];
	// 以下略...
}

要するに65,536の倍数に揃えられるということである。

※この計算に基づくと、エディタ版は gc.MaxObjectsInEditorのデフォルト値 25,165,824から、実際の最大オブジェクト数は 25,231,360になる。(65,536の385倍)

では gc.MaxObjectsInGameはいくつに設定したらよいのか (※結論なし)

gc.MaxObjectsInEditorは、BaseEngine.iniに初期値 25,165,824が与えられている。
一方、gc.MaxObjectsInGameは、どの.iniファイルにも初期値が与えられておらず、UObjectBase.cppのUObjectBaseInit()内に設定されたローカル変数の初期値 (2 * 1024 * 1024)が流用されているのみである。ここで、エディタ版とパッケージ版のオブジェクト上限数を並べてみると…

上限数
パッケージ版 2,097,152
エディタ版 25,165,824

縦に並べてみると、なんと1桁も違うことが分かる。
そうすると、果たしてパッケージ版(gc.MaxObjectsInGame)を現状のまま、つまり値を設定せずに放置しておいてよいものか不安になってしまう。これでは、エディタでは十分動いていたものが、パッケージ版では動かないという現象が起きてしまうかもしれない。
とは言え、エディタ版の25,165,824をそのまま、パッケージ版にも流用(コピペ)するのも気が引ける。なぜなら、エディタ版ではエディットの都合上、非常に多くのオブジェクトを使うことができるようにする必要があるが、パッケージ版では「必要最低限」にしておきたいからだ。

問題提起だけしておいて結論を出せないで申し訳ないが、ここで言えることは以下のような感じだろうか…

  • 2 * 1024 * 1024 (=2,097,152)のままでも十分パッケージ版が動く場合は、まあ触らぬ神に祟りなしでスルーしても良いだろう。
  • パッケージ版で、オブジェクト数の上限を超えてしまう場合は:
    • 安易にエディタ版の数値(25,165,824)をコピペして使うのだけはやめておきたい。その値はあくまでエディタでエディットするためのものである。
    • プラットフォームのメモリ量に応じており、かつそのアプリに必要十分な値を、試行錯誤して見つけて設定する必要があるだろう。そのためには、様々なケースを想定したテストプレイで、実際のオブジェクト数の遷移をプロファイリングする必要があるだろう。
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1