3장 움직이는 액터의 제작
프로그래밍 환경을 편리하게 만들어줄 매크로를 설정한다. 또한, 무브먼트 컴포넌트를 사용해 액터에 움직임을 부여한다.
3.1 로깅 환경의 설정
■ 3.1.1 출력 로그 윈도우
언리얼 엔진은 로깅 환경을 위해 UE_LOG 매크로를 제공하고 있다. 이 매크로를 사용해 생성한 로그는 에디터의 창 > 개발자 툴 > 출력 로그 메뉴를 선택하여 출력 로그 윈도우를 통해 확인하거나 Saved\Logs 폴더의 log 확장자 파일을 열면 된다.
UE_LOG(카테고리, 로깅 수준, 형식 문자열, 인자 등)
① 로그 카테고리
모든 로그에는 분류를 위해 카테고리가 지정된다. 언리얼 엔진은 기능마다 로그를 구분하기 위해 서로 다른 카테고리를 선언한다.
② 로깅 수준
로그의 중요도를 나타내는 로깅 수준은 크게 메시지(log), 경고(warning), 에러(error) 세 가지로 나뉜다. 이들은 각각 흰색, 노란색, 붉은색으로 출력 로그 윈도우에 표시된다.
③ 로그 필터
출력 로그 윈도우에는 필터 기능이 있으므로 원하는 로깅 수준과 카테고리에 해당하는 로그 정보만 출력하도록 설정할 수 있다.
④ 형식 문자열
로그 매크로는 C 언어의 printf 함수와 같은 형식 문자열 기능을 지원한다. 문자열을 정의할 때는 TEXT 매크로를 사용하는 것이 좋다. FString으로 선언된 변수에서 문자열 변수를 얻어오려면 반드시 *연산자를 앞에 지정해줘야 한다.
⑤ 로깅을 위한 공용 매크로 설정
ArenaBattle이라는 로그 카테고리를 새롭게 정의해본다. 로그 매크로를 ArenaBattle.h에 선언하고 모듈의 모든 헤더가 이 헤더를 참조하도록 구성한다.
/* Chapter 03 ArenaBattle.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "EngineMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(ArenaBattle, Log, All);
/* Chapter 03 ArenaBattle.cpp */
// Fill out your copyright notice in the Description page of Project Settings.
#include "ArenaBattle.h"
#include "Modules/ModuleManager.h"
DEFINE_LOG_CATEGORY(ArenaBattle);
IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, ArenaBattle, "ArenaBattle");
/* Chapter 03 Fountain.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "Fountain.generated.h"
UCLASS()
class ARENABATTLE_API AFountain : public AActor { ... }
/* Chapter 03 Fountain.cpp */
// Fill out your copyright notice in the Description page of Project Settings.
#include "Fountain.h"
void AFountain::BeginPlay() {
Super::BeginPlay();
UE_LOG(ArenaBattle, Warning, TEXT("Actor Name : %s, ID : %d, Location X : %.3f"), *GetName(), ID, GetActorLocation().X);
}
이번에는 추가로 발생한 함수 이름과 코드 라인을 함께 출력하도록 다음 두 종료의 매크로를 ArenaBattle.h에 선언한다.
- ABLOG_S: 코드가 들어있는 파일의 이름과 함수, 그리고 라인 정보를 추가해 ArenaBattle 카테고리로 로그를 남긴다.
- ABLOG: ABLOS_S 정보에 형식 문자열로 추가 정보를 지정해 로그를 남긴다.
/* Chapter 03 ArenaBattle.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "EngineMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(ArenaBattle, Log, All);
#define ABLOG_CALLINFO (FString(__FUNCTION__) + TEXT("(") + FString::FromInt(__LINE__) + TEXT(")"))
#define ABLOG_S(Verbosity) UE_LOG(ArenaBattle, Verbosity, TEXT("%s"), *ABLOG_CALLINFO)
#define ABLOG(Verbosity, Format, ...) UE_LOG(ArenaBattle, Verbosity, TEXT("%s%s"), *ABLOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
// Fill out your copyright notice in the Description page of Project Settings.
#include "Fountain.h"
void AFountain::BeginPlay() {
Super::BeginPlay();
ABLOG_S(Warning);
ABLOG(Warning, TEXT("Actor Name : %s, ID : %d, Location X : %.3f"), *GetName(), ID, GetActorLocation().X);
}
3.2 어설션
프로그래밍에서 어설션(assertion)은 반드시 확인하고 넘어가야 하는 점검 코드를 의미한다. 언리얼 엔진의 C++ 프레임워크는 다양하게 사용할 수 있는 어설션 구문을 제공하는데, 이 기능이 효과적으로 발휘되려면 디버깅 기호가 엔진에 있어야 한다.
3.3 액터의 주요 이벤트 함수
- 언리얼 엔진에 의해 자동으로 호출되는 함수를 이벤트 함수라고 한다.
- 액터에 속한 모든 컴포넌트의 세팅이 완료되면 액터의 PostInitializeComponents 함수를 호출한다.
- 액터는 게임에 참여할 때 BeginPlay 함수를 호출한다.
- 매프레임마다 Tick 함수를 호출한다.
- 게임에서 퇴장할 때 EndPlay 함수를 호출한다.
/* Chapter 03 Fountain.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "Fountain.generated.h"
UCLASS()
class ARENABATTLE_API AFountain : public AActor {
GENERATED_BODY()
...
protected:
// Called when the game starts or when spawned
virtual void PostInitializeComponents() override;
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
...
};
- 상속받은 자식 클래스는 먼저 부모 클래스 함수에 있는 중요한 로직을 실행해야 한다.
- Super 키워드로 부모 클래스의 로직을 실행한다.
/* Chapter 03 Fountain.cpp */
// Fill out your copyright notice in the Description page of Project Settings.
#include "Fountain.h"
void AFountain::EndPlay(const EEndPlayReason::Type EndPlayReason) {
// 게임 프레임워크가 정상적으로 동작하려면 자식 클래스는 부모 클래스의 로직을 먼저 실행해야 한다.
Super::EndPlay(EndPlayReason);
ABLOG_S(Warning);
}
void AFountain::PostInitializeComponents() {
// 게임 프레임워크가 정상적으로 동작하려면 자식 클래스는 부모 클래스의 로직을 먼저 실행해야 한다.
Super::PostInitializeComponents();
ABLOG_S(Warning);
}
3.4 움직이는 액터의 설계
이번에는 액터의 틱(tick) 함수를 사용해 액터의 움직임을 구현해본다. 언리얼 엔진에서 액터의 틱 함수는 화면을 만들어내는 렌더링 프레임 단위로 동작한다. 분수대를 z축으로 회전시킬 각속도의 정보는 값 유형이므로 UPROPERTY 매크로에 EditAnywhere 키워드를 넣는다. 정보를 은닉(information hiding)하여 캡슐화하기 위해 변수를 private으로 선언하고 언리얼 에디터에서 편집할 수 있도록 UPROPERTY 매크로에 AllowPrivateAccess라는 META 키워드를 추가한다.
/* Chapter 03 Fountain.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "Fountain.generated.h"
UCLASS()
class ARENABATTLE_API AFountain : public AActor
{
GENERATED_BODY()
...
private:
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true)) float RotateSpeed;
};
액터의 Tick 함수에는 AddActorLocalRotation 함수를 사용해 분수대가 틱마다 회전하도록 코드를 작성한다. FRotator는 회전 값을 지정하는 데이터이며 차례대로 Pitch, Yaw, Roll이라는 세 가지 회전 요소로 구성된다.
/* Chapter 03 Fountain.cpp */
// Fill out your copyright notice in the Description page of Project Settings.
#include "Fountain.h"
// Sets default values
AFountain::AFountain() {
RotateSpeed = 30.0f;
}
// Called every frame
void AFountain::Tick(float DeltaTime) {
Super::Tick(DeltaTime);
AddActorLocalRotation(FRotator(0.0f, RotateSpeed * DeltaTime, 0.0f));
}
3.5 무브먼트 컴포넌트의 활용
무브먼트 컴포넌트(movement component)는 액터의 움직임을 책임지며 Tick 함수와 DeltaSeconds를 활용하지 않고 액터의 움직임을 제공할 수 있다. 언리얼 엔진은 다음과 같은 무브먼트 컴포넌트를 제공한다.
- FloatingPawnMovement: 중력의 영향을 받지 않는 자유로운 액터의 움직임을 제공한다.
- RotatingMovement: 지정한 속도로 액터를 회전시킨다.
- InterpMovement: 지정한 위치로 액터를 이동시킨다.
- ProjectileMovement: 총알, 미사일 등 액터가 중력의 영향을 받아 포물선을 그리는 발사체의 움직임을 제공한다.
이번에는 RotatingMovement 컴포넌트를 사용해 이전과 동일한 움직임을 구현해본다.
/* Chapter 03 Fountain.h */
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "GameFramework/Actor.h"
#include "Fountain.generated.h"
UCLASS()
class ARENABATTLE_API AFountain : public AActor {
GENERATED_BODY()
public:
...
UPROPERTY(VisibleAnywhere) URotatingMovementComponent* Movement;
private:
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true)) float RotateSpeed;
};
/* Chapter 03 Fountain.cpp */
// Fill out your copyright notice in the Description page of Project Settings.
#include "Fountain.h"
// Sets default values
AFountain::AFountain() {
// 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;
...
Movement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("MOVEMENT"));
...
RotateSpeed = 30.0f;
Movement->RotationRate = FRotator(0.0f, RotateSpeed, 0.0f);
}
앞서 사용한 스태틱메시 컴포넌트처럼 트랜스폼 정보가 필수적인 컴포넌트를 신 컴포넌트(scene component)라고 하고, 무브먼트 컴포넌트와 같이 기능만 제공하는 컴포넌트를 액터 컴포넌트(actor component)라고 한다.
3.6 프로젝트의 재구성
언리얼 엔진에서 액터 제거 메뉴는 제공하지 않으므로 수동으로 제거해야 한다. 이번 예제에서는 분수대 액터를 프로젝트에서 제거해본다. 언리얼 프로젝트에서 액터를 제거하려면 Source 폴더에서 관련 파일을 지우고 다시 비주얼 스튜디오 프로젝트를 생성한다. 그러면 프로젝트 구성에서 분수대 액터 파일이 모두 제거된다.
- 탐색기에서 게임 프로젝트의 Source 폴더 안에 위치한 ArenaBattle 폴더에 있는 Fountain.h와 Fountain.cpp 파일을 삭제한다.
- 게임 프로젝트의 uproject 파일을 우클릭하고 Generate Visual Studio project files 메뉴를 선택해 비주얼 스튜디오 프로젝트를 다시 생성한다.
'Game Engines > 언리얼 엔진' 카테고리의 다른 글
언리얼 C++ 게임 개발의 정석 | Chapter 05 폰의 제작과 조작 (0) | 2022.07.25 |
---|---|
언리얼 C++ 게임 개발의 정석 | Chapter 04 게임플레이 프레임워크 (0) | 2022.07.16 |
언리얼 C++ 게임 개발의 정석 | Chapter 02 액터의 설계 (0) | 2022.03.28 |
댓글