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. Dismiss Notice

Question How to create a slider like a scale

Discussion in 'Scripting' started by Philipp_Goettler, May 30, 2023.

  1. Philipp_Goettler

    Philipp_Goettler

    Joined:
    Dec 11, 2021
    Posts:
    28
    Hey, atm i am trying to create a units inventory menu. And now i wanna implement a feature when you drag and drop a unit from one inventory to another, a slider will open and on the left and right are the numbers from both inventory (already got this). Now i wanna add a feature like a scale, when i move my slider to the left, the left number increases and the right one decreases, same on the other side.
    [Edit]
    Here is also a example image

    upload_2023-5-30_17-19-16.png
     
    Last edited: May 30, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    I did this a while ago and keep an example handy! See enclosed package for it fully set up.

    Code:

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class MutuallyLimitedSliders : MonoBehaviour
    7. {
    8.     [Tooltip( "Sliders will be in total limited to never exceed this.")]
    9.     public    float        MaximumTotal;
    10.  
    11.     [Tooltip( "Set this to reduce other sliders instead of limiting the moving one.")]
    12.     public    bool        ReduceOthers;
    13.  
    14.     [Header( "CAVEAT: only tested on 0.0 to 1.0 sliders!")]
    15.  
    16.     [Tooltip( "Sliders to observe and mutually limit.")]
    17.     public    Slider[]    Sliders;
    18.  
    19.     void Reset()
    20.     {
    21.         MaximumTotal = 1.0f;
    22.     }
    23.  
    24.     float LastMaximumTotal;
    25.     float[] LastSliderValues;
    26.  
    27.     void SetSliderGuarded( Slider slider, float value)
    28.     {
    29.         if (value < slider.minValue) value = slider.minValue;
    30.         if (value > slider.maxValue) value = slider.maxValue;
    31.         slider.value = value;
    32.     }
    33.  
    34.     void Update ()
    35.     {
    36.         // if you change the maximum, trigger the full restart checks
    37.         if (MaximumTotal != LastMaximumTotal)
    38.         {
    39.             LastSliderValues = null;
    40.             LastMaximumTotal = MaximumTotal;
    41.         }
    42.  
    43.         // is this our first time or did the count of sliders change?
    44.         if (LastSliderValues == null || LastSliderValues.Length != Sliders.Length)
    45.         {
    46.             LastSliderValues = new float[ Sliders.Length];
    47.  
    48.             float currentTotal = 0.0f;
    49.             for (int i = 0; i < Sliders.Length; i++)
    50.             {
    51.                 LastSliderValues[i] = Sliders[i].value;
    52.                 currentTotal += Sliders[i].value;
    53.             }
    54.  
    55.             if (currentTotal > MaximumTotal)
    56.             {
    57.                 Debug.LogWarning( "Total already greater than max at start!");
    58.  
    59.                 if (ReduceOthers)
    60.                 {
    61.                     for (int i = 0; i < Sliders.Length; i++)
    62.                     {
    63.                         float reducedValue = (Sliders[i].value * MaximumTotal) / currentTotal;
    64.                         SetSliderGuarded( Sliders[i], reducedValue);
    65.                     }
    66.                 }
    67.             }
    68.         }
    69.  
    70.         // check and limit
    71.         {
    72.             bool adjusted = false;
    73.             for (int i = 0; i < Sliders.Length; i++)
    74.             {
    75.                 if (Sliders[i].value != LastSliderValues[i])
    76.                 {
    77.                     if (!adjusted)
    78.                     {
    79.                         // tally others
    80.                         float othersTotal = 0.0f;
    81.                         for (int j = 0; j < Sliders.Length; j++)
    82.                         {
    83.                             if (i != j)
    84.                             {
    85.                                 othersTotal += Sliders[j].value;
    86.                             }
    87.                         }
    88.  
    89.                         // limit?
    90.                         if (othersTotal + Sliders[i].value > MaximumTotal)
    91.                         {
    92.                             adjusted = true;        // an adjustment has happend, don't do any other this frame
    93.  
    94.                             bool doLimiting = true;
    95.  
    96.                             if (ReduceOthers)
    97.                             {
    98.                                 // we will bring all the others down to fit, if possible
    99.                                 float overAmount = (othersTotal + Sliders[i].value) - MaximumTotal;
    100.  
    101.                                 // we need to reduce the others by overAmount... can we?
    102.                                 if (overAmount < othersTotal)
    103.                                 {
    104.                                     doLimiting = false;
    105.  
    106.                                     // proportionally reduce the others
    107.                                     for (int j = 0; j < Sliders.Length; j++)
    108.                                     {
    109.                                         if (i != j)
    110.                                         {
    111.                                             float reducedValue = Sliders[j].value - (Sliders[j].value * overAmount) / othersTotal;
    112.                                             SetSliderGuarded( Sliders[j], reducedValue);
    113.                                         }
    114.                                     }
    115.                                 }
    116.                                 else
    117.                                 {
    118.                                     // we cannot... therefore we must limit this one
    119.                                     doLimiting = true;
    120.                                     // meanwhile drive all the others to their minvaule and retotal
    121.                                     othersTotal = 0.0f;
    122.                                     for (int j = 0; j < Sliders.Length; j++)
    123.                                     {
    124.                                         if (i != j)
    125.                                         {
    126.                                             Sliders[j].value = Sliders[j].minValue;
    127.                                             othersTotal += Sliders[j].value;
    128.                                         }
    129.                                     }
    130.                                 }
    131.                             }
    132.  
    133.                             // we either ar not reducing, OR we were unable to reduce, so fall back to limit
    134.                             if (doLimiting)
    135.                             {
    136.                                 // we will prevent this slider from causing the total to exceed
    137.                                 float limited = MaximumTotal - othersTotal;
    138.                                 SetSliderGuarded( Sliders[i], limited);
    139.                             }
    140.                         }
    141.                     }
    142.  
    143.                     LastSliderValues[i] = Sliders[i].value;
    144.                 }
    145.             }
    146.         }
    147.     }
    148. }
     

    Attached Files:

    Philipp_Goettler likes this.
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    First of all, Kurt's answer above has you covered, this isn't intended to replace it.
    But, I can walk you through the math and logic behind this.

    First of all you have some value t.
    Such that 0 <= t <= 1.

    Next, you want two points A and B that will graphically pin the two extrema of the slider.
    Theoretically, you can find any point in between A and B if you apply linear interpolation or lerp.
    Code (csharp):
    1. static Vector3 lerp(Vector3 a, Vector3 b, float t) => (1f - t) * a + t * b;
    (Alternatively you can use Vector3.Lerp)
    To use this you use a value
    t
    between 0 and 1, imagine if this was percentage.

    Knowing t allows you to determine the values on the left and on the right. Let's call these values q1 and q2.

    So when t is at 0% then q1 = total (or total * 1.0)
    when t is at 100% then q1 = 0 (or total * 0.0)
    and when t is at 50% then q1 = total / 2 (or total * 0.5)

    Note that the following equation must be satisfied: q1 + q2 = total
    Meaning that you can compute q2, and get q1 as total - q2.

    This means there is a similar relationship like that lerp above, just with numbers
    Code (csharp):
    1. static float lerp(float a, float b, float t) => (1f - t) * a + t * b;
    And then
    Code (csharp):
    1. var q2 = lerp(0f, total, t);
    2. var q1 = total - q2;
    In the end, what you want is really this
    t
    , so how to find this from the set points?
    If you know A and B and have some P on this line, you can do inverse lerp.

    Ordinary inverse lerp for numbers is just reversing the straight lerp. I.e. you take the original formula and try to work out what
    t
    should be equal to:
    Code (csharp):
    1. n = (1f - t) * a + t * b
    2. n = a - ta + tb
    3. n = a + tb - ta
    4. n = a + t(b - a)
    5. t(b - a) = n - a
    6. t = (n - a) / (b - a)
    let's just assume that a and b won't be the same number, and this will work
    Code (csharp):
    1. static float invLerp(float a, float b, float n) => (n - a) / (b - a);
    Ok but now you need this for points A and B, and it's slightly more complicated because now you have 3 sets of values. However, because all three points basically all sit on one line, you don't actually need all 3 sets, you just pick the values that seem to be the most representative, say for horizontal axis that would be X coordinates.

    So you could do
    Code (csharp):
    1. static float invLerp(Vector3 a, Vector3 b, Vector3 n) => (n.x - a.x) / (b.x - a.x);
    And now you have everything you need
    Code (csharp):
    1. float qty = 20f; // for example
    2. float t = invLerp(minPoint, maxPoint, valuePoint);
    3. float q2 = lerp(0f, qty, t);
    4. float q1 = qty - q2;
    But you probably don't want these values to be floating points, but integers. You can do
    Code (csharp):
    1. int qty = 20;
    2. var t = invLerp(minPoint, maxPoint, valuePoint);
    3. int q2 = (int)Mathf.Round(lerp(0f, (float)qty, t));
    4. int q1 = qty - q2;
    You can similarly manipulate the actual slider to make it snap to whole numbers etc.

    Anyway, if this is perhaps too complicated for you -and- your slider already computes the value
    t
    for you, then simply do
    Code (csharp):
    1. int qty = 20;
    2. var t = slider.value; // or whatever is the name of this property
    3. int q2 = (int)Mathf.Round(lerp(0f, (float)qty, t));
    4. int q1 = qty - q2;
    You can also scale slider.value appropriately. For example if you've made it so it goes from 0..50, then
    Code (csharp):
    1. int qty = 20;
    2. var t = slider.value / slider.maxValue; // divide by 50
    3. int q2 = (int)Mathf.Round(lerp(0f, (float)qty, t));
    4. int q1 = qty - q2;
     
    Philipp_Goettler likes this.