본문 바로가기

개인 프로젝트(과제)

8번과제 맵 만들기 - 레벨2

레벨 2 = 어질어질 장애물 코스

출처 : https://www.redbull.com/kr-ko/fall-guys-10-survival-tips


위 사진과 같이 맵을 비슷하게 구현해봤다.

땅에 원형 발판을 랜덤한 방향으로 회전시키는 로직과 발판 사이 공간에 캐릭터가 부딪히면 튕겨내는 장애물을 둘 예정이다.

 

트로피와 골 트리거박스를 복사하여 붙여넣기했는데 1레벨 맵을 구현할 때 트리거 박스를 나갔다 오면 계속 트리거가 호출이 되는 문제가 있어서 bool 변수로 한 번 밟으면 true로 설정해서 false일때만 호출하도록 해서 막아뒀었는데 이게 다음 레벨에서도 true로 계속 되어있어서 goal을 해도 EndLevel함수를 호출을 안하는 문제가 있었다.

 

그래서 언리얼 에디터에서 true로 변경하던 로직을 C++에서 JinGameState에 bool 변수를 만들어서 기초값 false, EndLevel함수에서 true, StartLevel에서 false로 설정하도록 해서 레벨 시작마다 false로 초기화 시켜서 해결을 하려 한다.

// JinGameState.h
	UPROPERTY(BlueprintReadOnly, Category = "Level")
	bool bIsTrigger;

블루프린트에서 변수 값을 갖다 쓸 수 있도록 BlueprintReadOnly로 지정해줬다.

// JinGameState.cpp
// 생성자
	bIsTrigger = false;

// StartLevel 함수
void AJinGameState::StartLevel()
{
	bEndLevel = false;
	bIsTrigger = false;
    
// EndLevel 함수
void AJinGameState::EndLevel()
{
	bEndLevel = true; // 레벨 끝남
	bIsTrigger = true;

그리고 언리얼 에디터에서 레벨 블루프린트에서 GoalTrigger 함수에서 위에서 설정한 JinGameState의 bIsTrigger 변수를 Branch에 넣는다.

이제 장애물들을 구현하기 위해 C++로 RotationCircle(회전하는 원형발판) 클래스를 생성한다.

우선 SceneComponent와 StaticMeshComponent를 붙이고 회전에 필요한 회전속도 변수를 생성했다.

// RotationCicle.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RotationCircle.generated.h"

UCLASS()
class UNREAL_CPP_STUDY_API ARotationCircle : public AActor
{
	GENERATED_BODY()
	
public:	
	ARotationCircle();

	UPROPERTY(EditAnywhere, Category = "Speed")
	float RotatorSpeed;

	UPROPERTY(VisibleAnywhere)
	USceneComponent* Scene;
	UPROPERTY(EditDefaultsOnly)
	UStaticMeshComponent* StaticMesh;

	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
};

회전속도를 각 객체마다 랜덤한 값을 주고싶어서 BeginPlay에서 랜덤한 값을 넣도록 FMath::RandRange를 사용했다.

// RotationCircle.cpp
#include "RotationCircle.h"

ARotationCircle::ARotationCircle()
{
	Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
	SetRootComponent(Scene);
	StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
	StaticMesh->SetupAttachment(Scene);
    
	PrimaryActorTick.bCanEverTick = true;
	RotatorSpeed = 0.0f;
}

void ARotationCircle::BeginPlay()
{
	Super::BeginPlay();
	RotatorSpeed = FMath::RandRange(30.0f, 60.0f);
}

void ARotationCircle::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	AddActorLocalRotation({ 0, RotatorSpeed * DeltaTime, 0 });
}

테스트를 해보니 잘 돌아가지만 회전속도가 생각보다 너무 느려서 값을 조절해가며 테스트를 해보면서 적절한 범위를 찾았다.

회전속도 랜덤 범위를 90.0 ~ 120.0으로 변경하고 방향도 랜덤으로 설정해주면 더 재밌을 것 같아서 방향을 정하는 변수도 생성하고 이것도 랜덤한 값을 받았다.

원이 하얀색이면 어느방향으로 회전하는지 안보여서 인터넷에서 광대사진을 다운받아서 적용시켰다.

이제 원형발판 사이사이에 닿으면 튕겨져나가는 장애물을 만들어야 한다.

 

장애물은 C++이 아닌 에디터에서 액터를 만들어서 구현했다.

외형을 가시달린 원기둥으로 만들어서 회전시키고 닿으면 튕기도록 구현했다.

위처럼 구현하니 회전도 하고 튕겨내기도 하는데 튕길때 원기둥에 달린 가시들이 돌면서 여러번 캐릭터에게 부딪히면 캐릭터가 이상하게 튀는 현상이 발견됐다.

그래서 bool 변수를 생성해서 변수가 false면 튕겨나가는 로직을 실행하고 true로 변경해서 중복해서 Hit 이벤트가 발생하지 않도록 수정하고 Delay 노드를 사용해서 1초 후 다시 false로 변경하여 1초후 부딪히면 다시 튕겨나도록 구현했다.

 

구현한 로직은 모두 제대로 작동하고 시간이 40초는 빡빡한것 같아서 레벨별로 제한시간을 다르게 설정할 예정이다.