在圖形業,只有技術是不行的,你要明白我們從事的工作,我們可是在作詩,我們是詩人 - 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的藍圖編輯器裏看到,可以直接拖入編輯區進行編輯