[ProjectXZ] 모듈러 캐릭터 개발일지 4. 모듈러 시스템 개발 (ver.2)

KWONYEONGMIN·2024년 10월 7일

언리얼

목록 보기
3/15
post-thumbnail

깃 허브 주소

구조 설계

SkeletalMeshComponent

  • 캐릭터에서 생성하여 Attach 한다.
AXZCharacter::AXZCharacter()
{
...

	for ( EModularMeshType ModuleType : ModuleTypes )
	{
...
		UXZSkeletalMeshComponent* MeshComponent = CreateDefaultSubobject<UXZSkeletalMeshComponent>(*ComponentName);
		MeshComponent->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform);
		SkeletalMeshComponents.Add(ModuleType, MeshComponent);
	}
	SkeletalMeshComponents.Add(EModularMeshType::Body, GetMesh());
}

XZModularComponent

모듈 관리 방식

  • 모듈에 대한 관리는 직접적으로 SkeletalMeshComponent와 연결하여 수행한다.
  • 각각의 EModularMeshType 값의 이름 기반으로 SkeletalMeshComponent 와 연결하여 관리하는 방식으로 동적으로 처리한다.
USkeletalMeshComponent* UXZModularComponent::GetSkeletalMeshComponent(EModularMeshType ModuleType) const
{
    if ( AXZCharacter* Character = Cast<AXZCharacter>(GetOwner()) ) 
    {
        return Character->GetSkeletalMeshComponent(ModuleType);
    }

    return nullptr;
}

SkeletalMesh SkeletalMeshComponent에 붙이기

  • Character에서 해당하는 모듈타입의 SkeletalMeshComponent를 가지고 온다.
  • ItemID에 해당하는 SkeletalMesh 의 애셋 경로를 가지고 와서 SkeletalMeshComponent에 SkeletalMesh를 설정한다.
void UXZModularComponent::Attach(EModularMeshType ModuleType, int32 ItemID)
{
... 예외 처리
 if ( const UXZDataManager* DataManager = UGameInstance::GetSubsystem<UXZDataManager>(GetWorld()->GetGameInstance()) )
 {
     const FName ModuleIDName = FName(*FString::FromInt(ItemID));
 // ItemID의 값으로 DataManager에서 Asset 경로를 가지고 온다.
     if ( FItemTable_Module* ModuleAsset = DataManager->TryGetModuleAsset(ModuleIDName) )
     {
         ensure(SkeletalMeshComponent);

// 비동기 방식으로 애셋을 로드한다.
         UXZAssetManager& AssetManager = UXZAssetManager::GetXZAssetManager();
         FSoftObjectPath AssetPath = ModuleAsset->ASSETPATH;

         AssetManager.GetStreamableManager().RequestAsyncLoad(AssetPath, FStreamableDelegate::CreateLambda([this, SkeletalMeshComponent, AssetPath]()
         {
// 애셋 로드가 끝나면 Character의 SkeletalMeshComponent에 애셋을 설정한다.
                 AXZCharacter* Character = Cast<AXZCharacter>(GetOwner());
                 
                 ensure(Character);
                 ensure(Character->GetMesh());

             if (USkeletalMesh* SkeletalMesh = Cast<USkeletalMesh>(AssetPath.TryLoad()))
             {
                 SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh);

                 if (SkeletalMeshComponent != Character->GetMesh())
                 {
                     SkeletalMeshComponent->SetMasterPoseComponent(Character->GetMesh()); 
                 }
             }
         }));
     }
 }
}

Base Module Item 설정 방식

  • EnumPtr을 사용하여 EModularMeshType에 해당하는 모듈을 반복문으로 순회하며 처리한다.
  • 획득해야 하는 아이템은 DataTable의 bool값으로 판별하여 continue한다.
  • 동적 방식으로 모듈을 처리하므로 확장성과 관리 효율이 높다.
void UXZModularComponent::BeginPlay()
{
    Super::BeginPlay();

    const UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EModularMeshType"), true);

    if (false == IsValid(EnumPtr) )
    {
        return;
    }

    for ( int32 i = 0; i < EnumPtr->NumEnums() - 1; ++i )
    {
        const FName ModuleTypeName = *EnumPtr->GetNameStringByIndex(i);
        const EModularMeshType MeshType = static_cast<EModularMeshType>(i);
        if ( UXZDataManager* DataManager = UGameInstance::GetSubsystem<UXZDataManager>(GetWorld()->GetGameInstance()) )
        {
            // DataManager에서 Base Module Item 정보를 가지고 온다.
            if ( FItemTable_ModuleInfo* ModuleInfo = DataManager->TryGetModuleInfo(ModuleTypeName) )
            {
                // 획득 가능하지 않다면
                if (false == ModuleInfo->Obtainable )
                {
                    const int32 BaseModuleIndex = GetBaseModuleItemIndex(ModuleInfo->ID);
                    Attach(MeshType, BaseModuleIndex);
                }
                else 
                {
                    UE_LOG(LogTemp, Log, TEXT("Obtainable Module : [%s]"), *EnumPtr->GetNameStringByIndex(i));
                }
            }
        }
    }

}

결과

ItemID를 잘못 넣어주면 해당하는 애셋을 찾지 못하게 되기 때문에 아래와 같은(ㅋㅋㅋ) 상황이 벌어진다 !

profile
Hello World

0개의 댓글