SMeshWidget - Hardware Instanced Slate Meshes Thread

I’ve finally tackled this issue trying to make my enemy overhead healthbars. It turns out the issue @<a href=“https://forums.unrealengine.com/member.php?u=155” target=“_blank”>TheJamsh</a> had regarding the position of the widget caused me a great deal of confusion. Slate seems to apply some weird voodoo magic to properly scale and positions its widgets and getting the **** thing above the head of my characters was an exercise in frustration.

The issue ended up being in the code from @<a href=“https://forums.unrealengine.com/member.php?u=2522” target=“_blank”>Nick Darnell</a>, as he’s getting the mid-point of the widget and then moving stuff around in his example. I noticed that I could properly get a good screen projection when using UMG and canvas panel slots, so I looked at that code. Turns out that that positions the elements from the top-left corner. So my final positioning code ended up looking like this (I won’t paste the entire widget as the code is pretty similar to what’s been posted here already):


FVector2D TopLeft = AllottedGeometry.LocalToAbsolute(FVector2D::ZeroVector);


...


HPData.SetPosition(TopLeft + (Scale * HPBar.Position));

The HPBar.Position is calculated like so:


//#TODO: Need a way to pass in a reference point (like a socket above the head) and use that instead of the actor location
FVector CurrentLocation = ReferencedActor->GetActorLocation() + FVector(0.f, 0.f, ParentWidget->VerticalHealthbarOffset);

UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(ParentWidget->GetOwningPlayer(), CurrentLocation, Position);

The rest is pretty much identical to what’s been posted already. But I suppose I should mention the thing I had to do myself to get it working. I wanted to have a “depletion lerp” on my health bars, i.e. you hit the enemy, their health drops to the new amount, but the health that was there now turns red and slowly depletes down to the current value. Thus, aside from the Scale and Position I needed to pass in 2 more values that I had to pack into 1 float. This turned out to be pretty simple, and I didn’t have to use any byte operations, I simply used the whole part of the float for one value and the fraction for the other (as both are normalized).


//I update the depleted percentage every frame as I need to animate it going down to the current percentage.
float DepletedPercentage = FMath::Lerp(OldPercentage, NewPercentage, Alpha);
PercentagePacked = FMath::TruncToFloat(DepletedPercentage * 1000.f) + NewPercentage; //DepletedPercentage is normalized too, so I multiply it by 1000 to give it some resolution (As I'll only be using that truncated part in the shader).

Lastly, in my shader I do the following:

Lastly, the UnpackHealth function looks like this:

The subtraction and clamp at the bottom is to handle the case when the health is full, i.e. 1.0. In that case there won’t be a fraction, but the whole part will be 1 larger than it should be.

Hope this helps someone.

Edit:

Here’s how it ended up looking. Sorry for the poor FPS.

2 Likes