UE4 Chapter 3. 움직이는 액터의 제작
카테고리: UE4 Lesson 2
태그: Cpp UE4 Game Engine
이득우님의 책 이득우의 언리얼 C++ 게임 개발의 정석 을 공부하고 정리한 필기입니다. 😀
언리얼 엔진 공식 문서 https://docs.unrealengine.com/ko/index.html
Chater 3. 움직이는 액터의 제작
🔔 로깅 환경의 설정
- 디버깅이 편하도록 로깅 환경 구축
츨력 로그
- 창 - 개발자 툴 - 출력로그 메뉴로 출력되는 로그들 확인 가능
- log 파일로 Saced/Logs 폴더에 저장되기도 한다.
- 카테고리
- 로그들도 카테고리로 분류가 되어있다.
- 로깅 수준
- 메세지, 경고, 에러
로그 카테고리 선언하기
📜ArenaBattle.h 프로젝트 헤더 파일
DECLAUE_LOG_CATEGORY_EXTERN(ArenaBattle, Log, All);
📜ArenaBattle.cpp 프로젝트 cpp 파일
DEFINE_LOG_CATEGORY_EXTERN(ArenaBattle);
로그 사용
UE_LOG 사용
UE_LOG
(카테고리, 로깅 수준, 형식 문자열, 인자…)
UE_LOG
매크로이며 로그를 출력한다.
📜Fountain.cpp
void AFountain::BeginPlay()
{
Super::BeginPlay();
UE_LOG(ArenaBattle, Warning, TEXT("Actor Name : %s, ID : %d, Location X : %.3f"), *GetName(), ID, GetActorLocation().X);
}
- 게임이 시작되자마자 처음 실행되는 함수인
BeginPlay
이벤트 함수에 의해 게임이 시작되자마자 위 출력 메세지가 로그창에 찍히게 된다.BeginPlay
는 유니티에의 Start() 함수 격. 밑에서 또 다룸.
- FString::printf 참고
ABLOG
특정 카테고리를 고정으로 하고 로그를 남길 때 제작하는 매크로.
p103~104 참고
assertion
C++의 assert와 비슷. 반드시 확인하고 넘거아야 하는 점검 코드.
cheack
ensure
- 에픽게임즈 런처 옵션에서 디버깅을 위한 편집기 기호를 선택한 후 설치를 먼저 해야 사용할 수 있다.
🔔 액터의 주요 이벤트 함수
- 액터의 생명 주기 참고
- BeginPlay
- 액터가 게임에 참여할 때 자동 호출
- 유니티의 Start
- Tick
- 매 프레임마다 호출됨
- 유니티의 Update
- EndPlay
- 액터가 게임에서 퇴장하여 메모리에서 소멸될 때 자동 호출
액터 클래스에 이 같은 이벤트 함수들이 가상 함수
virtual
로 선언이 되어있다. 액터의 자식들은 이를 상속 받아 반드시 오버라이딩 해야 한다.
📜Fountain.h
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
- 함수 실행마다 알 수 있도록 로그 찍는 방법 p110
🔔 움직이는 액터 설계
Tick
: 매 프레임마다 실행되는 이벤트 함수.
속도 값 멤버 변수
- 속도 값을 표현할 멤버 변수
RotateSpeed
선언해주기 Meta = (AllowPrivateAccess = true)
priavte
은닉성을 유지하나 에디터에서 편집할 수 있게 해주는 키워드
📜Fountain.h
private:
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
float RotateSpeed;
액터를 Z 축으로 회전시키기
언리얼에서는 인수를 (Y축, Z축, X축) 순으로 넘긴다. 즉, 좌우 회전, 상하 회전, 정면 회전 이 순서로.
AddActorLocalRotation
함수- 액터를 인수만큼 추가로 회전시킴.
📜Fountain.cpp
AFountain::AFountain()
{
...
RotateSpeed = 30.0f;
}
...
void AFountain::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
AddActorLocalRotation(FRotator(0.0f, RotateSpeed * DeltaTime, 0.0f)); // Z 축으로 30도 * 프레임당 시간 만큼 회전
}
🔔 최종 코드
📜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:
// Sets default values for this actor's properties
AFountain();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void PostInitializeComponents() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent *Body;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent *Water;
UPROPERTY(VisibleAnywhere)
UPointLightComponent *Light;
UPROPERTY(VisibleAnywhere)
UParticleSystemComponent *Splash;
UPROPERTY(VisibleAnywhere)
URotatingMovementComponent* Movement;
UPROPERTY(EditAnywhere, Category=ID)
int32 ID;
private:
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
float RotateSpeed;
};
📜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 = true;
Body = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BODY"));
Water = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WATER"));
Light = CreateDefaultSubobject<UPointLightComponent>(TEXT("LIGHT"));
Splash = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("SPLASH"));
Movement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("MOVEMENT"));
RootComponent = Body;
Water->SetupAttachment(Body);
Light->SetupAttachment(Body);
Splash->SetupAttachment(Body);
Water->SetRelativeLocation(FVector(0.0f, 0.0f, 135.0f));
Light->SetRelativeLocation(FVector(0.0f, 0.0f, 195.0f));
Splash->SetRelativeLocation(FVector(0.0f, 0.0f, 195.0f));
static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BODY(TEXT("/Game/InfinityBladeGrassLands/Environments/Plains/Env_Plains_Ruins/StaticMesh/SM_Plains_Castle_Fountain_01.SM_Plains_Castle_Fountain_01"));
if (SM_BODY.Succeeded())
{
Body->SetStaticMesh(SM_BODY.Object);
}
static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_WATER(TEXT("/Game/InfinityBladeGrassLands/Effects/FX_Meshes/Env/SM_Plains_Fountain_02.SM_Plains_Fountain_02"));
if (SM_WATER.Succeeded())
{
Water->SetStaticMesh(SM_WATER.Object);
}
static ConstructorHelpers::FObjectFinder<UParticleSystem> PS_SPLASH(TEXT("/Game/InfinityBladeGrassLands/Effects/FX_Ambient/Water/P_Water_Fountain_Splash_Base_01.P_Water_Fountain_Splash_Base_01"));
if (PS_SPLASH.Succeeded())
{
Splash->SetTemplate(PS_SPLASH.Object);
}
RotateSpeed = 30.0f;
Movement->RotationRate = FRotator(0.0f, RotateSpeed, 0.0f);
}
// Called when the game starts or when spawned
void AFountain::BeginPlay()
{
Super::BeginPlay();
ABLOG_S(Warning);
ABLOG(Warning, TEXT("Actor Name : %s, ID : %d, Location X : %.3f"), *GetName(), ID, GetActorLocation().X);
}
void AFountain::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
ABLOG_S(Warning);
}
void AFountain::PostInitializeComponents()
{
Super::PostInitializeComponents();
ABLOG_S(Warning);
}
// Called every frame
void AFountain::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기