UE4 Chapter 3. 움직이는 액터의 제작

Date:     Updated:

카테고리:

태그:

이득우님의 책 이득우의 언리얼 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);
}



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

UE4 Lesson 2 카테고리 내 다른 글 보러가기

댓글 남기기