C++에서 제작된 언리얼 오브젝트나 데이터는 블루프린트에서는 편집이나 수정할 수 없는 것이 원칙입니다. 따라서 C++에서 블루프린트에 충분한 권한을 주어야 합니다.
이러한 권한은 클래스 / 멤버 변수 / 멤버 함수의 세 종류에서 지정할 수 있습니다.
언리얼 오브젝트가 블루프린트에서 상속받을 수 있는 클래스가 되려면 UCLASS 매크로 안에 아래의 두 가지 타입을 넣어 주어야 합니다.
1. BlueprintType : 이 C++ 클래스는 블루프린트에서 변수로 선언이 가능한 타입임을 지정합니다.
2. Blueprintable : 블루프린트에서 이 C++ 클래스를 상속받아서 새롭게 클래스를 확장할 수 있습니다.
기본적으로 AActor에 이미 위의 두 선언이 들어가 있습니다. AActor를 선택하고 F12키를 눌러서 AActor의 정의를 보면 아래와 같이 BlueprintType과 Blueprintable이 가장 먼저 선언되어 있음을 확인할 수 있습니다. 따라서 액터 AActor를 상속받은 C++ 클래스는 블루프린트에서 상속 가능하고, 블루프린트에서 변수형으로 지정할 수 있습니다.
UCLASS(BlueprintType, Blueprintable ...
class ENGINE_API AActor : public UObject
{
멤버 변수의 권한은 UPROPERTY 매크로를 통해서 지정할 수 있습니다. 블루프린트 로직에서 해당 멤버 변수에 읽기쓰기를 할 수 있는지, 읽기만 하는지에 대한 권한을 각각 BlueprintReadWrite, BlueprintReadOnly 값을 지정할 수 있습니다.
우리가 생성한 스켈레탈메시 컴포넌트인 Weapon 변수에 대해 BluerprintReadWrite 키워드를 부여한다고 가정해봅시다. 이 변수의 UPROPERTY(BlueprintReadWrite)라고 선언하면 스켈레탈 메시 컴포넌트는 CDO에서 액터와 함께 정적으로 생성되도록 지정했는데 블루프린트에서 오브젝트 값을 변경한다는 의미가 되어버립니다. 이렇다면 굳이 이 컴포넌트를 정적으로 생성하는 것이 의미가 없을 뿐더러, 엔진에서도 혼란스러워 할 것입니다. 따라서 CreateDefaultSubobject로 생성하는 정적인 컴포넌트들은 모두 UPROPERTY(BlueprintReadOnly)로 선언해야 초기 설계 개념에도 부합한 선언이 되겠습니다. 정리하자면 컴포넌트를 선언에 사용된 멤버 변수는 BluerprintReadOnly을 지정해야 한다라고 이해하시면 됩니다.
블루프린트에서 무기의 데미지를 항시 고정시키려면 UPROPERTY(BlueprintReadOnly)를 사용하고 물약 효과 등에 의해 검의 데미지를 블루프린트에서 변경하고 싶다면 UPROPERTY(BlueprintReadWrite)로 선언해주면 됩니다.
UPROPERTY(BlueprintReadOnly)
class USkeletalMeshComponent* Weapon;
UPROPERTY(BlueprintReadWrite)
float BaseDamage;
이는 블루프린트 스크립트의 읽고 쓰기 권한과 무관하게 에디터 인터페이스에서 변수를 편집할 수 있게 지정해줘야합니다. 이를 지정하는 키워드는 여섯 개나 되어서 조금 복잡합니다.
여섯 가지 키워드를 잘 살펴보면 전자인 Edit / Visible 그룹과 후자인 DefaultsOnly / InstanceOnly / Anywhere의 세가지로 나눌 수 있습니다.
전자인 Edit와 Visible은 편집이 가능한지, 보여주기만 할 것인지를 지정하는데 사용합니다. 위에서 액터의 컴포넌트는 설계 단위에서부터 목적이 명확하기 때문에 향후에 바꿀일이 있으면 안된다고 설명드렸습니다. 따라서 우리의 스켈레탈 메시 컴포넌트는 Visible을 사용해야합니다. 오브젝트에 Visible 키워드를 사용하면 오브젝트 레퍼런스는 변경이 되지 않지만, 오브젝트 내 속성들은 블루프린트에서 편집이 가능합니다. 그러면 딱 우리가 원하는 결과가 나옵니다. 반면, Value타입인 BaseDamage를 Visible로 지정하면 이는 읽기만 가능해집니다. 따라서 Value타입은 대부분 Edit를 사용하는 것이 일반적입니다.
후자인 DefaultsOnly / InstanceOnly / Anywhere은 변수의 활용 용도에 따라 다릅니다.
DefaultsOnly는 클래스 설계도에서만 변수 편집 화면을 보여주도록 지정하는 키워드입니다.
반면 InstanceOnly는 언리얼 오브젝트의 인스턴스에서만 변수 편집 화면을 보여주게 만드는 키워드이지요.
Anywhere은 말 그대로 둘 다 보여주게 하는 키워드입니다.
예를 들어서 하나의 몬스터를 기획하는데, 공격 범위와 몬스터의 레벨을 저장할 변수를 두 개를 선언했습니다. 이 몬스터는 에디터에서 블루프린트를 맵에 드래그하여 배치하는데, 플레이어의 시작지점과 가까운 위치에 있는 몬스터는 낮은 레벨로, 플레이어와 멀어질 수록 배치된 몬스터는 점점 높은 레벨로 설정할 수 있도록 몬스터를 설계한다고 가정합시다.
맵에 배치된 모든 몬스터의 공격 범위를 동일하게 만들려면, 공격 범위에 해당하는 변수는 DefaultsOnly키워드를 사용해 모든 배치된 몬스터 액터들이 같은 값을 가지도록 설계하는 것이 관리적인 측면에서 유리할 겁니다. 하지만 몬스터 레벨의 정보는 플레이어 시작 위치로부터의 거리에 따라 배치된 몬스터 액터 인스턴스들마다 점점 높아지는 값을 가져야 합니다. 이 경우 레벨 변수에 InstanceOnly 키워드를 사용하는 것이 관리적인 측면에서 편리하겠지요.
이 외에도 UPROPERTY에는 meta 네임스페이스에서 지정된 여러가지 키워드를 사용해 에디터에서 도움말과 같은 것들을 출력할 수 있거나 에디터 연동 관련해서 기타 다양한 값들을 지정하는 것이 가능한데, meta 키워드 중에서 눈여겨볼 부분은 AllowPrivateAccess라는 값입니다. 블루프린트는 C++ 클래스를 상속받는 개념이므로 기본적으로 public이나 protected 변수들만 적용이 가능합니다.
하지만 AllowPrivateAccess 키워드를 추가하면 private 변수도 블루프린트 스크립트에서 활용이 가능합니다. 이는 OOP 설계시 멤버 변수에 직접 접근하는 디자인보다 액세서(Accessor)라 불리는 함수를 통하는 디자인 방식을 선호하는 분을 위해 제공하는 기능입니다. 아래와 같이 UPROPERTY 매크로에 해당 키워드를 추가하면 OOP 코딩 설계를 따르면서도 블루프린트로도 노출하는 변수 제작이 가능합니다. 이 방식은 선호하시는 분들만 참고하셔서 활용하시면 되겠습니다.
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Stat", meta=(AllowPrivateAccess="true"))
float BaseDamage;
언리얼 오브젝트에서 클래스 멤버 함수도 마찬가지로 기본적으로 블루프린트에서 사용할 수 없지만 BlueprintCallable이라는 키워드를 사용하면 블루프린트에서 호출이 가능합니다. 함수를 블루프린트에서 사용하고자 위의 키워드를 사용할 때에는 반드시 카테고리를 지정해서 블루프린트에서 검색할 수 있게 만들어주어야 합니다.
UFUNCTION(BlueprintCallable, Category="Weapon|Stat")
float GetDamage() { return BaseDamage; }
이 외에도 멤버 함수에는 블루프린트에서의 이벤트로 지정할 수 있는 BlueprintImplementableEvent 키워드와 C++와 블루프린트 두 군데에서 모두 이벤트를 처리할 수 있는 BlueprintNativeEvent 등의 키워드들이 있습니다. 전자는 이벤트 구현의 의무를 완전히 블루프린트에 부여하고 싶을때에 사용하고, 후자는 C++에서 이벤트 로직을 구현하되, 블루프린트에서도 대신 구현할 수 있게 만들 때 유용합니다.