I want to write a health system like in the legend of zelda: breath of the wild, with the animations and all. For now I have it so that each heart is 4 health, with a maximum of 20 hearts or 80 health.
The problem is that I have no idea how to do this efficiently.
I have made the images for empty heart, 1/4, 1/2 and 3/4 and full heart, but the only solution I have is that I start writing like this:
if maxhealth.Value == 12 then
if health.Value == 1 then
for i,v in pairs(heartframe) do
if v:IsA("ImageLabel") then
v.Visible = false
end
end
heartframe.quarterheart1.Visible = true
heartframe.emptyheart1.Visible = true
heartframe.emptyheart2.Visible = true
elseif health.Value == 2 then
elseif health.Value == 3 then
end
end
ALL THE WAY TO 80…
and I need to do it for each health upgrade (same as botw: 1 heart / 4 health per upgrade)…
example:
if maxhealth.Value == 16 then
-- now copy the other version and add an extra empty heart container to each, while also adding healths 13-16
end
I’m already half way and about 3000 lines in. I got really bored of doing it, couldn’t come up with anything better and so decided to ask. There should be a way to do it without writing everything out, I hope.
So what you would do, is name the first heart, Heart1, the next Heart2, and so on. Then, when checking if you take damage, you get the highest number heart, which you can have a numbervalue set to the heartNumber when you purchase a heart. (Player purchases heart, numberValue+= 1, so once a player has 50 hearts, the value it as 50.)
Next, when a player takes damage, you could use a function, and do something like this.
(Please note this is not a very efficient way to do this.)
function dodamage(HeartNumber,Damage)
local Heart = Gui:FindFirstChild("Heart"..HeartNumber)
local Quarter = nil
if Heart.Quarter4.Visible == true then
Quarter = Heart.Quarter4
elseif Heart.Quarter3.Visible == true then
Quarter = Heart.Quarter3
elseif--and so on and so forth.
end
if Quarter == Heart.Quarter4 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter3
end
if Quarter == Heart.Quarter3 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter2
end
if Quarter == Heart.Quarter2 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter1
end
if Quarter == Heart.Quarter1 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
end
HeartNumber -=1
if Damage>=1 and HeartNumber ~= 0 then
dodamge(HeartNumber,Damage)
end
if HeartNumber == 0 then
--no more hearts left, game over
end
end
I would still have to write quite a bit, here for example:
if Heart.Quarter4.Visible == true then
Quarter = Heart.Quarter4
elseif Heart.Quarter3.Visible == true then
Quarter = Heart.Quarter3
elseif--and so on and so forth.
end
if Quarter == Heart.Quarter4 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter3
end
if Quarter == Heart.Quarter3 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter2
end
if Quarter == Heart.Quarter2 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
Quarter = Heart.Quarter1
end
if Quarter == Heart.Quarter1 and Damage >= 1 then
Quarter.Visible = false
Damage-=1
end
I would have to write what it should do when taking 2 damage, 3 damage or even 10 damage
I will be adding armor into the game as well, which would make it even harder, lol.
local function updateVisuals()
local numberOfWholeHearts = math.floor(health.Value / 4)
local numberOfQuarters = health.Value % 4 -- remainder when dividing health by 4
for heartIndex = 1, math.ceil(maxHealth.Value / 4) do
for _, imageLabel in Gui:FindFirstChild("Heart" .. heartIndex):GetChildren() do
imageLabel.Visible = false
end
end
for heartIndex = 1, numberOfWholeHearts do
Gui:FindFirstChild("Heart" .. heartIndex).Full.Visible = true
end
Gui:FindFirstChild("Heart" .. numberOfWholeHearts + 1):FindFirstChild(if numberOfQuarters == 0 then "Empty" else "Quarter" .. numberOfQuarters).Visible = true
for heartIndex = numberOfWholeHearts + 2, math.ceil(maxHealth.Value / 4) do
Gui:FindFirstChild("Heart" .. heartIndex).Empty.Visible = true
end
end
local maxHearts = math.ceil(maxhealth.Value/4)
Gui:FindFirstChild("Heart" .. numberOfWholeHearts + 1):FindFirstChild(if numberOfQuarters == 0 and numberOfWholeHearts ~= maxHearts then "Empty" else "Quarter" .. numberOfQuarters).Visible = true
as it was showing an extra empty heart if my max hp was 6 hearts and my current hp was a full 6 hearts
So now it just indexes nil when trying to find the next one when it shouldn’t(???) so I’ll consider it fixed lmao.
local maxHearts = math.ceil(maxHealth / 4)
if maxHearts >= numberOfWholeHearts + 1 then
Gui:FindFirstChild("Heart" .. numberOfWholeHearts + 1):FindFirstChild(if numberOfQuarters == 0 then "Empty" else "Quarter" .. numberOfQuarters).Visible = true
end
looks like it should function the same as
local maxHearts = math.ceil(maxhealth.Value/4)
Gui:FindFirstChild("Heart" .. numberOfWholeHearts + 1):FindFirstChild(if numberOfQuarters == 0 and numberOfWholeHearts ~= maxHearts then "Empty" else "Quarter" .. numberOfQuarters).Visible = true
(before you posted I didn’t even know you could FindFirstChild(if statement))
but, how do these two differ?
I suppose it would be that one of them starts finding a child; but is unable to as it can’t find it and then tried to set the quarter’s visibility.
Yes, when numberOfWholeHearts equals maxHearts, this
local maxHearts = math.ceil(maxhealth.Value/4)
Gui:FindFirstChild("Heart" .. numberOfWholeHearts + 1):FindFirstChild(if numberOfQuarters == 0 and numberOfWholeHearts ~= maxHearts then "Empty" else "Quarter" .. numberOfQuarters).Visible = true
tries to find a child that either doesn’t exist, or if it does exist, it shouldn’t be made visible.
It tries to find a heart frame which only exists if you have extra heart frames (more heart frames than maxHearts). You mentioned that an extra empty heart was shown with my original code so apparently you do have extra heart frames.
If you do have extra HeartFrames, and numberOfWholeHearts equals maxHearts (in which case there should only be full hearts), then it will try to find an ImageLabel called Quarter0 that is a child of the extra heart frame (And I assume such a child doesn’t exist).
The if statement in the function argument parentheses is not a regular if statement. It is a ternary operator. While a regular if statement determines whether a block of code will run based on a condition, a ternary operator chooses one of two values based on a condition.
So, in this case, the ternary operator does not affect whether the code looks for a child of the heart frame. It only affects the name with which it looks for a child.
When the original line of code is put into a regular if statement (my suggestion), the line that makes the first non-full (empty or partially full) heart visible is only run if there should be a non-full heart.
Based on the constraints of health and max health (both are integers and health cannot be greater than max health), the condition you added to the ternary operator is practically equivalent to my if statement condition in this case. It’s just in the wrong place because it should be in a regular if statement.