1. 程式人生 > >【UE4】 第07講 實現自定義的行走控制元件

【UE4】 第07講 實現自定義的行走控制元件

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