Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[SOLVED] UI bar based off of a public float? C#

Discussion in 'Scripting' started by topherbwell, Jun 10, 2015.

  1. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    I have a flashlight component on my player character, and when the flashlight is on, the batterypercentage decreases over time. The battery percentage is is a public float in the code that is a value of 0 - 100. What I am wanting to do is take that value and display a UI bar that fills as charge goes up, and depletes as charge goes down.

    Here is the Flashlight script. This script is on two lights that are children of the player object. The script allows for the flashlight to toggle on/off with the F key, causes the batteryPercentage to drain when the flashlight is on, dims the light as the battery drains, and plays a sound when the flashlight is turned on/off.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5.  
    6. [RequireComponent(typeof(Light), typeof(AudioSource))]
    7. public class Flashlight : MonoBehaviour {
    8.  
    9.      public AudioClip click;
    10.      public float batteryLifeInSec = 300f;
    11.      public float batteryPercentage = 100;  //this value decreses when the light is on and increases when a battery object is picked up; this is what I am wanting to convert to a UI bar.
    12.  
    13.  
    14.      public Light lite;
    15.  
    16.      private bool on;
    17.      private float timer;
    18.  
    19.      voidStart()
    20.           {
    21.           lite = GetComponent<Light>();
    22.            }    
    23.  
    24.      void Update()  //this part basically controls the on/off for the flashlight and controls the battery drain based on whether the light is on or off.
    25.           {
    26.           timer += Time.deltaTime;
    27.  
    28.           if(Input.GetKeyDown(KeyCode.F) && timer >= 0.3f && batteryPercentage > 0) {
    29.           on = !on;
    30.           GetComponent<AudioSource>().PlayOneShot(click);
    31.           timer = 0;
    32.            }
    33.      
    34.      if(on)
    35.           {
    36.           lite.enabled = true;
    37.           batteryPercentage -= Time.deltaTime * (100 / batteryLifeInSec);
    38.            }
    39.      else
    40.           {
    41.           lite.enabled = false;
    42.           }
    43.  
    44.       batteryPercentage = Mathf.Clamp(batteryPercentage, 0, 100);
    45. //the following 21 "if" statements are just adjusting the intensity of the light based off of the batteryPercentage value.  
    46.  
    47. if(batteryPercentage == 0) {
    48. lite.intensity = Mathf.Lerp(lite.intensity, 0, Time.deltaTime * 2);
    49.  }
    50.  
    51. if(batteryPercentage > 0 && batteryPercentage < 5) {
    52. lite.intensity = Mathf.Lerp(lite.intensity, 0.15f, Time.deltaTime);
    53.  }
    54.  
    55. if(batteryPercentage > 5 && batteryPercentage < 10) {
    56. lite.intensity = Mathf.Lerp(lite.intensity, 0.30f, Time.deltaTime);
    57.  }
    58.  
    59. if(batteryPercentage > 10 && batteryPercentage < 15) {
    60. lite.intensity = Mathf.Lerp(lite.intensity, 0.45f, Time.deltaTime);
    61.  }
    62.  
    63. if(batteryPercentage > 15 && batteryPercentage < 20) {
    64. lite.intensity = Mathf.Lerp(lite.intensity, 0.6f, Time.deltaTime);
    65.  }
    66.  
    67. if(batteryPercentage > 20 && batteryPercentage < 25) {
    68. lite.intensity = Mathf.Lerp(lite.intensity, 0.75f, Time.deltaTime);
    69.  }
    70.  
    71. if(batteryPercentage > 25 && batteryPercentage < 30) {
    72. lite.intensity = Mathf.Lerp(lite.intensity, 0.9f, Time.deltaTime);
    73.  }
    74.  
    75. if(batteryPercentage > 30 && batteryPercentage < 35) {
    76. lite.intensity = Mathf.Lerp(lite.intensity, 1.05f, Time.deltaTime);
    77.  }
    78.  
    79. if(batteryPercentage > 35 && batteryPercentage < 40) {
    80. lite.intensity = Mathf.Lerp(lite.intensity, 1.2f, Time.deltaTime);
    81.  }
    82.  
    83. if(batteryPercentage > 40 && batteryPercentage < 45) {
    84. lite.intensity = Mathf.Lerp(lite.intensity, 1.35f, Time.deltaTime);
    85.  }
    86.  
    87. if(batteryPercentage > 45 && batteryPercentage < 50) {
    88. lite.intensity = Mathf.Lerp(lite.intensity, 1.5f, Time.deltaTime);
    89.  }
    90.  
    91. if(batteryPercentage > 50 && batteryPercentage < 55) {
    92. lite.intensity = Mathf.Lerp(lite.intensity, 1.65f, Time.deltaTime);
    93.  }
    94.  
    95. if(batteryPercentage > 55 && batteryPercentage < 60) {
    96. lite.intensity = Mathf.Lerp(lite.intensity, 1.8f, Time.deltaTime);
    97.  }
    98.  
    99. if(batteryPercentage > 60 && batteryPercentage < 65) {
    100. lite.intensity = Mathf.Lerp(lite.intensity, 1.95f, Time.deltaTime);
    101.  }
    102.  
    103. if(batteryPercentage > 65 && batteryPercentage < 70) {
    104. lite.intensity = Mathf.Lerp(lite.intensity, 2.1f, Time.deltaTime);
    105.  }
    106.  
    107. if(batteryPercentage > 70 && batteryPercentage < 75) {
    108. lite.intensity = Mathf.Lerp(lite.intensity, 2.25f, Time.deltaTime);
    109.  }
    110.  
    111. if(batteryPercentage > 75 && batteryPercentage <= 80) {
    112. lite.intensity = Mathf.Lerp(lite.intensity, 2.4f, Time.deltaTime);
    113.  }
    114.  
    115. if(batteryPercentage > 80 && batteryPercentage <= 85) {
    116. lite.intensity = Mathf.Lerp(lite.intensity, 2.55f, Time.deltaTime);
    117.  }
    118.  
    119. if(batteryPercentage > 85 && batteryPercentage <= 90) {
    120. lite.intensity = Mathf.Lerp(lite.intensity, 2.7f, Time.deltaTime);
    121.  }
    122.  
    123. if(batteryPercentage > 90 && batteryPercentage <= 95) {
    124. lite.intensity = Mathf.Lerp(lite.intensity, 2.85f, Time.deltaTime);
    125.  }
    126.  
    127. if(batteryPercentage > 95 && batteryPercentage <= 100) {
    128. lite.intensity = Mathf.Lerp(lite.intensity, 3, Time.deltaTime);
    129.  }
    130.  }
    131. }
    132.  
    The other component which interacts with this and give recharge is a battery object which you pick up by walking over with the player character. Including this in case it is needed to address the question or if anyone is interested in using this code snippet for their own projects. Keep in mind that I am very new to this so while I have made changes to these code snippets for use with my game, credit goes to people on these forums for the base code. Thanks in advance for anyone who is willing to help me figure this out. Learning every day thanks to this awesome community! (the following code goes on the battery object)

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5.  
    6.      public class Battery : MonoBehaviour {
    7.  
    8.  
    9.      voidStart()  //automatically assigns a sphere collider with trigger to the object
    10.       {
    11.           this.GetComponent<SphereCollider>().isTrigger = true;
    12.       }
    13.  
    14.      void OnTriggerEnter(Collider other)  //check to see if object it is colliding with is tagged "Player", then looks to see if children of the player have a component named "Flashlight> and restores 50 batteryPercentage to each light that is a child of the player that has a Flashlight component.
    15.       {
    16.           if(other.gameObject.tag == "Player")
    17.            {
    18.                foreach (varflashlightinother.GetComponentsInChildren<Flashlight>())
    19.                 {
    20.                     flashlight.batteryPercentage += 50;
    21.                 }
    22.            }
    23.       }
    24. }
    25.  
    I also have this snippet on my character movement script. I'm not sure if this should be in a different place, but all it does is destroys (actually just turns SetActive to false) pick up objects on collision and plays a sound for the pick up.
    Code (csharp):
    1.  
    2.    void OnTriggerEnter(Collider other)
    3.      {
    4.           if (other.gameObject.CompareTag("Pick Up"))
    5.           {
    6.                GetComponent<AudioSource>().PlayOneShot(collect);
    7.                other.gameObject.SetActive (false);
    8.            }
    9.  }
    10.  
     
  2. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    So what you want to do is use the Slider component you will need a UI script that has a reference to the torchlight and in its update method it sets the sliders value to the percentage.

    Have a go a writing the code yourself first but if you have any problems please ask.

    P.S - Just looking at your torch code - while this not a big problem you seem to be enabling your light every frame that it is on, even if the user hasnt pressed the button that frame. It might make sense to put that code inside the input checking if statement, the battery calculation still needs to be in the same place though.
     
    topherbwell likes this.
  3. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    topherbwell likes this.
  4. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Thank You, Korno. I'll have a look at that link and see what I am able to come up with. I'll post what I can figure out in a while.

    As for the enabling light every frame, I'd love some more detail about that after I get the first part figured out. Again, I have only been learning code for about a week so every task is a learning opportunity at this point.
     
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    You don't need a slider component. You can use a filled Image component - change the image type to Filled, and you can set image.fillAmount to anything between 0 and 1. (In fact, the slider component bases its filled background bit on this value, as well.
     
  6. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Wow! Three replies in under 20 minutes! I'm going to see what I can figure out. You guys rock. I'll post what I come up with and the inevitable bumps in the road I encounter. :)
     
  7. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Before I dive too deep in either direction, is there an advantage of the filled image or slider in this case?
     
  8. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    This is is actually a better idea. I forgot about filled image. Although, you cant use sliced images if I remember correctly? If you are just using a red/blue bar though is fine.

    The script will still be the same though.

    Make a script that:
    1. References the image component and torch
    2. in its update method, convert the torch percentage into a value between 0-1 (devide by 100 - but careful about devide by zero when the torch is empty)
    3. set that value to be filled percentage of the filled image
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    The slider includes extra pieces that you may or may not need, and you have to remember to disable its interactivity or the player will be able to cause visual glitches by dragging it. It's just sort of overkill.
     
    Kiwasi likes this.
  10. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    The same applies to a Slider. Slider uses a filled Image - look at the way the GameObject and its children are set up. It's subject to all the same inabilities and limitations.
     
  11. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Still, if you really want to use a ninesliced sprite there's a bit of trickery to be had.
     
  12. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Actually, the default Slider created using the GameObject/UI/Slider option creates a slider that uses a sliced sprite.
     
    Korno likes this.
  13. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    Oh, hmm. That must have changed at some point during the 4.6 beta. I could swear it used to use a filled sprite.

    In any case: you can do this with a sliced sprite by using a Mask.

    ObjectA
    - > ObjectB

    On ObjectA, have an Image that is a filled image (it can be anything). Also, a Mask component, with "Show Mask Graphic" unchecked.
    On ObjectB, put your sliced sprite. You'll probably want to set its RectTransform to stretch in all directions.
     
    Korno likes this.
  14. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    So being that I am a complete novice at this, I've been watching tutorials on the unity site and youtube all day. I cannot for the life of me even figure out how to change the image type to filled. On most of the tutorials I see, there is a drop down menu on the component for the UI image, but no such option exists when I create an image. How do I accomplish this in Unity 5? everything I am finding is Unity 4.6.

    Thanks again.
     
  15. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Anyone willing to help make this a bit more understandable for someone new to this? Unfortunately all sample code I can find is in Java and all of the UI bar tutorials I am finding online seem to be out of date. I am using Unity 5 so any help with this is of course appreciated. Once I understand a bit better how to implement this, it will transfer into several UI features I plan on including in the project.
     
  16. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    The Code

    Ok - first the code using a Slider. I dont want to get into the sliced image vs filled image debate again. The reason I used a slider is that it provides two images - a background color and a filled color for the current amount and is very quick to get working.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. // use the unity UI
    5. using UnityEngine.UI;
    6.  
    7. // dont worry about this, its from my project
    8. using Koneko.Lights;
    9.  
    10. public class TorchBatteryDisplay : MonoBehaviour {
    11.  
    12.  
    13.     public Slider BatterySlider;
    14.     public Torch TheTorch;
    15.  
    16.  
    17.     // Use this for initialization
    18.     void Start () {
    19.  
    20.     }
    21.  
    22.     // Update is called once per frame
    23.     void Update () {
    24.         BatterySlider.value = TheTorch.CurrentPower;
    25. }
    26. }
    This is all it took for my batter slider to display the battery value.

    My Torch class exposes a property CurrentPower - which is a percentage value of the current power - if I remember correctly, the torch class you are using has the same. The update mode just sets the sliders value property to this. The MaxValue property of the slider needs to be set to 100 too.

    Edit: I just noticed the max value property, I guess if you set this to 100 then you dont have to normalise the number. Ok Updated code and description.

    This is very simple, in the editor I just assign the TheTorch property and the Slider property.

    Setting up the slider

    So I added a Canvas to my scene - set to screen space overlay. Then I added a slider to this, for my project I positioned it in the top left corner but where is up to you.By default the Slider is interactable (i.e. you can move it) so I disabled this in the editor.

    Next, I wanted the current amount of power to be blue so if you look at the slider game object you will find a few child objects. There should be one called FillArea, and that has a child called Fill. If you look at this you will find an Image component, modify this components color property to change the current amount color.

    I left the background at white, because it looked fine for me, but if you want to modify the background part use the Image component on the Background child object.

    (By the way, you can also modify the sprite used by the slider by modifying those images source image property but for a basic bar the default is fine)

    On the Slider script (on the parent object) I set the transition to none - as the user cant interact with the slider. Then I also set the Handle rect transform property to none - again as it cant be interacted. Finally I disabled the Handle child game object as we are using it. Now, you might need to just stretch the FillArea bar a tiny bit as it might not completely cover the background (it assumes there was a Handle there)

    Done! You will have a health bar style bar, that displays the battery power. The same approach works for health bars and you can probably make it generic enough to work with any values.

    If you want a prefab or something or have any questions or want to know how to do the same with a filled image pleas just ask.

    Also, I am sorry for all the typos I have probably made in this....
     
    Last edited: Jun 11, 2015
    topherbwell likes this.
  17. JakeBilbe

    JakeBilbe

    Joined:
    Jun 10, 2015
    Posts:
    57
    This script will allow you to toggle a flashlight, restore, drain and create a texture that represents the energy of intensity. Just attach it to whatever component has a Light component attached.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Flashlight : MonoBehaviour {
    6.  
    7.     public Light flashLight; //This will be a holder for the light
    8.     public Texture2D LightBar; //Create a texture to act as your bar and drop into this
    9.     public bool isEnabled; //This is to check if the light is on or off
    10.     public float Restore; //This is the amount to Restore the light by
    11.     public float Drain; //This is how much to drain the light by
    12.  
    13.     void Start() {
    14.         flashLight = GetComponent<Light> ();
    15.     }
    16.  
    17.     void Update() {
    18.         if (isEnabled && flashLight.intensity > 0f) //If the light is enabled and intensity is over 0, drain
    19.             flashLight.intensity -= Drain;
    20.  
    21.         if (!isEnabled && flashLight.intensity < 1f) //If the light is enabled and intensity is under 1, restore
    22.             flashLight.intensity += Restore;
    23.  
    24.         if (Input.GetKey (KeyCode.E)) //If E is pressed
    25.             isEnabled = !isEnabled; //Set isEnabled to the opposite value
    26.  
    27.         flashLight.enabled = isEnabled; //Set the flashlight to whatever value isEnabled is
    28.     }
    29.  
    30.     void OnGUI() {
    31.         GUI.DrawTexture (new Rect (0, 0,flashLight.intensity * 100, 50), LightBar); //Draw a texture in the top left corner
    32.         //because we are multiplying by 100 if the value was 0.2f then it is equal to 20, this way you can scale to 100 pixels and not 0.2 pixels
    33.     }
    34. }
    35.  
    36.  
     
  18. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    He was using the new UI. I dont think going back to the old UI is the best of ideas these days, right?
     
    Kiwasi likes this.
  19. JakeBilbe

    JakeBilbe

    Joined:
    Jun 10, 2015
    Posts:
    57
    He didn't mention in his original post that he wanted Legacy or the new UI, I saw you mentioned to use a Slider which IMO would be a bad idea for what he requires. If he just wants to show the percentage using a Legacy Texture or even a UI Image would do perfectly fine.
     
  20. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Korno, thank you for taking the time. Here is what I have done so far. I have set up the slider per your suggestions, and attached the following code to the flashlight component which is a child on the player.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEngine.UI;
    5.  
    6. public class BatteryDisplay : MonoBehaviour
    7. {
    8.        public Slider BatSlider;
    9.      public Light Flashlight;
    10.  
    11.      void Start ()
    12.      {
    13.      }
    14.  
    15.      void Update ()
    16.      {
    17.      BatSlider.value = Flashlight.batteryPercentage;
    18.      }
    19. }  
    20.  
    I am not sure if I am assigning this to the correct object, but the console is kicking back this error;
    Assets/Scripts/BatteryDisplay.cs(18,46): error CS1061: Type `UnityEngine.Light' does not contain a definition for `batteryPercentage' and no extension method `batteryPercentage' of type `UnityEngine.Light' could be found (are you missing a using directive or an assembly reference?)

    I have a public float "BatteryDisplay" on the Flashlight, which is the field that displays the current charge. Am I not referencing this correctly or do I need to do something like GetComponentsinChildren? There is an array of the Flashlight Script; there are two lights that are children of the player object which both use the Flashlight script.

    Thank you again for all of your help! progress!
     
  21. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    Do you have a sprite assigned to the Image component? When you first create it, it's null and shows up as a white square, and this dropdown does not appear; it'll show up when you assign a sprite.

    There should be little or no difference between 4.6 and 5.0 as far as the UI system is concerned; AFAIK they are working on identical codebases for both versions.
     
  22. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    No I do not have a sprite assigned; I am currently trying to use Korno's method with the slider. There is def a difference between 4.6 and 5. Screengrabs from tutorials show options in 4.6 on the image component which do not exist in 5.0; namely, "image type" is missing in 5.0.

    That being said, i've been on this issue for two days now so I am going to stick with the slider method taht Kornos referenced above. At this point I just want to get something up and running so I can spend the time looking at it and playing with is so that I can actually understand what is going on and transfer that knowledge to other aspects of the project.
     
  23. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    Like I said, this option does not appear if no sprite is assigned. That's your problem in its entirety. (I just checked the changelog for 5.0 and confirmed - there are no changes to the UI system.)
     
  24. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Ah ok that makes sense. Currently I am not using sprites; I am trying to use the slider. This current issue is toying with many concepts that I have previously not been introduced to.
     
  25. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Currently I have my slider set and have the following code as a component of the slider itself.

    My questions are:
    1) is this the appropriate place to be placing such a script.
    2) I am referencing the "Flashlight" (uppercase) script component, but am getting an error telling me that I need to reference an object. The object would be the Light itself "flashlight" (lowercase). How do I make this reference in this context?

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEngine.UI;
    5.  
    6. public class BatDisplay : MonoBehaviour {
    7.  
    8.  
    9.      public Slider BatSlider;
    10.      public Light flashlight;
    11.  
    12.      voidStart ()
    13.       {
    14.       }
    15.  
    16.      void Update ()
    17.       {
    18.      BatSlider.value = Flashlight.batteryPercentage;
    19.       }
    20. }
    21.  
    The error code I am getting is this:Assets/Scripts/BatDisplay.cs(17,46): error CS0120: An object reference is required to access non-static member `Flashlight.batteryPercentage'
     
  26. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    It needs to be "public Flashlight flashlight;" if that's your class name, and then you access it with "flashlight.batteryPercentage" (case is exact on everything in quotes). "Light" is an internal Unity class and I assume is not what you want.
     
    topherbwell likes this.
  27. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Oh my gosh! A light just came on with me! (no puns intended here) :)

    This was totally the problem. for some reason in my mind the public "light" was a component type... it makes much more sense that I have a public class named "flashlight".

    lol thank you so much. It is totally working now and I feel like I understand where I was going wrong. This helps me understand this part of the object oriented programming so much better!

    Thank you again!