Health Bar Prefabs
There are two parts to this package the first is the MainHealthBarPrefab. This prefab is a screen space overlay object which is meant for the main player’s health. This features a verticle fill image in the bottom left corner. The second part is the WorldSpaceHealtBarPrefab this is a world space overlay canvas meant for other avatars in your game. This feature a horizontal fill image that floats about the avatars. These UI elements will fill based on the current and max health of the player or enemy avatar. There are three main functions that you will use to update the health bars TakeDamage, HealHealth, and LevelUpHealth. TakeDamage will remove health, HealHealth will give health back, and LevelUpHealth increases the total max health.
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 != "" && 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 < 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 }