Unreal
4. 인터페이스
튀김족발
2023. 8. 31. 20:08
하... 야근 짱짱많다 ㅠㅠ
인터페이스
- 인터페이스란?
- 객체가 반드시 구현해야 할 생동을 지정하는데 활동되는 타입
- 다형성(Polymorphism)의 구현, 의존성이 분리(Decouple)된 설계에 유용하게 활용
- 언리얼 엔진에서 게임 콘텐츠를 구성하는 오브젝트의 설계 예시
- 월드에 배치되는 모든 오브젝트, 안 움직이는 오브젝트를 포함(Actor)
- 움직이는 오브젝트 (Pawn)
- 길찾기 시스템을 받느시 사용하면서 움직이는 오브젝트
(INavAgentInterface 인터페이스를 구현한 Pawn)
이번 예제
- 수업에 참여하는 사람과 참여하지 않는 사람의 구분
- 수업에 반드시 참여해야 하는 학교 구성원 : 학생, 선생
- 수업에 참여하지 않는 학교 구성원 : 교직원
- 수업 행동에 관련된 인터페이스 : ILessonInterface
언리얼 C++ 인터페이스 특징
- 인터페이스를 생성하면 두 개의 클래스가 생성됨
- U로 시작하는 타입 클래스
- I로 시작하는 인터페이스 클래스
- 객체를 설계할 때 I 인터페이스 클래를 사용
- U타입 클래스 정보는 런타임에서 인터페이스 구현 여부를 파악하는 용도로 사용됨.
- 실제로 U타입 클래스에서 작업할 일은 없음.
- 인터페이스에 관련된 구성 및 구현은 I인터페이스 클래스에서 진행
- C++ 인터페이스의 특징
- 추상 타입으로만 선언할 수 있는 Java, C#과 달리 언리얼은 인터페이스에도 구현이 가능함.
예제
Person
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Person.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UPerson : public UObject
{
GENERATED_BODY()
public:
UPerson();
FORCEINLINE FString& GetName() { return Name; }
FORCEINLINE void SetName(const FString& InName) { Name = InName; }
protected:
UPROPERTY()
FString Name;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Person.h"
UPerson::UPerson()
{
Name = TEXT("홍길동");
}
선생, 학생, 스태프의 상위 클래스인 Person을 선언해준다.
Teacher
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "Teacher.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UTeacher : public UPerson
{
GENERATED_BODY()
public:
UTeacher();
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Teacher.h"
UTeacher::UTeacher()
{
Name = TEXT("스앵님");
}
선생을 만들어준다.
Student
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "Student.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UStudent : public UPerson
{
GENERATED_BODY()
public:
UStudent();
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Student.h"
UStudent::UStudent()
{
Name = TEXT("유학생");
}
학생을 만들어준다.
Staff
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "Staff.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UStaff : public UPerson
{
GENERATED_BODY()
public:
UStaff();
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Staff.h"
UStaff::UStaff()
{
Name = TEXT("스태프");
}
이 전에 사용했던 학생과 선생처럼 수업에 참여하지 않는 스태프를 추가한다.
MyGameInstance
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
/**
*
*/
UCLASS()
class UNREALINTERFACE_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UMyGameInstance();
virtual void Init() override;
private:
UPROPERTY()
FString SchoolName;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Student.h"
#include "Teacher.h"
#include "Staff.h"
UMyGameInstance::UMyGameInstance()
{
SchoolName = TEXT("기본학교");
}
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("================================="));
TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>()};
for(const TArray<UPerson*>::ElementType Person : Persons)
{
UE_LOG(LogTemp, Log, TEXT("구성원 이름 : %s"), *Person->GetName());
}
UE_LOG(LogTemp, Log, TEXT("================================="));
}
TArray 배열을 사용해서 선생, 학생, 스태프를 만들어주고, 각 이름을 출력한다.
이 상태로 실행을 하면
LogTemp: =================================
LogTemp: 구성원 이름 : 학생
LogTemp: 구성원 이름 : 스앵님
LogTemp: 구성원 이름 : 스태프
LogTemp: =================================
이렇게 출력이 된다.
이제 인터페이스를 만들어 넣어보자
Interface
cpp파일과 h파일 둘다 나오지만, 여기서는 헤더 파일만 수정한다.
// 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()
public:
virtual void DoLesson()
{
UE_LOG(LogTemp, Log, TEXT("수업에 입장합니다."));
}
};
인터페이스를 추적하기 위해 UINTERFACE 매크로가 나오고 그 밑에 타입 정보를 생성하기 위한 클래스가 생성된다.
사용하려는 함수들과 기능을 구현하기 위해선 그 밑에 있는 ILessonInterface를 수정해준다.
DoLesson()을 가상함수로 만들어준다. 추상 함수로 만들어도 문제 없다. 그리고 DoLesson()을 호출하면 나오는 기본 값을 넣어준다.
이 인터페이스를 학생, 선생에만 넣어주자.
Teacher
// 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;
};
// 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);
}
Student
// 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;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Student.h"
UStudent::UStudent()
{
Name = TEXT("학생");
}
void UStudent::DoLesson()
{
ILessonInterface::DoLesson();
UE_LOG(LogTemp, Log, TEXT("%s님은 가르칩니다."), *Name);
}
이 상태로 GameInstance에서 ILeesonInterface를 호출하자.
MyGameInstance
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("================================="));
TArray<UPerson*> Persons = { NewObject<UStudent>(), NewObject<UTeacher>(), NewObject<UStaff>()};
for(const TArray<UPerson*>::ElementType Person : Persons)
{
UE_LOG(LogTemp, Log, TEXT("구성원 이름 : %s"), *Person->GetName());
}
UE_LOG(LogTemp, Log, TEXT("================================="));
for (const TArray<UPerson*>::ElementType Person : Persons)
{
ILessonInterface* LessonInterface = Cast<ILessonInterface>(Person);
if (LessonInterface != nullptr)
{
UE_LOG(LogTemp, Log, TEXT("%s님은 수업에 참여할 수 있습니다."), *Person->GetName());
LessonInterface->DoLesson();
}
else
{
UE_LOG(LogTemp, Log, TEXT("%s님은 수업에 참여할 수 없습니다."), *Person->GetName());
}
}
UE_LOG(LogTemp, Log, TEXT("================================="));
}
인터페이스 클래스가 없다면 nullptr을 반환한다.
이 상태로 실행을 하면
LogTemp: =================================
LogTemp: 구성원 이름 : 학생
LogTemp: 구성원 이름 : 스앵님
LogTemp: 구성원 이름 : 스태프
LogTemp: =================================
LogTemp: 학생님은 수업에 참여할 수 있습니다.
LogTemp: 수업에 입장합니다.
LogTemp: 학생님은 가르칩니다.
LogTemp: 스앵님님은 수업에 참여할 수 있습니다.
LogTemp: 수업에 입장합니다.
LogTemp: 스앵님님은 가르칩니다.
LogTemp: 스태프님은 수업에 참여할 수 없습니다.
LogTemp: =================================
이렇게 출력이 된다.