How to do simple Health bar using Slate in C++

Hi,

How can I do a Health bar using Slate in C++?

I have checked both ShooterGame and StrategyGame, and they use FCanvasTileItem. But I need it to be in Slate because Canvas is rendered behind the Slate widgets I already have, and I need the health bar to be on top on the Slate widget.

Thanks

If you want static HEalth bar, lets say on top/bottom of your screen then you can do it in Slate but if you want to have Multiple health bars on your screen for multiple/dynamic actors, it would be much easier to make it using HUD and Deproject function.

Anyway, here is really simple dynamic health bar implementation in Slate you can start with:

So all you have to do is create “SHealthBar.h” in your project and paste this into it:



#pragma once

#include "Slate.h"

DECLARE_DELEGATE_RetVal(float, FOnGetFloat)

class SHealthBar : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SHealthBar)
	{}
		SLATE_ARGUMENT(float, MaxHealth)
		SLATE_EVENT(FOnGetFloat, OnGetCurrentHealth)
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs)
	{
		MaxHealth = InArgs._MaxHealth;
		OnGetCurrentHealth = InArgs._OnGetCurrentHealth;
	}

	virtual int32 OnPaint(const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const OVERRIDE
	{
		// Initialize our brush here
		const FSlateBrush* BrushResource = new FSlateBrush();

		// Draw background box
		FSlateDrawElement::MakeBox(
		OutDrawElements,
		LayerId,
		AllottedGeometry.ToPaintGeometry(),
		BrushResource,
		MyClippingRect,
		ESlateDrawEffect::None,
		FLinearColor::Gray *.35f
		);

		if (OnGetCurrentHealth.IsBound())
		{
			// Calculate CurrentHealth / MaxHealth ratio and turn it into screen width
			FVector2D WidgetSize = MyClippingRect.GetSize();
			float HealthWidth = ( OnGetCurrentHealth.Execute() * WidgetSize.X ) / MaxHealth;

			// Draw current health
			FSlateDrawElement::MakeBox(
				OutDrawElements,
				LayerId,
				AllottedGeometry.ToPaintGeometry(FVector2D::ZeroVector, FVector2D(HealthWidth, WidgetSize.Y) ),
				BrushResource,
				MyClippingRect,
				ESlateDrawEffect::None,
				FLinearColor::Green *.65f
				);


			// Draw text on health bar
			FSlateFontInfo MyFont(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 15);

			const FText Text = GetHealthText();
			const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
			FVector2D DrawSize = FontMeasureService->Measure(Text, MyFont);
			FVector2D Pos = WidgetSize / 2.0f - DrawSize / 2.0f;


			FSlateDrawElement::MakeText(
				OutDrawElements,
				LayerId,
				AllottedGeometry.ToPaintGeometry(Pos, DrawSize),
				Text,
				MyFont,
				MyClippingRect,
				ESlateDrawEffect::None,
				FLinearColor::White
				);
		}

		return SCompoundWidget::OnPaint(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
	}

protected:

	FText GetHealthText() const
	{
		if (OnGetCurrentHealth.IsBound())
		{
			return FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), OnGetCurrentHealth.Execute(), MaxHealth));
		}

		return FText();
	}

	float MaxHealth;
	FOnGetFloat OnGetCurrentHealth;
};


after that you should be ready to go. To use it you need to pass 2 arguments: MaxHealth and Delegate that will return current health value. I did it in my TestCharacter class like that



GEngine->GameViewport->AddViewportWidgetContent(
			SNew(SVerticalBox)
				+ SVerticalBox::Slot()
				.HAlign(HAlign_Center)
				.VAlign(VAlign_Top)
				
					SNew(SBox)
					.WidthOverride(500)
					.HeightOverride(40)
					
						SNew(SHealthBar)
						.MaxHealth(MaxHealth)
						.OnGetCurrentHealth(FOnGetFloat::CreateUObject(this, &ATestCharacter::GetHealth))
					]
				]
			);


where ATestCharacter::GetHealth looks like this:



float ATestCharacter::GetHealth() const { return Health; }


this is how it should look like
g5ol4tl.png

Hope this helps