UE Blueprint convert to C++

Taegang Yun·2023년 7월 21일
0

Character 블루프린트 C++ 변환 예제

Character(Self)에,

  • Capsule Component

    • Shadow Body

      • WeaponInBack
    • LowerBody

    • Camera

      • FirstPerson
        • WeaponInHand

이렇게 컴포넌트가 있을 때, 이걸 어떻게 바꿀까?

컴포넌트 탭에 기본적으로 배치된 것
Capsule Component
Mesh Component
Arrow Comonent
Character Movement
해당 컴포넌트 옆에는 Edit in C++ 이 있다.
소스 코드로 이동해보면, Character.h 소스코드를 볼 수 있고, Ctrl + T를 이용해서 우리가 찾고자 하는 component (ex : capsulecomponent) 를 검색하면 해당 정보를 볼 수 있다.

새롭게 만든 6개의 컴포넌트를 C++ 코드에서 구현해보자.
ShadowBody, WeaponInBack, LowerBody, Camera, FirstPerson, WeaponInHand

ShooterCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ShooterCharacter.generated.h"

UCLASS()
class AShooterCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	AShooterCharacter();
protected:
	virtual void BeginPlay() override;
    
public:
	virtual void Tick(float DeltaTime) override;
    
    virtual void SetupPlayerInputComponent(clss UInputComponent* PlayerInputComponent) override;
    
private:
	TObjectPtr<> ShadowBody;
    TObjectPtr<> WeaponInBack;
    TObjectPtr<> LowerBody;
    TObjectPtr<> Camera;
    TObjectPtr<> FirstPerson;
    TObjectPtr<> WeaponInHand;

여기서 < > 안에는 뭐가 들어가야 할까?

아까 Character.h 헤더 파일에서 찾아보면,

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TObject<USkeletalMeshComponent> Mesh;

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TObject<UCharacterMovementComponent> CharacterMovement;

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TObject<UCapsuleComponent> CapsuleComponent;

과 같이 써있는 것을 볼 수 있다.

	TObjectPtr<USkeletalMeshComponent> ShadowBody;
    TObjectPtr<> WeaponInBack;
    TObjectPtr<USkeletalMeshComponent> LowerBody;
    TObjectPtr<> Camera;
    TObjectPtr<USkeletalMeshComponent> FirstPerson;
    TObjectPtr<> WeaponInHand;

Mesh 컴포넌트에 대해선 이렇게 채울 수 있다.

WeaponInBack 이게 어떤 클래스를 사용하는 컴포넌트인지 모르면 어떻게 할까?

에디터에서 해당 컴포넌트를 우클릭 하면 Open <컴포넌트 이름>.h 를 볼 수 있다.
눌러보면 해당 컴포넌트는 ChildActorComponent이고, ChildActorComponent.h 헤더 파일로 이동한다.

여기서 Ctrl+T를 통해 childactorcomponent 검색 후, type 을 보고 싶은 거니까 types를 눌러준다.

	TObjectPtr<USkeletalMeshComponent> ShadowBody;
    TObjectPtr<UChildActorComponent> WeaponInBack;
    TObjectPtr<USkeletalMeshComponent> LowerBody;
    TObjectPtr<> Camera;
    TObjectPtr<USkeletalMeshComponent> FirstPerson;
    TObjectPtr<UChildActorComponent> WeaponInHand;

이제 카메라만 남았다.
카메라도 동일한 방법으로 찾을 수 있다.

	TObjectPtr<USkeletalMeshComponent> ShadowBody;
    TObjectPtr<UChildActorComponent> WeaponInBack;
    TObjectPtr<USkeletalMeshComponent> LowerBody;
    TObjectPtr<UCameraComponent> Camera;
    TObjectPtr<USkeletalMeshComponent> FirstPerson;
    TObjectPtr<UChildActorComponent> WeaponInHand;

근데 UCameraComponent에 빨간 밑줄이 켜져있는데, 이때 해당 밑줄 쳐진 부분에서 오른쪽 클릭-> Quick Actions and refactoring -> Add '#include <Camera/CameraComponent.h>' 를 하면 된다.

이러면 또 오류가 난다.

~.generated.h 가 선언되어 있는 파일에서는 해당 헤더 선언이 모든 include 헤더 가장 밑에 있어야 한다.

그런데 이렇게만 선언을 하면 에디터에는 표시되지 않는다. 왜 그럴까? 이는 UPROPERTY 매크로와 관련이 있다.

UPROPERTY()
TObjectPtr<USkeletalMeshComponent> ShadowBody;

UPROPERTY()
TObjectPtr<UChildActorComponent> WeaponInBack;

UPROPERTY()
TObjectPtr<USkeletalMeshComponent> LowerBody;

UPROPERTY()
ObjectPtr<UCameraComponent> Camera;
TObjectPtr<USkeletalMeshComponent> FirstPerson;
    
UPROPERTY()
TObjectPtr<UChildActorComponent> WeaponInHand;

Ctrl + Alt + F11 로 라이브 코딩을 하면 할 수 있다.
하지만 이래도 에디터에 컴포넌트가 표시되지 않는다.

왜일까?

TObjectPtr는 포인터일 뿐이다.

실제로 오브젝트들은 CreateDefaultSubObject를 통해 생성해야 한다.

그럼 어디서 생성해줘야 할까? 생성자에서 해주면 된다.

AShooterCharacter::AShooterCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
    
    ShadowBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ShadowBody(컴포넌트 출력될 이름)"));
    WeaponInBack = CreateDefaultSubobject<UChildActorComponent>(TEXT("WeaponInBack"));
    LowerBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("LowerBody"));
    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    FirstPerson = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPerson"));
    WeaponInHand = CreateDefaultSubobject<UChildActorComponent>(TEXT("WeaponInHand"));
 }

이런 식으로 말이다.
라이브 코딩 시에 크래시가 많이 나는 경우가 있는데, 직접 빌드하면 된다. Visual Studio에서 프로젝트 우클릭 -> 빌드

이제 에디터에서 보이게 된다!

그런데 우린 컴포넌트 간에 Hierarchy가 존재했다.

하지만 지금은 모두가 RootComponent에 붙어져 있는 것을 볼 수 있다.
이건 어떻게 구현해줄까?

AShooterCharacter::AShooterCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
    
    ShadowBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ShadowBody(컴포넌트 출력될 이름)"));
    ShadowBody->SetUpAttachment(RootComponent);
    WeaponInBack = CreateDefaultSubobject<UUChildActorComponent>(TEXT("WeaponInBack"));
    WeaponInBack->SetupAttachment(ShadowBody);
    LowerBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("LowerBody"));
    LowerBody->SetupAttachment(RootComponent);
    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(RootComponent);
    FirstPerson = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPerson"));
    FirstPerson->SetupAttachment(Camera);
    WeaponInHand = CreateDefaultSubobject<UChildActorComponent>(TEXT("WeaponInHand"));
    WeaponInHand->SetupAttachment(FirstPerson);
 }

이런 식으로 해주면 된다.
그런데 이렇게 해도, Details 탭에는 아무것도 표시가 되지 않는다.

이제 UPROPERTY 에서 안에 지정자들을 만져줄 차례이다.

UPROPERTY(VisibleAnywhere) 

이렇게 해주면, Details 탭이 보인다!
VisibleAnywhere은 , blueprint에서도 C++ 컴포넌트를 보여주게 해준다.

UPROPERTY(Category = Character, VisibleAnywhere) 

이렇게 하면, Details 탭에서 Category에 Character가 추가된 것을 볼 수 있다.

그런데 엄밀히 말하면, Details 탭Blueprint 영역은 다르다. Detail 탭에서 보이는 거지, Blueprint에서 보이는 게 아니다.

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly) 

그런데 이렇게 하면 오류가 발생한다.
BlueprintReadOnly should not be used on private members

UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true")) 

이렇게 해주면 된다.

그런데 Null 포인터 문제가 있다. 만약 포인터가 초기화되지 않았다면, 언리얼 에디터는 Crash가 발생한다.

AShooterCharacter::AShooterCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
    
    ShadowBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ShadowBody(컴포넌트 출력될 이름)"));
    if (ShadowBody){
    	ShadowBody->SetUpAttachment(RootComponent);
    }

그리고 모두 개별적으로 이루어지는게 아니라, WeaponInBack 같은 경우 ShadowBody가 없으면 무의미하니까

AShooterCharacter::AShooterCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
    
    ShadowBody = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ShadowBody(컴포넌트 출력될 이름)"));
    if (ShadowBody){
    	ShadowBody->SetUpAttachment(RootComponent);
        WeaponInBack = CreateDefaultSubobject<UChildActorComponent>(TEXT("WeaponInBack"));
        if(WeaponInBack){
        	WeaponInBack->SetUpAttachment(ShadowBody);
        }
    }

이런 식으로 구현한다.

이제 블루프린트 상에서 바꿨던 값들을 옮겨줄 차례이다.

Detail 탭에서 Show Only Modified Properties 를 누르면 바꾸었던 값들만 표시된다.

간혹 animation같은 경우는 표시가 안 될때가 있는데, 이런 경우는 개별 적용해주자.

모두 값을 옮겼다면, 이제 지워줄 차례다.
해당 컴포넌트 우클릭 -> Find References 하면 어디서쓰였는지 알 수 있다. 모두 대체를 해주었다면, 원래 블루프린트들을 제거한다.

하지만 Socket 관련해서는 블루프린트로 처리를 해야하는데,
Construction Script에서
Attach Component to Component를 이용해서 부모 소켓들을 붙여준다.

Character 클래스에서 enum 값들을 받아오고 싶다면,

UENUM(BlueprintType)
enum class StateOfCharacter : uint8
{
	Idle,
    Aiming,
    Reloading
};

이런 식으로 받을 수 있다.

protected:
	UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadWrite)
	StateOfCharacter State;

	UPROPERTY(Category = Character, VisibleAnywhere, BlueprintReadWrie)
	bool isRunPressed;

BlueprintReadOnly 를 쓰면 블루프린트에서 get만 쓸 수 있고,
BlueprintReadWrite 을 쓰면 블루프린트에서 get, set모두 할 수 있다.

블루프린트에서 작성한 함수를 옮기고 싶다면 어떻게 해야할까?

이미 블루프린트 상에서 작성된 함수(ex : GetVelocity()) 와 같은 경우, Go To Definition (정의 보기) 를 하면 visual studio에서 해당 함수를 볼 수 있다.

UFUNCTION(BlueprintCallable)
virtual float GetSpeed() const;
float AShooterCharacter::GetSpeed() const
{
	return GetVelocity().Length();
}

const 가 붙어있는 함수들을 const function이라 하는데,
해당 함수 내에선 멤버 변수를 수정할 수 없다.
Pure 함수의 기능을 자동으로 수행한다. 원래는 해당 목적으로BlueprintPure 기능이 존재한다.

float AShooterCharacter::GetSpeed() const
{
	/*IsRunPressed = true;*/
	return GetVelocity().Length();
}
profile
언젠간 전문가가 되겠지

0개의 댓글

관련 채용 정보