본문 바로가기

Development/Unreal Engine 4, 5

Slate 코드 관련

하하하 어느새 11번째 시간이 되었군요.

헤더파일 먼저 준비해야겠지요?

//class declare
class SVehicleMenuItem : public SCompoundWidget
{
public:
 SLATE_BEGIN_ARGS(SVehicleMenuItem)
 {}

 /** Weak pointer to the parent HUD base */
 SLATE_ARGUMENT(TWeakObjectPtr<AHUD>, OwnerHUD)

 /** Called when the button is clicked */
 SLATE_EVENT(FOnClicked, OnClicked)

 /** menu item text attribute */
 SLATE_ATTRIBUTE(FString, Text)

 /** menu item text transparency when item is not active, optional argument */
 SLATE_ARGUMENT(TOptional<float>, InactiveTextAlpha)

 SLATE_END_ARGS()

이번엔 클래스 선언만 합니다. 근데 앞에 뭔가 안붙어있네요?(는 사실 다른 헤더[DeclarativeSyntaxSupport.h]에서 이미 선언된 구문들입니다.)

부모 HUD에 포인터를 가져다 놨을 때, 버튼 클릭 했을 때, 메뉴 아이템에서 텍스트 불러오는거, 활성화 되지 않은 것들 텍스트 투명화 등등을 하려고 선언했네요.

 /** Needed for every widget */
 void Construct(const FArguments& InArgs);

 /** Says that we can support keyboard focus */
 virtual bool SupportsKeyboardFocus() const override { return true; }

 /** mouse button down callback */
 virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;

 /** mouse button up callback */
 virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;

 /** mouse move callback */
 virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;

 /** sets this menu item as active (selected) */
 void SetMenuItemActive(bool bIsMenuItemActive);

Construct를 정의해주는 이유는 아무래도 위젯을 만들 때 필요하기 때문이겠지요.

키보드에 포커스를 맞추는 것도 지원해주고 마우스 버튼을 눌렀을때, 안눌렀을 때도 선언해주셔야 됩니다.(메뉴 아이템들도 위젯이기때문에 마우스로 해주는게 편하겠지요) 마우스 이동 또한 되야 하니 이동 선언 해 주시고 아이템 업데이트 해야되니 SetMenuItemActive또한 선언해주시는게 좋겠지요.

protected:
 /** The delegate to execute when the button is clicked */
 FOnClicked OnClicked;

private:
 /** menu item text attribute */
 TAttribute< FString > Text;

 /** getter for menu item background color */
 FSlateColor GetButtonBgColor() const;

 /** getter for menu item text color */
 FSlateColor GetButtonTextColor() const;

 /** inactive text alpha value*/
 float InactiveTextAlpha;

 /** active item flag */
 bool bIsActiveMenuItem;

 /** Pointer to our parent HUD */
 TWeakObjectPtr<class AHUD> OwnerHUD;

 /** style for this menu item */
 const struct FVehicleMenuItemStyle *MenuItemStyle;
};

온클릭 이벤트 만들어주시고 밑에는 첫번째부터 메뉴 속성 정보를 텍스트로 나타내주려고 만든것이구요. 메뉴 항목의 배경색을 지정, 텍스트 색 지정, 텍스트의 알파값, 활성 항목 설정, 이 클래스의 부모 HUD클래스 포인터 지정, 메뉴 항목 스타일 설정 등등을 하기 위해 선언했다고 보시면 됩니다.

헤더파일은 끝이구요. 이제 C++파일입니다.(양이 얼마 안해요. 살았어요. 첫번째것만 하면 양이 아주 적어요.)

void SVehicleMenuItem::Construct(const FArguments& InArgs)
{
 MenuItemStyle = &FVehicleStyle::Get().GetWidgetStyle<FVehicleMenuItemStyle>("DefaultVehicleMenuItemStyle");

 OwnerHUD = InArgs._OwnerHUD;
 Text = InArgs._Text;
 OnClicked = InArgs._OnClicked;
 bIsActiveMenuItem = false;
 //if attribute is set, use its value, otherwise uses default
 InactiveTextAlpha = InArgs._InactiveTextAlpha.Get(1.0f);

 ChildSlot
 .VAlign(VAlign_Fill)
 .HAlign(HAlign_Fill)
 [
  SNew(SOverlay)
  + SOverlay::Slot()
  .HAlign(HAlign_Fill)
  .VAlign(VAlign_Fill)
  [
   SNew(SBox)
   .WidthOverride(374.0f)
   .HeightOverride(23.0f)
   [
    SNew(SImage)
    .Image(&MenuItemStyle->BackgroundBrush)
   ]
  ]
  + SOverlay::Slot()
  .HAlign(HAlign_Fill)
  .VAlign(VAlign_Fill)
  [
   SNew(SBox)
   .WidthOverride(374.0f)
   .HeightOverride(23.0f)
   [
    SNew(SImage)
    .ColorAndOpacity(this,&SVehicleMenuItem::GetButtonBgColor)
    .Image(&MenuItemStyle->HighlightBrush)
   ]
  ]
  +SOverlay::Slot()
  .HAlign(HAlign_Left)
  .VAlign(VAlign_Center)
  .Padding(FMargin(40.0f,0,0,0))
  [
   SNew(STextBlock)
   .TextStyle(FVehicleStyle::Get(), "VehicleGame.MenuTextStyle")
   .ColorAndOpacity(this,&SVehicleMenuItem::GetButtonTextColor)
   .Text(Text)
  ]
  
 ];
}

헤더에서 정의 했던 MenuItemStyle부터 위젯을 만들기 시작하네요. FVehicleMenuItemStyle(왠지 다음번에 이거 할 때 길거같은데...)을 불러와서 위젯스타일 기본형을 VehicleMenuItemStyle로 하네요.

그 밑에 있는 것들은 불러오기 방식이구요.(사용자가 설정한 것들로 불러오는 방식)

온클릭 이벤트를 불러와서 클릭 되면 다른 화면으로 넘어가는 역활을 만들었네요. 크기 지정한 대로 다 채우고 이미지는 불러온 스타일(맨위에서 방금 불러왔던 스타일), 슬롯을 똑같이 하나 더 만드네요.(이미지 부분만 다를 뿐.. 이미지도 사실상 SVehicleMenuItem[자기자신]이니 거의 비슷할듯. 그냥 거기에 하이라이트 하나만 덧 붙여 줬다는 것?) 슬롯을 하나 더 만들어서 이번엔 색깔을 씌우는 것을 만들어주네요.

FSlateColor SVehicleMenuItem::GetButtonTextColor() const
{
 FLinearColor ResultColor;
 if (bIsActiveMenuItem)
 {
  ResultColor = MenuItemStyle->ControlsListTableRowStyle.SelectedTextColor.GetSpecifiedColor();
 }
 else
 {
  ResultColor = MenuItemStyle->ControlsListTableRowStyle.TextColor.GetSpecifiedColor();
  ResultColor.A *= InactiveTextAlpha;
 }
 return ResultColor;
}

FVehicleStyle은 있지만 자기 자신의 컬러와 스타일은 아직 안만들어졌으니까 만들어야겠지요. 컬러부터 만들어봅시다. 컬러는 기본 컬러로 결과를 확정 짓지만 만약 이 버튼이 액티브 된다면 결과컬러를 다른 헤더(SlateColor.h)에 선언되어있는 선택된 텍스트 컬러를 설정해SpecifiedColor로 바꿔줍니다.

만약 IsActiveMenuItem상태가 아니라면 기본 텍스트 컬러로 설정하게 되어있네요.

결과컬러를 초기화 해주구요.

FSlateColor SVehicleMenuItem::GetButtonBgColor() const
{
 float BgAlpha;
 const float MinAlpha = 0.7f;
 const float MaxAlpha = 1.0f;
 const float AnimSpeedModifier = 1.5f;
 float GameTime = OwnerHUD->GetWorld()->GetRealTimeSeconds();
 float AnimPercent = FMath::Abs(FMath::Sin(GameTime*AnimSpeedModifier));
 if (bIsActiveMenuItem)
 {
  BgAlpha = FMath::Lerp(MinAlpha, MaxAlpha, AnimPercent);
 }
 else
 {
  BgAlpha = 0.0f;
 }
 return FLinearColor(1,1,1,BgAlpha);
}

이번엔 버튼 배경 색깔입니다.

저같았으면 랜드함수 적용해서 색깔을 좀 더 재미있게(는 불규칙 무지개) 표현하려 했을텐데 여기서는 애니메이션처럼 일정 속도로 색깔이 반짝거리게 해주는 것인가 봅니다. IsActiveMenuItem이 활성화 되어있으면 배경의 Alpha값을 조절해주고 안되어있으면 0.0f로 정의 해주고 FLinearcolor를 초기화해주네요.

FReply SVehicleMenuItem::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
 //execute our "OnClicked" delegate, if we have one
 if(OnClicked.IsBound() == true)
 {
  return OnClicked.Execute();
 }

 return FReply::Handled();
}

FReply SVehicleMenuItem::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
 return FReply::Handled();
}

FReply SVehicleMenuItem::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
 return FReply::Unhandled();
}

이번엔 마우스 버튼 관련된 것들이네요.

버튼이 눌리면(OnClicked Event) OnClicked.Excute()(온클릭은 방금 헤더에서 선언한거고 Excute()는 언리얼의 inl파일에 선언되어있음)를 실행하네요.

그 후엔 처리 이벤트를 반드시 실행해주네요. 다른 것들은 처리 이벤트를 실행하고 취소하는 것을 실행해주네요.

void SVehicleMenuItem::SetMenuItemActive(bool bIsMenuItemActive)
{
 this->bIsActiveMenuItem = bIsMenuItemActive;
}

네 마지막으로 MenuItemActive시켜주는 이벤트입니다. 이렇게 하려고 SetMenuItemActive를 만들었지요.

 

아 이거 다 분석하려면 아직 한참 남았네...