게임 개발/언리얼 C++

언리얼C++ - 인터페이스(Interface)

싹난 감자 2024. 11. 14. 21:15

언리얼 C++ 인터페이스

인터페이스:

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

언리얼 엔진에서 게임 콘텐츠를 구성하는 오브젝트의 설계 예시

  • 월드에 배치되는 모든 오브젝트. (움직이지 않은 오브젝트를 포함(Actor))
  • 움직이는 오브젝트(Pawn)
  • 길찾기 시스템을 반드시 사용하면서 움직이는 오브젝트
    • INavAgentInterface 인터페이스를 구현한 Pawn

 

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

  • 인터페이스를 생성하면 두 개의 클래스가 생성됨
    • U로 시작하는 타입 클래스
    • I로 시작하는 인터페이스 클래스
  • 객체를 설계할 때는 I 인터페이스 클래스를 사용
    • U타입 클래스 정보는 런타임에서 인터페이스 구현 여부를 파악하는 용도
    • 실제로 U타입 클래스에서 작업할 일은 없음
    • 인터페이스에 관련된 구성 및 구현은 I 인터페이스 클래스에서 진행

  • 언리얼 C++ 인터페이스 특징
    • 추상 타입으로만 선언할 수 있는 Java, C#과 달리 인터페이스에서도 구현이 가능
    • 언리얼이 C++의 클래스를 사용해 인터페이스를 구현했기 때문에 추상 타입으로 강제할 방법이 없음

Interface 실습 예제

소스 코드를 바로 붙여넣기해서 코드를 가져올 수 있는데

VS에서 인식할 수 있도록 리프레시 해주어야함

그 후 리로드 해주면 솔루션 탐색기에서 정상적으로 확인 가능

하지만 그대로 바로 쓸 수는 없음

OBJECTREFLECTION_API가 정의되어 있지 않기 때문에 에러 발생

GameModeBase.h의 키워드를 복사해서 바꿔준 뒤

헤더 파일을 변경했기 때문에 에디터를 종료하고 컴파일해주면 됨

void UMyGameInstance::Init()
{
	Super::Init(); //원래 로직 실행

	UE_LOG(LogTemp, Log, TEXT("======================================"));
	//UPerson 부모 객체 포인터의 배열
	TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>() };
	for (const auto Person : Persons) //포인터니까 그냥 auto로
	{
		UE_LOG(LogTemp, Log, TEXT("구성원 이름: %s"), *Person->GetName());
	}
	UE_LOG(LogTemp, Log, TEXT("======================================"));
}

MyGameInstance.h

 

인터페이스 생성

Common Classes의 제일 아래 Unreal Interface

 

UINTERFACE 매그로와 U로 시작하는 클래스

인터페이스에 관련된 정보를 기록하기 위해 자동으로 생성된 클래스

타입 정보를 보관하기 위한 클래스로 따로 손 대지 않아도 됨

실제로 구현할 인터페이스 클래스 부분

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class ULessonInterface : public UInterface
{
	GENERATED_BODY()
};	

/**
 * 
 */
class UNREALINTERFACE_API ILessonInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	//가상 함수로 만들어야 상속받는 클래스들이 구현
	//abstract 가상함수로 선언하면 해당 인터페이스를 상속받는 클래스들은 반드시 구현해야함
	virtual void DoLesson() 
	{
		UE_LOG(LogTemp, Log, TEXT("수업에 입장합니다."));
	};
	//abstract 상태로 두는 것이 좋지만 인터페이스 내부에 구현할 수 있다.
	//더 이상 추상 클래스가 아니기 때문에 하위 클래스에서 구현하지 않아도 컴파일 에러가 발생하지 않음
	//언리얼 내부에서도 이런 것들을 활용한 코드가 종종 있음
};

LessonInterface.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Person.h"
#include "LessonInterface.h"
#include "Student.generated.h"

/**
 * 
 */
UCLASS()
class UNREALINTERFACE_API UStudent : public UPerson, public ILessonInterface
{
	GENERATED_BODY()

public:
	UStudent();

	virtual void DoLesson() override; //구현하지 않으면 컴파일 에러가 발생해 구현을 강제

};

Student.h

// Fill out your copyright notice in the Description page of Project Settings.

#include "Student.h"

UStudent::UStudent()
{
    Name = TEXT("이학생");
}

void UStudent::DoLesson()
{
    //상위 클래스가 ILessonInterface가 아닌 Person으로 되어 있기 때문에 Super를 사용할 수 없음
    //단일 상속만 지원
    ILessonInterface::DoLesson(); 
    UE_LOG(LogTemp, Log, TEXT("%s님은 공부합니다."), *Name);
}

Student.cpp

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Person.h"
#include "LessonInterface.h"
#include "Teacher.generated.h"

/**
 * 
 */
UCLASS()
class UNREALINTERFACE_API UTeacher : public UPerson, public ILessonInterface
{
	GENERATED_BODY()

public:
	UTeacher();
	
	virtual void DoLesson() override;
};

Teacher.h

// Fill out your copyright notice in the Description page of Project Settings.

#include "Teacher.h"

UTeacher::UTeacher()
{
    Name = TEXT("이선생");
}

void UTeacher::DoLesson()
{
    ILessonInterface::DoLesson();
    UE_LOG(LogTemp, Log, TEXT("%s님은 가르칩니다."), *Name);
}

Teacher.cpp

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Person.h"
#include "Staff.generated.h"

/**
 * 
 */
UCLASS()
class UNREALINTERFACE_API UStaff : public UPerson
{
	GENERATED_BODY()
	
public:
	UStaff();

	virtual void DoLesson() override;
};\\

Staff.h

// Fill out your copyright notice in the Description page of Project Settings.

#include "Staff.h"

UStaff::UStaff()
{
    Name = TEXT("이직원");
}

Staff.cpp

 

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyGameInstance.h"
#include "Person.h"
#include "Student.h"
#include "Teacher.h"
#include "Staff.h"

UMyGameInstance::UMyGameInstance() //생성자
{
	SchoolName = TEXT("기본학교"); //CDO 기본값 설정
}

void UMyGameInstance::Init()
{
	Super::Init(); //원래 로직 실행

	UE_LOG(LogTemp, Log, TEXT("======================================"));
	//UPerson 부모 객체 포인터의 배열
	TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>() };
	for (const auto Person : Persons) //포인터니까 그냥 auto로
	{
		UE_LOG(LogTemp, Log, TEXT("구성원 이름: %s"), *Person->GetName());
	}
	UE_LOG(LogTemp, Log, TEXT("======================================"));

	//구성원이 가지고 있는 모든 DoLesson 함수를 호출할 것
	//그 중 LessonInterface를 상속받은 클래스만 찾아야함
	//casting을 유용하게 사용할 수 있다
	for (const auto Person : Persons)
	{
		//언리얼은 형변환을 안전하게 할 수 있음
		//형변환에 실패하면 null을 반환해 구현했는지 안했는지 알 수 있음
		ILessonInterface* LessonInterface = Cast<ILessonInterface>(Person);
		if (LessonInterface)
		{
			UE_LOG(LogTemp, Log, TEXT("%s님은 수업에 참여할 수 있습니다."), *Person->GetName());
		}
		else
		{
			UE_LOG(LogTemp, Log, TEXT("%s님은 수업에 참여할 수 없습니다."), * Person->GetName());
		}
		UE_LOG(LogTemp, Log, TEXT("======================================"));
	}
}

MyGameInstance.cpp

 

인터페이스를 상속받은 Student와 Teacher는 수업에 참여할 수 있음

인터페이스에서 구현한 DoLesson을 실행한 뒤

각 클래스가 가진 DoLesson을 실행

Staff는 인터페이스를 상속받지 않았기 때문에 DoLesson을 실행하지 못함


정리

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

인터페이스를 사용하면 클래스가 수행해야 할 의무를 명시적으로 지정할 수 있어 좋은 객체 설계를 만드는데 도움을 줄 수 있다.

 

 


이득우의 언리얼 프로그래밍 Part1 | 인프런

https://www.inflearn.com/course/%EC%9D%B4%EB%93%9D%EC%9A%B0-%EC%96%B8%EB%A6%AC%EC%96%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-part-1/dashboard

 

이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해 강의 | 이득우 - 인프런

이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 엔진 프로그래머라면 게임 개발전에 반드시 알아야 하는 언리얼 C++ 기초에 대해 알려드립니다., [사진] 언

www.inflearn.com