0. 강의 내용 요약
더보기
언리얼 C++ 델리게이트
1. 느슨한 결합(Loose Coupling) 이 가지는 장점
- 향후 시스템 변경 사항에 대해 손쉽게 대처할 수 있음.
2. 느슨한 결합(Loose Coupling) 으로 구현된 발행 구독 모델의 장점
- 클래스는 자신이 해야 할 작업에만 집중할 수 있음.
- 외부에서 발생한 변경 사항에 대해 영향받지 않음.
- 자신의 기능을 확장하더라도 다른 모듈에 영향을 주지 않음.
3. 언리얼 C++ 의 델리게이트의 선언방법과 활용
- 몇 개의 인자를 가지는가?
- 어떤 방식으로 동작하는가? (MULTICAST 사용 유무 결정)
- 언리얼 에디터와 함께 연동할 것인가? (DYNAMIC 사용 유무 결정)
- 이를 조합해 적합한 매크로 선택
데이터 기반의 디자인 패턴을 설계할 때 유용하게 사용
1. 실습 코드
1.1 MyGameInstance
더보기
1. 헤더 파일
// 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 UNREALDELEGATE_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UMyGameInstance();
virtual void Init() override;
private:
UPROPERTY()
TObjectPtr<class UCourseInfo> CourseInfo;
UPROPERTY() // 언리얼 엔진이 관리할 수 있도록 매크로 지정
FString SchoolName; // 이제 언리얼 엔진이 파아갛고 관리할 수 있음, 리플렉션 시스템을 사용해서 이 정보를 런타임이든 컴파일 타임이든 언제든지 가져올 수 있게됨
// 기본값을 주고 싶다면 생성자에 기본값을 지정해주면 됨
};
2. cpp 파일
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h" // 이 헤더는 가장 위에 있어야함.
#include "Student.h"
#include "Teacher.h"
#include "Staff.h"
#include "Card.h"
#include "CourseInfo.h"
UMyGameInstance::UMyGameInstance()
{
// CDO 의 생성 시점은 대체 언제인가?
// 클래스 정보와 CDO 엔진 초기화 과정에서 생성되므로 게임 개발에서 안전하게 사용 가능
// 생성자 코드에서 Class Default Object 의 기본값을 변경하는 경우에는 에디터를 끄고 컴파일해서 다시 실행해주기~
SchoolName = TEXT("학교"); // 기본값 지정
}
void UMyGameInstance::Init() {
Super::Init();
// CourseInfo 를 CDO 안에서 생성할수도 있지만 이번엔 외부에서(여기서) 필요할 때 생성하도록 할 것..
CourseInfo = NewObject<UCourseInfo>(this); // outer 를 myGameInstance 로 설정 => CourseInfo 는 MyGameInstance 의 서브 오브젝트가 됨.
UE_LOG(LogTemp, Log, TEXT("========================"));
UStudent* Student1 = NewObject<UStudent>(); // 이 경우 이 오브젝트는 자동으로 소멸되므로 굳이 outer 설정 안 해줘도 됨.
Student1->SetName(TEXT("학생1"));
UStudent* Student2 = NewObject<UStudent>(); // 이 경우 이 오브젝트는 자동으로 소멸되므로 굳이 outer 설정 안 해줘도 됨.
Student2->SetName(TEXT("학생2"));
UStudent* Student3 = NewObject<UStudent>(); // 이 경우 이 오브젝트는 자동으로 소멸되므로 굳이 outer 설정 안 해줘도 됨.
Student3->SetName(TEXT("학생3"));
CourseInfo->OnChanged.AddUObject(Student1, &UStudent::GetNotification); // 연결!
CourseInfo->OnChanged.AddUObject(Student2, &UStudent::GetNotification);
CourseInfo->OnChanged.AddUObject(Student3, &UStudent::GetNotification);
// 이제 CourseInfo 변경
CourseInfo->ChangeCourseInfo(SchoolName, TEXT("변경된 학사 정보"));
UE_LOG(LogTemp, Log, TEXT("========================"));
}
1.2 CourseInfo
더보기
1. 헤더 파일
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "CourseInfo.generated.h"
// 매크로 지정
// 두개의 인자를 가진 멀티캐스트 델리게이트
// 이제 이렇게 선언한 델리게이트를 멤버 변수로 선언해주면 됨
DECLARE_MULTICAST_DELEGATE_TwoParams(FCousreInfoOnChangedSignature, const FString&, const FString&);
/**
*
*/
UCLASS()
class UNREALDELEGATE_API UCourseInfo : public UObject
{
GENERATED_BODY()
public:
UCourseInfo();
// 마치 객체처럼 델리게이트 정보를 멤버 변수로 심플하게 등로갷줭
FCousreInfoOnChangedSignature OnChanged;
void ChangeCourseInfo(const FString& InSchoolName, const FString& InNewContents);
private:
UPROPERTY()
FString Contents;
};
2. cpp 파일
// Fill out your copyright notice in the Description page of Project Settings.
#include "CourseInfo.h"
UCourseInfo::UCourseInfo()
{
Contents = TEXT("기존 학사 정보");
}
void UCourseInfo::ChangeCourseInfo(const FString& InSchoolName, const FString& InNewContents)
{
// 새로운 학사 정보 들어오면 변경해줌
Contents = InNewContents;
UE_LOG(LogTemp, Log, TEXT("[CourseInfo] 학사 정보가 변경되어 알림을 발송합니다."));
OnChanged.Broadcast(InSchoolName, Contents); // OnChanged 에 연결된 모든 함수들한테 방송
}
1.3 Person
더보기
1. 헤더 파일
// 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 UNREALDELEGATE_API UPerson : public UObject
{
GENERATED_BODY()
public:
UPerson();
// forchinline 을 통해서 최대한 인라인이 되도록 선언..
FORCEINLINE const FString& GetName() const { return Name; }
FORCEINLINE void SetName(const FString& InName) { Name = InName; }
FORCEINLINE class UCard* GetCard() const { return Card; }
FORCEINLINE void SetCard(class UCard* InCard) { Card = InCard; }
protected:
UPROPERTY() // 언리얼이 관리할 수 있는 속성으로 발전시키기위해 매크로 추가
FString Name;
//// 언리얼 엔진 4버전까지 정석이었음
//// 컴포지션은 헤더로 포함하지 않고 전방선언해서 사용할수도 있음(의존성 최대한 없앨 수 있음)
//// 보통 오브젝트는 포인터로 관리
//UPROPERTY()
//class UCard* Card;
// 언리얼 엔진 5
// 원시포인터를 TObjectPtr 로 감싸줭
// 얘는 기본 자체가 포인터라 * 연산자 안 써
// 전방 선언은 그대로
TObjectPtr<class UCard> Card;
};
2. cpp 파일
// Fill out your copyright notice in the Description page of Project Settings.
#include "Person.h"
#include "Card.h"
UPerson::UPerson()
{
Name = TEXT("홍길동");
// 이 서브오브젝트의 내부 식별자(Name)를 지정하기 위해서 인자로 TEXT 를 받음(FName 타입)
Card = CreateDefaultSubobject<UCard>(TEXT("NAME_Card")); // NAME_ 이라는 접두사를 붙이면 코드를 보는 사람이 이것은 String 이 아니고 FName 이구나라고 알 수 있음
}
1.4 Student
더보기
1. 헤더 파일
// 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 UNREALDELEGATE_API UStudent : public UPerson, public ILessonInterface
{
GENERATED_BODY()
public:
UStudent();
// 인터페이스 상속했으니까 해당 인터페이스의 가상 함수를 무조건 구현해야함
virtual void DoLesson() override;
void GetNotification(const FString& School, const FString& NewCourseInfo);
};
2. cpp 파일
// Fill out your copyright notice in the Description page of Project Settings.
#include "Student.h"
#include "Card.h"
UStudent::UStudent()
{
Name = TEXT("이학생");
Card->SetCardType(ECardType::Student);
}
void UStudent::DoLesson()
{
// LessonInterface 의 DoLesson 함수는 Super 로 가져올 수 없음
// 얘의 Supter 는 Person 이기 때문..
ILessonInterface::DoLesson(); // 어쩔 수 없이 직접 ILessonInterface 써줘야함
UE_LOG(LogTemp, Log, TEXT("%s님은 공부합니다."), *Name);
}
void UStudent::GetNotification(const FString& School, const FString& NewCourseInfo)
{
UE_LOG(LogTemp, Log, TEXT("[Student] %s님이 %s로부터 받은 메시지 : %s"), *Name, *School, *NewCourseInfo);
}