はじめに
突然ですが、UE5のProject Launcher機能って便利ですよね!
「テストで必要なレベルのみをCook ▶ 実機に転送 ▶ 起動」までを 1クリックでできるので、実機上の確認・テストプレイが捗ります!また、それらの設定をProfileとして編集・管理できるので、目的・用途に応じたProfileを沢山用意している人も多いと思います。
dev.epicgames.com
(余談ですが、UE5.6からProject Laucnherが急に新しくなってビックリ!)
チームで開発をしていると、このProfileを共有・バージョン管理したくなります。が、共有するには<エンジンのルート>/Engine/Programs/UnrealFrontend/Profiles にProfileのデータ(.ulp2)を配置する必要があります。そのため、ランチャー版の場合はProfileデータを該当フォルダにコピーする作業をメンバー全員がすることになります。
…色々めんどくさい!ビルド版のEngineの場合でも、複数のプロジェクトが同時進行する時にトラブル起きそう!プロジェクト内のフォルダでProfileを管理したい!例えば、Config/Profiles とかでさ!
ということで何とかできないか?とエンジンコードなどを調べてみたという話です。 なお、検証環境は UE5.6.0 です。
結論
で先に結論を…
- エンジン外にあるProfileデータをエディタに認識させることはできる(エンジン改造なしで)
- が、Profileデータの読み書き処理にて
/Engine/Programs/UnrealFrontend/Profilesが直書きされてるため、エディタ上で↑のProfileを編集するとエンジン側にデータが複製されてしまう - なので、ちゃんとやるなら…エンジン改造が必要…
という、記事タイトルの通りです。つらい。 以下は結論を聞いた上で詳細を知りたいという物好きな方向けです。
調査・検証したこと
エンジンコードを見た所…ProjectLauncherのProfileは FLauncherProfileManagerにて管理されており、例えばProfileのロードはFLauncherProfileManager::LoadProfiles( ) にて 行われていました。また、このFLauncherProfileManagerはLauncherServicesモジュールから取得することができようです
(参考:FMobileLauncherProfileWizardModule)
ということは…プロジェクト側の特定フォルダ以下にあるProfileデータをFLauncherProfileManagerに渡せばいいのでは?
で、やってみました!
void FXXXModule::StartupModule() { ILauncherServicesModule::ProfileManagerInitializedDelegate.AddRaw(this, &FXXXModule::OnProfileManagerInitialized); // Check if ProfileManager was already initialized if (ILauncherServicesModule* LauncherServicesModule = FModuleManager::GetModulePtr<ILauncherServicesModule>("LauncherServices")) { TSharedRef<ILauncherProfileManager> LauncherProfileManager = LauncherServicesModule->GetProfileManager(); OnProfileManagerInitialized(LauncherProfileManager.Get()); } } void FXXXModule::OnProfileManagerInitialized(ILauncherProfileManager& ProfileManager) { TArray<FString> ProfileFileNames; const FString ProfileFolder = FPaths::ProjectConfigDir() / TEXT("Profiles"); IFileManager::Get().FindFilesRecursive(ProfileFileNames, *ProfileFolder, TEXT("*.ulp2"), true, false); for (TArray<FString>::TConstIterator It(ProfileFileNames); It; ++It) { FString ProfileFilePath = *It; ILauncherProfilePtr LoadedProfile = ProfileManager.LoadJSONProfile(*ProfileFilePath); if (LoadedProfile.IsValid()) { ProfileManager.AddProfile(LoadedProfile.ToSharedRef()); } else { IFileManager::Get().Delete(*ProfileFilePath); } } }
このコード自体はうまくいって、<プロジェクトのルート>Config/Profilesに配置したProfileデータ(.ulp2)が無事エディタに認識されました!やったね!
が、エディタ上でそのProfileを編集すると…その編集内容が反映されたProfileデータは <エンジンのルート>/Engine/Programs/UnrealFrontend/Profiles に保存されます!(プロジェクト内のものは編集されずに)
/(^o^)\
原因は非常にシンプル! Profileの読み書きで使われる処理にてEngine内のパスが直書きされてるためです!
// Engine/Source/Developer/LauncherServices/Private/Profiles/LauncherProfile.h static FString GetProfileFolder(bool bNotForLicensees) { if (bNotForLicensees) { return FPaths::EngineDir() / TEXT("Restricted/NotForLicensees/Programs/UnrealFrontend/Profiles"); } return FPaths::EngineDir() / TEXT("Programs/UnrealFrontend/Profiles"); } virtual FString GetFilePath() const override { return GetProfileFolder(bNotForLicensees) / GetFileName(); }
(´;ω;`)
なので、ちゃんと対応するとなると…
FLauncherProfileManager::LoadProfiles( )に Project側のフォルダをチェックする処理を追加- エディタ設定でどのフォルダかを設定できるようにすると丁寧
- Load時に
FLauncherProfileにパス情報を保存し、GetFilePath関数はそれを返すようにする
といったエンジン改造が必要になります(他に細かい修正がちょこちょこ必要になる予感)
…「Project LauncherのProfile(.ulp2)をプロジェクト側で管理したかったけど…大変そうだったという話」でした
おしまい