SLATE_ATTRIBUTE 와 TAttribute 에 대해서 알아보겠습니다
먼저 언리얼 내부를 보다보면 SLATE_ATTRIBUTE 라는 것을 볼 수있다 그런데 이 매크로를 보면 다음과 같습니다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #define SLATE_ATTRIBUTE( AttrType, AttrName ) \ TAttribute< AttrType > _##AttrName; \ WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \ { \ _##AttrName = InAttribute; \ return this->Me(); \ } \ \ /* Bind attribute with delegate to a global function * NOTE: We use a template here to avoid 'typename' issues when hosting attributes inside templated classes */ \ template< typename StaticFuncPtr > \ WidgetArgsType& AttrName##_Static( StaticFuncPtr InFunc ) \ { \ _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc ) ); \ return this->Me(); \ } \ template< typename Var1Type > \ WidgetArgsType& AttrName##_Static( typename TAttribute< AttrType >::FGetter::template TStaticDelegate_OneVar< Var1Type >::FFuncPtr InFunc, Var1Type Var1 ) \ { \ _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc, Var1 ) ); \ return this->Me(); \ } \ | cs |
이 안에 TAttribute 라는 변수를 생성하고 있습니다
그런데 언뜻 보면 Slate_Attribute 매크로에서 이 TAttribute 타입 변수에 별의별 타입을 다 담는것 처럼 보여서 이것의 정체가 무엇인지
혼동 할 수가 있는데 이것은 함수와 인자를 담는 델리게이트의 역할을 같이 하고 있습니다
또한 델리게이트를 생성하는 방법은 다양합니다 함수를 델리게이트에 담는 방법, reference count 가 있는 shared 포인터와 연관된
델리게이트 등 다양하게 델리게이트와 연관 될 수 있게 설계 되어 있습니다
example..
1 2 3 4 5 6 | template< class UserClass, typename Var1Type > \ WidgetArgsType& AttrName( TSharedRef< UserClass > InUserObjectRef, typename TAttribute< AttrType >::FGetter::template TSPMethodDelegate_OneVar_Const< UserClass, Var1Type >::FMethodPtr InFunc, Var1Type Var1 ) \ { \ _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateSP( InUserObjectRef, InFunc, Var1 ) ); \ return this->Me(); \ } \ | cs |
정리하자면
언리얼 내부에 TAttribute 라는 template 클래스를 볼 수 가 있는데
예를 들어 TAttribute<bool> 해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고,
또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있습니다
변수 타입의 확장형이라고 볼 수 있습니다 (확장이란 말이 약간 오해의 소지가 있을 수 있지만 코드를 보면 무슨 의미인지 아실 수 있을 겁니다)
SLATE_ATTRIBUTE 는 곧 변수와 델리게이트를 갖는 TAttribute 를 만들고 생성시 지정한 AttrName 에 델리게이트를 담는 함수를
define 한 매크로이다
Tip :
또 매크로가 있는 특성상 지정한 이름에 뒤에 _Static 등을 붙여 함수로 만들어 치환 하는 경우가 많아서 그냥 소스를 보다가 갑자기
듣보 보도 못한 함수나 변수가 나오는게 이러한 이유때문이다, 자세한건 아래 소스를 참고하면 됩니다
아래 분석 구문을 보면.. 중간 중간 주석을 달아 놓았습니다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | #define FUNC_DECLARE_DELEGATE( DelegateName, ... ) \ typedef TBaseDelegate<__VA_ARGS__> DelegateName; // 이것이 delegate 생성기, 함수 등을 bind 할 수 있다 #define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType ) /*예로 아래 구문 중 이런 구문이 있다 static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr ) { const bool bExplicitConstructor = true; return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor ); //이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정 } FGetter 는 아래 정의된 델리게이트인데 , 델리게이트 FGetter 를 통해서 CreateStatic 함수로 InFuncPtr이라는 수를 바인딩해서 만든 델리게이트를 리턴해 TAttribute 를 생성한다 */ //예를 들어 TAttribute<bool> 해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고, //또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있다 /** * Attribute object */ template< typename ObjectType > class TAttribute { public: /** * Attribute 'getter' delegate * * ObjectType GetValue() const * * @return The attribute's value */ DECLARE_DELEGATE_RetVal( ObjectType, FGetter ); /** Default constructor. */ TAttribute() : Value() // NOTE: Potentially uninitialized for atomics!! , bIsSet(false) , Getter() { } /** * Construct implicitly from an initial value * * @param InInitialValue The value for this attribute */ template< typename OtherType > TAttribute( const OtherType& InInitialValue ) : Value( InInitialValue ) , bIsSet(true) , Getter() { } ........ 중략 ........ /** * Static: Creates an attribute that's pre-bound to the specified 'getter' delegate * * @param InGetter Delegate to bind */ static TAttribute Create( const FGetter& InGetter ) { const bool bExplicitConstructor = true; return TAttribute( InGetter, bExplicitConstructor ); } /** * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand. * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound * function will always be called to generate the value. * * @param InFuncPtr Member function to bind. The function's structure (return value, arguments, etc) must match IBoundAttributeDelegate's definition. */ static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr ) { //이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정 const bool bExplicitConstructor = true; return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor ); } /** * Sets the attribute's value * * @param InNewValue The value to set the attribute to */ template< typename OtherType > void Set( const OtherType& InNewValue ) { Getter.Unbind(); Value = InNewValue; bIsSet = true; } /** Was this TAttribute ever assigned? */ bool IsSet() const { return bIsSet; } /** * Binds an arbitrary function that will be called to generate this attribute's value on demand. * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound * function will always be called to generate the value. * * @param InUserObject Instance of the class that contains the member function you want to bind. * @param InFunctionName Member function name to bind. */ template< class SourceType > void BindUFunction( SourceType* InUserObject, const FName& InFunctionName ) { bIsSet = true; Getter.BindUFunction(InUserObject, InFunctionName); } /** * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand. * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound * function will always be called to generate the value. * * @param InUserObject Instance of the class that contains the member function you want to bind. * @param InFunctionName Member function name to bind. */ template< class SourceType > static TAttribute< ObjectType > Create(SourceType* InUserObject, const FName& InFunctionName) { TAttribute< ObjectType > Attrib; Attrib.BindUFunction<SourceType>(InUserObject, InFunctionName); return Attrib; } ........ 중략 ........ /** * Is this attribute identical to another TAttribute. * * @param InOther The other attribute to compare with. * @return true if the attributes are identical, false otherwise. */ bool IdenticalTo(const TAttribute& InOther) const { const bool bIsBound = IsBound(); if ( bIsBound == InOther.IsBound() ) { if ( bIsBound ) { return Getter.GetHandle() == InOther.Getter.GetHandle(); } else { return Value == InOther.Value; } } return false; } ........ 중략 ........ private: /** Special explicit constructor for TAttribute::Create() */ TAttribute( const FGetter& InGetter, bool bExplicitConstructor ) : Value() , bIsSet( true ) , Getter(InGetter) { } // We declare ourselves as a friend (templated using OtherType) so we can access members as needed template< class OtherType > friend class TAttribute; /** Current value. Mutable so that we can cache the value locally when using a bound Getter (allows const ref return value.) */ mutable ObjectType Value; //이건 이전에 이전에 지정한 타입에 대한 변수이고 /** true when this attribute was explicitly set by a consumer, false when the attribute's value is set to the default*/ bool bIsSet; /** Bound member function for this attribute (may be NULL if no function is bound.) When set, all attempts to read the attribute's value will instead call this delegate to generate the value. */ /** Our attribute's 'getter' delegate */ FGetter Getter; //그래서 이게 delegate 가 됨 }; ---------------------------------------------------------------------------------------- /** * Use this macro to add a attribute to the declaration of your widget. * An attribute can be a value or a function. */ #define SLATE_ATTRIBUTE( AttrType, AttrName ) \ TAttribute< AttrType > _##AttrName; \ WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \ { \ _##AttrName = InAttribute; \ return this->Me(); \ } \ \ ...... //some example.. SLATE_ATTRIBUTE( FText, ToolTipText ) SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor ) SLATE_ATTRIBUTE( bool, IsEnabled ) SLATE_ATTRIBUTE( EVisibility, Visibility ) SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform ) SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot ) | cs |
그렇다면 왜 Slate_ATTRIBUTE 를 만들언 놓은 것인가 왜 델리게이트와 엮이는 매크로를 만들어 놓은 것인가..?
다음 구문을 한번 봅시다
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 /* Base class for named arguments. Provides settings necessary for all widgets. */template<typename WidgetType>struct TSlateBaseNamedArgs{typedef typename WidgetType::FArguments WidgetArgsType;TSlateBaseNamedArgs(): _ToolTipText(), _ToolTip(), _Cursor( TOptional<EMouseCursor::Type>() ), _IsEnabled( true ), _Visibility( EVisibility::Visible ), _RenderTransform( ), _RenderTransformPivot( FVector2D::ZeroVector ), _ForceVolatile( false ){}/** Used by the named argument pattern as a safe way to 'return *this' for call-chaining purposes. */WidgetArgsType& Me(){return *(static_cast<WidgetArgsType*>(this));}/** Add metadata to this widget. */WidgetArgsType& AddMetaData(TSharedRef<ISlateMetaData> InMetaData){MetaData.Add(InMetaData);return Me();}/** Add metadata to this widget - convenience method - 1 argument */template<typename MetaDataType, typename Arg0Type>WidgetArgsType& AddMetaData(Arg0Type InArg0){MetaData.Add(MakeShareable(new MetaDataType(InArg0)));return Me();}/** Add metadata to this widget - convenience method - 2 arguments */template<typename MetaDataType, typename Arg0Type, typename Arg1Type>WidgetArgsType& AddMetaData(Arg0Type InArg0, Arg1Type InArg1){MetaData.Add(MakeShareable(new MetaDataType(InArg0, InArg1)));return Me();}//이 아래 부분을 보면 됨..SLATE_ATTRIBUTE( FText, ToolTipText )SLATE_ARGUMENT( TSharedPtr<IToolTip>, ToolTip )SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor )SLATE_ATTRIBUTE( bool, IsEnabled )SLATE_ATTRIBUTE( EVisibility, Visibility )SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform )SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot )SLATE_ARGUMENT( FName, Tag )SLATE_ARGUMENT( bool, ForceVolatile )TArray<TSharedRef<ISlateMetaData>> MetaData;};cs
여기에서 보면 SLATE_ATTRIBUTE 를 뭉태이로 쓴 것이 보이는데 변수 명 들을 보면 델리게이트와 역일 법한 것들로 보이죠?즉 SLATE_ATTRIBUTE 로 변수를 만든 다는 것은 나중에 이 변수를 통해서 함수를 바인딩 하고 원하는 시점에 바인딩 된 함수들을 호출하기 위해존재하는 것 정도로 그 의도를 추측 할 수가 있습니다이것들을 묶고 있는 클래스 명 (TSlateBaseNamedArgs) 또한 이와 유사한 성향의 연관성을 띈 네이밍으로 볼 수 있습니다3dmpengines.tistory.com참고 : 언리얼엔진4 소스URL : https://www.unrealengine.com
반응형
'게임엔진(GameEngine) > Unreal4' 카테고리의 다른 글
Calling Blueprints in the Editor (0) | 2020.04.27 |
---|---|
비주얼 어시스트 언리얼엔진 옵션 (0) | 2018.09.26 |
Unreal Engine4 Build process (0) | 2017.05.13 |
BlueprintCallable, BlueprintNativeEvent 를 동시에 포함 (0) | 2017.04.19 |
언리얼 안드로이드 빌드 오류 (0) | 2017.04.06 |