Making a health bar with hearts

Hey everyone,
I am pretty new to Codea and I wanted to make a mini game to test my skills, but I have some difficulties with making a Zelda-like health bar made out of 10 hearts. This means that everytime something hits my Hero, the heart on the far right becomes a “half-heart” and if it happens again the heart becomes empty. Then it continues with the heart on its left, again and again until all the hearts are empty. I’ve already drawn the 3 stages a heart can have and made them into sprites, but I am struggling with making the code to make the health diminish. I thought of having a table Hearts= {full, full,full, full,full, full,full, full,full, full} and a variable Health = 20 and everytimes the Health decreases by one, the first heart on the far right to be not empty, becomes either a Half-heart or empty depending on its previous state. Do you have any suggestions or ideas on how I could make it? I am sorry if it is a little complicated and not well explained, I’m not a native speaker in english :confused:
Thank you very much for taking the time to help me. Have a nice day :slight_smile:
ColourCoder

@ColourCoder Here’s a start. You’ll probably have to change this to fit your needs.


displayMode(FULLSCREEN)

function setup()
    h=readImage("Planet Cute:Heart")
    -- create half a heart sprite
    setContext(h)
    fill(0)
    rect(h.width/2,0,h.width/2,h.height)
    setContext()
    -- number of hearts
    nbr=10    
end

function draw()
    background(0)
    fill(255)
    text("tap screen",WIDTH/2,HEIGHT/2)
    -- draw full hearts
    for z=1,nbr do
        sprite("Planet Cute:Heart",200+40*z,300,40)
    end 
    -- draw half heart 
    if nbr>0 and nbr%1~=0 then
        sprite(h,200+40*(nbr+1-nbr%1),300,40)
    end
end

function touched(t)
    if t.state==BEGAN then
        nbr=nbr-.5
    end
end

Here’s a heart health bar in a class, just replace the images with the ones you are going to use. I just used a green gem for a full heart and a yellow gem for a half heart (which, now that I think of it, is kind of funny since your name is ColourCoder and the gem changes color).

EDIT: changed something in the code that caused an error


--# Main
-- Health

function setup()
    print("tap the screen to lose health")
    fullHeart = readImage("Planet Cute:Gem Green")
    halfHeart = readImage("Planet Cute:Gem Orange")
    h = HeartHealthBar(WIDTH/2, HEIGHT/2, 400)
end

function draw()
    background(0, 0, 0, 255)
    h:draw()
end

function touched(touch)
    if touch.state==BEGAN then
        h:loseHealth()
    end
end

--# HeartHealthBar
HeartHealthBar = class()

function HeartHealthBar:init(x, y, w)
    self.x = x
    self.y = y
    self.w = w
    self.health = {}
    for i=1, 10 do
        table.insert(self.health, {num=i, typ="full"})
    end
end

function HeartHealthBar:draw()
    translate(self.x-self.w/2, self.y)
    for a,b in pairs(self.health) do
        if b.typ == "full" then
            sprite(fullHeart, b.num*self.w/10, 0, self.w/10)
        elseif b.typ == "half" then
            sprite(halfHeart, b.num*self.w/10, 0, self.w/10)
        end
    end
end

function HeartHealthBar:loseHealth()
    if self.health[#self.health] then
        if self.health[#self.health].typ=="full" then
            self.health[#self.health].typ="half"
        else
            self.health[#self.health]=nil
        end
    end
end

@ColourCoder If you’re after a class, here’s my above code written as a class. You give it the x,y position, the number of hearts to start with, and the size of the hearts.

displayMode(FULLSCREEN)

function setup()
    -- x pos, y pos, nbr of hearts, size of hearts
    hb=healthBar(0,300,10,70)
end

function draw()
    background(0)
    fill(255)
    text("tap screen",WIDTH/2,HEIGHT/2)
    hb:draw()
end

function touched(t)
    if t.state==BEGAN then
        hb:touched(t)
    end
end

healthBar = class()

function healthBar:init(x,y,nbr,size)
    self.x=x
    self.y=y
    self.nbr=nbr
    self.size=size
    self.h=readImage("Planet Cute:Heart")
    setContext(self.h)
    fill(0)
    rect(self.h.width/2,0,self.h.width/2,self.h.height)
    setContext()
end

function healthBar:draw()
    for z=1,self.nbr do
        sprite("Planet Cute:Heart",self.x+self.size*z,self.y,self.size)
    end 
    if self.nbr>0 and self.nbr%1~=0 then
        sprite(self.h,self.x+self.size*(self.nbr+1-self.nbr%1),self.y,self.size)
    end
end

function healthBar:touched(t)
    if t.state==BEGAN then
        self.nbr=self.nbr-.5
    end
end

I updated my code using @dave1707’s method for making a half heart, with a slight variation. I changed the blendmode so that instead of just adding a black rectangle to the heart image, it makes that part of the image transparent.

--# Main
-- HeartHealthBar

function setup()
    values={bgc=0}
    tween(2.0, values, {bgc=75}, {loop=tween.loop.pingpong})
    -- these two lines change the background color so that you know that the half hearts are transparent
    
    print("tap the screen to lose health")
    fullHeart = readImage("Planet Cute:Heart")
    halfHeart = readImage("Planet Cute:Heart")
    setContext(halfHeart)
    fill(0, 0, 0, 255)
    blendMode(ZERO, ONE_MINUS_SRC_ALPHA)
    rect(halfHeart.width/2, 0, halfHeart.width/2, halfHeart.height)
    setContext()
    blendMode(NORMAL)
    h = HeartHealthBar(WIDTH/2, HEIGHT/2, 400)
end

function draw()
    background(math.floor(values.bgc))
    h:draw()
end

function touched(touch)
    if touch.state==BEGAN then
        h:loseHealth()
    end
end

--# HeartHealthBar
HeartHealthBar = class()

function HeartHealthBar:init(x, y, w)
    self.x = x
    self.y = y
    self.w = w
    self.health = {}
    for i=1, 10 do
        table.insert(self.health, {num=i, typ="full"})
    end
end

function HeartHealthBar:draw()
    translate(self.x-self.w/2, self.y)
    for a,b in pairs(self.health) do
        if b.typ == "full" then
            sprite(fullHeart, b.num*self.w/10, 0, self.w/10)
        elseif b.typ == "half" then
            sprite(halfHeart, b.num*self.w/10, 0, self.w/10)
        end
    end
end

function HeartHealthBar:loseHealth()
    if self.health[#self.health] then
        if self.health[#self.health].typ=="full" then
            self.health[#self.health].typ="half"
        else
            self.health[#self.health]=nil
        end
    end
end

@Saturn031000 I guess transparent is a lot better than just black. Good job.

@dave1707 Thanks.

Thank you very much guys!!!

@Saturn031000 I tried to change your codes a little bit so that you can actually see empty hearts. However I’ve come across a weird problem: the hearts do not empty in a “continuous flow”. I’ve spend hours trying to fix it but in vain. Is there anyway you could help me out on this? Here’s my code:


-- Test
function setup()
    Grid:init()
    parameter.integer("Health",0,20,20)
    Hearts = {3,3,3,3,3,3,3,3,3,3}
    Hearts[0] = 3
end
function draw()
    background(0, 0, 0, 255) --black
    Grid:draw()
        for i=0,19 do
            if Health<=i then 
                Hearts[math.floor(i/2)+1] = 2
            end
            if Health>=i then 
                Hearts[math.floor(i/2)+1] = 3
            end
                for j=1,10 do
                    if Hearts[j-1] == 2 or Hearts[j-1] == 1 then
                        Hearts [j] = 1 
                    end
                    if Health == 0 then
                        Hearts[1] = 1
                    end
                    if Hearts[j] == 3 then
                        sprite("Documents:FullHeart",j*10,100,10)
                    end
                    if Hearts[j] == 2 then
                        sprite("Documents:HalfHeart",j*10,100,10)
                    end
                    if Hearts[j] == 1 then
                        sprite("Documents:heartempty",j*10,100,10)
                    end
                end
          end
end

Whoops I used the wrong button. Here’s my code:

-- Test

function setup()
    Grid:init()
    parameter.integer("Health",0,20,20)
    Hearts = {3,3,3,3,3,3,3,3,3,3}
    Hearts[0] = 3
end
function draw()
    background(0, 0, 0, 255) --black
    Grid:draw()
        for i=0,19 do
            if Health<=i then 
                Hearts[math.floor(i/2)+1] = 2
            end
            if Health>=i then 
                Hearts[math.floor(i/2)+1] = 3
            end
                for j=1,10 do
                    if Hearts[j-1] == 2 or Hearts[j-1] == 1 then
                        Hearts [j] = 1 
                    end
                    if Health == 0 then
                        Hearts[1] = 1
                    end
                    if Hearts[j] == 3 then
                        sprite("Documents:FullHeart",j*10,100,10)
                    end
                    if Hearts[j] == 2 then
                        sprite("Documents:HalfHeart",j*10,100,10)
                    end
                    if Hearts[j] == 1 then
                        sprite("Documents:heartempty",j*10,100,10)
                    end
                end
          end
end

@ColourCoder, try putting your code between three ~'s, it’ll make it easier to copy your code. (The mods should fix it for you if you don’t know what I mean)

I tried but it didn’t work :confused: Guess I am a real @CodeaNoob

The ~~~ should be on a line by themselves.

Try pressing return after the tildes and put the second set on their own line. Your code should be ‘sandwiched’ between the tildes @ColourCoder

Thanks! So here is my code if anybody can help me out:

-- Test
function setup()
    Grid:init()
    parameter.integer("Health",0,20,20)
    Hearts = {3,3,3,3,3,3,3,3,3,3}
    Hearts[0] = 3
end
function draw()
    background(0, 0, 0, 255) --black
    Grid:draw()
        for i=0,19 do
            if Health<=i then 
                Hearts[math.floor(i/2)+1] = 2
            end
            if Health>=i then 
                Hearts[math.floor(i/2)+1] = 3
            end
                for j=1,10 do
                    if Hearts[j-1] == 2 or Hearts[j-1] == 1 then
                        Hearts [j] = 1 
                    end
                    if Health == 0 then
                        Hearts[1] = 1
                    end
                    if Hearts[j] == 3 then
                        sprite("Documents:FullHeart",j*10,100,10)
                    end
                    if Hearts[j] == 2 then
                        sprite("Documents:HalfHeart",j*10,100,10)
                    end
                    if Hearts[j] == 1 then
                        sprite("Documents:heartempty",j*10,100,10)
                    end
                end
          end
end

We dont have the Grid class

@ColourCoder Any sprite you use from your Documents folder isn’t available to anyone on the forum. It’s only useable by you.