Posted on

How to Code and Create Zelda Hearts Health Bar in Unity

In this lesson on to create fun game mechanics in Unity, I will teach you how to code and create Zelda Hearts in Unity. The famous heart health can be found in every Zelda game since the original. This icon game mechanic and is loved by every Zelda fan and add a different take on a typical fill health bar.

To create the Zelda health bar we will need to create a UI panel for holding all the hearts. We will then create the heart prefab. Each heart prefab will form a single linked list. We will then create an algorithm that will recurse through each heart, filling them with the correct fill amount.

Unlock Code and Member Content

HeartContainer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class HeartContainer : MonoBehaviour
{
    public HeartContainer next;

    [Range(0, 1)] float fill;
    [SerializeField] Image fillImage;

    public void SetHeart(float count)
    {
        fill = count;
        fillImage.fillAmount = fill;
        count--;
        if (next != null)
        {
            next.SetHeart(count);
        }
    }
}

ZeldaHealthBar.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ZeldaHealthBar : MonoBehaviour
{
    public static ZeldaHealthBar instance;

    [SerializeField] GameObject heartContainerPrefab;

    [SerializeField] List<GameObject> heartContainers;
    int totalHearts;
    float currentHearts;
    HeartContainer currentContainer;

    // Start is called before the first frame update
    void Start()
    {
        instance = this;
        heartContainers = new List<GameObject>();
        
    }

    //ZeldaHealthBar.instance.SetupHearts(valueIn);
    public void SetupHearts(int heartsIn)
    {
        heartContainers.Clear();
        for(int i = transform.childCount -1; i >=0; i--)
        {  
            Destroy(transform.GetChild(i).gameObject);
        }

        totalHearts = heartsIn;
        currentHearts = (float)totalHearts;
        
        for (int i = 0; i < totalHearts; i++)
        {
            GameObject newHeart = Instantiate(heartContainerPrefab, transform);
            heartContainers.Add(newHeart);
            if(currentContainer != null)
            {
                currentContainer.next = newHeart.GetComponent<HeartContainer>();
            }
            currentContainer = newHeart.GetComponent<HeartContainer>();
        }
        currentContainer = heartContainers[0].GetComponent<HeartContainer>();

    }

    //ZeldaHealthBar.instance.SetCurrentHealth(valueIn);
    public void SetCurrentHealth(float health)
    {
        currentHearts = health;
        currentContainer.SetHeart(currentHearts);
        
    }

    //ZeldaHealthBar.instance.AddHearts(valueIn);
    public void AddHearts(float healthUp)
    {
        currentHearts += healthUp;
        if(currentHearts > totalHearts)
        {
            currentHearts = (float)totalHearts;
        }
        currentContainer.SetHeart(currentHearts);
    }

    //ZeldaHealthBar.instance.RemoveHearts(valueIn);
    public void RemoveHearts(float healthDown)
    {
        currentHearts -= healthDown;
        if(currentHearts < 0)
        {
            currentHearts = 0f;
        }
        currentContainer.SetHeart(currentHearts);
    }

    //ZeldaHealthBar.instance.AddContainer(valueIn);
    public void AddContainer()
    {
        GameObject newHeart = Instantiate(heartContainerPrefab, transform);
        currentContainer = heartContainers[heartContainers.Count - 1].GetComponent<HeartContainer>();
        heartContainers.Add(newHeart);
        

        if (currentContainer != null)
        {
            currentContainer.next = newHeart.GetComponent<HeartContainer>();
        }
    
        currentContainer = heartContainers[0].GetComponent<HeartContainer>();

        totalHearts++;
        currentHearts = totalHearts;
        SetCurrentHealth(currentHearts);
    }
}

DemoZeldaHealth.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DemoZeldaHealth : MonoBehaviour
{
    int total;
    float amountUp;
    float amountDown;

    public void TotalInput(string valueIn)
    {
        total = int.Parse(valueIn);
    }

    public void SubmitSetup()
    {
        ZeldaHealthBar.instance.SetupHearts(total);
    }

    public void UpAmountInput(string valueIn)
    {
        amountUp = float.Parse(valueIn);
    }

    public void SubmitUp()
    {
        ZeldaHealthBar.instance.AddHearts(amountUp);
    }

    public void DownAmountInput(string valueIn)
    {
        amountDown = float.Parse(valueIn);
    }

    public void SubmitDown()
    {
        ZeldaHealthBar.instance.RemoveHearts(amountDown);
    }

    public void AddHeartContainer()
    {
        ZeldaHealthBar.instance.AddContainer();
    }
}
using Photon.Chat;
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PhotonChatManager : MonoBehaviour, IChatClientListener
{
    #region Setup

    [SerializeField] GameObject joinChatButton;
    ChatClient chatClient;
    bool isConnected;
    [SerializeField] string username;

    public void UsernameOnValueChange(string valueIn)
    {
        username = valueIn;
    }

    public void ChatConnectOnClick()
    {
        isConnected = true;
        chatClient = new ChatClient(this);
        //chatClient.ChatRegion = "US";
        chatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new AuthenticationValues(username));
        Debug.Log("Connenting");
    }

    #endregion Setup

    #region General

    [SerializeField] GameObject chatPanel;
    string privateReceiver = "";
    string currentChat;
    [SerializeField] InputField chatField;
    [SerializeField] Text chatDisplay;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (isConnected)
        {
            chatClient.Service();
        }

        if (chatField.text != "" &amp;&amp; Input.GetKey(KeyCode.Return))
        {
            SubmitPublicChatOnClick();
            SubmitPrivateChatOnClick();
        }
    }

    #endregion General

    #region PublicChat

    public void SubmitPublicChatOnClick()
    {
        if (privateReceiver == "")
        {
            chatClient.PublishMessage("RegionChannel", currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    public void TypeChatOnValueChange(string valueIn)
    {
        currentChat = valueIn;
    }

    #endregion PublicChat

    #region PrivateChat

    public void ReceiverOnValueChange(string valueIn)
    {
        privateReceiver = valueIn;
    }

    public void SubmitPrivateChatOnClick()
    {
        if (privateReceiver != "")
        {
            chatClient.SendPrivateMessage(privateReceiver, currentChat);
            chatField.text = "";
            currentChat = "";
        }
    }

    #endregion PrivateChat

    #region Callbacks

    public void DebugReturn(DebugLevel level, string message)
    {
        //throw new System.NotImplementedException();
    }

    public void OnChatStateChange(ChatState state)
    {
        if(state == ChatState.Uninitialized)
        {
            isConnected = false;
            joinChatButton.SetActive(true);
            chatPanel.SetActive(false);
        }
    }

    public void OnConnected()
    {
        Debug.Log("Connected");
        joinChatButton.SetActive(false);
        chatClient.Subscribe(new string[] { "RegionChannel" });
    }

    public void OnDisconnected()
    {
        isConnected = false;
        joinChatButton.SetActive(true);
        chatPanel.SetActive(false);
    }

    public void OnGetMessages(string channelName, string[] senders, object[] messages)
    {
        string msgs = "";
        for (int i = 0; i &lt; senders.Length; i++)
        {
            msgs = string.Format("{0}: {1}", senders[i], messages[i]);

            chatDisplay.text += "\n" + msgs;

            Debug.Log(msgs);
        }

    }

    public void OnPrivateMessage(string sender, object message, string channelName)
    {
        string msgs = "";

        msgs = string.Format("(Private) {0}: {1}", sender, message);

        chatDisplay.text += "\n " + msgs;

        Debug.Log(msgs);
        
    }

    public void OnStatusUpdate(string user, int status, bool gotMessage, object message)
    {
        throw new System.NotImplementedException();
    }

    public void OnSubscribed(string[] channels, bool[] results)
    {
        chatPanel.SetActive(true);
    }

    public void OnUnsubscribed(string[] channels)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserSubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    public void OnUserUnsubscribed(string channel, string user)
    {
        throw new System.NotImplementedException();
    }

    #endregion Callbacks
}

Posted on

Unity Health Bar Tutorial – Using Fill Images

How to make a health bar

Welcome to this quick game mechanic tutorial on how to make a health bar using fill images in Unity 3D. In this lesson, we will teach you how to create and update fill meters that you can use for health bars, shield bars, stamina bars, or even experience bars.

Create a Health Bar Object in Unity

To create a fill bar all you have to do is Is create a UI image object. you then you need to set the rect transform so the health bar is positioned and sized the way you want. You then need to apply a source image to the image component so that the Image type drop-down menu becomes available. You then can set the image type to Filled which will make it so you can determine the fill amount of our health bar. You can then decide if you want your fill meter to be a linear or a radial health bar by setting the fill mode. With your health bars created we will now switch over to the code we need.

Update Health Bar with C# Code

To get your health bar to update you will need at least three variables. Two of them need to be int or float. One will be for your current health. The other will be for the max health and the last one will be for holding the image component of the. To then update the health bar you only need one line of code. You will first need to access the fill amount of the image variable and set it equal to the current health divided by the max health. This will give you a decimal value of a percentage of how much health you currently have over how much total health you could have. You will need to have this line of code right after any time you change your current health value.

Test your Work

Inside Unity, you need to make sure you set all the variables and then you can test your game. If you follow along with this video you should then have a working health bar.