Post

UE - 부트캠프 과제 6번 풀이-1

UnrealEngine

UE - 부트캠프 과제 6번 풀이-1

6번 과제 풀이

3️⃣ 필수 과제 (기본 요구 사항) 🐣

필수 과제 1번 - 서로 다른 Actor 클래스 2개 이상 구현

  • 메시가 정상적으로 표시되고, 서로 다른 동작 로직(회전 vs 이동)을 수행해야 합니다.

필수 과제 2번 - Tick 함수 기반 동적 Transform 변경

  • 회전: AddActorLocalRotation() 사용하여 초당 일정 각도 회전
  • 이동: SetActorLocation() 또는 AddActorLocalOffset() 등을 통해 왕복 이동 구현
  • DeltaTime 적용으로 프레임별 움직임을 보정합니다.

필수 과제 3번 - 리플렉션 적용

  • 주요 변수 (회전 속도, 이동 속도, 이동 범위 등)를 UPROPERTY로 선언, 에디터에서 조절 가능하게 만들어야 합니다.
  • 카테고리(Category="")와 EditAnywhere, BlueprintReadWrite 등을 적절히 활용하세요.
  • 플레이 도중 Details 패널에서 값 조정 시 즉시 반영되는지 확인합니다.

4️⃣ 도전 과제 (선택 요구 사항) 🦅

도전 과제 1번 - 시간 제한과 카운트다운 활용

  • N초 후 발판이 사라지거나, N초마다 발판이 교체되는 식의 타이머를 제한한 액터를 구현합니다.
  • FTimerHandle과 타이머 함수(GetWorld()->GetTimerManager().SetTimer(...))를 적용해보세요.
  • 매 프레임 Tick 대신, 타이머로 특정 시점마다 기능을 수행함으로써 퍼포먼스도 개선할 수 있습니다.

도전 과제 2번 - 랜덤 퍼즐 생성

  • 게임 시작 시 SpawnActor를 통해 여러 개의 회전 발판/이동 플랫폼임의 위치에 배치합니다. (언리얼 공식 문서 참조)
  • 랜덤 범위, 각도 등을 설정해 매번 다른 퍼즐 코스를 생성해보세요.
  • 로그라이크나 프로시저럴 스테이지의 기초를 체험할 수 있습니다.

도전 과제 2번은 아직 완성하지 못했고, 우선 도전 과제 1번까지만 구현했다.

필수 과제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#pragma once

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

UCLASS()
class SPARTA_JAN_API AC_Platform : public AActor
{
	GENERATED_BODY()
	
public:	
	AC_Platform();

protected:
	virtual void PostInitializeComponents() override;
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	virtual void Destroyed() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	UFUNCTION(BlueprintCallable, Category="Platform|Move")
	void LinearMove();
	
	UFUNCTION(BlueprintCallable, Category="Platform|Move")
	void AngularMove();
	
	UFUNCTION(BlueprintImplementableEvent, Category="Platform|Event")
	void MoveEvent();

	UFUNCTION(BlueprintPure, Category="Platform|Properties")
	FRotator GetRotateAxis() const {return RotateAxis;}	

	UFUNCTION(BlueprintPure, Category="Platform|Properties")
	float GetAngularSpeed() const {return AngularSpeed;}

	UFUNCTION(BlueprintPure, Category="Platform|Properties")
	FVector GetLinearDirection() const {return LinearDirection;}

	UFUNCTION(BlueprintPure, Category="Platform|Properties")
	float GetLinearSpeed() const {return LinearSpeed;}

	UFUNCTION(BlueprintCallable, Category="Platform|Properties")
	void SetAngularSpeed(float Spd) {AngularSpeed = Spd;}

	UFUNCTION(BlueprintCallable, Category="Platform|Properties")
	void SetLinearSpeed(float Spd) {LinearSpeed = Spd;}

	UFUNCTION(BlueprintCallable, Category="Platform|Properties")
	void SetLinearDirection(FVector Dir) {LinearDirection = Dir;}

	UFUNCTION(BlueprintCallable, Category="Platform|Properties")
	void SetRotateAxis(FRotator Axis) {RotateAxis = Axis;}

protected:
	USceneComponent* SceneRoot;

	UPROPERTY(EditAnywhere, Category="Platform|Properties")
	float Amplitude;

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Platform|Components")
	UStaticMeshComponent* PlatformMesh;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Platform|Properties")
	FRotator RotateAxis;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Platform|Properties")
	float AngularSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Platform|Properties")
	FVector LinearDirection;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Platform|Properties")
	float LinearSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Platform|Properties")
	FVector OriginLocation;
};

C++ 클래스로 C_Platform 을 만들고, 블루프린트로 이 클래스를 상속한 BPC_Platform을 만들었다. 그리고 인스턴스들의 이동 관련 변수들의 값을 조정하는 방식을 사용했다.

C_Platform으로 만든 블루프린트 클래스는 Linear, Angular, Platfrom 세 가지다.

가장 처음 건 도전과제 1번 내용이고, 그 다음부터 이름대로 선운동만 하는 플랫폼, 각운동만 하는 플랫폼, 둘 다 가능한 플랫폼을 만들었다.

이동 관련 변수들은 전부 사용 가능하지만 이동을 처리하는 함수는

1
2
 UFUNCTION(BlueprintImplementableEvent, Category="Platform|Event")
	void MoveEvent();

이 함수인데, BlueprintImplementableEvent를 사용하면 블루프린트 에디터에서 함수를 오버라이딩 할 수 있기에 이걸 이용해봤다.

각 BP 클래스별 MoveEvent는 이렇게 구현했다.

선 운동만 하는 플랫폼

각 운동만 하는 플랫폼

두 운동이 모두 가능한 플랫폼

저 함수들의 내용은 각각

1
2
3
4
5
void AC_Platform::LinearMove()
{
	FVector NewPos = OriginLocation + LinearDirection * Amplitude * sin(LinearSpeed * GetWorld()->GetTimeSeconds());
	SetActorLocation(NewPos);
}
1
2
3
4
void AC_Platform::AngularMove()
{
	AddActorLocalRotation(RotateAxis * AngularSpeed);
}

이렇게 작성했다.

도전과제 1

도전과제 1의 내용도 구현했는데, 이건 SetTimer 함수를 이용해서 구현해봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "C_Platform.h"
#include "C_TimerPlatform.generated.h"

//DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTimerEvent);

UCLASS()
class AC_TimerPlatform : public AC_Platform
{
	GENERATED_BODY()
public:
	virtual void BeginPlay() override;
	
	UFUNCTION(BlueprintImplementableEvent, Category="Platform|Event")
	void TimerEvent();
	
	
	virtual void Tick(float DeltaTime) override;
	
protected:
	UFUNCTION(BlueprintCallable, Category="Platform|Timer")
	void StartTimer();
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Platform|Properties")
	float Timer;
	
	FTimerHandle TimerHandle;

private:
	//UPROPERTY(BlueprintAssignable, BlueprintCallable, meta=(AllowPrivateAccess=true))
	//FOnTimerEvent OnTimerBeginEvent;
	//
	//UPROPERTY(BlueprintAssignable, BlueprintCallable, meta=(AllowPrivateAccess=true))
	//FOnTimerEvent OnTimerEndEvent;
};

주석친 부분은 조원 중 언리얼 고수분이 저걸 자주 사용하니 공부해보라고 하셔서 일단 적어놨다.

기본적인 내용은 C_Platform을 상속했기에 같고, 추가로 타이머 기능이 추가되었다. 타이머 이후 작동할 함수도 BP에서 직접 정할 수 있도록 BlueprintImplementableEvent를 달아주었다.

1
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AC_TimerPlatform::TimerEvent, Timer, false);

타이머는 TimerEventTimer 변수 초 뒤에 실행하도록 했다.

참고로 마지막 매개변수를 true로 하면 계속 반복해서 타이머를 설정할 수 있다고 한다.

하지만 이번엔 단일 타이머 기능을 사용할 때를 고려해서 false로 해놓고, 반복을 원하면 블루 프린트에서 StartTimer 함수를 호출해 타이머를 또 시작할 수 있도록 만들어봤다.

이번엔 타이머를 이용해 사라졌다 나타나기를 반복하는 플랫폼을 만들어봤다. 위에서 말했듯 타이머 후 실행할 내용을 오버라이딩 해줬고, 반복할 것이기에 끝날 때 마다 계속 StartTimer함수를 호출해줬다.

InvisiblePlatform의 블루프린트 내용

결과

배운 점

확실히 C++과 블루프린트는 각자 가진 큰 장점들이 존재한다.

때문에 하나만 사용하기보단 C++에서 설계를 하고, 그걸 토대로 BP에서 구현을 하는 방식이 좋다고 생각한다.

C++에서 리플랙션을 잘 사용하면 팀플을 할 때 C++에 미숙하신 분들이 C++ 클래스를 상속한 블루프린트에서 작업을 할 수 있기에 이번 팀플에선 이런 식으로 코드를 짜보려고 한다.

This post is licensed under CC BY 4.0 by the author.