UE5 C++ UInterface & 인터페이스
개요 — 왜 UInterface를 쓰는가
Section titled “개요 — 왜 UInterface를 쓰는가”C++ 순수 가상 함수로도 인터페이스를 구현할 수 있지만, UE의 UInterface는 다음 장점을 추가로 제공합니다.
| 기능 | 일반 C++ 인터페이스 | UInterface |
|---|---|---|
| Blueprint 연동 | 불가 | BlueprintImplementableEvent / BlueprintNativeEvent |
| 런타임 타입 확인 | dynamic_cast (RTTI) | Implements<T>() (리플렉션) |
| Soft Reference 지원 | 불가 | TScriptInterface<T> |
| 에디터 노출 | 불가 | UPROPERTY(EditAnywhere) 등 |
1. UInterface 선언
Section titled “1. UInterface 선언”UE 인터페이스는 항상 두 클래스 쌍으로 선언됩니다.
UMyInterface:UInterface를 상속, 리플렉션 정보 담당 (비어 있어도 됨)IMyInterface: 실제 인터페이스 함수 선언
#pragma once
#include "CoreMinimal.h"#include "UObject/Interface.h"#include "InteractableInterface.generated.h"
// UInterface 클래스 — 리플렉션 등록용, 함수 선언 없음UINTERFACE(MinimalAPI, BlueprintType)class UInteractableInterface : public UInterface{ GENERATED_BODY()};
// 실제 인터페이스 — 함수를 여기에 선언class MYGAME_API IInteractableInterface{ GENERATED_BODY()
public: // C++ 순수 가상 함수 (C++에서 반드시 구현) virtual FString GetInteractionPrompt() const = 0;
// Blueprint에서 재정의 가능 (C++ 기본 구현 있음) UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction") void Interact(AActor* Interactor); virtual void Interact_Implementation(AActor* Interactor) {}
// Blueprint에서만 구현 (C++는 선언만) UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Interaction") void OnInteractionFailed(AActor* Interactor);
// 인터페이스 함수 — 기본 구현 있는 일반 가상 함수 virtual bool CanInteract(AActor* Interactor) const { return true; }};UINTERFACE 지정자
Section titled “UINTERFACE 지정자”| 지정자 | 의미 |
|---|---|
MinimalAPI | 최소 노출 (엔진 내부 규칙) |
BlueprintType | Blueprint에서 이 인터페이스 타입을 변수로 사용 가능 |
Blueprintable | Blueprint 클래스가 이 인터페이스를 구현 가능 |
NotBlueprintable | C++만 구현 가능 (기본값 아닐 때 명시) |
2. 인터페이스 구현
Section titled “2. 인터페이스 구현”#pragma once
#include "CoreMinimal.h"#include "GameFramework/Actor.h"#include "InteractableInterface.h"#include "PickupItem.generated.h"
UCLASS()class MYGAME_API APickupItem : public AActor, public IInteractableInterface{ GENERATED_BODY()
public: // C++ 순수 가상 함수 구현 (필수) virtual FString GetInteractionPrompt() const override;
// BlueprintNativeEvent 구현 — _Implementation 접미사 필수 virtual void Interact_Implementation(AActor* Interactor) override;
// CanInteract 재정의 virtual bool CanInteract(AActor* Interactor) const override;
private: UPROPERTY(EditDefaultsOnly, Category = "Pickup") FName ItemID;
UPROPERTY(EditDefaultsOnly, Category = "Pickup") bool bIsEnabled = true;};#include "PickupItem.h"#include "GameFramework/Character.h"
FString APickupItem::GetInteractionPrompt() const{ return FString::Printf(TEXT("E 키를 눌러 %s 줍기"), *ItemID.ToString());}
void APickupItem::Interact_Implementation(AActor* Interactor){ // Blueprint에서 재정의하지 않으면 이 코드가 실행됨 UE_LOG(LogTemp, Log, TEXT("Picking up item: %s"), *ItemID.ToString()); Destroy(); // 아이템 제거}
bool APickupItem::CanInteract(AActor* Interactor) const{ return bIsEnabled && IsValid(Interactor);}3. 인터페이스 호출 방법
Section titled “3. 인터페이스 호출 방법”3.1 Cast<> 방식 (C++ only 인터페이스)
Section titled “3.1 Cast<> 방식 (C++ only 인터페이스)”void APlayerCharacter::TryInteract(AActor* Target){ // Cast로 인터페이스 포인터 획득 IInteractableInterface* Interactable = Cast<IInteractableInterface>(Target); if (Interactable) { if (Interactable->CanInteract(this)) { FString Prompt = Interactable->GetInteractionPrompt(); UE_LOG(LogTemp, Log, TEXT("Prompt: %s"), *Prompt); } }}3.2 Execute_ 방식 (Blueprint 연동 함수 필수)
Section titled “3.2 Execute_ 방식 (Blueprint 연동 함수 필수)”BlueprintNativeEvent / BlueprintImplementableEvent로 선언된 함수는 Cast 대신 Execute_ 정적 함수를 사용해야 Blueprint 구현이 올바르게 호출됩니다.
void APlayerCharacter::TryInteract(AActor* Target){ // Implements<> — 인터페이스 구현 여부 확인 (Blueprint 구현 포함) if (Target->Implements<UInteractableInterface>()) { // Execute_ 접두사 정적 함수 — Blueprint/C++ 구현 모두 올바르게 호출 IInteractableInterface::Execute_Interact(Target, this); }}3.3 Cast vs Execute_ 정리
Section titled “3.3 Cast vs Execute_ 정리”| 호출 방식 | Blueprint 구현 호출 | C++ 구현 호출 | 권장 |
|---|---|---|---|
Cast<IMyInterface>(Actor)->Func() | X | O | C++ only 함수 |
IMyInterface::Execute_Func(Actor) | O | O | BlueprintNativeEvent / ImplementableEvent |
Actor->Implements<UMyInterface>() | 확인용 | 확인용 | 구현 여부 체크 |
4. TScriptInterface — UPROPERTY에서 인터페이스 저장
Section titled “4. TScriptInterface — UPROPERTY에서 인터페이스 저장”UCLASS()class MYGAME_API AMyController : public APlayerController{ GENERATED_BODY()
// 에디터에서 인터페이스를 구현한 Actor를 할당 가능 UPROPERTY(EditAnywhere, Category = "Interaction") TScriptInterface<IInteractableInterface> CurrentInteractable;
void ActivateInteractable() { if (CurrentInteractable) { IInteractableInterface::Execute_Interact( CurrentInteractable.GetObject(), this); } }};5. 다중 인터페이스 구현
Section titled “5. 다중 인터페이스 구현”// DamageableInterface.h (별도 선언)UINTERFACE(MinimalAPI, BlueprintType)class UDamageableInterface : public UInterface { GENERATED_BODY() };
class MYGAME_API IDamageableInterface{ GENERATED_BODY()public: UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Damage") void TakeDamage(float Amount, AActor* DamageCauser); virtual void TakeDamage_Implementation(float Amount, AActor* DamageCauser) {}
virtual bool IsAlive() const = 0;};// Enemy.h — 두 인터페이스 동시 구현UCLASS()class MYGAME_API AEnemy : public ACharacter, public IInteractableInterface, public IDamageableInterface{ GENERATED_BODY()
public: // IInteractableInterface virtual FString GetInteractionPrompt() const override; virtual void Interact_Implementation(AActor* Interactor) override;
// IDamageableInterface virtual void TakeDamage_Implementation(float Amount, AActor* DamageCauser) override; virtual bool IsAlive() const override;
private: float Health = 100.f;};6. 실전 패턴 — 상호작용 시스템
Section titled “6. 실전 패턴 — 상호작용 시스템”// InteractionComponent.cpp — LineTrace로 상호작용 가능한 오브젝트 탐지void UInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction){ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
FHitResult HitResult; FVector Start = Camera->GetComponentLocation(); FVector End = Start + Camera->GetForwardVector() * InteractionRange;
FCollisionQueryParams Params; Params.AddIgnoredActor(GetOwner());
if (GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, Params)) { AActor* HitActor = HitResult.GetActor();
if (HitActor && HitActor->Implements<UInteractableInterface>()) { // 인터페이스를 구현한 Actor 감지 if (IInteractableInterface::Execute_CanInteract(HitActor, GetOwner())) { // UI에 프롬프트 표시 FString Prompt = Cast<IInteractableInterface>(HitActor)->GetInteractionPrompt(); ShowInteractionPrompt(Prompt); FocusedActor = HitActor; return; } } }
HideInteractionPrompt(); FocusedActor = nullptr;}
void UInteractionComponent::TryInteract(){ if (FocusedActor && FocusedActor->Implements<UInteractableInterface>()) { IInteractableInterface::Execute_Interact(FocusedActor, GetOwner()); }}- UE 인터페이스는
UMyInterface(리플렉션용) +IMyInterface(함수 선언용) 두 클래스 쌍으로 구성 BlueprintNativeEvent함수는_Implementation접미사로 C++ 구현, 호출 시Execute_사용Execute_Func(Target)방식은 Blueprint·C++ 구현 모두 올바르게 디스패치- UPROPERTY에서 인터페이스를 저장할 때
TScriptInterface<IMyInterface>사용 Implements<UMyInterface>()로 Blueprint 클래스의 인터페이스 구현 여부도 런타임 확인 가능