I’m looking to replicate a pretty common health system, where your health is shown as a series of hearts (or some other image) on your HUD. The hearts/whatevers deplete as you take damage, and can be expanded by upgrading your total health in some way. This system is most obviously apparent in the Zelda series, of course, where you can collect Heart Pieces to upgrade health.
After a few days of searching I’ve failed to find any direction on this, so I’m coming here for help. I thought I would be okay if I found some tutorials on using the Expandable Area palette and/or the Uniform Grid Panel palette, but no dice so far.
While I haven’t done something like this, I feel like it’s a pretty similar methodology to a health bar. With a health bar, when the player takes damage -> tell widget to update the bar -> widget gets current health and max health of player and adjusts the bar accordingly.
For “full” hearts, I suppose you’d want your health variable to be an int and when changed -> tell widget to update hearts -> it gets player health and updates the hearts accordingly.
For “part” hearts (like latest zelda where a heart can be divided up into 3 parts), either the above would work but multiply it by 3, or you could use a float where whole numbers represents a full heart and the left over would tell the last heart to update accordingly, only showing a part of the heart.
So how would the hearts update accordingly?
You could get the player health and the widgets current hearts, compare and add or remove as needed.
To keep things simple, the hearts could be contained inside a horizontal box, and you’d just add to or remove from the box.
For a grid panel, some extra math is needed to put the heart into the correct grid slot.
The heart would probably be its own widget, and for part hearts, it would hold the logic for only displaying part of itself.
Note that when I write “add heart”, I mean that either creating the heart widget as needed, or create the maximum amount of hearts and then just toggling their visibility as desired.
Try to keep important logic inside the player or controller. The widget should only have the logic for the visuals.
Thank you, and I like the sound of this, but I have questions. How can I tell the widget which heart is the last heart? How do I divide one float value representing health among three or four or however many heart widgets? I’m not familiar with the methodology for any of that.
When the HUD widget is created, create the max number of hearts that there can be, add them to the container (in this case a horizontal box) and hide them.
When the player health changes:
2A: loop through the currently displayed hearts and hide them
2B: get the players current health, floor it to get number of whole hearts and using a loop with that number, set that many as visible.
2C: Get the remainder of the player health, get the heart widget and somehow tell it to update to account for the remainder.
2D: update how many hearts the widget is displaying.
The only thing I am not sure how to do is displaying part of the heart. There are many ways to display it, like -
Display it partially, lerping the texture from top to bottom?
Have it like a pie/clock thing, whatever that is called?
Like in newest zelda where each heart consists of a number of pieces?
And I’m not sure how to only show part of a texture.
And etc.
The blueprint setup above can of course be optimized and adjusted in various ways. I notice now, you may want to start the loops at index 0. Don’t take the blueprints too litteral, it’s more of an example.
I approached the issue with a dynamic material. When I was only working with one heart, all I did was feed my current health into the material’s Alpha, which depleted the heart in a pie shape.
As you can see, I’ve changed things so that I’m feeding your % variable into the Alpha instead, but I’ll be honest, I’m not sure what this variable is for. And it’s not really working the way it should be yet.
There are a couple of aesthetic issues too.
For one thing, I don’t actually want to remove/hide the hearts as they are depleted. After all, the player wouldn’t be able to tell at a glance how many total hearts they have if they’ve taken damage. Instead I just want the hearts in question to be grayed out. (Kind of like how it looks in that image, but all of those are a work in progress.)
For another, right now the hearts are appearing and disappearing from the left-hand side of the screen. How would I go about changing that? In your average Zelda, for instance, the right-most heart depletes first, and so on, until only the left-most heart remains when the player is near death. Like this:
I like where all this is going, though. I feel like I’ll have it wrapped up with a little more back-and-forth. You’re a huge help, dude.
So now all the heart widgets are always visible, driving their visual state with a 0-1 float. So a whole heart, 1, will be full and then the left over heart will receive what’s left over. Which is shown in the bottom right - I simply use a progress bar for this example. But you could drive the alpha of a material or the pie shape you mentioned should work.
As for the hearts depleting left to right, they don’t for me when I use a horizontal box to hold the hearts with the code above. What are you using to hold them?
Another thing to think about is when the player increases the max life, so when that happens there should be an event inside the widget that adds new hearts.
Hmm. I wanted to use a wrap box, in case the hearts grow too many, but I switched back to a horizontal box. I’m having problems, though.
For the sake of clarity I swapped out my hearts for progress bars, like you. That’s full health. (I set the default max health to seven for this test, although it appears that eight are created.)
The event I’m using to update the hearts/bars is from a Blueprint Interface. Whenever the player gets hurt (or finds health) we call that event, sending the player’s current and max health. So here’s that:
Looking at it now, I’m noticing a possible discrepancy between your graph and mine. You’re grabbing the “health” from the player reference, but is that the current or maximum health?
I think I see the problem. As for your last comment, see it as the current health.
I think that the problem is that during sequence 0, the bars are reset but the loops last index is the current health - the new health, where as you’d want to use the old number of displayed hearts - the “current hearts” variable in my setup, which is a variable inside the widget.
If it makes it easier, you can “get children” of the horizontal box and foreach loop each of them to reset them.
“(I set the default max health to seven for this test, although it appears that eight are created.)” - Is it the zero index that’s adding the extra?
Thank you! Your directions seem to have fixed most of the issues.
This is where I’m still having trouble. Check this out:
That’s what the health UI looks like at 0 health. The character is dead. And yet there’s still a whole bar of health remaining in the UI. That would sure confuse anybody playing. There are two For Loops in the event (not counting the For Loop in Event Construct). I tried changing their index from 0 to 1, but all that seemed to do was shift the issue, like this:
Oh, you’re using max health to set the last loop index. I don’t think you ever need the max health at all, only the current health. Or if you prefer to keep it, I think it would fix it to flop those lines from max health and current health.
So max health should lead to last index of first loop. I would use a floor +1 instead.
Current health should lead to that floor node in your screen shot and the - node.
As you can see, I’m not using Max Health for anything anymore. And I took your advice and added a variable in the UI that keeps track of our current hearts. That variable is first set in Event Construct, and used to determine how many hearts/bars to display.
The problem persists, though.
Also, thanks for the suggestion, @OptimisticMonkey, but that wouldn’t quite capture the effect I’m going for.
Ah now I know what it is, because I had the same bug. It was my setup all along - When updating the whole hearts, the loop starts at index 0, so most things needs to be reduced by 1.
So it should be:
Floor - -1 - last index of the last loop.
Floor - Get child at (so removing the addition node).
Floor - current health (so removing the addition node).
I also went and added a -1 to the last index in the Event Construct, since the UI was creating an extra, empty bar/heart. As far as I can tell this is mostly working, but I’m still seeing a discrepancy between the health displayed in the HUD and the actual health in the player blueprint. Now, when the player drops to 0 health, half a bar is displayed on the HUD.
Now it’s looking identical to what I have and mine’s working.
When health becomes 0, does it always display half a bar, or the previous health amount? I mean, does it display 0.5 bar even if the previous hp was say 5.2?
Is it possible that you query the health and if <= 0, it does not update the widget?
Okay, so, it works now. I can’t rightly explain why, though.
The problem seemed to be happening back in the character blueprint, not in the UMG. Whenever the character takes damage I set a new amount of Current Health, right?
See that struct being set? The one that contains the current health? Up until now I was running a line out of that into the BPI Update Stats event next to it, which is called in the UMG. On a whim I tried pulling directly from the clamped value instead and… it works.
But shouldn’t those be the exact same value? That has me worried.
Oh well.
I switched back to hearts, and it looks good. Barring any unexpected problems, everything seems to be fixed.
Thank you so much for your help, @ste1nar! I was afraid this would be a total headache, but you really smoothed things out.
Welp. I’ve been having some more issues. Taking damage works fine, but recovering health? Not so much. Improving health is also turning out to be trickier than I would’ve thought.
But one thing at a time…
So, according to the strings I’m printing, they’re not the same value.
I changed the max hearts to 3, not that it really matters. The character takes damage – 0.25 points of damage, to be precise. This should break his health down from 3.0 to 2.75, right?
Well, it sort of does. The first number (2.5) is the number being printed from the Clamp node, and the second (2.75) is the Current Health according to my struct. But that value only exists because it’s being set by the Clamp node! They should be identical.
In case you’re wondering, the only thing happening before this is I’m taking the Current Health and deducting the 0.25 points of damage from it.
So. Really sorry to bug you all over again. I’m not sure what’s going on.
I can’t reproduce your issue with the struct/clamp value.
I notice that your struct has a target, so what’s the target and in what kind of blueprint is this setup executing? What method is used to reduce health, for example button press, timer, widget button?
Does it always subtract to the wrong number, or is it only the first time?
All of the player’s stat values – stuff I want to easily save and load – are contained in the Game Instance. Stuff like health, money, weapons, etc. So, every time I need to use or change one of those values, I reference the GI. That’s what the target is. I wouldn’t have thought there would be a problem doing things that way, but since you can’t reproduce the issue, that has me worried.
A few. There’s a trigger volume in the level that damages the player, the player takes damage from long falls, and I also just put in a button press for testing. All of them cause different amounts of damage, but the discrepancy between the clamp’s printed value and the final printed value is there every time.
Always, it seems.
Although, now that you mention it I’m noticing a new problem here. Whenever I take damage the first time I play-in-editor, the damage is apparently doubled. (Or maybe it’s because I’m at full health?) For instance, the trigger volume that causes 0.25 points of damage should cut one of my hearts in a quarter, right? And it does, but only the second or third time. The first time it cuts the heart in half.
The printed values are off every time, though. Gah.