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

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *