要約
- ソースアセット( 以後 Source )とローカライズされたアセット( 以後 Localized )の間には参照関係がないため、ChunkID設定などで困るときがある
- SourceからLocalizedのパスを取得できる
FPackageLocalizationUtil::ConvertSourceToLocalized
関数 が便利 - 上記関数を組み込んだPrimaryAssetLabelを用意すると、Localized の ChunkID設定対応が少し楽になるかも?
※ 今回はローカライズやChunk分けについてある程度知っているプログラマ向けの内容なので、少し説明がラフになっている箇所があります。ごめんなさい!
はじめに
多言語対応する上でアセットのローカライズはほぼ必須です。地域ごとに異なるテクスチャ素材を使ったり、言語設定に応じた音声データを再生したりなど様々な用途で用いられます。
ReferenceViewerで見ると一目瞭然なのですが、SourceとLocalizedの間には参照関係がありません。ソフト参照もありません。 これは余計な処理・メモリ消費を避けるための仕様(のはず)ですが、一部の作業で困る場合があります。
例えばDLCを作るためには各アセットをChunk機能を使ってグループ分けをする必要があるのですが、上記の仕様によりSourceとLocalizedは個別にグループ分け操作・設定をする必要があります。何らかの参照関係があればSourceに対して対応すれば良いのですが…これは少し面倒です。
こういったケースで便利なのが、SourceからLocalizedのパスを取得できる FPackageLocalizationUtil::ConvertSourceToLocalized
関数です。
SourceからLocalizedのパスを取得する方法について
早速ですが、コードと使い方をぺたり。
#include "Internationalization/PackageLocalizationUtil.h"
bool UMyEditorBlueprintFunctionLibrary::ConvertSourceToLocalized(const FString& InSource, const FString& InCulture,
FString& OutLocalized)
{
return FPackageLocalizationUtil::ConvertSourceToLocalized(InSource, InCulture, OutLocalized);
}
やってることは簡単で、
-
Get Localized Cultures
ノードでローカライズ対象の言語リストを取得 - 各言語ごとのLocalized のパスを
ConvertSourceToLocalized
で取得
という感じで、結果は以下のようになります。
Source:
/Game/AssetLocalization/Test.Test
ローカライズ対象:
日本語、英語、中国語(簡体)
結果
/Game/L10N/ja/AssetLocalization/Test.Test
/Game/L10N/en/AssetLocalization/Test.Test
/Game/L10N/zh-Hans/AssetLocalization/Test.Test
かんたんですね!しかし、注意点があります。
ConvertSourceToLocalized関数の注意点
ConvertSourceToLocalized
関数は出力したアセットパス( OutLocalized
)にアセットが存在するか、つまり対象の言語に対してローカライズ済みかは考慮していません。そのため、実現したい処理によっては各アセットパスにアセットが存在するか否かを確認する必要がでてきます。
#include "AssetRegistry/AssetRegistryModule.h"
if (FPackageLocalizationUtil::ConvertSourceToLocalized(Source, Culture, OutLocalized))
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FAssetData LocalizedAssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*OutLocalized);
if (LocalizedAssetData.IsValid())
{
// ローカライズ済みのアセットが存在する場合の処理
}
}
Soruce からちゃんと存在する Localized のアセットパスを取得できるようになりました!これでSourceのChunk IDと同じ値を Lozalicedに設定したり、逆にローカライズされていないアセットの洗い出しをしたりなど、色んなことができるようになります!
ローカライズ済みのアセットがあったらAssetBundleに追加するオレオレPrimaryAssetLabelの作り方
最後に、Label Assets In My Directory
を有効にして収集した各アセットに対してLocalizedが存在するかをチェックし、もし存在していたら同じAssset Bundle
に追加するPrimary Asset Label
派生クラスのコードを貼って終わりにします。
#include "MyPrimaryAssetLabel.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/AssetManager.h"
#include "Internationalization/PackageLocalizationUtil.h"
#if WITH_EDITORONLY_DATA
void UMyPrimaryAssetLabel::UpdateAssetBundleData()
{
Super::UpdateAssetBundleData();
if (!UAssetManager::IsValid())
{
return;
}
FAssetBundleEntry* DirectoryBundleEntry = AssetBundleData.FindEntry(DirectoryBundle);
if(DirectoryBundleEntry)
{
const TArray<FString> LocalizedCultureNames = FTextLocalizationManager::Get().GetLocalizedCultureNames(ELocalizationLoadFlags::Game);
TArray<FSoftObjectPath> LocalizedPaths;
for( FSoftObjectPath BundleAssetPath : DirectoryBundleEntry->BundleAssets )
{
for( FString LocalizedCultureName : LocalizedCultureNames )
{
FString LocalizedObjectPath;
if (FPackageLocalizationUtil::ConvertSourceToLocalized(BundleAssetPath.ToString(), LocalizedCultureName, LocalizedObjectPath))
{
// Does this localized asset already exist?
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FAssetData LocalizedAssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*LocalizedObjectPath);
if (LocalizedAssetData.IsValid())
{
LocalizedPaths.Add(LocalizedAssetData.ToSoftObjectPath());
}
}
}
}
if(!LocalizedPaths.IsEmpty())
{
AssetBundleData.AddBundleAssets(DirectoryBundle, MoveTemp(LocalizedPaths));
}
}
// Update rules
UAssetManager& Manager = UAssetManager::Get();
FPrimaryAssetId PrimaryAssetId = GetPrimaryAssetId();
Manager.SetPrimaryAssetRules(PrimaryAssetId, Rules);
}
#endif
これでSourceとLocalizedを同じChunkに入れたいのに別々に管理する…という面倒な作業が必要なくなります。ただ見ての通り、Primary Asset LabelのExplicit Asset(Blueprints) や Asset Collectionに対しての処理は割愛していますので、必要な場合は上記コードやUPrimaryAssetLabel::UpdateAssetBundleData()
を参考にご対応くださいまし。
あとは、Primary Asset Label
派生クラスをAssetManagerに登録することをお忘れなく(僕は忘れて数十分無駄にしました)
おまけ
逆に Localized から Sourceのアセットパスを取得したい場合は FPackageLocalizationUtil::ConvertLocalizedToSource
関数を使うことになります。
おしまい!