Unreal에서 Router 사용하기
개요
이번에 언리얼에서 시뮬레이션을 만들 일이 있었습니다. 그리고 이 프로젝트가 페이지를 많이 필요로했기 때문에 효율적으로 페이지를 관리하기 위해서는 웹에서 사용하는 라우터 같은 기능이 있으면 좋다고 생각했죠.
그래서 구현했습니다.
아이디어
기본적으로 특정 폴더를 기반으로 폴더 구조를 가져와 url 을 구성합니다.
Navigate To 함수를 통해 Url을 밭은 후 해당 경로가 있는지 확인합니다.
그리고 기존 페이지를 삭제시키고 새로 받은 페이지를 추가합니다.
없으면 index 페이지를 출력합니다.
구현
간단한 클래스 구조 및 폴더 구조 기반으로 URL을 파싱하는 코드입니다.
UCLASS()
class NOVAAIR_API AWidgetRouter : public AActor
{
GENERATED_BODY()
private:
class UOverlay *RootWidget;
UUserWidget *ActiveWidget;
TArray<FString> History;
public:
UPROPERTY(BlueprintAssignable)
FRouteChanged RouteChanged;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
TMap<FString, TSubclassOf<UUserWidget>> PageMap;
public:
// Sets default values for this actor's properties
AWidgetRouter();
protected:
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable)
void Initialize(UOverlay *Target);
UFUNCTION(BlueprintCallable)
void NavigateTo(const FString &To);
UFUNCTION(BlueprintCallable)
void Back();
};
void AWidgetRouter::Initialize(UOverlay *Target) {
static const FString Path = TEXT("/Game/NovaAir/Blueprints/Widgets/TabPages");
RootWidget = Target;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
// 에셋 포폴더 내에 있는 모든 에셋을 가져온다.
TArray<FAssetData> Found;
AssetRegistryModule.Get().GetAssetsByPath(*Path, Found, true);
for (auto &Asset : Found) {
// 소환 가능한 이름으로 변환
FString Name = FString::Printf(TEXT("%s'%s_C'"), *Asset.AssetClassPath.ToString(), *Asset.GetSoftObjectPath().ToString());
FString AssetPath = Asset.PackageName.ToString();
AssetPath = AssetPath.Replace(*Path, TEXT(""));
AssetPath = AssetPath.ToLower();
// 페이지 이름 -> 오브젝트 클래스 맵으로 페이지 추가
FSoftClassPath ClassPath = FSoftClassPath(Name);
UClass *Class = ClassPath.TryLoadClass<UUserWidget>();
if (Class->IsValidLowLevel()) {
if (PageMap.Contains(AssetPath)) {
UE_LOG(LogRouter, Error, TEXT("Duplicated Route!: %s"), *AssetPath);
continue;
}
UE_LOG(LogRouter, Log, TEXT("Page Found: %s"), *AssetPath);
PageMap.Add(AssetPath, Class);
}
}
NavigateTo(TEXT("/index"));
}
url을 가져와서 해당 경로가 존재하면 위젯을 바꾸는 코드입니다.
void AWidgetRouter::NavigateTo(const FString &To) {
FString AssetPath = To.ToLower();
if (!AssetPath.StartsWith(TEXT("/"))) {
AssetPath = TEXT("/") + AssetPath;
}
if (!PageMap.Contains(AssetPath)) {
UE_LOG(LogRouter, Error, TEXT("Unable to find page: '%s'. Try to access '/index'"), *AssetPath);
if (!PageMap.Contains(TEXT("/index"))) {
UE_LOG(LogRouter, Error, TEXT("Unable to find index page."));
return;
}
return;
}
// 기존 위젯이 존재한다면 삭제
if (ActiveWidget) {
ActiveWidget->RemoveFromParent();
}
// 츨래스를 통해 소환
TSubclassOf<UUserWidget> Class = PageMap[AssetPath];
ActiveWidget = CreateWidget(GetWorld(), Class);
auto Slot = ActiveWidget->AddChild(Widget);
auto OverlaySlot = Cast<UOverlaySlot>(Slot);
if (OverlaySlot == nullptr) {
return;
}
History.Add(To);
// 최대화
OverlaySlot->SetHorizontalAlignment(HAlign_Fill);
OverlaySlot->SetVerticalAlignment(VAlign_Fill);
if (RouteChanged.IsBound()) {
RouteChanged.Broadcast(To);
}
}
뒤로가는 기능입니다.
void AWidgetRouter::Back() {
if (History.Num() < 1) {
History.Reset();
NavigateTo(TEXT("/index"));
return;
}
auto Last = History.Last(1);
History.Pop();
NavigateTo(Last);
}
Leave a Reply
Want to join the discussion?Feel free to contribute!