Interface

2025. 2. 7. 21:37·Unreal/Core Concepts

Interfac(인터페이스)

인터페이스란?

클래스가 언리얼 인터페이스(Unreal Interface) 클래스에서 상속하면 인터페이스는 새 클래스가 공통의 함수를 구현하도록 합니다. 이는 특정 함수 기능을 서로 다른 규모의 복잡한 클래스에서 공유할 수 있을 때 유용합니다. 즉, 인터페이스 자체는 아무런 기능을 가지지 않지만, 이를 상속받은 클래스가 인터페이스에 정의된 함수들을 구현해야 합니다. 이 방식은 객체 간 결합도를 낮추고 유지보수를 용이하게 하며, 다양한 클래스에서 동일한 기능을 일관되게 구현할 수 있도록 합니다.

 

  • 객체가 반드시 구현해야 할 행동을 지정하는데 활용되는 타입
  • 다형성(Polymorphism)의 구현, 의존성이 분리(Decouple)된 설계에 유용하게 활용


인터페이스는 특히 게임 게발에서 다음과 같은 역할을 수행할 수 있습니다.

  • 여러 오브젝트에 공통된 기능을 부여: 예를 들어 상호작용 가능한 오브젝트를 정의하고 싶다면, 문, 상자, NPC 등 다양한 액터들이 이 인터페이스를 구현하면 됩니다.
  • 유지보수성과 확장성을 향상: 인터페이스를 사용하면 특정 기능을 추가하거나 변경할 때도 기존 코드의 수정 없이 새로운 클래스에 쉽게 적용할 수 있습니다.
  • 코드의 유연성을 증가: 특정 클래스의 구현에 의존하지 않고도, 인터페이스를 통해 다양한 오브젝트를 동일한 방식으로 다룰 수 있습니다.

✅ 인터페이스의 장점

  1. 유연성과 확장성
    • 인터페이스를 사용하면 기존 클래스를 수정하지 않고도 새로운 기능을 쉽게 추가할 수 있습니다.
    • 클래스 간의 결합도를 낮추어 코드 확장이 용이해집니다.
    • 특히 다중 상속이 불가능한 언어(예: C++, C#에서는 가능하지만, Java, Unreal Engine의 블루프린트에서는 불가능)에서 대체재로 유용하게 쓰입니다.
  2. 코드 재사용성 증가
    • 여러 개의 클래스가 같은 인터페이스를 구현하면, 동일한 방식으로 객체를 처리할 수 있어 코드 중복이 줄어듭니다.
    • 예를 들어, IDamageable 인터페이스를 구현한 객체(적, 플레이어, 오브젝트 등)는 하나의 TakeDamage() 함수로 동일하게 다룰 수 있습니다.
  3. 유지보수 용이
    • 특정 기능이 변경될 때 인터페이스를 구현한 클래스들만 수정하면 되므로 유지보수가 편리해집니다.
    • 예를 들어, IInteractable 인터페이스를 가진 모든 객체에 새로운 인터랙션 로직을 추가할 때, 해당 인터페이스를 구현한 클래스들만 수정하면 됩니다.

❌ 인터페이스의 단점

  1. 구현의 강제성
    • 인터페이스를 상속받은 클래스는 반드시 해당 인터페이스의 모든 함수를 구현해야 합니다.
    • 필요하지 않은 기능도 구현해야 하는 경우가 생길 수 있습니다.
    • 이 단점은 인터페이스를 여러 개로 나누어 설계하거나, 추상 클래스와 인터페이스를 조합하여 해결할 수 있습니다.
  2. 코드의 복잡성 증가
    • 인터페이스를 과도하게 사용하면 구조가 복잡해질 수 있습니다.
    • 작은 기능을 너무 세분화해서 여러 인터페이스로 분리하면 코드가 난잡해지고 가독성이 떨어질 수 있습니다.
    • 간단한 상속 관계라면, 인터페이스보다 **일반적인 상속(inheritance)이나 조합(composition)**을 활용하는 것이 더 나을 수도 있습니다.

언리얼 C++ 인터페이스 특징

  1. `U`로 시작하는 타입 클래스
    • 리플렉션 시스템에서 사용되며, 런타임에 객체가 해당 인터페이스를 구현했는지 여부를 확인하는 용도로 사용됩니다.
    • 직접 작업에 사용할 일은 없습니다.
  2. `I`로 시작하는 인터페이스 클래스
    • 인터페이스의 기능 정의 및 구현을 담당합니다.
    • 객체 설계 시, 이 클래스를 기반으로 인터페이스를 구현하며, 순수 가상 함수를 재정의 합니다.

C++에서 인터페이스 선언하기

C++에서의 인터페이스 선언은 보통의 언리얼 클래스 선언과 비슷합니다. 하지만 다음과 같은 몇 가지 기본적인 차이가 있습니다.

  • 인터페이스 클래스는 `UCLASS`매크로 대신 `UINTERFACE`매크로를 사용합니다.
  • 인터페이스 클래스는 `UObject`대신 `UInterface`에서 상속합니다.
📌`UINTERFACE`는 언리얼의 리플렉션 시스템에 인터페이스의 타입 정보를 노출하기 위한 클래스입니다. 이 클래스는 함수 구현을 포함하지 않으며, 실제 인터페이스 동작은 `I`로 시작하는 클래스에서 정의됩니다.

인터페이스 지정자

인터페이스 지정자를 사용하여 클래스를 언리얼 리플렉션 시스템에 노출시킵니다. 다음 표에 관련 인터페이스 지정자가 포함되어 있습니다.

인터페이스 지정자 설명
`Blueprintalbe` 블루프린트에서 구현할 수 있도록 이 인터페이스를 노출시킵니다. 블루프린트에서 인터페이스를 구현하려면, 안터페이스에 포함된 함수들이 반드시 `BlueprintImpementableEvent` 또는 `BlueprintNativeEvent`로 선언되어야 합니다. 블루프린트 구현이 적합하지 않다면 `NotBlueprintable` 또는 `meta=(CannotImplementInterfaceInBlueprint)`로 명시해야 합니다.
`BlueprintTyep` 이 클래스를 블루프린트의 변수에 사용할 수 있는 타입으로 노출합니다.
`DepedsOn=(ClassName1, ClassName2, ...) 빌드 시스템에서 이 클래스를 컴파일하기 전에 이 지정자로 나열된 모든 클래스를 컴파일 합니다. 컴파일 순서를 명확히 하여 빌드 에러를 방지할 수 있으며, 같은 패키지나 이전 패키지의 클래스를 대상으로 지정해야 하며, 쉼표로 여러 클래스를 한 줄에 지정하거나, 각 클래스에 대해 별도로 지정할 수 있습니다.
MinimalAPI 다른 모듈에서 사용하도록 클래스의 타입 정보만 익스포트합니다. 클래스는 외부에서 캐스팅되거나 타입으로 참조될 수 있지만, 인라인 함수 외의 대부분의 함수는 호출할 수 없습니다. 외부에서 호출해야 하는 함수는 반드시 명시적으로 익스포트 매크로를 추가해야 하며 다른 모듈에서 액세스할 수 있는 모든 함수가 필요하지 않는 클래스에 대해 모든 것을 익스포트할 필요가 없어 컴파일 시간이 단축됩니다.

인터페이스 활용한 설계

인터페이스 활용 예시로 각각의 미사일과 레이저가 나가는 터렛을 만드는 것을 가정하고 작성해 보겠습니다.

 

터렛 인터페이스 정의

1. ITurretInterface.h

// TurretInterface.h
#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "TurretInterface.generated.h"

// UInterface로 선언 (리플렉션 시스템에서 사용)
UINTERFACE(Blueprintable)
class YOURGAME_API UTurretInterface : public UInterface
{
    GENERATED_BODY()
};

// ITurretInterface로 구현
class YOURGAME_API ITurretInterface
{
    GENERATED_BODY()

public:
    // 모든 터렛이 구현해야 하는 Fire 함수
    virtual void Fire() = 0;

    // 터렛의 타입 반환
    virtual FString GetTurretType() const = 0;
};

 

📌 블루프린트 예시

더보기
더보기
// TurretInterface.h
UINTERFACE(BlueprintType)
class UTurretInterface : public UInterface
{
    GENERATED_BODY()
};

class ITurretInterface
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    void Fire();

    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    FString GetTurretType() const;
};

// 블루프린트에서 LaserTurret과 MissileTurret의 동작을 각각 정의할 수 있습니다.
// 예: 블루프린트에서 레이저 터렛은 적을 따라다니는 이펙트 추가, 미사일 터렛은 폭발 이펙트를 추가.

2. ALaserTurret.h

// LaserTurret.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TurretInterface.h"
#include "LaserTurret.generated.h"

UCLASS()
class YOURGAME_API ALaserTurret : public AActor, public ITurretInterface
{
    GENERATED_BODY()

public:
    // ITurretInterface의 Fire 함수 구현
    virtual void Fire() override
    {
        UE_LOG(LogTemp, Warning, TEXT("레이저 발사!"));
    }

    // ITurretInterface의 GetTurretType 함수 구현
    virtual FString GetTurretType() const override
    {
        return TEXT("Laser");
    }
};

3. AMissileTurret.h

// MissileTurret.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TurretInterface.h"
#include "MissileTurret.generated.h"

UCLASS()
class YOURGAME_API AMissileTurret : public AActor, public ITurretInterface
{
    GENERATED_BODY()

public:
    // ITurretInterface의 Fire 함수 구현
    virtual void Fire() override
    {
        UE_LOG(LogTemp, Warning, TEXT("미사일 발사!"));
    }

    // ITurretInterface의 GetTurretType 함수 구현
    virtual FString GetTurretType() const override
    {
        return TEXT("Missile");
    }
};

4. UTurretManager.h

// TurretManager.cpp
#include "MissileTurret.h"
#include "LaserTurret.h"
#include "TurretInterface.h"

void HandleTurretFire(UObject* TurretObject)
{
    // TurretObject가 ITurretInterface를 구현했는지 확인
    ITurretInterface* Turret = Cast<ITurretInterface>(TurretObject);
    if (Turret)
    {
        // 터렛 타입 가져오기
        FString TurretType = Turret->GetTurretType();

        // 터렛 타입에 따른 로직 처리
        if (TurretType == TEXT("Missile"))
        {
            UE_LOG(LogTemp, Warning, TEXT("미사일 터렛이 선택되었습니다."));
        }
        else if (TurretType == TEXT("Laser"))
        {
            UE_LOG(LogTemp, Warning, TEXT("레이저 터렛이 선택되었습니다."));
        }

        // 공통 Fire 함수 호출
        Turret->Fire();
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("터렛이 ITurretInterface를 구현하지 않았습니다."));
    }
}

5. 호출 테스트

void TestTurretFiring(UWorld* WorldContext)
{
    if (!WorldContext)
    {
        UE_LOG(LogTemp, Error, TEXT("유효하지 않은 월드 컨텍스트입니다."));
        return;
    }

    // 미사일 터렛 스폰
    AActor* MissileTurret = WorldContext->SpawnActor<AMissileTurret>(AMissileTurret::StaticClass(), FTransform());
    if (MissileTurret)
    {
        HandleTurretFire(MissileTurret);
    }

    // 레이저 터렛 스폰
    AActor* LaserTurret = WorldContext->SpawnActor<ALaserTurret>(ALaserTurret::StaticClass(), FTransform());
    if (LaserTurret)
    {
        HandleTurretFire(LaserTurret);
    }
}


// AGameModeBase.cpp

void AMyGameModeBase::BeginPlay()
{
    Super::BeginPlay();

    // 현재 월드를 매개변수로 전달
    TestTurretFiring(GetWorld());
}

 

 

코드 요약

  1. 인터페이스 정의: `Fire`와 `GetTurretType`메서드로 모든 터렛이 공통적으로 가져야 할 기능을 선언
  2. 미사일 터렛과 레이저 터렛 구현: 각각 `ITurretInterface`를 구현하여 개별적으로 동작 정의
  3. 핸들링 함수: 모든 터렛을 `ITurretInterface`로 묶어 관리하며, 타입에 따라 다르게 처리
  4. 테스트 호출: 미사일 터렛과 레이저 터렛을 각각 테스트
  5. 이 인터페이스는 블루프린트에서 구현 가능하며, 블루프린트 상에서 터렛의 `Fire`기능을 추가적으로 정의하거나 확장할 수 있습니다.

'Unreal > Core Concepts' 카테고리의 다른 글

Navigation Mesh ( 내비게이션 메시 )  (0) 2025.05.17
Reflection System  (0) 2025.02.08
Actor Lifecycle  (0) 2025.02.04
LEVEL  (0) 2025.01.24
Actor  (0) 2025.01.15
'Unreal/Core Concepts' 카테고리의 다른 글
  • Navigation Mesh ( 내비게이션 메시 )
  • Reflection System
  • Actor Lifecycle
  • LEVEL
Mr.Vulpes
Mr.Vulpes
여우비가 내리는 시간입니다.
  • Mr.Vulpes
    여우비 개발실
    Mr.Vulpes
  • 전체
    오늘
    어제
    • Browse All Categories (82) N
      • Project (5)
        • Unreal (5)
        • DirectX (0)
      • Unreal (17)
        • Core Concepts (12)
        • Unreal For C++ (5)
      • C++ Programming (8)
        • C Basic (8)
      • DirectX (15)
        • Basic (11)
        • DirectX - Class (4)
      • Math & Physics (9)
        • Vectors (3)
      • Software Engineering (27)
        • Software Development Princi.. (24)
        • Design Pattern (3)
  • hELLO· Designed By정상우.v4.10.3
Mr.Vulpes
Interface
상단으로

티스토리툴바