200325 UE4 애니메이션 에셋 브라우저 Column 추가

2020. 3. 26. 01:24프로그래밍/TIL

1. 애니메이션 에디터의 에셋 브라우저 커스텀

 

애니메이션 에셋 브라우저 ( 이하 에셋 브라우저 )에는 딱 두 가지 Column 만 노출되게끔 되어있다.

그 두 가지는 에셋명, 에셋경로 이다. ( 주석으로 친절히 써져있다. 우린 이름이랑 경로 빼곤 다 숨길꺼야 )

 

하지만,

UPROERTY() 중에서, AssetRegistrySearchable 타입이면 에셋 브라우저 Column 으로 추가되는 것이 디폴트다.

그래서 이미 엔진코드에서 정의된 프로퍼티들은 모두 숨겨지도록 한딴함땀 목록을 구성해서 에셋 이름,경로만 노출되게 하고 있다.

 

AssetRegistrySearchable ?

언리얼은 에셋을 모두 로드하면 부하가 심하니, 에셋 관리에 필요한 최소한의 데이터만 에디터가 실행시점에 메모리에 올려둔다. 이 데이터들은 FAssetData 구조체로 표현되는데, 이 AssetData 를 통해 실제 에셋 (UObject) 을 로드해서 편집하는 방식이다.

USTRUCT(BlueprintType)
struct FAssetData
{
	GENERATED_BODY()
public:

	/** The object path for the asset in the form PackageName.AssetName. Only top level objects in a package can have AssetData */
	UPROPERTY(BlueprintReadOnly, Category=AssetData, transient)
	FName ObjectPath;
	/** The name of the package in which the asset is found, this is the full long package name such as /Game/Path/Package */
	UPROPERTY(BlueprintReadOnly, Category=AssetData, transient)
	FName PackageName;
	/** The path to the package in which the asset is found, this is /Game/Path with the Package stripped off */
	UPROPERTY(BlueprintReadOnly, Category=AssetData, transient)
	FName PackagePath;
	/** The name of the asset without the package */
	UPROPERTY(BlueprintReadOnly, Category=AssetData, transient)
	FName AssetName;
	/** The name of the asset's class */
	UPROPERTY(BlueprintReadOnly, Category=AssetData, transient)
	FName AssetClass;
	/** The map of values for properties that were marked AssetRegistrySearchable or added by GetAssetRegistryTags */
	FAssetDataTagMapSharedView TagsAndValues;
	/** The IDs of the chunks this asset is located in for streaming install.  Empty if not assigned to a chunk */
	TArray<int32> ChunkIDs;
	/** Asset package flags */
	uint32 PackageFlags;
    
    ...
 }

코드에서 확인 할 수 있듯, AssetData 는 전체 에셋 중에서 원하는 에셋들만 뽑아낼 수 있도록 (필터링을 거칠 수 있게) Path, Name, Class 등 정보를 가진다.

그중에서 User 가 커스텀 할 수 있도록 TagAndValues 라는 컨테이너를 제공한다.

KeyValue 타입으로, 각 클래스의 특징에 맞게 '에셋 로드'를 하지 않고 에셋을 검색 할 수 있도록 정보를 담는다.

 

예를 들면, Animation 에셋의 경우엔 SequenceLength 정보가 추가된다.

이 때문에 시퀀스길이가 5초 이상인 에셋 목록을 뽑아내는 작업이 가능해진다. 대표적으로 컨텐츠브라우저에서 검색창에 SequenceLength >= 5 를 치면 된다.

( 에셋 필터링은 FARFilter, FrontendFilter_XXX 등이 관련되어 있는데. 여기까지 파고들면 글이 길어지니 패스 )

 

AssetRegistrySearchable 메타지정자는, 바로 이 TagAndValues 에 해당 프로퍼티가 포함될 수 있도록 한다.

자세한건 아래 코드들 참고하면 된다.

UObject::GetAssetRegistryTags(...)
FAssetRegistryTag::GetAssetRegistryTagsFromSearchableProperties(...)

 

다시 본론으로 넘어와서, AssetRegistrySearchable 프로퍼티이면 애니메이션 에셋 브라우저에 노출되는 것이 디폴트인데

만약 숨기고 싶다면, 애니메이션 에셋 브라우저가 생성 될 때 '숨길 프로퍼티 목록' 에 추가해주면 된다.

실제로 애니메이션 에셋의 SequenceLength 프로퍼티가 AssetRegistrySearchable 타입인데도 노출되지 않는 이유는, 이 목록에 추가되고 있기 때문이다.

자세한건 관련 코드들을 참고하면 된다.

SAnimationSequenceBrowser::Construct(...)  -  애니메이션 에셋 브라우저 생성.
FContentBrowserSingleton::CreateAssetPicker(...)
SAssetPicker
FAssetPickerConfig - 어떤 에셋들을 선택 할 지에 대한 Config
SAssetView - 에셋 정보들을 표시하는 슬레이트

AssetView, AssetPicker 는 애니메이션 에셋 브라우저 뿐만 아니라, 언리얼 에디터에서 에셋 목록을 표시하는 Slate에서 범용적으로 사용하는 개념들이다.

 

 

한 가지 생각과는 다르게 동작했던 것이 있다.

UPROPERTY() 의 경우, NativeCode 와 에디터 상에서 다른 이름으로 표시 할 수가 있는데, ( DisplayName 메타지정자 )

에셋 브라우저에서는 이 DisplayName 을 사용하지 않았다. 즉, NativeCode 변수명 그대로 표시됬다.

 

확인해보니 에셋 브라우저에선 DisplayName 메타지정자가 아니라, FAssetRegistryTagMetadata 데이터를 사용한다.

 

AssetRegistryTagMetadata ?

이름 그대로 AssetDataTag 에 대한 꾸밈(?) 데이터라고 보면 된다.

이 구조체에서도 DisplayName 을 지정할 수 있는데, 애니메이션 에셋 브라우저에서는 이걸 쓴다.

 

덤으로 알게된 사실인데, AssetRegistryTagMetaData 에선 ImportantValue 라고 해서 중요값을 지정 할 수 있는데. 지정한 중요값과 프로퍼티의 값이 일치하면 '에셋 정보' 에서 글자색을 다르게 해서 보여준다. ( 내가 본건 노란색 )

 

-관련 코드

// 코드에서 추가
UObject::GetAssetRegistryTagMetadata(...) - 클래스 마다 오버라이딩해서 사용.


// 에디터 '프로젝트 세팅'에서 추가 할 수도 있다. ( 관리면에서 썩 좋은 방법은 아니지 싶다. )
static TSet<FName> MetaDataTagsForAssetRegistry;
UObject::GetMetaDataTagsForAssetRegistry() { return MetaDataTagsForAssetRegistry; }

 

 

그리고 또 하나 당황했지만 합리적이라 생각했던 것이 있는데,

애니메이션 에셋 브라우저에는 여러가지 애니메이션 에셋 타입들이 표시된다.

즉, 몽타주, 애님컴퍼짓, 애님시퀀스 등등 다 표시 될 수 있는데, 이들은 서로 다른 클래스들이기 때문에 메타데이터들도 다를 것이다.

 

그래서..에셋 브라우저의 Column 은 현재 목록에 있는 에셋들 중에서 가장 수가 많은 에셋 타입 기준으로 설정된다.

   ex. AnimComposite 5개, AnimMontage 3개면 AnimComposite 의 메타데이터들로 Column 이 생성된다.

 

물론 항상 노출 될 수 있게끔 Column 을 커스텀 할 수 있다. ( 그래서 나는 이 방법을 택했다. )

AssetView 를 생성 할 때, 커스텀할 Column 목록을 구성해서 넘겨주고, 이에 대한 Value 값을 리턴시키는 델리게이트 함수를 물리는 방식이다.

자세한건 코드를 살펴보면 된다.

 

-관련 코드

struct FAssetPickerConfig
{
   ...
   TArray<FAssetViewCustomColumn> CustomColumns;
   ...
}

 

 

한 가지 아쉬운 점은, 위 모든 작업이 '리터럴 문자열'을 Key 값으로 써서 관리되는 것이다. ( 하드하다ㅜ )

아래처럼.

 

// Create the ignore set for asset registry tags
// Making Skeleton to be private, and now GET_MEMBER_NAME_CHECKED doesn't work
AssetRegistryTagsToIgnore.Add(TEXT("Skeleton"));
AssetRegistryTagsToIgnore.Add(GET_MEMBER_NAME_CHECKED(UAnimSequenceBase, SequenceLength));
AssetRegistryTagsToIgnore.Add(GET_MEMBER_NAME_CHECKED(UAnimSequenceBase, RateScale));

변수명이 바뀐다면 같이 케어해줘야 하는데, 놓치기 딱 쉬워보인다.

이 문제를 방지하고자, 위 코드에서 보이는 GET_MEMBER_NAME_CHECKED 매크로가 있는데,

private 변수인 경우엔 쓸 수 없다. 그래서 Skeleton 은 리터럴 문자열을 사용하고 있다.

 

한 가지 완화 할 수 있는 방법은, 언리얼의 CDO 와 Property 관련 함수들을 사용하면 될 듯 하다.

CDO 를 가져와서 Property 들 목록을 가져와서 가공하는 식으로.

실제로 언리얼 Slate 코드들을 보면 이 방식으로 많이 쓴다.

 

[CODE_SNIPPET]

UProperty* Property = UStaticMesh::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_STRING_CHECKED(UStaticMesh, StaticMaterials));

UProperty* Property = Blueprint->ParentClass->FindPropertyByName(NewAnimationNameAsName);