간단한 퍼즐용 오브젝트를 준비하는 과제인데 회전 발판, 이동 플랫폼등 동적으로 움직이는 발판을 만들고 이거를 C++로직, Tick함수로 제어하는 과제
최소 2가지 이상의 C++ Actor클래스를 가져야하고, ㄴStaticMeshComponent를 가져야 한데, 이것들은 그냥 발판으로 2가지 이상 만들면 될 것 같다.
Tick함수를 활용해서 회전, 이동 발판을 만들어야 하고 리플렉션 시스템을 이용해서 각각 다른속도, 이동범위, 회전값등을 적용해보고 실험도 해보자
또, 마지막으로 타이머 시스템을 활용해서 일정 시간 후에 발판이 사라지거나 주기걱으로 다른 위치로 이동하는 추가 구현과제랑
SpawnActor를 통해 임의 좌표에 여러개 배치하는 랜덤 스테이지의 기초개념을 공부할 수 있는 추가 구현 과제도 구현해보자
--- 필수 과제 ---
--- 도전 과제 ---
--- 과제를 끝내며 ---




void AMovingPlatform::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(MoveSpeed))
{
if (GetActorLocation().X > MaxRange) {
MoveDirection *= -1;
}
AddActorLocalOffset(FVector(MoveSpeed * MoveDirection * DeltaTime, 0.0f, 0.0f));
}
}
void AMovingPlatform::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(MoveSpeed))
{
if (FMath::Abs(StartLocation.X - GetActorLocation().X) > MaxRange) {
MoveDirection *= -1;
}
AddActorLocalOffset(FVector(MoveSpeed * MoveDirection * DeltaTime, 0.0f, 0.0f));
}
}

void AFinishZone::BeginPlay()
{
Super::BeginPlay();
Collision->OnComponentBeginOverlap.AddDynamic(this, &AFinishZone::OnOverlap);
}
void AFinishZone::OnOverlap(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult)
{
if (!OtherActor) return;
if (HUDWidgetClass)
{
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
if (PlayerController)
{
UUserWidget* HUDWidget = CreateWidget<UUserWidget>(PlayerController, HUDWidgetClass);
if (HUDWidget)
{
HUDWidget->AddToViewport();
}
PlayerController->SetIgnoreMoveInput(true);
PlayerController->SetIgnoreLookInput(true);
PlayerController->bShowMouseCursor = true;
UKismetSystemLibrary::QuitGame(this, PlayerController, EQuitPreference::Quit, false);
}
}
}
--- h ---
// 타이머
FTimerHandle QuitTimerHandle;
UFUNCTION()
void QuitGameDelayed();
--- cpp ---
GetWorld()->GetTimerManager().SetTimer(
QuitTimerHandle,
this,
&AFinishZone::QuitGameDelayed,
2.0f,
false // 반복없게끔
);
void AFinishZone::QuitGameDelayed()
{
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
UKismetSystemLibrary::QuitGame(this, PlayerController, EQuitPreference::Quit, false);
}

회전하는 발판에서 사라지는것을 구현하고, 이동하는 발판에서 순간이동을 구현하기로 하였다.
// ARotatingPlatform.h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timer")
bool bUseToggle = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timer")
float TimerDelay = 2.0f;
UFUNCTION()
void ToggleVisibility();
FTimerHandle TimerHandle;
bool bIsVisible;
// ARotatingPlatform.cpp
void ARotatingPlatform::BeginPlay()
{
Super::BeginPlay();
if (bUseToggle) {
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
this,
&ARotatingPlatform::ToggleVisibility,
TimerDelay,
true // 반복
);
}
}
void ARotatingPlatform::ToggleVisibility()
{
bIsVisible = !bIsVisible;
SetActorHiddenInGame(!bIsVisible);
SetActorEnableCollision(bIsVisible);
}

// AMovingPlatform.h
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Moving")
bool bUseTeleportMode = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Moving")
float TimerDelay = 2.0f;
FTimerHandle TeleportTimerHandle;
UFUNCTION()
void TeleportToRandom();
// AMovingPlatform.cpp
void AMovingPlatform::BeginPlay()
{
Super::BeginPlay();
...
if (bUseTeleportMode) {
PrimaryActorTick.bCanEverTick = false;
GetWorld()->GetTimerManager().SetTimer(
TeleportTimerHandle,
this,
&AMovingPlatform::TeleportToRandom,
TimerDelay,
true
);
}
}
void AMovingPlatform::TeleportToRandom()
{
float RandomX = StartLocation.X + FMath::RandRange(-MaxRange, MaxRange);
FVector RandomLocation(RandomX, StartLocation.Y, StartLocation.Z);
SetActorLocation(RandomLocation);
}
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawning")
TArray<TSubclassOf<ARotatingPlatform>> PlatformClasses;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
float TimerDelay;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Spawning")
TArray<ARotatingPlatform*> SpawnedPlatforms;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawning")
int32 SpawnCount;
FTimerHandle TimerHandle;
UFUNCTION(BlueprintCallable, Category = "Spawning")
void SpawnRandomPlatforms();
FVector GetRandomPointInVolume() const;
// SpawnVolume.cpp
void ASpawnVolume::BeginPlay()
{
Super::BeginPlay();
SpawnRandomPlatforms();
GetWorldTimerManager().SetTimer(
TimerHandle,
this,
&ASpawnVolume::SpawnRandomPlatforms,
TimerDelay,
true
);
}
void ASpawnVolume::SpawnRandomPlatforms()
{
for (ARotatingPlatform* Platform : SpawnedPlatforms)
{
if (Platform && Platform->IsValidLowLevel())
{
Platform->Destroy();
}
}
SpawnedPlatforms.Empty();
for (int32 i = 0; i < SpawnCount; ++i)
{
int32 RandomIndex = FMath::RandRange(0, PlatformClasses.Num() - 1);
TSubclassOf<ARotatingPlatform> SelectedClass = PlatformClasses[RandomIndex];
if (!SelectedClass) continue;
FVector SpawnLocation = GetRandomPointInVolume();
ARotatingPlatform* SpawnedPlatform = GetWorld()->SpawnActor<ARotatingPlatform>(
SelectedClass, SpawnLocation, FRotator::ZeroRotator,
FActorSpawnParameters()
);
if (SpawnedPlatform)
{
SpawnedPlatform->bUseToggle = FMath::RandBool();
if (SpawnedPlatform->bUseToggle) {
SpawnedPlatform->TimerDelay = FMath::RandRange(1.0f, 3.0f);
}
SpawnedPlatform->RotationSpeed = FMath::RandRange(50.0f, 200.0f);
SpawnedPlatforms.Add(SpawnedPlatform);
}
}
}
FVector ASpawnVolume::GetRandomPointInVolume() const
{
FVector Origin = SpawningBox->GetComponentLocation();
FVector BoxExtent = SpawningBox->GetScaledBoxExtent();
return FVector(
FMath::RandRange(Origin.X - BoxExtent.X, Origin.X + BoxExtent.X),
FMath::RandRange(Origin.Y - BoxExtent.Y, Origin.Y + BoxExtent.Y),
FMath::RandRange(Origin.Z - BoxExtent.Z, Origin.Z + BoxExtent.Z)
);
}
