-
[UE5] ExperienceUE5/Lyra Clone Coding 2025. 1. 23. 21:53
Experience란?
- Experience는 Lyra에서 게임플레이 경험을 정의하는 핵심 시스템이다.
- Experience는 게임의 특정 모드(예: 팀 데스매치, 배틀로얄 등)를 정의하는 설정 집합
- Gameplay Experience Definition (DataAsset) 형태로 저장되며, UExperienceDefinition 클래스를 기반으로 함
- 게임이 시작될 때, 현재 적용할 Experience를 결정하고, 해당 Experience가 로드되면서 관련된 모든 설정(Abilities, Input Mapping, UI 등)이 적용
Experience의 장점
- 모듈화된 시스템
- 게임 규칙(GameMode)뿐만 아니라 **UI, Input, 캐릭터 능력(Ability)**까지 통합하여 관리 가능.
- 런타임에서 변경 가능
- 게임 도중 특정 조건에서 Experience를 변경할 수 있음.
(예: 튜토리얼 모드 → 일반 게임 모드 전환)
- 게임 도중 특정 조건에서 Experience를 변경할 수 있음.
- 자동 로딩 기능 제공
- Experience에 설정된 Gameplay Feature 플러그인이 자동으로 활성화됨.
- 특정 Experience에서만 특정 무기/아이템을 사용하도록 설정 가능.
- 멀티플레이 친화적
- UGameFeatureData와 연동되어, 서버가 특정 Experience를 로드하면 모든 클라이언트도 동일한 설정을 받음.
- 비개발자도 설정 가능
- GameMode는 C++/Blueprint로 개발해야 하지만,
Experience는 DataAsset 형태라서 디자이너도 쉽게 설정 변경 가능.
- GameMode는 C++/Blueprint로 개발해야 하지만,
InitGame
void AZSGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) { Super::InitGame(MapName, Options, ErrorMessage); GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::HandleMatchAssignmentIfNotExpectingOne); }
InitGame에서 실행하는 HandleMatchAssignmentIfNotExpectingOne이라는 함수는 설정한 Experience를 불러오는 로직인데, 도식에서 보듯이 InitGame이 실행되었을 때는 Experience에 대한 어떤 정보도 담기기 전이다. 따라서 NextTick 즉, 다음 프레임에 함수를 실행하여 초기 세팅을 할 시간을 벌어준다.
void AZSGameModeBase::HandleMatchAssignmentIfNotExpectingOne() { FPrimaryAssetId ExperienceId; UWorld* World = GetWorld(); if (!ExperienceId.IsValid()) { ExperienceId = FPrimaryAssetId(FPrimaryAssetType("ZSExperienceDefinition"), FName("B_ZSDefaultExperience")); } OnMatchAssignmentGiven(ExperienceId); }
ExperienceMangerComponent
ExperienceManagerComponent는 특정 Gameplay Experience를 로드하고, 적용하며, 실행이 끝날 때 정리하는 역할을 하는 Experience를 다루는 데 있어 핵심 역할을 수행한다.
InitGame을 실행했던 GameMode에서는 ExperienceManagerComponent에 Experience를 실행해 달라고 요청하는 역할을 하고 실질적인 동작은 매니저에서 진행한다.
AZSGameState::AZSGameState() { ExperienceManagerComponent = CreateDefaultSubobject<UZSExperienceManagerComponent>(TEXT("ExperienceManagerComponent")); }
ExperienceManagerComponent는 UGameStateComponent를 상속받는데, 타고 올라가면 ActorComponent를 상속받는다는 것을 알 수 있다. 즉, GameState에 다양한 Component를 붙여서 활용 가능한, 확장성이 뛰어난 Lyra의 구조를 엿볼 수 있다.
ExperienceManagerComponent에서 핵심 로직은 바로 CallOrRegister_OnExperienceLoaded 함수이다.
void UZSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded(FOnZSExperienceLoaded::FDelegate&& Delegate) { if (IsExperienceLoaded()) { Delegate.Execute(CurrentExperience); }else { OnExperienceLoaded.Add(MoveTemp(Delegate)); } }
Experience가 로드되어있으면, 함수에 인자로 들어온 Delegate를 바로 실행하고 CurrentExperience를 넘겨준다.
로드되어 있지 않다면 Multicast Delegate에 추가를 해주어 이후에 Broadcast되었을 때 함수를 실행한다.
CallOrRegister_OnExperienceLoaded함수는 GameState가 생성이 된 직후 호출되는 InitGameState에서 호출된다.
void AZSGameModeBase::InitGameState() { Super::InitGameState(); UZSExperienceManagerComponent* ExperienceManagerComponent = GameState->FindComponentByClass<UZSExperienceManagerComponent>(); check(ExperienceManagerComponent); ExperienceManagerComponent->CallOrRegister_OnExperienceLoaded(FOnZSExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded)); }
이제 Experience의 초기화 과정을 진행하고, 플레이어의 캐릭터를 소환하고자 한다.
그런데 플레이어가 갖고있는 캐릭터에 대한 정보나 조작 방법, 규칙 등을 Experience가 들고 있는데 아직 Experience는 들어오기 전이다. 어떻게 해야할까?
HandleStartingNewPlayer_Implementation 오버라이딩 => Experience가 로딩되기 전까지 플레이어 관련 로직을 오버라이드해서 막아 놓을 것이다!! Experience 로딩이 완료된 후에 재호출해서 Experience를 이용해 플레이어 설정을 진행하게 된다.
void AZSGameModeBase::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) { if (IsExperienceLoaded()) { Super::HandleStartingNewPlayer_Implementation(NewPlayer); } }
HandleStartingNewPlayer의 실행을 막아버려서 이후의 과정이 진행되지 못하게 한다.
이제 다음 프레임에서 Experience를 가져오는 과정을 살펴보자.
위에서 살펴봤던 HandleMatchAssignmentIfNotExpectingOne 함수를 살펴보면
void AZSGameModeBase::HandleMatchAssignmentIfNotExpectingOne() { .. if (!ExperienceId.IsValid()) { ExperienceId = FPrimaryAssetId(FPrimaryAssetType("ZSExperienceDefinition"), FName("B_ZSDefaultExperience")); } ... }
AssetManager에서 Type과 Name으로 Scan을 해서 PrimaryAssetId를 갖고 오는 것을 확인할 수 있다.
void AZSGameModeBase::OnMatchAssignmentGiven(FPrimaryAssetId ExperienceId) { check(ExperienceId.IsValid()); UZSExperienceManagerComponent* ExperienceManagerComponent = GameState->FindComponentByClass<UZSExperienceManagerComponent>(); check(ExperienceManagerComponent); ExperienceManagerComponent->ServerSetCurrentExperience(ExperienceId); }
ExperienceManager에게 ExperienceId를 넘겨주어 Experience를 호출할 수 있도록 한다.
void UZSExperienceManagerComponent::ServerSetCurrentExperience(FPrimaryAssetId ExperienceId) { UZSAssetManager& AssetManager = UZSAssetManager::Get(); TSubclassOf<UZSExperienceDefinition> AssetClass; FSoftObjectPath AssetPath = AssetManager.GetPrimaryAssetPath(ExperienceId); AssetClass = Cast<UClass>(AssetPath.TryLoad()); const UZSExperienceDefinition* Experience = GetDefault<UZSExperienceDefinition>(AssetClass); check(Experience != nullptr); check(CurrentExperience == nullptr); CurrentExperience = Experience; StartExperienceLoad(); }
StartExperienceLoad에서 비동기로 Experience가 갖고 있는 추가적인 Asset정보를 불러오고, Experience를 호출하기 위한 델리게이트를 실행한다.
그 후 Experience가 로드되면 RestartPlayer를 통해 위에서 언급했던 것처럼 Experinece의 정보를 담은 Player Spawn을 시작한다.
void AZSGameModeBase::OnExperienceLoaded(const UZSExperienceDefinition* CurrentExperience) { for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator) { APlayerController* PC = Cast<APlayerController>(*Iterator); if (PC && PC->GetPawn() == nullptr) { if (PlayerCanRestart(PC)) { RestartPlayer(PC); } } } }
추가적으로 중요한 과정이, CDO에서 DefaultPawnClass를 가져올 때, 우리가 설정한 PawnClass로 변경하기 위해서
UClass* AZSGameModeBase::GetDefaultPawnClassForController_Implementation(AController* InController) { if (const UZSPawnData* PawnData = GetPawnDataForController(InController)) { if (PawnData->PawnClass) { return PawnData->PawnClass; } } return Super::GetDefaultPawnClassForController_Implementation(InController); }
와 같이 Class정보를 변경해준다.
'UE5 > Lyra Clone Coding' 카테고리의 다른 글
[UE5] Camera (0) 2025.02.05 [UE5] PawnExtension(2) - 구성 요소 분석 (0) 2025.02.03 [UE5] PawnExtension (0) 2025.01.31 [UE5] AssetManager Scan (0) 2025.01.21 [UE5] AssetManager (0) 2025.01.21