필요없어진 C++ 클래스 삭제하기

 

작성버전 :: 4.20.3

 

처음부터 끝까지 설계가 완벽하고 수정할 일이 없다면 그럴 일이 없겠지만, 코드 작업을 하다보면 기존에 있던 클래스를 삭제해야하는 일이 가끔 발생한다. 특히 아직 프로토타입 작업을 하는 과정이라면 작성해둔 클래스가 필요없어져서 삭제해야하는 일이 생각보다 자주 발생할 수 있다.

 

 

 

하지만 위의 이미지와 같이 간단하게 삭제할 수 있는 블루프린트 클래스와 달리 C++ 클래스는 에디터 내에서 삭제할 수 있는 방법이 존재하지 않는다. 그렇다고 더이상 사용하지 않게된 C++ 클래스를 무작정 쌓아두고 있을 수만은 없는 법이다.

 

 

 

1. 필요 없어진 C++ 클래스를 삭제하기 전에 에디터를 닫는다.

 

 

 

2. 비주얼 스튜디오로 가서 솔루션 탐색기에서 지우고자 하는 클래스의 헤더(.h)와 소스파일(.cpp)를 선택한 뒤 제거한다.

 

 

 

3. 프로젝트 폴더의 Source 폴더 안에 남아있는 클래스의 헤더(.h)와 소스파일(.cpp) 역시 삭제해준다.

 

 

4. 비주얼 스튜디오로 돌아가서 [빌드 > 솔루션 다시 빌드]를 선택해서 프로젝트를 다시 빌드한다.

 

 

 

5. 프로젝트 빌드가 성공적으로 끝났다면 에디터를 다시 실행시킨다. 그렇게 하고 콘텐츠 브라우저를 보면 필요없는 C++ 클래스가 성공적으로 삭제된 것을 확인할 수 있다.

 

 

주의사항

 

블루프린트 클래스는 관련되어 있거나 레퍼런스가 있는 상태라면 삭제하기 전에 경고창을 띄워주고 정말로 삭제할 것인지 확인을 하지만, C++ 클래스는 그런 과정이 없기 때문에 지우고자하는 클래스가 레벨에 배치되어있는지, 다른 곳에서의 레퍼런스가 있는지, 또는 다른 클래스에서 헤더를 포함시켜서 사용하고 있는 것은 아닌지 신중하게 확인하고 삭제하는 것이 좋다.

 

또한 필요없어진 C++ 클래스를 삭제함으로서 신텍스 에러가 발생한다면 4번 과정에서 프로젝트를 리빌드가 실패하게 될 것이다. 그렇기 때문에 클래스를 삭제한 뒤에 오류목록을 살펴서 클래스를 삭제한 여파로 발생한 에러가 없는지 확인하는 과정 역시 필요하다.

 

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

C++ / USTRUCT 사용자 정의 구조체 만들기

 

작성버전 :: 4.20.3

 

구조체는 기존에 존재는 데이터 타입을 조합하여 새로운 데이터 타입을 만들어내는 유용한 개념이다.

 

struct UserDefinedStruct

{

public:

    int i;

    float f;

};

 

일반적인 C++ 프로젝트에서는 구조체를 위와 같이 정의하고 사용하게 된다.

 

하지만 언리얼 엔진 프로젝트에서 이러한 정규 구조체는 C++ 코드 내부에서는 사용될 수 있지만, 에디터의 디테일 패널에 노출되지 않고, 블루프린트에서도 사용이 불가능하다.

 

에디터에서 사용가능한 구조체를 만들고자 한다면 언리얼 구조체 즉, USTRUCT를 만들어야 한다.

 

 

블루프린트에서만 사용할 구조체라면 위의 이미지와 같은 방법으로 구조체를 생성할 수 있는데, 블루프린트 구조체는 C++ 코드에서는 사용할 수 없다. 하지만 C++ 코드에서 만든 구조체는 C++ 코드는 물론 블루프린트에서도 사용할 수 있다는 장점이 있다.

 

C++ 언리얼 구조체는 간단한 블루프린트 구조체 생성 방법과 비교했을 때, 엔진 내부에서 명시적인 생성 방법이 없기 때문에 생성 과정이 조금 복잡하다.

 

 

언리얼 구조체 만들기

 

언리얼 구조체를 만드는 과정을 따라가보도록 하자.

 

 

 

 

 

우선 사용자가 정의한 UStruct를 담을 헤더를 만들어야 한다. 만약 구조체가 특정한 클래스에서만 자주 사용될 것이라면 그 클래스의 헤더 파일 하단에 구조체를 정의하는 편이 좋지만, 범용적으로 여러 곳에서 사용될 구조체라면 사용자가 정의한 헤더에 몰아서 정의하는 편이 좋다.

 

CustomStruct00 클래스의 추가가 끝났다면 아래의 예시 코드와 같이 클래스 정의 아래 쪽에 커스텀 구조체를 정의해보자.

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class CUSTOMSTRUCTTEST_API ACustomStruct00 : public AActor
{
    GENERATED_BODY()
   
public:   
    // Sets default values for this actor's properties
    ACustomStruct00();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:   
    // Called every frame
    virtual void Tick(float DeltaTime) override;
};


USTRUCT(Atomic, BlueprintType)
struct FCustomStruct
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
        AActor* actor;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
        float f;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
        int32 i;
};

 

클래스에는 UCLASS() 매크로가 붙지만 구조체의 경우에는 USTRUCT() 매크로가 붙는다. 그리고 구조체 지정자는 Atomic과 BlueprintType으로 지정해뒀는데 Atomic은 이 구조체가 항상 하나의 단위로 직렬화(Serialize)됨을 의미하고 BlueprintType은 이 구조체가 블루프린트에서 사용될 수 있음을 의미한다.

 

만약 이 구조체가 에디터의 디테일 창에서 표시되고 수정 가능하기만 원한다면 지정자를 Atomic으로만 설정하기를 권한다. 또한 모든 멤버 변수의 UPROPERTY() 매크로의 지정자를 EditAnywhere로 설정해야 한다.

 

혹은 구조체가 디테일 창에서는 보이지 않고 코드 내부나 블루프린트에서만 사용되기를 원한다면 USTRUCT() 매크로의 지정자를 BlueprintType으로, UPROPERTY() 매크로의 지정자를 BlueprintReadWrite로 설정해야 한다.

그리고 구조체의 이름은 F로 시작되게 작성해야 하며, 댕글링(Dangling) 포인터 문제에 대해서 보호받기 위해서 구조체의 모든 멤버 변수들에 UPROPERTY() 매크로를 붙이는 것을 권장한다.

 

또한 구조체의 멤버 변수에 포인터를 사용한다면 깊은 복사 얕은 복사 문제에 주의를 기울여야 한다.

 

사용할 구조체를 모두 정의했다면, 이 구조체를 사용할 코드의 헤더에 CustomStruct00.h를 포함시켜준다.

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CustomStruct00.h"
#include "TestActor.generated.h"

UCLASS()
class CUSTOMSTRUCTTEST_API ATestActor : public AActor
{
    GENERATED_BODY()
   
public:   
    // Sets default values for this actor's properties
    ATestActor();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:   
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    UPROPERTY(EditAnywhere)
    FCustomStruct st;
};

 

 

 

이렇게 구조체를 테스트 액터의 멤버 변수로 추가시킨 후 에디터로 돌아가서 컴파일을 해주고, 액터를 레벨에 배치하고 선택해보면 위의 이미지처럼 구조체가 디테일 패널에서 수정가능하록 노출된 것을 확인할 수 있다.

 

Tip :: 이후에 구조체의 멤버 변수 종류를 수정하고 컴파일했을 때, 디테일 패널에 곧바로 적용이 되지 않는 문제가 가끔있는데 이런 경우 해당 구조체를 가진 클래스의 멤버 변수에 임시 변수 하나를 추가하고 컴파일하면 적용이 된다.

 

 

 

 

 

생성한 구조체 블루프린트에서 사용하기(Use Custom Struct at Blueprint)

 

C++ 코드에서 정의한 구조체를 블루프린트에서 사용하는 방법은 간단하다.

 

 

 

블루프린트에서 정의한 CustomStruct 변수 유형으로 변수를 추가할 수 있다.

 

 

 

이미지와 같이 이벤트 그래프에서 우클릭을 한 뒤 정의한 구조체의 이름을 검색하면 이벤트 플로우 도중에 CustomStruct를 만들거나 구조체를 분해해서 구조체의 변수를 따로 뽑아내서 사용할 수도 있다.

 


 

참고

 

Unreal Engine 4 Wiki :: Structs, USTRUCTS(), They're Awesome(https://wiki.unrealengine.com/Structs,_USTRUCTS(),_They%27re_Awesome)

Unreal Engine 4 Wiki :: How To Make UStruct(https://wiki.unrealengine.com/How_To_Make_UStruct)

 

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

로그 출력하기

 

작성버전 :: 4.20.3

 

로그는 개발중에 여러가지 피드백을 얻기 위해서 중요한 도구다. 그렇기 때문에 항상 새로운 언어, 새로운 엔진 등을 배울 때는 거기서 어떤 방식으로 로그를 출력하는지 알아두는 것이 좋다. 이번에는 언리얼 엔진 4에서 로그를 출력하는 방법을 알아보자.

 

 

로그가 출력되는 출력 로그 탭 열기

 

 

 

우선은 로그를 출력하는 법을 배운 이후에 로그가 출력될 로그 탭을 여는 방법에 대해서 배워보자.

 

- 창 -> 개발자 툴 -> 출력 로그 (Window -> Developer Tools -> Output Log) 항목을 선택한다.

 

 

 

지시대로 출력 로그(Output Log) 항목을 선택하면 출력 로그 탭이 열리는 것을 확인할 수 있다. 에디터에서 우리가 출력하도록 명령한 로그들은 저 출력 로그 탭에서 출력될 것이다.

 

 

간단한 사용법

 

UE_LOG(LogTemp, Log, TEXT("Log Message"));

 

위의 코드를 사용하는 것으로 간단하게 로그를 출력할 수 있다. 언리얼에서 로그 기능은 매크로 함수로 정의 되어있으며, 매개변수는 앞에서부터 순서대로 로그 카테고리(Log Category), 로그 상세 수준(Log Verbosity Level), 로그 내용이다.

 

 

로그 상세 수준(Log Verbosity Level)

 

로그 상세 수준의 종류는 다음과 같다.

 

  • Fatal
Fatal 수준 로그는 항상 콘솔 및 로그 파일에 출력되며 로그가 비활성화된 경우에도 작동이 중단된다.
  • Error
Error 수준 로그는 콘솔 및 로그 파일에 출력되며, 이 로그는 기본적으로 빨간색으로 표시된다.
  • Warning
Warning 수준 로그는 콘솔 및 로그 파일에 출력되며, 이 로그는 기본적으로 노란색으로 표시된다.
  • Display
Display 수준 로그는 콘솔및 로그 파일에 출력된다.
  • Log
Log 수준 로그는 로그 파일에는 출력되지만, 게임 내의 콘솔에서는 출력되지 않지만, 에디터의 출력 로그 탭을 통해서는 계속 출력된다.
  • Verbose
Verbose 수준의 로그는 로그 파일에는 출력되지만, 게임 내의 콘솔에는 출력되지 않는다. 일반적으로 자세한 로깅 및 디버깅에 사용된다.
  • VeryVerbose
VeryVerbose 수준의 로그는 로그 파일에는 출력되지만, 게임 내의 콘솔에는 출력되지 않는다. 이 수준의 로그는 일반적으로 대량의 로그를 출력하는 상세한 로깅에 사용된다.

 

 

로그 상세 수준 중에 자주 사용될 에러, 경고, 로그 수준을 사용해서 로그를 출력하면 위의 이미지와 같이 로그가 출력된다.

 

Fatal 수준의 로그는, 출력되거나 컴파일 혹은 빌드할 때 코드가 실행되는 상황이라면 충돌 리포트를 띄우지만 이것은 의도된 충돌이기 때문에 로그 파일이나 충돌 호출 스택(Crash call stack)을 확인하면 된다.

 

 

 

 

 

로그 카테고리(Log Category)

 

로그 카테고리는 출력된 로그가 어떤 시스템에서 발생한 로그인지 알려주는 역할을 한다. 위의 간단한 사용법 파트에서는 이 로그 카테고리에 LogTemp를 넣어서 사용했는데, 이것은 특정한 카테고리에 속하지 않고 임시로 띄우는 로그라는 의미다. 언리얼 엔진에서는 이러한 카테고리를 90개 이상을 기본적으로 제공한다.

 

 

 

어떤 카테고리에서 로그가 발생했는지 알려줄 수 있기 때문에 로그 출력 코드를 작성할 때, 제대로 된 카테고리를 분류해서 넣어주기만 한다면 로그가 제공하는 정보가 좀 더 상세해질 수 있다.

 

 

커스텀 로그 카테고리(Custom Log Category)

 

언리얼 엔진에서 제공하는 로그 카테고리 이외에 개발자가 필요한 카테고리를 직접 만들어서 사용할 수 있다.

 

만약, 당신의 프로젝트 이름이 MyGame이라면 비주얼 스튜디오의 솔루션 탐색기에서 MyGame.h와 MyGame.cpp를 찾아서 다음과 같이 추가하면 된다.

 

MyGame.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All);

 

MyGame.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyGame.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, MyGame, "MyGame" );

DEFINE_LOG_CATEGORY(LogMyGame);

 

그리고 추가한 로그 카테고리를 사용할 때는 아래의 예시 코드와 같이 사용할 소스 파일에 MyGame.h를 포함한뒤 사용하면 된다.

 

MyActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyActor.h"
#include "Engine.h"
#include "MyGame.h"

// Sets default values
AMyActor::AMyActor()
{
     // 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;

    UE_LOG(LogMyGame, Log, TEXT("My Log"));
}

 

 

 

 

 

로그 포맷(Log Formatting)

 

로그를 작성할 때, 로그의 내용이 고정되어 있는 경우보다, 상황이나 데이터의 내용에 따라 유동적으로 바뀌는 경우가 많다. 그렇기 때문에 간단한 사용법 파트의 예시처럼 고정된 문자열 방식이 아니라 포맷팅을 통해서 원하는 데이터의 내용 등을 고정된 로그의 내용과 함께 출력되도록 해야한다. 언리얼 엔진에서 로그 포맷팅을 하는 방법은 CPP에서 문자열 포맷팅하는 방법과 같다.

 

일반 메시지

UE_LOG(LogTemp, Log, TEXT("Log Message"));

 

FString

 

언리얼 엔진에서 기본적으로 사용되는 문자열 클래스는 FString이다. 로그에서 %s는 TCHAR* 타입을 받는다. 이것은 *FString에 대응한다.

UE_LOG(LogTemp, Log, TEXT("Character Name :: %s"), *MyCharacter->GetName());

 

bool

UE_LOG(LogTemp, Log, TEXT("Character is Die :: %s"), MyCharacter->IsDie ? TEXT("true") : TEXT("false"));

 

int

UE_LOG(LogTemp, Log, TEXT("Character HP :: &d"), MyCharacter->Hp);

 

float

UE_LOG(LogTemp, Log, TEXT("Character Stamina :: %f"), MyCharacter->Stamina);

 

FVector

 

FVector는 언리얼 엔진에서 위치를 표현하는 클래스이다.

UE_LOG(LogTemp, Log, TEXT("Character Location :: %s"), MyCharacter->GetActorLocation().ToString());

 

FName

UE_LOG(LogTemp, Log, TEXT("Character FName :: &d"), MyCharacter->GetFName().ToString());

 

여러 자료형 한번에 로그로 출력하기

UE_LOG(LogTemp, Log, TEXT("CharacterName :: &s / HP :: &d / Stamina :: %f"), *MyCharacter->GetName(), MyCharacter->Hp, MyCharacter->Stamina);

 

추가로...

 

bool 같은 논리 변수는 로그로 출력하는 과정이 조금 번거롭기 때문에, 로그를 자주 사용한다면 이러한 번거로운 과정을 간소화하기 위해서 로그를 위한 클래스로 래핑해서 사용하는 것도 추천해볼만하다.

 


 

참고

 

Unreal Engine 4 Wiki - Logs, Printing Messages To Yourself During Runtime (https://wiki.unrealengine.com/index.php?title=Logs,_Printing_Messages_To_Yourself_During_Runtime&action=edit)

 

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

프로그래밍 작업시 헤더(Header) 포함(Include) 문제

 

작성버전 :: 4.20.3

 

언리얼 엔진4(이하 언리얼 엔진 혹은 언리얼)에서의 프로그래밍은 C++기반으로 구성되어 있기 때문에 다른 헤더 파일에 정의된 클래스 등을 사용하기 위해서는 헤더를 포함하는 전처리가 필수적이다.

 

일반적인 C++ 프로젝트에서 헤더가 꼬이거나 중복 호출되는 경우만 조심하면 주의하면 되고 헤더의 순서는 별로 중요하지 않은 것에 비해서 언리얼에서의 헤더 포함은 약간 복잡하고 귀찮은데다가 버그까지 있다.

 

언리얼 엔진이 아닌 다른 C++ 프로젝트에서는 프로그래밍 작업 도중에 필요한 헤더가 생긴다면 상단의 전처리기들 아래에 필요한 헤더의 포함 선언을 추가할 것이다. 하지만 언리얼에서는 헤더 포함의 위치가 중요하다.

 

 

 

만약 AMyActor 클래스에서 AMyActorComponent를 사용하기 위해서 MyActorComponent.h의 선언을 일반적인 C++ 프로젝트에서 하듯이 MyActor.h의 헤더 포함 리스트 제일 아래에 추가하면 위의 이미지처럼 신텍스 에러(E0077 this declaration has no storage class or type specifier)가 발생하고 에디터에서의 컴파일 역시 실패한다.

 

언리얼에서는 다른 헤더를 포함할 때, 항상 generated.h 헤더보다 위쪽에 포함 선언을 추가해야 된다.

 

 

 

위의 이미지처럼 새롭게 추가하는 헤더 포함 선언을 generated.h 헤더 선언 위로 올리고 수정한 소스 파일을 저장하면 대부분은 신텍스 에러가 사라진다. 하지만 여기서 고질적인 문제가 발생하는데 꽤나 높은 빈도로 정상적으로 generated.h 위쪽으로 헤더 포함 선언을 옮겼는데도 불구하고 아래의 이미지처럼 신텍스 에러가 사라지지 않는 경우가 있다.

 

 

 

이러한 문제는 인텔리전스 버그로 실제 컴파일에서는 전혀 문제가 되지 않는다. 실제로도 에디터에서 컴파일을 해보면 전혀 문제 없이 컴파일이 진행되는 것을 알 수 있다. 이런 인텔리전스 버그는 잠시 후에 없어지기도 하고, 에디터에서 컴파일하거나, 비주얼 스튜디오나 언리얼을 재실행 하는 것으로도 없어진다.

 

이런 헤더 순서 문제가 매우 중요함에도 불구하고 언리얼 엔진 레퍼런스 문서에서는 쉽게 찾을 수 없는게 문제다.

 

 

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

+ Recent posts