icon안동민 개발노트

블루프린트에서 C++ 클래스 사용


 C++로 작성된 클래스를 블루프린트에서 확장하고 사용하는 것은 언리얼 엔진의 강력한 기능 중 하나입니다.

 이 절에서는 C++ 클래스를 블루프린트와 효과적으로 통합하는 방법을 살펴보겠습니다.

UCLASS, UPROPERTY, UFUNCTION 매크로를 사용한 C++ 클래스 노출

 C++ 클래스를 블루프린트에서 사용 가능하게 만들려면 적절한 매크로를 사용해야 합니다.

UCLASS(Blueprintable, BlueprintType)
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()
 
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyProperties")
    float MyFloat;
 
    UFUNCTION(BlueprintCallable, Category = "MyFunctions")
    void MyFunction(int32 MyParameter);
 
    UFUNCTION(BlueprintImplementableEvent, Category = "MyEvents")
    void MyBlueprintEvent();
 
    UFUNCTION(BlueprintNativeEvent, Category = "MyEvents")
    void MyNativeEvent();
};
 
void AMyActor::MyFunction(int32 MyParameter)
{
    // 구현...
}
 
void AMyActor::MyNativeEvent_Implementation()
{
    // 기본 구현...
}

블루프린트에서 상속 가능한 C++ 클래스 설계

 블루프린트에서 쉽게 확장할 수 있는 C++ 클래스를 설계하려면 다음을 고려하세요.

  1. 가상 함수 사용
  2. 보호된(protected) 멤버 변수 활용
  3. 블루프린트 이벤트 제공
UCLASS(Blueprintable)
class MYGAME_API AMyExtendableActor : public AActor
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Gameplay")
    virtual void PerformAction();
 
protected:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
    float Health;
 
    UFUNCTION(BlueprintImplementableEvent, Category = "Events")
    void OnActionPerformed();
};
 
void AMyExtendableActor::PerformAction()
{
    // 기본 동작...
    OnActionPerformed();
}

C++ 함수와 속성을 블루프린트에서 오버라이드

 블루프린트에서 C++ 함수와 속성을 오버라이드하려면,

  1. 가상 함수로 선언
  2. BlueprintOverride 지정자 사용
UCLASS(Blueprintable)
class MYGAME_API AMyOverridableActor : public AActor
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Overrides")
    void CustomFunction();
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Overrides")
    float OverridableValue;
};
 
void AMyOverridableActor::CustomFunction_Implementation()
{
    // 기본 구현...
}

블루프린트에서 C++ 클래스의 인스턴스 생성 및 조작

 블루프린트에서 C++ 클래스의 인스턴스를 생성하고 조작하는 방법

  1. 생성자 함수 노출
  2. 팩토리 함수 제공
UCLASS(Blueprintable)
class MYGAME_API UMyObject : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Factory")
    static UMyObject* CreateMyObject(UObject* Outer);
 
    UFUNCTION(BlueprintCallable, Category = "Manipulation")
    void Initialize(float InitialValue);
};
 
UMyObject* UMyObject::CreateMyObject(UObject* Outer)
{
    return NewObject<UMyObject>(Outer);
}
 
void UMyObject::Initialize(float InitialValue)
{
    // 초기화 로직...
}

C++ 코드와 블루프린트 간의 효율적인 데이터 교환

 C++와 블루프린트 간의 효율적인 데이터 교환을 위해서는,

  1. 구조체(USTRUCT) 활용
  2. 인터페이스(UINTERFACE) 사용
  3. 데이터 테이블(UDataTable) 활용
USTRUCT(BlueprintType)
struct FMyData
{
    GENERATED_BODY()
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Name;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 Value;
};
 
UINTERFACE(MinimalAPI, Blueprintable)
class UMyInterface : public UInterface
{
    GENERATED_BODY()
};
 
class IMyInterface
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
    void ProcessData(const FMyData& Data);
};
 
UCLASS()
class MYGAME_API UMyDataTableFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Data")
    static FMyData GetDataFromTable(UDataTable* DataTable, FName RowName);
};

C++ 클래스의 블루프린트 확장 시 고려해야 할 설계 패턴

  1. 템플릿 메소드 패턴
  2. 전략 패턴
  3. 옵저버 패턴
UCLASS(Blueprintable)
class MYGAME_API AMyGameplayActor : public AActor
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Gameplay")
    void ExecuteGameplayLogic();
 
protected:
    UFUNCTION(BlueprintNativeEvent, Category = "Gameplay")
    void PreExecute();
 
    UFUNCTION(BlueprintNativeEvent, Category = "Gameplay")
    void PostExecute();
 
private:
    void ExecuteCore();
};
 
void AMyGameplayActor::ExecuteGameplayLogic()
{
    PreExecute();
    ExecuteCore();
    PostExecute();
}
 
void AMyGameplayActor::PreExecute_Implementation()
{
    // 기본 구현...
}
 
void AMyGameplayActor::PostExecute_Implementation()
{
    // 기본 구현...
}
 
void AMyGameplayActor::ExecuteCore()
{
    // 핵심 로직...
}

C++와 블루프린트 혼용 시의 성능 영향

 C++와 블루프린트를 혼용할 때의 성능 고려사항

  1. 블루프린트는 C++보다 약간 느림
  2. 빈번한 C++-블루프린트 경계 넘나들기 피하기
  3. 성능 중요 로직은 C++로 구현

 성능 최적화 예

UCLASS()
class MYGAME_API UMyOptimizedClass : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Optimization")
    void PerformBatchOperation(const TArray<AActor*>& Actors);
 
private:
    void ProcessSingleActor(AActor* Actor);
};
 
void UMyOptimizedClass::PerformBatchOperation(const TArray<AActor*>& Actors)
{
    for (AActor* Actor : Actors)
    {
        ProcessSingleActor(Actor);
    }
}
 
void UMyOptimizedClass::ProcessSingleActor(AActor* Actor)
{
    // 최적화된 C++ 로직...
}

디버깅 전략

 C++와 블루프린트 혼용 시 디버깅 전략

  1. C++ 디버거 활용
  2. 블루프린트 비주얼 디버깅 사용
  3. 로깅 및 화면 메시지 활용
UCLASS()
class MYGAME_API UMyDebugHelper : public UObject
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Debug")
    static void LogDebugMessage(const FString& Message);
 
    UFUNCTION(BlueprintCallable, Category = "Debug")
    static void DrawDebugSphere(UWorld* World, FVector Center, float Radius, FColor Color, float Duration);
};
 
void UMyDebugHelper::LogDebugMessage(const FString& Message)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *Message);
    GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, Message);
}

대규모 팀에서의 효과적인 협업 방법

  1. 명확한 인터페이스 정의
  2. 문서화 및 주석 작성
  3. 코드 리뷰 및 스타일 가이드 준수

 협업을 위한 C++ 클래스 예

UCLASS(Blueprintable, Abstract)
class MYGAME_API AGameplayActorBase : public AActor
{
    GENERATED_BODY()
 
public:
    UFUNCTION(BlueprintCallable, Category = "Gameplay")
    virtual void PerformAction();
 
    UFUNCTION(BlueprintImplementableEvent, Category = "Gameplay")
    void OnActionCompleted();
 
protected:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay")
    float ActionDuration;
 
    UFUNCTION(BlueprintNativeEvent, Category = "Gameplay")
    void PrepareForAction();
};
 
void AGameplayActorBase::PerformAction()
{
    PrepareForAction();
    // 액션 수행 로직...
    OnActionCompleted();
}
 
void AGameplayActorBase::PrepareForAction_Implementation()
{
    // 기본 준비 로직...
}

 C++로 작성된 클래스를 블루프린트에서 효과적으로 활용하면 개발 효율성과 유연성을 크게 향상시킬 수 있습니다. C++의 성능과 타입 안전성, 그리고 블루프린트의 빠른 이터레이션과 시각적 스크립팅의 장점을 모두 활용할 수 있습니다.

 UCLASS, UPROPERTY, UFUNCTION 매크로를 적절히 사용하여 C++ 클래스를 블루프린트에 노출시키고, 가상 함수와 이벤트를 통해 블루프린트에서 쉽게 확장할 수 있는 구조를 만드는 것이 중요합니다. 또한, 효율적인 데이터 교환을 위해 구조체, 인터페이스, 데이터 테이블을 활용하는 것이 좋습니다.

 성능 최적화를 위해서는 C++와 블루프린트의 장단점을 이해하고, 성능이 중요한 부분은 C++로 구현하는 것이 좋습니다. 디버깅 시에는 C++ 디버거와 블루프린트 비주얼 디버깅을 적절히 조합하여 사용하면 효과적입니다.

 대규모 팀에서 협업할 때는 명확한 인터페이스 정의, 철저한 문서화, 그리고 일관된 코딩 스타일 유지가 중요합니다. 추상 기본 클래스를 제공하고 블루프린트에서 이를 확장하게 하는 방식으로 프로그래머와 디자이너 간의 효율적인 작업 분담이 가능합니다.

 마지막으로, C++와 블루프린트의 통합은 지속적인 학습과 실험이 필요한 분야입니다. 프로젝트의 요구사항과 팀의 기술 스택에 맞춰 최적의 통합 방식을 찾아가는 것이 중요합니다.