2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[UE4][UE5]ソースアセットからローカライズされたアセットのパスを取得する方法について

Last updated at Posted at 2022-08-31

要約

  • ソースアセット( 以後 Source )とローカライズされたアセット( 以後 Localized )の間には参照関係がないため、ChunkID設定などで困るときがある
  • SourceからLocalizedのパスを取得できる FPackageLocalizationUtil::ConvertSourceToLocalized関数 が便利
  • 上記関数を組み込んだPrimaryAssetLabelを用意すると、Localized の ChunkID設定対応が少し楽になるかも?

※ 今回はローカライズやChunk分けについてある程度知っているプログラマ向けの内容なので、少し説明がラフになっている箇所があります。ごめんなさい!

はじめに

image.png
多言語対応する上でアセットのローカライズはほぼ必須です。地域ごとに異なるテクスチャ素材を使ったり、言語設定に応じた音声データを再生したりなど様々な用途で用いられます。

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);
}

image.png

やってることは簡単で、

  1. Get Localized Culturesノードでローカライズ対象の言語リストを取得
  2. 各言語ごとの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)にアセットが存在するか、つまり対象の言語に対してローカライズ済みかは考慮していません。そのため、実現したい処理によっては各アセットパスにアセットが存在するか否かを確認する必要がでてきます。

例えばこんな感じ。BP版とcpp版の両方を載せておきます。
image.png

#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関数を使うことになります。

おしまい!

2
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?