UE - 부트캠프 과제 6번 풀이-1
UnrealEngine
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);
타이머는 TimerEvent
를 Timer
변수 초 뒤에 실행하도록 했다.
참고로 마지막 매개변수를 true
로 하면 계속 반복해서 타이머를 설정할 수 있다고 한다.
하지만 이번엔 단일 타이머 기능을 사용할 때를 고려해서 false
로 해놓고, 반복을 원하면 블루 프린트에서 StartTimer
함수를 호출해 타이머를 또 시작할 수 있도록 만들어봤다.
이번엔 타이머를 이용해 사라졌다 나타나기를 반복하는 플랫폼을 만들어봤다. 위에서 말했듯 타이머 후 실행할 내용을 오버라이딩 해줬고, 반복할 것이기에 끝날 때 마다 계속 StartTimer
함수를 호출해줬다.
결과
배운 점
확실히 C++과 블루프린트는 각자 가진 큰 장점들이 존재한다.
때문에 하나만 사용하기보단 C++에서 설계를 하고, 그걸 토대로 BP에서 구현을 하는 방식이 좋다고 생각한다.
C++에서 리플랙션을 잘 사용하면 팀플을 할 때 C++에 미숙하신 분들이 C++ 클래스를 상속한 블루프린트에서 작업을 할 수 있기에 이번 팀플에선 이런 식으로 코드를 짜보려고 한다.