BulletAnt 개발일지 (7) - TribeManager, Anim Slot

김펭귄·6일 전

Today What I Learned (TIL)

목록 보기
122/139

Tribe 색상 적용 및 머티리얼 캐싱 구조

종족 색상을 실제 적에게 적용하는 작업을 진행했다.

처음에는 형광 머티리얼을 Overlay 형태로 덮어씌우는 방식을 고려했다.
하지만 이 방식은 기존 머티리얼과 Overlay 머티리얼을 각각 Draw하는 것이므로 렌더링 비용이 증가한다고 판단해 제외했다.

대신 기존 머티리얼에 AdditiveColor 파라미터를 추가하고, 색상을 Blend하는 방식으로 변경했다.

문제는 TribeDataAsset에 등록된 색상을 어떻게 각 적의 머티리얼에 전달할 것인가였다.

처음에는 몬스터 종류 × 종족 수 만큼 머티리얼 인스턴스를 미리 제작하려 했다.
하지만 몬스터 종류와 종족 수가 늘어날수록 만들어야 할 인스턴스가 너무 많아졌기에 폐기했다.

그래서 런타임에 Material Instance Dynamic을 생성하고, 여기에 색상 파라미터만 주입하는 구조로 변경했다.

UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(InBaseMat, this);
if (IsValid(MID))
{
	MID->SetVectorParameterValue(TEXT("AdditiveColor"), InColor);
}

Dynamic Instance를 통해 런타임에 스폰할 적과 종족값이 정해지면 동적으로 종족색이 적용된 머티리얼을 만들어 적에게 적용시킬 수 있었다.

너무 많은 MID

하지만 여기서 또 문제가 생겼다. 적 객체마다 Dynamic Instance를 생성하면:

몬스터 수만큼 머티리얼 인스턴스가 계속 생성된다.

실제로 필요한 머티리얼의 수는:

스폰된 (몬스터 종류 × 종족 수)

정도인데, 적 개체 수만큼 생성되는 것은 비효율적이었다.

그래서 머티리얼 인스턴스를 캐싱 및 공유하는 시스템을 추가했다.

TribeManager

SpawnManager가 적 스폰 시:

  • 적의 기본 머티리얼
  • Tribe 색상

을 전달하면, TribeManager가 TMap에서 기존 인스턴스를 검색한다.

TMap<FTribeMaterialKey, UMaterialInstanceDynamic*>

TMap의 Key는 Base Material , Tribe Color 조합으로 구성했다.
그리고 operator==, GetTypeHash를 구현해 Custom Key 기반 탐색이 가능하도록 만들었다.

// TribeManagerSubsystem.h
struct FTribeMaterialKey
{
    GENERATED_BODY()

    UPROPERTY()
    TObjectPtr<UMaterialInterface> BaseMaterial;

    UPROPERTY()
    FLinearColor TribeColor = FLinearColor(0, 0, 0, 0);

    // Key Comparison
    bool operator==(const FTribeMaterialKey& Other) const
    {
        return (TribeColor == Other.TribeColor && BaseMaterial == Other.BaseMaterial);
    }

    // Hash Function
    friend uint32 GetTypeHash(const FTribeMaterialKey& Key)
    {
        return HashCombine(GetTypeHash(Key.TribeColor), GetTypeHash(Key.BaseMaterial));
    }
};

덕분에:

  • 새로운 몬스터/종족의 경우 MID를 새롭게 만들고 TMap에 등록 (캐싱)
  • 중복 Dynamic Instance 생성 방지
  • 동일 종족/동일 몬스터는 캐싱 접근, 머티리얼 공유 (O(1))

구조를 만들 수 있었다.

// TribeManagerSubsystem.cpp
UMaterialInstanceDynamic* UTribeMaterialManagerSubsystem::GetTribeMaterial(UMaterialInterface* InBaseMat, const FLinearColor& InColor)
{
	FTribeMaterialKey key(InBaseMat, InColor);
	if (TribeMaterialCache.Contains(key))
	{
		return TribeMaterialCache[key];
	}

	UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(InBaseMat, this);
	if (IsValid(MID))
	{
		MID->SetVectorParameterValue(TEXT("AdditiveColor"), InColor);
		TribeMaterialCache.Add(key, MID);
		return MID;
	}
	return nullptr;
}

Anim Montage가 재생되지 않던 문제

폭발 애님몽타주만 사용하는 적이 있어, 해당 몽타주를 재생시켰으나 작동하지 않았다.

처음에는:

  • Montage 재생 함수 문제
  • GAS 문제

등을 의심했지만 원인은 AnimBlueprint 설정 자체에 있었다.

Anim Montage는 단순히 재생 함수만 호출한다고 동작하는 것이 아니었다.

우선:

  • 해당 SkeletalMesh가 AnimBlueprint를 사용해야 하고
  • AnimGraph 내부에 DefaultSlot 같은 Slot 노드가 등록되어 있어야 한다.

Anim Montage는 이 Slot을 통해 그래프에 삽입되기 때문이다.

기존에는 단순히 Anim Sequence를 바로 Output Pose에 연결하고 있었기 때문에,
Montage가 들어갈 Slot 자체가 존재하지 않았다.

결국 아무리 Montage를 재생해도 적용되지 않았던 것이다.

그래서 AnimGraph에 Slot을 넣어 수정했고, 이후 몽타주가 정상적으로 재생되는 것을 확인할 수 있었다.

profile
반갑습니다

0개의 댓글