메모내용

UnrealEngine Interface

Nav

Interface

인터페이스란?

  • 객체가 반드시 구현해야 할 행동을 지정하는 데 활용되는 타입
  • 다형성 (Polymorphism) 의 구현, 의존성이 분리(Decouple) 된 설계에 유용하게 활용된다.
  • 기능적으로 본다면순수가상함수로만 구성되어 있는 함수

  • # 순수 가상함수 ?

                                
                                    virtual void functionName() = 0;
                            

    언리얼 c++ 인터페이스

    • 클래스가 반드시 구현해야 하는 기능을 지정하는 데 사용합니다.
    • C++ 은 기본적으로 다중상속을 지원하지만, 언리얼 c++ 인터페이스를 사용해 가급적 축소된 다중상속의 형태로 구현하는 것이 향후 유지보수에 도움이 됩니다.
    • 언리얼 C++ 인터페이스는 두개의 클래스를 생성합니다.
    • 언리얼 c++ 인터페이스는 추상타입으로 강제되지 않고, 내부에 기본 함수를 구현할 수 있습니다.
    • 언리얼 c++ 인터페이스를 사용하면, 클래스가 수행해야 할 의무를 명시적으로 지정할 수 있어, 좋은 객체 설계를 만드는데 도움을 줄 수 있습니다.

    사용예시

    인터페이스를 사용해서 게임 프레임 워크를 구현한 예시 월드에 배체되는 모든 오브젝트는 Actor 로 만들어진다. ( 움직이는, 안움직이는) 이때 움직이는 오브젝트의 경우 Actor 를 상속하는 Pawn 이라는 클래스로 만들어지는데, 이때 길찾기 시스템을 반드시 사용하면서 움직이기 때문에 INavAgentInterface 라는 인터페이스를 구현해야 합니다.

    # 인터페이스 포맷

    MyInterface.h

                            
                                #pragma once
        
                                #include "CoreMinimal.h"
                                #include "UObject/Interface.h"
                                #include "MyInterface.generated.h"
        
                                UINTERFACE(BlueprintType)
                                class UMyInterface : public UInterface
                                {
                                    GENERATED_BODY()
                                };
        
        
                                /**
                                * 인터페이스
                                */
                                class MODULENAME_API IMyInterface
                                {
                                    GENERATED_BODY()
                                    
                                public:
                                    
                                    /** 순수 가상함수
                                        인터페에스를 상속하는 클래스는 모든 인터페이스의 함수를 구현해주어야합니다.
                                        virtual void functionName () override;
                                    */
    
                                    // 강제로 구현해야 하는 함수
                                    virtual void Interfacefunc1()=0;
    
                                    /**
                                        인터페이스에서 구현해버릴수도 있다.
                                        - 모던 객체지향과는 거리가 멀지만 언리얼에서는 종종 활용한 코드들이 있다.
                                        - 부모함수의 함수를 호출하고 싶을때
                                          Interface 를 상속하는 클래스에서는 
                                          Super::func2() 가 아닌 IMyInterface::func2() 함수의 호출을 통해 해당함수를 호출할 수 있다.
                                          Super 는 단일 상속에서만 호출이 가능하다. 어떤 부모의 함수를 호출할지 모호하기 때문
                                    */
                                    virtual void Interfacefunc2()
                                    {
                                        UE_LOG(LogTemp,Log,TEXT("Interface Func2 Called"));
                                    }
                                };
                            
                        

    구현 여부 확인하기

    해당 인터페이스로의 Cast 를 통해 해당 인터페이스가 구현 되어 있는지 확인할 수 있습니다. blueprint 에서의 DoesImplementInterface 와 비슷하게 쓸 수 있다.
                            
    
                                ...
    
                                TArray<UObject*> ObjectArr = {NewObject<???>(), NewObject<???>(), NewObject<???>()};
                                for(auto ObjectElem : ObjectArr)
                                {
                                    UMyInterface* InterfacedObjectElem = Cast<UMyInterface>(ObjectElem);
                                    if(InterfacedObjectElem)
                                    {
                                        UE_LOG(LogTemp,Log, TEXT("인터페이스가 구현되어 있습니다."));
                                        InterfacedObjectElem->Interfacefunc1();
                                    }
                                    else{
                                        UE_LOG(LogTemp,Log, TEXT("인터페이스가 구현되어 있지 않습니다."));
                                    }
                                }
                            
                        

    # C++ 과 블루프린트에서 (양쪽에서) 구현가능한 순수가상함수를 가진 인터페이스

    UFUNCTION(BlueprintNativeEvent) 지정자는 함수를 순수 가상함수로 만들어 준다.
    인터페이스의 함수에서 UFUNCTION(BlueprintNativeEvent) 를 사용할 수 있다.
    UFUNCTION(BlueprintImplementableEvent) 는 사용할 수 없다. (순수가상함수로 만들어 주지 않기 때문이다)

  • 참조
    UFUNCTION(BlueprintNattiveEvent) 이벤트 함수 구현방법 ( 콜백함수 )
  • MyInterface.h

                                    
                                        #pragma once
        
                                        #include "CoreMinimal.h"
                                        #include "UObject/Interface.h"
                                        #include "MyInterface.generated.h"
        
                                        /** 언리얼엔진에서 타입정보를 보관하기 위해 만든 클래스. 수정할 필요가 없습니다. */
                                        UINTERFACE(BlueprintType)
                                        class UMyInterface : public UInterface
                                        {
                                            GENERATED_BODY()
                                        };
        
        
                                        /**
                                        * 인터페이스
                                        */
                                        class MODULENAME_API IMyInterface
                                        {
                                            GENERATED_BODY()
                                            
                                        public:
                                            
                                            /** 인터페이스를 상속하는 클래스에서도 _Implemantation 함수로 구현할 수 있고,
                                                상속하는 블루프린트에서도 구현할 수 있는 함수
                                            */
                                            UFUNCTION(BlueprintCallable,BlueprintNativeEvent, Category="MyInterface")
                                            void Action(int32 Idx, float Value,const FString& Message);
    
                                            // 구현 강제
                                            virtual void InterfaceFunc1() = 0;
        
                                        };
    
    
    
    
                                    
                                

    MyCharacter.h (Implemantation Class)

                                    
                                        #pragma once
    
                                        #include "EngineMinimal.h"
                                        #include "GameFramework/Character.h"
                                        #include "MyInterface.h"
                                        #include "MyCharacter.generated.h"
    
                                        /**
                                        * 
                                        */
                                        UCLASS(Blueprintable)
                                        UCLASS(Blueprintable)
                                        class MODULENAME_API AMyCharacter : public ACharacter , public IMyInterface
                                        {
                                            GENERATED_BODY()
        
                                            public:
                                            // 인터페이스 함수의 구현부 선언
                                            virtual void Action_Implementation(int32 Idx, float Value, const FString& Message) override;
    
                                            // 인터페이스 함수 구현
                                            virtual void InterfaceFunc1 () override;
    
                                        }
                                    
                                

    MyCharacter.cpp (Implemantation Class)

                                    
                                        #include "MyCharacter.h"
    
                                        void ACreature::CreatureAction_Implementation(int32 ActionIdx, float Value, const FString& Message)
                                        {
                                            CUSTOMLOG(Warning, TEXT("CreatureAction_Implementation : %d"), ActionIdx);
                                        }
                                        
                                        void ACreature::InterfaceFunc1()
                                        {
                                            //...
                                        }
                                    
                                

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

    • 인터페이스를 생성하면 두개의 클래스가 생성된다.
      • U 로 시작하는 타입 클래스 (클래스 타입 정보 제공)
      • I 로 시작하는 인터페이스 클래스 (실질적인 설계 및 구현)
    • 객체를 설계할 때 I 인터페이스 클래스를 사용합니다.
      • U 타입 클래스 정보는 런타입에서 인터페이스 구현 여부를 파악하는 용도로 사용됩니다.
      • 실제로 U 타입 클래스에서 작업할 일은 없습니다.
      • 인터페이스에 관련된 구성 및 구현은 I 인터페이스 클래스에서 진행됩니다.
    • Unreal c++ 인터페이스의 특징
      • 추상 클래스 타입으로만 선언할 수 있는 JAVA, c# 과 달리 언리얼c++ 에서는 인터페이스에서 구현이 가능하다!
      • 언리얼 클래스를 사용해서 구현하기 때문에 추상타입으로 강제할 방법이 없다.