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 Can I reference a float..?

Discussion in 'Scripting' started by Deleted User, Jul 16, 2022.

  1. Deleted User

    Deleted User

    Guest

    I want to populate a drop-down with all the floats from a component. I would then like to reference the selected float. What can I do?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    You can use Reflection to do runtime introspection of variables, methods, properties, etc.

    This is usually a last resort method and not at all a trivial or good thing to implement.

    This sounds like and XY Problem that could probably be solved another way.

    https://en.wikipedia.org/wiki/XY_problem
     
    Deleted User likes this.
  3. Deleted User

    Deleted User

    Guest

    I was afraid of that.

    Probably.

    I wanted to make a slider where I could reference a component and pick from the component which float I wanted to use to be the sliders value. That way I won't have to keep writing scripts to set the value of sliders.
     
    Last edited by a moderator: Jul 16, 2022
  4. Deleted User

    Deleted User

    Guest

    Is reflection really the best option for what I'm trying to do here? I've never used reflection, and it sounds like it will be slow if I have to do it alot/every frame..
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    Lots of nice data-bindy ways to handle this without going to reflection.

    My Datasacks package uses this to bind a Datasack (a ScriptableObject) to UI items, such as a Slider.

    You can look at it and use it as-is, or just model a much simpler single-purpose stripped down version if you prefer.

    20180724_datasacks_overview.png

    Datasacks is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/datasacks

    https://github.com/kurtdekker/datasacks

    https://gitlab.com/kurtdekker/datasacks

    https://sourceforge.net/projects/datasacks/
     
    Deleted User likes this.
  6. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    This is something UI Elements (Unity's new UI) has native support for. It's called Bindings, and allows you to bind public properties together.
     
  7. Deleted User

    Deleted User

    Guest

    Last edited by a moderator: Jul 17, 2022
  8. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,136
    Is it necessary to support more than a known set of components?
     
  9. Deleted User

    Deleted User

    Guest

    I'm trying to understand the examples from the repo but I think i'm too dumb to understand how to use this package...

    Edit: Had to sleep on it. I think I'm understanding it better and this seems pretty neat. I think what would make Datasacks appeal to me more would be the ability to store any value from any component into a sack with no code.

    I'm gonna try, ty.
     
    Last edited by a moderator: Jul 17, 2022
  10. Deleted User

    Deleted User

    Guest

    I'm sorry I'm not sure what you're asking
     
    Last edited by a moderator: Jul 17, 2022
  11. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,136
    No. You answered my question with that second sentence.

    Edit: Here is a basic implementation. The data in the list is stored as a tuple if you haven't seen it before.

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using UnityEngine;
    5.  
    6. public class Foo : MonoBehaviour
    7. {
    8.     public void Start()
    9.     {
    10.         foreach ((string, float) entry in GetObjectFloats(GetComponent<Rigidbody>()))
    11.         {
    12.             Debug.Log($"Name: {entry.Item1}, Value: {entry.Item2}");
    13.         }
    14.     }
    15.  
    16.     public List<(string, float)> GetObjectFloats(Object obj)
    17.     {
    18.         List<(string, float)> entries = new List<(string, float)>();
    19.         BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
    20.  
    21.         FieldInfo[] fields = obj.GetType().GetFields(flags);
    22.         foreach (FieldInfo fieldInfo in fields)
    23.             if (fieldInfo.FieldType == typeof(float))
    24.             {
    25.                 //Debug.Log($"Obj: {obj.name}, Fields: {fieldInfo.Name}, Value: {fieldInfo.GetValue(obj)}");
    26.                 entries.Add((fieldInfo.Name, (float)fieldInfo.GetValue(obj)));
    27.             }
    28.  
    29.         PropertyInfo[] properties = obj.GetType().GetProperties(flags);
    30.         foreach (PropertyInfo propertyInfo in properties)
    31.             if (propertyInfo.PropertyType == typeof(float))
    32.             {
    33.                 //Debug.Log($"Obj: {obj.name}, Fields: {propertyInfo.Name}, Value: {propertyInfo.GetValue(obj)}");
    34.                 entries.Add((propertyInfo.Name, (float)propertyInfo.GetValue(obj)));
    35.             }
    36.  
    37.         return entries;
    38.     }
    39. }
     
    Last edited: Jul 17, 2022
    Deleted User likes this.
  12. Cyber-Dog

    Cyber-Dog

    Joined:
    Sep 12, 2018
    Posts:
    352
    If you don't want to use reflection, then you could combine a Model(Basic Class or Scriptable Object)/JSON approach.

    Use Newtonsoft to convert your object to a JObject, and get all properties where the type is number.
    Then use that list to populate dropdown names. And get/set methods to get or update values.

    On the Set method, update the components object.

    ---

    Note: On a low level, this probably does similar to reflection, so I'm not sure if it will have any better performance.
    And both can be written in similar amounts of code.
     
    Deleted User likes this.
  13. Deleted User

    Deleted User

    Guest

    Thanks everyone. Here's what I got:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class SliderExtended : Slider {
    8.  
    9.     [Tooltip("Inserting a component into this slot allows you to pick a float from the component to be used as the slider value.")]
    10.     public Component valueOverride;
    11.     [HideInInspector]
    12.     public int selectedEntry;
    13.     public List<(string, float)> entries;
    14.  
    15.     public void Update() {
    16.         base.Update();
    17.         if (valueOverride) {
    18.             GetObjectFloats(valueOverride);
    19.             if (entries.Count > 0) {
    20.                 value = entries[selectedEntry].Item2;
    21.             }
    22.         }
    23.     }
    24.  
    25.     public List<(string, float)> GetObjectFloats(UnityEngine.Object obj) {
    26.         entries = new List<(string, float)>();
    27.         BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
    28.         FieldInfo[] fields = obj.GetType().GetFields(flags);
    29.         foreach (FieldInfo fieldInfo in fields)
    30.             if (fieldInfo.FieldType == typeof(float)) {
    31.                 entries.Add((fieldInfo.Name, (float)fieldInfo.GetValue(obj)));
    32.             }
    33.         PropertyInfo[] properties = obj.GetType().GetProperties(flags);
    34.         foreach (PropertyInfo propertyInfo in properties)
    35.             if (propertyInfo.PropertyType == typeof(float)) {
    36.                 entries.Add((propertyInfo.Name, (float)propertyInfo.GetValue(obj)));
    37.             }
    38.         return entries;
    39.     }
    40.  
    41.     public string[] GetListStringFromObj(UnityEngine.Object obj) {
    42.         List<String> result = new List<string>();
    43.         GetObjectFloats(obj);
    44.         for (int i = 0; i < entries.Count; i++) {
    45.             result.Add(entries[i].Item1);
    46.         }
    47.         return result.ToArray();
    48.     }
    49. }
    50.  
    Editor script
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(SliderExtended))]
    5. public class SliderExtendedEditor : Editor {
    6.  
    7.     public override void OnInspectorGUI() {
    8.         // Show default inspector property editor
    9.         DrawDefaultInspector();
    10.         GUIStyle style = new GUIStyle(GUI.skin.GetStyle("label")) {
    11.             wordWrap = true
    12.         };
    13.         EditorGUILayout.LabelField("Checks component for values that can override slider value. Leave blank to disable.", style);
    14.         SliderExtended sliderExtended = target as SliderExtended;
    15.         //Popup
    16.         if (sliderExtended && sliderExtended.valueOverride && !Application.isPlaying) {
    17.             string[] options = sliderExtended.GetListStringFromObj(sliderExtended.valueOverride);
    18.             int index = EditorGUILayout.Popup("Available Values (float)", sliderExtended.selectedEntry, options);
    19.             sliderExtended.selectedEntry = index;
    20.         }
    21.     }
    22. }
    23.  
     
    Last edited by a moderator: Jul 17, 2022