Slider.onValueChanged in your scripts for more control

LONG POST INCOMING:

Seeing as we’re talking about sliders here, it may be useful to include something about how to call methods when the value of the slider changes in a script. This will probably come up in later courses, I’m not far enough through to know yet.

You CAN use the standard “On Value Changed (Single)” Area, but your options are limited. For example, if you want to:

  • Update the text in a label to have [(slider.value*100).ToString() + “%”] (Pictures 1 & 2),
  • Change the text in the label conditionally based on slider.value (Pictures 1 & 2), or even
  • Change the fill colour of your slider based on it’s value (think of a healthbar starting at green and going to red as it empties - Pictures 3 & 4 from my Laser Defender game),

you can’t (to my knowledge) do that without a script (easily).

So how to actually do it?
You CAN use an Update method if there aren’t a lot of sliders. However, this can cause a lot of useless calls to your Update method if the slider’s value isn’t changed much. Also, if there are a lot of sliders, then the performance of your game may suffer.

It’s better to run this code only when the slider’s value is changed.
This is a script I attached to the options sliders:

using UnityEngine;
using UnityEngine.UI;

public class OptionsSliderScript : MonoBehaviour {

    Slider slider; // The slider this script is attached to

    public Text attachedValue; // The Text Label I want to change with the slider
	public void Awake(){
		slider = GetComponent<Slider> (); // Grab the Slider
        // The line below ALL OF THESE COMMENTS is important: we tell the Slider that
        // whenever its value changes, it should automatically call the method
        // "OnSliderWasChanged".

        // slider = our slider
        
        // slider.onValueChanged = the "callback" executed when the slider's value 
        //     is changed.
        
        // slider.onValueChanged.AddListener(    ...    ); = adds a runtime callback,
        // or a "non-persistent listener" to the slider's "onValueChanged" callback.
        
        // ...(delegate {OnSliderWasChanged();}); = This is where we add the method
        // to call when the slider is changed. The method should be public and take 
        // no arguments.
        
        // The Method I used is named "OnSliderWasChanged", and it still functions as
        // a normal method (it's called below to automagically replace the 
        // placeholder).

        // MOAR INFO: https://docs.unity3d.com/ScriptReference/UI.Slider-onValueChanged.html
		slider.onValueChanged.AddListener (delegate {OnSliderWasChanged(); });
		OnSliderWasChanged ();
	}

    // OnDisabled() & OnDestroy(): When the GameObject is not in use anymore, we
    // should use "slider.onValueChanged.RemoveAllListeners ();". This removes any
    // Listeners we added via code; such as our "OnSliderWasChanged" method.
	void OnDisabled(){    slider.onValueChanged.RemoveAllListeners ();    }
	void OnDestroy() {    slider.onValueChanged.RemoveAllListeners ();    }

    // These are methods to change the value of the slider programatically - this is 
    // more useful for healthbars, or sliders you have disabled.
	public void ChangeSlider(float h)   {    slider.value = h;     }
	public void incrementSlider(float h){    slider.value += h;    }
	public void decrementSlider(float h){    slider.value -= h;    }

    // This is the method that is called when the slider's value changes.
	public void OnSliderWasChanged(){
        // If this is the "Difficulty Option Slider" (determined by whether it has 
        // the word "Difficulty" in its name)...
		if (slider.name.Contains ("Difficulty")) {
            // We have this slider set to whole numbers only, so "cast" (forcefully 
            // convert) the slider's value to an int, and use it in a switch
            // statement.
            
            // If the value = "1", text = "Easy", value = "2", text = "Normal",
            // value = "3", text = "Hard". If the user somehow gets a value 
            // besides 1, 2 or 3, let your displeasure be known.
			switch ((int)slider.value) {
			case 1:
				attachedValue.text = "Easy";
				break;
			case 2:
				attachedValue.text = "Normal";
				break;
			case 3:
				attachedValue.text = "Hard";
				break;
			default:
                // Text.text supports VERY LIMITED rich text formatting:
                
                // <b>This text is bold, regardless of other text.</b>
                // <i>This text is italics, regardless of other text.</i>
                // <b><i>This text is bold italics, regardless of other text.</i></b>
                
                // MOAR INFO: https://docs.unity3d.com/Manual/StyledText.html
				attachedValue.text = "... OMFG WHAT DID YOU <b><i>DO</i></b>!?";
				break;
			}
        // Otherwise, if this slider is NOT the Difficulty slider (Slider's name 
        // does NOT contain "Difficulty"), We round the slider's value to the 
        // nearest int and multiply by 100 to get a percentage, add a "%" symbol at 
        // the end and put the result into the label's text.
		} else {
			attachedValue.text = Mathf.RoundToInt (slider.value * 100) + "%";
		}
	}

As a bonus, if you want to have a healthbar slider change fill colour based on the value, you can do this:

public void OnSliderWasChanged(){
    ...
    
    // Grab the fill GameObject from your Slider, get its Image component, and play 
    // with its colour.
    
    // Assuming you HAVE NOT altered the Slider's hierarchy, it should come in like in 
    // the image below (Picture 5). In this case, the following code can be used:
    GameObject fill = slider.transform.GetChild (1).GetChild (0).gameObject; // 1
    Image fillImage = fill.GetComponent<Image> ();                           // 2
    Color newColour = new Color(                                             // 3
                                1f - (slider.value/slider.maxValue),     // R - empty
                                slider.value/slider.maxValue,            // G - full
                                0f                                       // B - Unused
                            );
    fillImage.color = newColour;                                             // 4
    
    // 1: Grab the Transform of the SECOND Child of the Slider ("Fill Area"), grab
    // that Transform's FIRST (and ONLY) Child (Fill), then grab the GameObject that 
    // Transform is attached to.

    // 2: Grab the IMAGE component off the GameObject you found.
    
    // 3: Create a new colour based on the Slider Value - in this case:
    //     The CURRENT value should be CURRENT Health, and
    //     The MAXIMUM value should be MAXIMUM Health.
    
    //     Example: If Health is 62 / 500, then:
    //         Red Colour   = 1 - (62 / 500 = 0.124) = 0.876f,
    //         Green Colour = 62 / 500 = 0.124f
    //         Blue Colour  = 0
    //     OR rgb(223, 32, 0)
}

Play around with the values to get different effects! You can have a “Morality Meter” with Red for evil, “Cyan/Aqua/(rgb(0, 255, 255))” for good and Grey for neutral by doing this:

Color moralityColour = new Color(
                                 1f - (slider.value/slider.maxValue),     // R - EVIL
                                 slider.value/slider.maxValue,            // G - GOOD
                                 slider.value/slider.maxValue             // B - GOOD
                             );

This way, if you’re more evil, the colour’s more red, if you’re more good, the colour’s more “cyan”, BUT, if you’re neutral(ish), all the colours will be closer to 127, or 0.5f, resulting in Grey.

So yeah, hope this helps. Thoughts?

1 Like

Privacy & Terms