您的位置:首页 > 产品设计 > UI/UE

【UE4】 第07讲 【MOBA制作日记】 实现自定义的行走控件

2017-02-28 09:39 1201 查看
      在图形业,只有技术是不行的,你要明白我们从事的工作,我们可是在作诗,我们是诗人 - 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的蓝图编辑器里看到,可以直接拖入编辑区进行编辑
              
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  UE4 UMG