무한 맵의 제작
‘이득우의 언리얼 c++ 게임 개발의 정석’ 책을 참고하여 작성한 포스트입니다.
무한 맵의 생성
- 하나의 섹션을 클리어하면 새로운 섹션이 등장하는 무한 맵 스테이지를 제작해본다
- 섹션에서 제공하는 일은 다음과 같다.
- 섹션의 배경과 네 방향으로 캐릭터 입장을
- 섹션 액터 제작을 위해 Actor를 부모 클래스로 하는 클래스를 생성한다.
- 플레이어만을 감지하는 콜리전 프리셋을 하나 추가한다.
// section.h
...
class UBoxComponent;
UCLASS()
class LOOTERSHOOTER_API ALSSection : public AActor
{
...
private:
UPROPERTY(VisibleAnywhere, Category = Mesh, Meta = (AllowPrivateAccess = true))
UStaticMeshComponent* Mesh;
UPROPERTY(VisibleAnywhere, Category = Trigger, Meta = (AllowPrivateAccess = true))
UBoxComponent* Trigger;
};
// Section.cpp
#include "LSSection.h"
#include "Components/BoxComponent.h"
// Sets default values
ALSSection::ALSSection()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false; //true;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MESH"));
RootComponent = Mesh;
FString AssetPath = TEXT("/Game/LS/Meshes/item.item");
static ConstructorHelpers::FObjectFinder<UStaticMesh> ITEM(*AssetPath);
if (ITEM.Succeeded())
{
Mesh->SetStaticMesh(ITEM.Object);
}
else
{
LSLOG_S(Warning);
}
//#include "Components/BoxComponent.h"
Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("TRIGGER"));
Trigger->SetupAttachment(RootComponent);
Trigger->SetBoxExtent(FVector(40.0f, 40.0f, 100.0f));
//Trigger->SetRelativeLocation()
Trigger->SetCollisionProfileName(TEXT("LSTrigger"));
}
...
섹션 로직
- 스테이트 머신으로 설계해 본다.
- 준비 스테이트
- 플레이어의 진입을 감지하면 전투 스테이트로 이동한다
- 전투 스테이트
- NPC를 소환하고, 일정 시간 후 아이템 상자도 소환한다.
- NPC가 죽으면 완료 스테이트로 이동
- 완료 스테이트
- 닫힌 문 혹은 공간을 연다.
- 혹은 다음 섹션으로 나아간다.
- 준비 스테이트
- 액터에는 에디터와 연동되는 OnConstruction 이라는 함수가 설계돼 있다.
- 에디터 작업에서 선택한 액터의 속성이나 트랜스폼 정보가 변경될 때 이 OnConstruction 함수가 실행된다.
- 박스 컴포넌트의 OnComponentBeginOverlap 델리게이트도 이용해서,
- 특정 물체와 닿으면 게임 스테이트를 battle 로 바뀌게 구현하자.
// section.h
...
private:
enum class ESectionState : uint8
{
READY = 0,
BATTLE,
COMPLETE
};
void SetState(ESectionState NewState);
ESectionState CurrentState = ESectionState::READY;
UFUNCTION()
void OnTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);
// section.cpp
ALSSection::ALSSection()
{
...
Trigger->OnComponentBeginOverlap.AddDynamic(this, &ALSSection::OnTriggerBeginOverlap);
}
...
void ALSSection::SetState(ESectionState NewState)
{
switch (NewState)
{
case ESectionState::READY :
{
Trigger->SetCollisionProfileName(TEXT("LSTrigger"));
break;
}
case ESectionState::BATTLE :
{
Trigger->SetCollisionProfileName(TEXT("NoCollision"));
break;
}
case ESectionState::COMPLETE :
{
Trigger->SetCollisionProfileName(TEXT("NoCollision"));
break;
}
}
CurrentState = NewState;
}
void ALSSection::OnTriggerBeginOverlap(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
if (CurrentState == ESectionState::READY)
{
SetState(ESectionState::BATTLE);
LSLOG(Warning, TEXT("Set Battle State"));
}
}
내비게이션 메시 시스템 설정
- NPC 와 아이템 상자를 생성하는 기능을 추가해 보자.
- 타이머 기능을 이용한다.
- 새로 생성되는 NPC도 네비게이션 메시 영역이 설정되게 해주려면 프로젝트 세팅의 내비게이션 메시 설정에서 Runtime Generation 속성의 값을 Dynamic으로 변경해 준다.
FTimerHandle
- Unique handle that can be used to distinguish timers that have identical delegates.
struct FTimerHandle
UWorld::GetTimerManager
- Returns TimerManager instance for this world.
FTimerManager & GetTimerManager() const
FTimerManager::SetTimer
- Sets a timer to call the given native function at a set interval.
template<class UserClass>
void SetTimer
(
FTimerHandle & InOutHandle,
UserClass * InObj,
typename FTimerDelegate::TUObjectMethodDelegate< UserClass >::FMethodPtr InTimerMethod,
float InRate,
bool InbLoop,
float InFirstDelay
)
// FTimerManager.h
template< class UserClass >
FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TMethodPtr< UserClass > InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)
{
InternalSetTimer(InOutHandle, FTimerUnifiedDelegate( FTimerDelegate::CreateUObject(InObj, InTimerMethod) ), InRate, InbLoop, InFirstDelay);
}
Leave a comment