Character 블루프린트 C++ 변환 예제
Character(Self)에,
Capsule Component
Shadow Body
LowerBody
Camera
컴포넌트 탭에 기본적으로 배치된 것
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에서 프로젝트 우클릭 -> 빌드
이제 에디터에서 보이게 된다!
하지만 지금은 모두가 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();
}