C++에는 언어 자체적으로 인터페이스라는 개념이 존재하지 않는다. 하지만 언리얼 엔진에서는 인터페이스를 구현한다.
이는 언리얼 엔진의 객체 지향 설계와 리플렉션 시스템을 활용하여 게임 개발의 유연성과 확장성을 높이기 위한 것.
언리얼 인터페이스는 다양한 기능을 정의하고, 이를 여러 클래스에서 구현할 수 있도록 하는 도구라고 생각하면 된다.
C++에 인터페이스가 없는 이유
인터페이스가 있는 다른 언어들(C#, Java..)는 C++보다 이후에 나왔다. 더 웰메이드 언어라고 볼 수 있다.(안전하다는 얘기다. 안좋게 보면 구조적인 제약이 있고, 성능이 이전 언어들에 비해 떨어진다.)
C++은 계속해서 업데이트되고 있으나, 기본적으로 다중상속을 지원하기 때문에 인터페이스와 유사한 기능을 구현할 수 있다. (아래 예시 참고)
그러므로 따로 도입을 하지 않았다고 볼 수 있다.
다중상속의 문제점. 특히 다이아몬드 문제가 생기지 않도록 유의해야한다.
(@@TODO 다중상속의 문제점 링크)
---
언리얼 인터페이스 특징
- 다형성 제공: 다양한 클래스가 동일한 함수나 동작을 구현하도록 하여 코드의 일관성을 유지하고 재사용성을 높여줌.
- 유연성: 객체 간의 의존성을 낮추고, 클래스들이 독립적으로 동작할 수 있도록 설계.
- 리플렉션 시스템(런타임에 객체의 타입 정보를 확인하거나 특정 기능을 호출하는 시스템)과의 통합
- 확장성: 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있어 유지보수와 확장이 용이.
Unreal Interface를 선택하여 생성하면 아래와 같은 코드가 생성된다.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMyInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class UNREALINTERFACE_API IMyInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
};
언리얼 엔진에서 인터페이스는 두 가지 클래스를 생성한다.
- U타입 클래스: 리플렉션 시스템에서 인터페이스 구현 여부를 확인하는 용도로 사용
- I타입 클래스: 실제 인터페이스를 정의하며, 가상 함수로 구현해야 할 행동을 명시
언리얼에서 자동으로 생성해주는 헤더파일의 주석을 보면
// This class does not need to be modified.
UMyInterface 클래스는 수정할 필요가 없다.
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
IMyInterface 클래스에 인터페이스 함수를 추가해라. 이 클래스는 이 인터페이스를 구현하기 위해 상속될 클래스다.
IMyInterface클래스에 구현을 하면 된다.
예를들어 캐릭터들의 스킬을 정의한다 치면
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMyInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class UNREALINTERFACE_API IMyInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual void Passive() = 0;
virtual void Attack() = 0;
virtual void Skill1() = 0;
virtual void Skill2() = 0;
virtual void Skill3() = 0;
virtual void Ult() = 0;
};
위와 같이 순수 가상함수를 만든다.
순수 가상함수가 아닌 객체는 인터페이스.cpp에 정의할 수 있다. 실제로 .cpp파일에 아래와 같이 주석으로 적혀있다.
//Add default functionality here for any IMyInterface functions that are not pure virtual.
순수 가상이 아닌 모든 IMyInterface 함수에 대한 기본 기능을 여기에 추가해라.
그리고 인터페이스를 상속받는 객체에 구현을 하면 된다.
PlayablePlayer.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyInterface.h"
#include "PlayablePlayer.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UPlayablePlayer : public UObject, public IMyInterface
{
GENERATED_BODY()
public:
// Inherited via IMyInterface
void Passive() override;
void Attack() override;
void Skill1() override;
void Skill2() override;
void Skill3() override;
void Ult() override;
};
PlayablePlayer.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayablePlayer.h"
void UPlayablePlayer::Passive()
{
//todo passive
}
void UPlayablePlayer::Attack()
{
//todo attack
}
void UPlayablePlayer::Skill1()
{
//todo skill1
}
void UPlayablePlayer::Skill2()
{
//todo skill2
}
void UPlayablePlayer::Skill3()
{
//todo skill3
}
void UPlayablePlayer::Ult()
{
//todo ultimate
}
인터페이스 장점
- 표준화
- 선언부의 함수를 만들지 않으면 컴파일이 되지 않기 때문에 모든 개발자가 동일한 방식으로 구현하도록 강제할 수 있다.
- 코드의 일관성이 유지되고 정형화 된다.
- 다형성 지원
- 다양한 클래스가 동일한 메소드를 구현함으로 다형성을 실현한다.
- 느슨한 결합
- 클래스간의 직접 의존성(IS-A, HAS-A)을 줄이고, 간접적인 관계를 유지해 코드의 유지보수가 편하다.
- 독립적인 프로그래밍 가능
- 클래스 선언과 구현을 분리시켰기 때문이다.
언리얼 엔진에서 인터페이스의 장점
- Blueprintable 속성을 추가하면 블루프린트에서도 인터페이스를 활용할 수 있어 비프로그래머도 쉽게 사용할 수 있다.
- 런타임 타입 확인이 가능하다. 아래는 런타임에 특정 객체가 해당 인터페이스를 구현했는지 확인하는 코드
bool bImplements = MyObject->GetClass()->ImplementsInterface(UMyInterface::StaticClass());
인터페이스 단점
- 관리가 어렵다.
- 새로 메서드를 추가하면 인터페이스를 구현한 모든 클래스에서 컴파일 에러가 발생하기 때문에 모두 변경을 해야 한다.
- 자기 자신을 객체화 할 수 없으며, 멤버변수를 가질 수 없다.
- 설계가 복잡하다.
- 잘못 설계하거나 남용하면 코드가 복잡해지고 구현체간의 의존성이 증가하여 인터페이스의 장점을 살릴 수 없다.
- 순수 가상 함수만을 정의하므로, 공통 로직을 구현할 수 없다. 동일한 로직이 여러 클래스에 반복될 가능성이 있다.
- 다형성을 구현하기위해 가상함수 테이블을 활용하게 되어, 런타임 성능에 영향을 줄 수 있다.
언리얼 엔진에서 인터페이스의 단점
- UInterface, IInterface두가지 클래스를 생성하므로 구조가 복잡하다.
- 디버깅이 어렵다
- UInterface클래스가 리플렉션을 사용하기 때문에 컴파일 시간이 증가한다.
인터페이스를 블루프린트에서 사용이 되는 것 같은데 이는 추후에 학습 후 다루도록 하겠다.
'개발일지 > Unreal&C++' 카테고리의 다른 글
[Unreal] 리플렉션(Reflection)? 작동 원리 및 장단점 (0) | 2025.04.04 |
---|---|
[Unreal] 오브젝트 그리고 CDO(Class Default Object), generated.h, GENERATED_BODY (0) | 2025.04.01 |
[C++] 가상 함수와 비 가상 함수 (0) | 2025.03.18 |
[Unreal] 게임 플레이 프레임워크 (0) | 2025.03.18 |
[Unreal] 서브 레벨 구성 및 스트리밍 레벨 로드 (0) | 2025.03.18 |