UE5

Delegate 사용방법

게임 개발 2023. 12. 5. 11:11

 

델리게이트란?
 C++ 오브젝트 상의 멤버 함수를 가리키고 실행시키는 데이터 유형이다.

 

 

 

 델리게이트로 C++ 오브젝트 상의 멤버 함수 호출을 일반적이고 유형적으로 안전한 방식으로 할 수 있다.

델리게이트를 사용하여 임의의 오브젝트의 멤버 함수에 동적으로 바인딩시킬 수 있으며,

그런 다음 그 오브젝트에서 함수를 호출할 수 있다.

호출하는 곳에서 오브젝트의 유형을 몰라도 무관하다.

 

델리게이트 오브젝트는 복사시에도 안전성이 보장된다.

델리게이트는 값으로의 전달이 가능하나,

heap 영역에서 메모리를 할당하기 때문에 보통 추천할 만 하지는 않다.

가급적이면 델리게이트는 항상 참조로 전달하는 것이 좋다.

 

 

델리게이트는 싱글 - 캐스트 (형변환)와 멀티-캐스트 모두 지원되며,

디스크에 안전하게 Serialize 시킬 수 있는 "다이내믹"델리게이트도 마찬가지이다.

 

++ Plus ++

Serialize 란?

 

객체를 데이터스트림으로 만드는 것이다.

즉 객체에 저장된 데이터를 스트림에 쓰기 위하여 연속적인 데이터를 변환하는

직렬화를 하는 것이다.

데이터 스트로지 문맥에서 데이터 구조나 오브젝트 상태를 동일하거나,

다른 컴퓨터 환경에 저장하여 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다.

 

오브젝트를 직렬화하는 과정은 오브젝트를 마샬링한다고 한다.

반대로, 일련의 바이트로부터 데이터 구조를 추출하는 일은 역직렬화 또는 deserialization이라고 한다.

 

++ 마샬링이란?

 

컴퓨터 과학에서 마셜링이란 한 객체의 메모리에서 표현방식을 저장 또는 전송에 적합한

다른 데이터 형식으로 변환하는 과정이다.

또한 이는 데이터를 컴퓨터 프로그램의 서로 다른 부분 간에 혹은

한 프로그램에서 다른 프로그램으로 이동해야 할 때도 사용된다.

마셜링은 직렬화와 유사하며, 한 오브젝트로 멀리 떨어진 오브젝트와 통신하기 위해 사용된다.

이는 복잡한 통신을 단순화 하기 위해, 기본 요소 대신 통신을 위한 맞춤형 오브젝트를 사용한다.

 

마셜링의 용도는

프로세스간 또는 스레드간 데이터 전송에 필요한 원격 프로시저 호출 매커니즘의 구현에 사용된다.

 

++

 

++ ++

 

  • 싱글-캐스트
  •  
    • 이벤트
  • [멀티-캐스트](programming-and-scripting/programming-language-implementation/delegates-in-unreal-engine/multicast-delegates-in-unreal-engine)
  • [다이내믹 (UObject, Serialize 가능)](programming-and-scripting/programming-language-implementation/delegates-in-unreal-engine/dynamic-delegates-in-unreal-engine)

 

 

 

델리게이트 선언하기

 

델리게이트의 선언은 제공되어 있는 선언 매크로 중 하나를 사용하여 이루어진다.

사용되는 매크로는 델리게이트에 바인딩되는 함수의 시그니처에 따라 결정된다.

시스템에서는 델리게이트 유형을 선언해 낼 수 있는 범용 함수 시그니처의 다양한 조합을 미리 정의,

이를 통해 반환값과 파라미터에 대한 유형 이름을 필요한 곳에 채운다.

현재 다음 항목들의 조합에 대해서 델리게이트 시그니처를 지원하고 있다.

 

 

  • 값을 반환하는 함수
  • "페이로드"(payload, 유상) 변수 4 개 까지
  • 함수 파라미터 8 개 까지
  • 'const' 로 선언된 함수

 

이 표에서 델리게이트 선언에 사용할 매크로를 찾을 수 있다.

함수 시그너처 선언 매크로
void Function() DECLARE_DELEGATE( DelegateName )
void Function( <Param1> ) DECLARE_DELEGATE( DelegateName, Param1Type )
void Function( <Param1> , <Param2> ) DECLARE_DELEGATE_TwoParams ( DelegateName, 
Param1Type, Param2Type )
void Function ( <Param1> , <Param2>, ... ) DECLARE_DELEGATE_<Num>Params ( DelegateName,
Param1Type, Param2Type, ... )
<RetVal> Function() DECLATE_DELEGATE_RetVal
( RetValType, DelegateName )
<RetVal> Function( <Param1> ) DECLARE_DELEGATE_RetVal_OneRaram 
( RetValType, DelegateName, Param1Type )
<RetVal> Function ( <Param1> , <Param2> ) DECLARE_DELEGATE_RetVal_TwoParams
( RetValType, DelegateName, Param1Type, Param2Type )
<RetVal> Function ( <Param1>, <Param2>, ... ) DECLARE_DELEGATE_RetVal_<Num>Params
( RetValType, DelegateName, Param1Type, Param2Type,
... )

 

 

멀티 - 캐스트, 다이내믹, 래핑된(wrapped) 델리게이트에 대한 변종 매크로도 제공된다:

 

  • DECLARE_MULTICAST_DELEGATE...
  • DECLARE_DYNAMIC_DELEGATE...
  • DECLARE_DYNAMIC_MULTICAST_DELEGATE...
  • DECLARE_DYNAMIC_DELEGATE...
  • DECLARE_DYNAMIC_MULTICAST_DELEGATE...

 

델리게이트 시그너처 선언은 글로벌 영역에, 네임스페이스 안이나 심지어 (함수 본문을 제외한)

클래스 선언부 안에까지도 존재가능하다.

 

이러한 델리게이트 유형 선언에 있어서의 자세한 정보는

[](programming-and-scripting/programming-language-implementation/delegates-in-unreal-engine/dynamic-delegates-in-unreal-engine)

, 

[](programming-and-scripting/programming-language-implementation/delegates-in-unreal-engine/multicast-delegates-in-unreal-engine)

 

페이지를참고하도록 하기.

 

 

델리게이트 바인딩하기

 

델리게이트 시스템은 특정 유형의 오브젝트를 이해하고 있으며,

이러한 오브젝트를 사용할 때는 추가적으로 사용할 수 있는 기능이 있다.

UObject 나 공유 포인터 클래스 멤버에 델리게이트를 바인딩하는 경우,

델리게이트 시스템은 그 오브젝트에 대한 약한 레퍼런스를 유지할 수 있어,

델리게이트 치하에서 오브젝트가 소멸된 경우 IsBound() 나 ExecuteIfBound() 함수를 호출하여 처리해 줄 수 있다.

참고로 여러가지 지원되는 오브젝트 유형에 대해서는 특수한 바인딩 문법이 사용된다.

 

함수 설명
Bind() 기존 델리게이트 오브젝트에 바인딩한다.
BindStatic() raw C++ 포인터 글로벌 함수 델리게이트를 바인딩한다.
BindRaw() raw C++ 포인터 델리게이트에 바인딩한다.
원시 포인터는 어떤 종류의 레퍼런스도 사용하지 않아,
만약 오브젝트가 델리게이트 치하에서 삭제된 호출하기가 안전하지 않을 수도 있다.
Execute() 호출시에는 조심해야한다.
BindSP() 공유 포인터-기반 멤버 함수 델리게이트에 바인딩한다.
공유 포인터 델리게이트는 오브젝트로의 약한 레퍼런스를 유지한다.
ExecuteIfBound() 로 호출할 수 있다.
BindUObject() UObject 기반 멤버 함수 델리게이트를 바인딩합니다.
UObject 델리게이트는 오브젝트로의 약한 레퍼런스를 유지한다.
ExecuteIfBound() 로 호출할 수 있습니다.
UnBind() 이 델리게이트 바인딩을 해제합니다.

 

이 함수들의 변종, 인수, 구현을 확인하시려면(..\UE4\Engine\Source\Runtime\Core\Public\Templates\ 에 있는)

DelegateSignatureImp.inl 파일을 확인해 주시기 바란다.

 

페이로드 데이터

 

델리게이트에 바인딩할 때, 페이로드 데이터를 같이 전해줄 수 있다.

페이로드 데이터란 바인딩된 함수를 불러낼 (invoke) 때 직접 전해지는 임의의 변수를 말한다.

바인딩 시간에 델리게이트 자체적으로 파라미터를 보관할 수 있게 디니 유용한다.

("다이내믹"을 제외한) 모든 델리게이트 유형은 페이로드 변수를 자동으로 지원한다.

이 예제는 커스텀 변수 둘, 즉, bool 과 int32를 델리게이트로 전달해야한다.

그런 다음 델리게이트를 불러낼 때 이 파라미터가 바인딩된 함수에 전달된다.

여분의 변수 인수는 반드시 델리게이트 유형 파리미터 인수 이후에 받아야 한다.

 

 MyDelegate.BindRaw( &MyFunction, true, 20 );

 

 

델리게이트 실행하기

 

델리게이트에 바인딩된 함수는 델리게이트의 Execute() 함수를 호출하여 실행된다.

델리게이트를 실행하기 전 "바인딩" 되었는지 반드시 확인해야한다.

이는 코드 안전성을 도모하기 위함인데,

초기화되지 않은 상태로 접근이 가능한 반환값과 출력 파라미터가 델리게이트에 있을 수 있기 때문이다.

바인딩되지 않은 델리게에트를 실행시키면 일부 인스턴스에서 메모리에 일치하지 않는 값을 넣을 수 있기 때문이다.

델리게이트가 실행해도 안전한 지는 IsBound()를 호출하여 검사해 볼 수 있다.

또한 반환값이 없는 델리게이트에 대해서는 ExecuteIfBound()를 호출할 수 있으나,

출력 파라미터는 초기화되지 않을 수 있다는 점을 주의하자.

 

실행 함수 설명
Execute()  
ExecuteIfBound()  
IsBound()  

 

멀티 - 캐스트 델리게이트 관련 자세한 내용은

 

 

사용 예제

 

아무데서나 호출했으면 하는 메서드를 가진 클래스가 있다고 쳐 본다면,

 

    class FLogWriter
    {
        void WriteToLog( FString );
    };

 

WriteToLog 함수를 호출하려면, 해당 함수의 시그너처에 맞는 델리게이트 유형을 생성해야 한다.

그러기 위해서는 먼저 아래 매크로 중 하나를 사용하여 델리게이트를 선언해야 한다.

여기 예제에서는 단순한 델리게이트 유형이다.

    DECLARE_DELEGATE_OneParam( FStringDelegate, FString );

 

이는 'FStringDelegate' 라는 델리게이트 유형을 생성하며,

'FString' 유형 파리미터를 하나 받는다.

 

클래스에서 이 'FStringDelegate' 를 어떻게 사용하는가, 예제는 이렇다.

 

    class FMyClass
    {
        FStringDelegate WriteToLogDelegate;
    };

 

이렇게 하여 위 클래스의 임의 클래스 안에 있는 메서드로의 포인터를 담을 수 있다.

위 클래스가 이 델리게이트에 대해 아는 것이라고는, 함수 시그니처 뿐이다.

 

이제 델리게이트 할당을 위해, 단순히 델리게이트 클래스의 인스턴스를 생성하고,

그 메서드를 소유하는 클래스와 함께 템플릿 파라미터로 전해준다.

자기 오브젝트의 인스턴스와 그 메서드의 실제 함수 주소 역시도 전해줘야 한다.

그래서 여기서는 아르 'FLogWriter' 클래스의 인스턴스를 생성한 다음,

그 오브젝트 인스턴스의 'WriteToLog' 메서드에 대한 델리게이트를 생성해야한다.

 

    TSharedRef< FLogWriter > LogWriter( new FLogWriter() );

    WriteToLogDelegate.BindSP( LogWriter, &FLogWriter::WriteToLog );

 

위 예제는 클래스의 메서드에 델리게이트를 동적으로 바인딩한 내용이다.

 

BindSP의 SP 부분은 shared pointer, 공유 포인터를 뜻하는데,

공유 포인터에 소유된 오브젝트에 바인딩하고 있기 때문이다.

BindRaw(), BindUObject()처럼 다양한 오브젝트 유형 버전도 있다.

 

이제 'FLogWriter' 클래스에 대해 아무것도 모를지라도 FMyClass 를 통해 'WriteToLog' 메서드를 호출할 수 있다.

자신의 델리게이트를 호출하려면, 그냥 'Execute()' 메서드를 사용하면 된다.

 

    WriteToLogDelegate.Execute( TEXT( "델리게이트 쥑이네!" ) );

 

 

델리게이트에 함수를 바인딩하기 전 Execute() 를 호출하면 assert가 발동된다.

그런 경우를 피하기 위해 대부분 이렇게 하는 것이 좋다.

 

    WriteToLogDelegate.ExecuteIfBound( TEXT( "함수가 바인딩되었을 때만 실행!" ) );

 

 

 

참고 문서

 

https://docs.unrealengine.com/5.0/ko/delegates-and-lamba-functions-in-unreal-engine/ 

 

델리게이트

C++ 오브젝트 상의 멤버 함수를 가리키고 실행시키는 데이터 유형입니다.

docs.unrealengine.com

https://ko.wikipedia.org/wiki/%EB%A7%88%EC%83%AC%EB%A7%81_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)

'UE5' 카테고리의 다른 글

Survival - UE5 - Multiplayer의 작동  (1) 2023.12.31
GitHub에 프로젝트 저장하기  (0) 2023.12.29
언리얼 내의 포인터  (0) 2023.11.02
언리얼 게임 모드 클래스  (0) 2023.10.27
UE5 멀티스레딩과 enum을 이용한 상태 변환  (1) 2023.10.22