【UE4】 第07講 實現自定義的行走控制元件
阿新 • • 發佈:2018-11-12
在圖形業,只有技術是不行的,你要明白我們從事的工作,我們可是在作詩,我們是詩人 - Nvidia創始人黃仁勳(圖形皇帝)
(版權宣告,禁止轉載)
UE4製作產品時,實際上幾乎所有的UI控制元件都需要自定義實現,UE4本身只提供少量基礎的Button,Image,Text,參考這些自帶控制元件的實現,根據需要進行自定義擴充套件還是比較靈活高效的,不會有太多的冗餘。
參考UE4自帶的SVirtualJoystick來實現一下行走控制元件SWalkWidget
實現過程分兩步
第一步 實現UE4渲染部分的SWalkWidget,繼承自SLeafWidget
SWalkWidget.h
#pragma once #include "SWidget.h" #include "SLeafWidget.h" /** * */ class MOBAHERO_API SWalkWidget : public SLeafWidget { public: /** The settings and current state of each zone we render */ struct FControlInfo { FControlInfo() { // default to all 0 FMemory::Memzero(this, sizeof(*this)); CapturedPointerIndex = -1; InputScale = FVector2D(1.f, 1.f); } // Set by the game /** The brush to use to draw the background for joysticks, or unclicked for buttons */ TSharedPtr< FSlateDynamicImageBrush > Image1; /** The brush to use to draw the thumb for joysticks, or clicked for buttons */ TSharedPtr< FSlateDynamicImageBrush > Image2; /** The actual center of the control */ FVector2D Center; /** The size of a joystick that can be re-centered within InteractionSize area */ FVector2D VisualSize; /** The size of the thumb that can be re-centered within InteractionSize area */ FVector2D ThumbSize; /** The size of a the interactable area around Center */ FVector2D InteractionSize; /** The scale for control input */ FVector2D InputScale; /** The input to send from this control (for sticks, this is the horizontal/X input) */ FKey MainInputKey; /** The secondary input (for sticks, this is the vertical/Y input, unused for buttons) */ FKey AltInputKey; /** Positioned center in viewport */ FVector2D PositionedCenter; private: friend SWalkWidget; /** * Reset the control to a centered/inactive state */ void Reset(); // Current state /** The position of the thumb, in relation to the VisualCenter */ FVector2D ThumbPosition; /** For recentered joysticks, this is the re-center location */ FVector2D VisualCenter; /** The corrected actual center of the control */ FVector2D CorrectedCenter; /** The corrected size of a joystick that can be re-centered within InteractionSize area */ FVector2D CorrectedVisualSize; /** The corrected size of the thumb that can be re-centered within InteractionSize area */ FVector2D CorrectedThumbSize; /** The corrected size of a the interactable area around Center */ FVector2D CorrectedInteractionSize; /** The corrected scale for control input */ FVector2D CorrectedInputScale; /** Which pointer index is interacting with this control right now, or -1 if not interacting */ int32 CapturedPointerIndex; /** Time to activate joystick **/ float ElapsedTime; /** Visual center to be updated */ FVector2D NextCenter; /** Whether or not to send one last "release" event next tick */ bool bSendOneMoreEvent; /** Whether or not we need position the control against the geometry */ bool bHasBeenPositioned; /** Whether or not to update center position */ bool bNeedUpdatedCenter; }; SLATE_BEGIN_ARGS(SWalkWidget) {} SLATE_END_ARGS() void Construct(const FArguments& InArgs); virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; virtual FVector2D ComputeDesiredSize(float) const override; virtual FReply OnTouchStarted(const FGeometry& MyGeometry, const FPointerEvent& Event) override; virtual FReply OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& Event) override; virtual FReply OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& Event) override; virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; private: /** List of controls set by the UTouchInterface */ FControlInfo Control; /** True if the joystick should be visible */ uint32 bVisible : 1; /** Target opacity */ float CurrentOpacity; bool bPressed; };
SWalkWidget.cpp
獲取控制元件需要的兩個Image - Engine.Joystick.Image1 Engine.Joystick.Image2
在Construct 裡指定大小和尺寸,用在OnPaint裡指定繪製區域
通過ThumbPosition指定小圓圈的相對位置
#include "MobaHero.h" #include "SWalkWidget.h" #include "CoreStyle.h" void SWalkWidget::Construct(const FArguments& InArgs) { // CurrentOpacity = 0.4f; bVisible = 1; bPressed = false; UTexture2D* Tex(0); Control.Image1 = FCoreStyle::GetDynamicImageBrush("Engine.Joystick.Image1", Tex, "VirtualJoystick_Thumb"); Control.Image2 = FCoreStyle::GetDynamicImageBrush("Engine.Joystick.Image2", Tex, "VirtualJoystick_Background"); Control.Center = FVector2D(135.f,-135.f); Control.VisualSize = FVector2D(192.f,192.f); Control.CorrectedVisualSize = FVector2D(365.f,365.f); Control.VisualCenter = Control.CorrectedVisualSize / 2.f; Control.CorrectedThumbSize = FVector2D(150.f,150.f); Control.ThumbPosition = FVector2D(0.f,0.f); } //在這裡繪製兩個Image,大圓圈的先繪製,小圓圈的後繪製 int32 SWalkWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { int32 RetLayerId = LayerId; if (bVisible) { FLinearColor ColorAndOpacitySRGB = InWidgetStyle.GetColorAndOpacityTint(); ColorAndOpacitySRGB.A = CurrentOpacity; if (Control.Image2.IsValid()) { FSlateDrawElement::MakeBox( OutDrawElements, RetLayerId++, AllottedGeometry.ToPaintGeometry( Control.VisualCenter - FVector2D(Control.CorrectedVisualSize.X * 0.5f, Control.CorrectedVisualSize.Y * 0.5f), Control.CorrectedVisualSize), Control.Image2.Get(), MyClippingRect, ESlateDrawEffect::None, ColorAndOpacitySRGB ); } if (Control.Image1.IsValid()) { FSlateDrawElement::MakeBox( OutDrawElements, RetLayerId++, AllottedGeometry.ToPaintGeometry( Control.VisualCenter + Control.ThumbPosition - FVector2D(Control.CorrectedThumbSize.X * 0.5f, Control.CorrectedThumbSize.Y * 0.5f), Control.CorrectedThumbSize), Control.Image1.Get(), MyClippingRect, ESlateDrawEffect::None, ColorAndOpacitySRGB ); } } return RetLayerId; } FVector2D SWalkWidget::ComputeDesiredSize(float) const { return FVector2D(100, 100); } FReply SWalkWidget::OnTouchStarted(const FGeometry& MyGeometry, const FPointerEvent& Event) { FVector2D LocalCoord = MyGeometry.AbsoluteToLocal(Event.GetScreenSpacePosition()); Control.ThumbPosition = LocalCoord - Control.VisualCenter; bPressed = true; return FReply::Handled(); } FReply SWalkWidget::OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& Event) { FVector2D LocalCoord = MyGeometry.AbsoluteToLocal(Event.GetScreenSpacePosition()); GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Walk Widget Touch Move")); if (bPressed) { Control.ThumbPosition = LocalCoord - Control.VisualCenter; } return FReply::Handled(); } FReply SWalkWidget::OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& Event) { bPressed = false; return FReply::Handled(); } void SWalkWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { // }
第二步 實現C++反射後藍圖可編輯的UWalkWidget
這個類需要通過UE4嚮導建立,選擇父類為Widget
WalkWidget.h
#pragma once
#include "SWalkWidget.h"
#include "Widget.h"
#include "WalkWidget.generated.h"
/**
*
*/
UCLASS()
class MOBAHERO_API UWalkWidget : public UWidget
{
GENERATED_BODY()
protected:
/** Native Slate Widget */
TSharedPtr<SWalkWidget> MyWalkWidget;
//~ Begin UWidget Interface
virtual TSharedRef<SWidget> RebuildWidget() override;
//~ End UWidget Interface
};
WalkWidget.cpp
#include "MobaHero.h"
#include "WalkWidget.h"
TSharedRef<SWidget> UWalkWidget::RebuildWidget()
{
MyWalkWidget = SNew(SWalkWidget);
return MyWalkWidget.ToSharedRef();
}
編譯成功之後,可以在Widget的藍圖編輯器裡看到,可以直接拖入編輯區進行編輯