언리얼 엔진 본캠프 9주차-1 언리얼 엔진 C++ : 게임 루프 및 UI 구현 전체 흐름

정재훈·2025년 2월 10일
0

unreal engine

목록 보기
25/45

아이템 설계 : 인터페이스를 사용

  • 공통적으로 쓰일 함수 순수 가상함수로 구현
    • 각 클래스에서 각 기능에 맞게 세부 구현
  1. 인터페이스 정의 - UInterface : 인터페이스 생성
    • 함수 원형들만 정의(가상 함수 형태)
      • OnItemOverlap()
      • OnItemEndOverlap()
      • ActivateItem()
      • GetItemType()
  2. 아이템 클래스들이 이 인터페이스 함수들을 구현

아이템 설계 구조

- 아이템 인터페이스
- 부모 클래스(Item) : ItemType, DestoryItem(), 
  - 공통 코인 클래스 : PointValue
    - 작은 코인 클래스
    - 큰 코인 클래스
  - 지뢰 클래스 : ExplosionDelay, ExplosionRadius, ExplosionDamage
  - 회복 클래스 : HealAmount

아이템 충돌

  • 기본적인 Component 추가 : Scene, Static Mesh
  • 충돌 감지를 하기 위한 Collision Component 추가
    • Collision Preset 설정
  • 이벤트 바인딩
    • 바인딩될 함수를 구현 : OnItemOverlap(), OnItemEndOverlap()
    • 클래스 별로 필요한 함수들 구현 : ActivateItem() 등
      • 플레이어 태그를 추가 : 충돌 대상이 플레이어인지 확인 가능
      • 일단 로그만 출력
PrimitiveComponent::OnComponentBeginOverlap.AddDynamic(UserObject, FuncName)

virtual void OnItemOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) override;

PrimitiveComponent::GetOverlappingActors(TArray<AActor*>& OverlappingActors)

AActor::Tags.Add(TEXT("JaeHoon"));
AActor::Tags.Add("Unreal Engine");
Actor->ActorHasTag("JaeHoon")

아이템 스폰

  • 아이템을 스폰할 액터 추가
  • 랜덤 스폰
    • 랜덤한 위치
    • 랜덤한 아이템 : 아이템마다 확률 존재
    • 스테이지에 따라 확률 변화
  • 데이터 테이블 생성
    • 데이터 테이블에서 사용할 구조체(FTableRowBase 상속받음) 생성
    • 에디터에서 데이터 테이블 생성
TSubclassOf<AActor> ItemClass;

BoxComponent::GetScaledBoxExtent()

UDataTable* ItemDataTable;
TArray<FItemSpawnRow*> AllRows;
ItemDataTable->GetAllRows(ContextString, AllRows);

아이템 기능 구현

  • 캐릭터 체력
    • 데미지 처리 시스템
      • 언리얼 지원 : UGameplayStatics::ApplyDamage(), AActor::TakeDamage() override
      • or 직접 구현
  • 점수 시스템
    • GameState 클래스 사용
TArray<AActor*> OverlappingActors;
UPrimitiveComponent::GetOverlappingActors(OverlappingActors);

UGameplayStatics::ApplyDamage(AActor* DamagedActor, float BaseDamage, AController* EventInstigator, AActor* DamageCauser, TSubclassOf<UDamageType> DamageTypeClass)
- 공격 대상 / 데미지 / 공격의 주체 / 데미지 유발 오브젝트 / 데미지 유형

AActor::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)

게임 흐름 구현

  • 어디서 구현해야할까
    • GameState : 게임의 전역 정보를 가지고 있음
      • 레벨당 1개 존재, 게임 로직에서 서버/클라이언트 모두 알아야할 데이터/상태 등을 담음
    • GameMode : 웬만하면 서버 전용, 멀티 플레이 게임 로직
  • 게임 시작
    • BeginPlay()
      • StartLevel() : 레벨을 생성하고나서 레벨에 추가 작업(아이템 스폰, 타이머) 수행
  • 게임 규칙
    • 레벨
      • 아이템 스폰 : 스폰 개수 누적
      • 레벨을 담을 배열 필요
    • 다음 레벨 [생성] : UGameplayStatics::OpenLevel()
      • 현재 레벨 종료, 새로운 레벨 생성
        • 이전 레벨의 정보가 전부 사라짐
          • 레벨 전환 시에는 GameState, GameMode 같은 기본 클래스를 비롯해, 맵 내에서 생성된 대부분의 객체가 처음부터 다시 생성됩니다.
          • Game Instance 사용 : 게임 전체에서 유지되는 객체
            • 맵 전환 시 파괴되지 않음
            • 게임 실행 ~ 게임 종료 동안 유일하게 계속 살아있는 객체
          • 멀티에서는 Seamless Travel(= 레벨 전환 방식)이라는 것을 사용
    • 코인 아이템 습득
      • 습득 코인 누적 및 점수 누적
      • 모든 코인 습득 시 다음 레벨
    • 제한 시간
      • 레벨 당 제한 시간 30초, 타임 오버 시 다음 레벨
    • 체력 0이 되면, 즉시 게임 오버
오히려 내가 과제를 만들 때는 개수 상관 없이 계속 소환되고 제한시간동안 최대한 많이 먹어야 하는 것도 괜찮을 듯
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolumes);

UObejctBaseUtility::IsA(ACoinItem::StaticClass())

TArray<>::IsValidIndex()
UGamePlayStatics::OpenLevel(GetWorld(), LevelMapNames[CurrentLevelIndex]);

UI 추가

UI 같은 경우는 PlayerController에서 관리

  • HUD(Head-Up Display) : Canvas 기반, UMG(Unreal Motion Graphics)-Widget Blueprint : UUserWidget
    • 현재 레벨, 제한 시간, 총 점수
    • Widget Blueprint를 쓰기 위해 빌드 파일에 "UMG" 모듈 추가
    • HUD Widget Blueprint 생성 : (IA, IMC 생각하면 됨)
    • PlayerController에서 HUD Widget Class 변수 생성
      • 에디터에서 해당 변수에 HUD Widget Blueprint 할당: (IA, IMC 생각하면 됨)
    • GameState와 Widget 출력 텍스트 연동
코인 개수도 그냥 획득/전체 이렇게 보여주는것도 괜찮을 듯
UUserWidget* HUDWidgetInstance;

if (UTextBlock* ScoreText = Cast<UTextBlock>(HUDWidgetInstance->GetWidgetFromName(TEXT("Score"))))
{
	if(UMyGameInstance* MyGameInstance = Cast<UMyGameInstance>(GetGameInstance()))
	{
		ScoreText->SetText(FText::FromString(FString::Printf(TEXT("Score : %d"), MyGameInstance->TotalScore)));
	}
}
  • 메뉴 UI 추가
    • Menu Widget Blueprint 추가
    • 메뉴 전용 map 추가
    • PlayerController에서 Menu Widget Class 변수 생성 및 할당
    • HUD UI, Menu UI를 보여주도록 하는 함수 구현
      • UI를 뷰포트에 렌더링
      • GameState에서 적절한 시기(게임 시작, 레벨 생성, 게임 종료)에 해당 함수들 호출
    • WBP에서 버튼 event 바인딩
FString CurrentMapName = GetWorld()->GetMapName();
if (CurrentMapName.Contains("MenuLevel"))
{
	ShowMainMenu(false);
}

UWidget::RemoveFromParent();

UUserWidget::AddToViewport();

bShowMouseCursor = true;
SetInputMode(FInputModeUIOnly());
  • UI 애니메이션
    • WBP의 Animation창에서 Animation 추가
      • +Add : 적용할 block 설정
        • track, key frame 설정
    • 애니메이션 재생 [블루프린트 함수] 생성
      • C++에서 이 함수를 가져와서 실행
  • 3D 위젯
    • 캐릭터에 3D 위젯 Component 추가 : 스크린 모드(화면에 고정), 월드 모드(월드에 고정)
UFunction* PlayAnimFunc = WidgetInstance->FindFunction(FName("PlayGameOverAnim"));
WidgetInstance->ProcessEvent(PlayAnimFunc, nullptr);

ConstructorHelpers::FClassFinder<UUserWidget> WBP_HP("경로");
WidgetComp->SetWidgetSpace(EWidgetSpace::Screen);
WidgetComp->SetWidgetClass(WBP_HP.Class);
  • 파티클 및 사운드
    • 아이템에 파티클과 사운드 이펙트 추가
UParticleSystem* Particle;
USoundBase* Sound;

UGameplayStatics::SpawnEmitterAtLocation(World, ParticleSystem, Location, Rotation, true)
UGameplayStatics::PlaySoundAtLocation(World, Sound, Location)

GetWorld()->GetMapName()
GetWorld()->GetTimerManager()
GetLocalPlayer()
GetSubsystem()
GetCharacterMovement()
GetController() / GetFirstPlayerController()
GetGameState()
GetGameInstance()
GetUserWidgetObject()
GetWidgetFromName()

  • TSubclassOf<> : 언리얼 엔진에서 UClass타입 안정성을 보장하는 템플릿 클래스
    • TSubclassOf에 전달된 대상이 꺽쇠괄호(<>)안에 넣어준 템플릿 인자의 타입과 일치하거나 템플릿 인자로 받은 타입을 상속받은 타입인지를 런타임 중에 확인하도록 도와주는 클래스
    • 비호환 TSubclassOf 유형을 서로에게 할당하려는 순간, 컴파일 오류
    • 범용 UClass를 할당하려는 경우, 할당이 가능한지 검증하는 RunTime 검사를 진행. RunTime 검사가 실패하면, 결과값은 nullptr

1. UObject

: Unreal Engine의 모든 클래스 계층의 최상위 클래스입니다. 엔진의 모든 클래스는 UObject로부터 파생됩니다.

  • 주요 역할: 객체의 기본적인 속성 및 메모리 관리, 가비지 컬렉션 등을 포함합니다.
  • 사용 목적: 기본적인 객체 생성을 위한 인터페이스를 제공하며, 블루프린트와 C++ 간의 상호작용을 가능하게 합니다.
  • 특징:
    • 직접 인스턴스화할 수는 없으며, NewObject<>() 또는 SpawnActor<>() 함수로 인스턴스화합니다.
    • 게임 데이터 관리, 시스템 간의 정보 전달 등 전반적인 객체 관리를 담당합니다.
    • 모든 UObject 기반 클래스는 Reflection System을 지원하며, 이는 런타임 타입 정보와 에디터에서의 편집 기능을 제공합니다.

2. UClass

: UClass는 Unreal의 클래스 메타데이터를 저장하는 클래스로, 클래스 자체에 대한 정보를 담고 있습니다. Unreal의 Reflection System을 통해 클래스의 속성과 함수를 탐색하고 조작할 수 있게 합니다.

  • 주요 역할: 클래스 타입을 정의하고 객체의 생성과 관계없이 해당 타입에 대한 정보를 저장합니다.
  • 사용 목적: 런타임에 객체 타입을 판별하고 동적으로 클래스를 로드하거나 참조할 때 사용됩니다.
  • 특징:
    • 객체 인스턴스가 아니라 클래스 정의 그 자체입니다.
    • StaticLoadClass() 함수로 클래스를 로드하여 타입 확인 및 객체 생성을 준비할 수 있습니다.
    • 클래스의 속성, 함수, 상속 구조 등을 포함하여 객체의 정의와 관련된 모든 메타데이터를 제공합니다.

3. UBlueprintGeneratedClass

: UBlueprintGeneratedClass는 블루프린트가 컴파일되었을 때 생성되는 클래스로, 블루프린트 기반의 클래스 정의를 포함합니다. 블루프린트에서 작성된 모든 함수와 변수를 포함하여 실행 가능한 형태로 제공됩니다.

  • 주요 역할: 블루프린트로 작성된 코드와 데이터가 결합된 형태로, C++로 작성된 기본 클래스와의 상호작용을 가능하게 합니다.
  • 사용 목적: 블루프린트 기반 객체를 동적으로 생성하거나 스폰할 때 사용됩니다.
  • 특징:
    • UClass의 하위 클래스이며, Blueprint가 런타임에 실행될 수 있는 형태로 컴파일된 결과물입니다.
    • Event Graph, 블루프린트 함수, 변수 등 블루프린트에서 정의된 모든 로직과 데이터를 포함하고 있어, 게임플레이 중 블루프린트 로직을 실행할 수 있습니다.
    • StaticLoadClass()로 로드한 후 스폰하여 블루프린트 기반의 객체를 생성할 수 있습니다.






참조 사이트
1. https://dev.epicgames.com/documentation/ko-kr/unreal-engine/typed-object-pointer-properties-in-unreal-engine
2. https://igh01ti.tistory.com/34
3. https://velog.io/@whoamicj/UE5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%95%A0%EC%85%8B-%EB%A1%9C%EB%94%A9-TSoftObjectPtr%EC%99%80-TSubclassOf%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
4. https://cru6548.tistory.com/42
5. https://velog.io/@whoamicj/UE5-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%97%94%EC%A7%84-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%B2%B4%EA%B3%84-%EC%99%84%EB%B2%BD-%EB%B6%84%EC%84%9D-UObject-UClass-BlueprintGeneratedClass%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EA%B4%80%EA%B3%84

profile
드가자

0개의 댓글