Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by tuf91497 · Jul 14, 2019 at 10:34 PM · scripting problemreferenceinputfieldevents

How to select an int/float variable from another script in the inspector?

I have an input field that lets the user change the value of a variable of another script. I want the placeholder text (for if the input field is empty or inactive) to just display the current value of that variable.

I want to be able to select an object's variable as you would when setting up an event, by dragging and dropping an object into a field and selecting the variable from a drop-down menu. I don't need to know the type of the script. I only need to know that the variable is a float or an int. Then my input field will have a reference to that specified float value and can set the placeholder text from that.

Edit: Basically, I want to have a public float variable on my input field, and instead of typing in a value as you normally would, I want to drag and drop a reference to a float variable from a different script, without needing to know the type of that different script (or the name of the variable) beforehand.

Comment
Add comment · Show 4
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image pyramidhead01 · Jul 15, 2019 at 01:52 PM 0
Share

Do you mean like a scriptable object? https://docs.unity3d.com/$$anonymous$$anual/class-ScriptableObject.html

You can create a scritable object with public float ID, and then you can scriptable object reference to the script you are talking about.

This is what scriptableobject script would look like:

 public class ExampleClass : ScriptableObject
 {
     public float ID;
 }

Then in the script you want to reference the scriptable object, add these variables:

 public ExampleClass reference;
 float ID;

This will create the drag and drop feature you are looking for in the inspector, then wherever in runtime you are trying to get the value of this drag and drop value, you would reference it there; in this example I will show you how to do it in Start()

 public void Start()
 {
      ID = reference.ID;
 }

Is this what you would be looking for?

avatar image tuf91497 pyramidhead01 · Jul 15, 2019 at 02:59 PM 0
Share

Not quite. The script that references ExampleClass should not need to know the type ExampleClass. It should only need to know that it is a GameObject or Component. The referencing script should just allow you to pick from a list of properties/fields on any script that you drag in to it which are of type float.

This is cause my input field works for any kind of script. It has an event that it invokes when I submit the input text which sends the input to a script that I add from a drop-down menu. I only need one small method in my receiving script which can convert the input from a string to a float, and then assign that float to the property/field that I want my input field to control.

All I want to do is take the current value of the property/field that my input field is controlling and display that value in the input field if the user isn't currently typing in a new value.

avatar image pyramidhead01 tuf91497 · Jul 15, 2019 at 03:31 PM 0
Share

So let me understand. It sounds like this is an interactive part of your project and it requires user input, let me know if this is the process:

  1. One script captures user input

  2. Another script contains the answer value

  3. If the user input does not match the answer value, show the user what input they should be pressing

If that is what you are trying to do, provide your user input script and I can create you a script that will have a list of floats that you can input in the inspector, or I can code it so it creates random float values for you.

One thing though, float values are not great values to compare with, an int value would be much more appropriate; if the value you are trying to compare is a float you can round to an int that way you won't run into bugs with Unity not being able to compare exactly.

avatar image LeonardSchoyen · Jul 15, 2019 at 03:47 PM 0
Share

Is this link between the custom input field and the generic object going to last during runtime too, only in editor mode, or only when the editor with the custom input field is open?

2 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by LeonardSchoyen · Jul 15, 2019 at 05:03 PM

This can be done by manually creating a serialized object from a given generic mono-behavior. This approach requires quite some editor scripting, which can be finicky and weird. The core idea of the method is that you can create your own serialized object from components, and potentially scriptable objects, that can be manipulated like any other custom editor would. The usage of EditorGUILayout.Popup(...) and EditorGUILayout.PropertyField(...) is just as an example to show how you can display things.

A big weakness with the code as it stands now is that nothing is saved to the actual NumberFIeldChange component, so if you hide the editor by going to another game object, the link will be gone. This can be fixed by saving the values used in the custom editor to the actual object. You can look into the static Undo class if you need this functionality.

Example classes:

ComponentWithNumberFields.cs

 using UnityEngine;
 
 public class ComponentWithNumberFields : MonoBehaviour
 {
     [SerializeField]
     private float floatField = 0;
     [SerializeField]
     private int intField = 0;
 }

ComponentWithoutNumberFIelds.cs

 using UnityEngine;
 
 public class ComponentWithoutNumberFields : MonoBehaviour
 {
     [SerializeField]
     private string stringField = "Hello Unity";
 }

NumberFieldChanger.cs

 using UnityEngine;
 
 public class NumberFieldChanger : MonoBehaviour { }

NumberFieldChanger_Editor.cs

 using System.Collections.Generic;
 using UnityEditor;
 using UnityEngine;
 
 [CustomEditor(typeof(NumberFieldChanger))]
 public class NumberFieldChanger_Editor : Editor
 {
     private string fieldName;
     private List<string> fieldNames;
     private MonoBehaviour targetScript;
     private SerializedProperty targetSerializedProperty;
 
     public override void OnInspectorGUI()
     {
         base.OnInspectorGUI();
 
         // Field to select generic script component from scene.
         MonoBehaviour newTargetScript = (MonoBehaviour)EditorGUILayout.ObjectField("Generic Object", this.targetScript, typeof(MonoBehaviour), true);
         if (newTargetScript != this.targetScript) {
             this.fieldName = null;
             this.targetScript = newTargetScript;
         }
         if (this.targetScript != null) {
             // Creating SerializedObject from selected component.
             // Updating serialized object is not necessary as it is created the same frame.
             SerializedObject targetSerializedObject = new SerializedObject(this.targetScript);
             // Lists for relevant fields found:
             this.fieldNames = new List<string>();
             List<SerializedProperty> serializedProperties = new List<SerializedProperty>();
             // Goes through all properties of the selected object.
             // Serialized properties have iterator functionality built in.
             SerializedProperty serializedProperty = targetSerializedObject.GetIterator();
             serializedProperty.Next(true);
             do {
                 // Runs code block if property is of correct type.
                 switch (serializedProperty.propertyType) {
                     case SerializedPropertyType.Float:
                     case SerializedPropertyType.Integer:
                         // Saves values found
                         this.fieldNames.Add(serializedProperty.name);
                         break;
                     default:
                         break;
                 }
             } while (serializedProperty.Next(false));
             // Removes two hidden int options one probably shouldn't mess with
             this.fieldNames.RemoveAt(0);
             this.fieldNames.RemoveAt(0);
             // Ensures that there exist fields to choose from
             if (this.fieldNames.Count != 0) {
                 // Selection of field
                 int oldIndex = this.fieldNames.Contains(this.fieldName) ? this.fieldNames.IndexOf(this.fieldName) : 0;
                 int newIndex = EditorGUILayout.Popup(oldIndex, this.fieldNames.ToArray());
                 this.fieldName = this.fieldNames[newIndex];
                 this.targetSerializedProperty = targetSerializedObject.FindProperty(this.fieldName);
             } else {
                 EditorGUILayout.HelpBox("Given component has no float or int fields", MessageType.None);
                 this.targetSerializedProperty = null;
             }
             if (this.targetSerializedProperty != null) {
                 // Displays a field with no label with the selected field in the target script.
                 EditorGUILayout.PropertyField(this.targetSerializedProperty, GUIContent.none);
                 // Makes changes to target object.
                 targetSerializedObject.ApplyModifiedProperties();
             }
         }
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by tuf91497 · Jul 15, 2019 at 04:40 PM

I realized that I could simply add an event to my input field that assigns the placeholder text from the input text whenever I hit submit... Now I just need a little script to get my starting value.

Somewhat unfortunately, before I realized this, I wrote this nice (But definitely imperfect) script that lets you drag and drop a GameObject in and then select from a list of float-type properties and fields and store the value of your selection locally. If anyone wants it, I will put it below (Obviously, use is at your own risk). It only works for float values, but you could fairly easily convert it to work with any type. But using this means that you only need to know the type FloatHolder and the field activeValue, and you can siphon any float from any other script into activeValue.

alt text
alt text
Edit: Wasn't saving values when played or when the editor view was moved. Now it should work better.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using System.Reflection;
 using System;
 using UnityEditor;
 
 public class FloatHolder : MonoBehaviour
 {
     public bool runAtAwake = true;
     public bool runAtStart = true;
     public bool runAtRuntime = true;
     public float activeValue;
     public GameObject obj;
     [HideInInspector]
     public int componentSelected;
     [HideInInspector]
     public int propertyOrFieldSelected;
     [HideInInspector]
     public string[] components;
     [HideInInspector]
     public string[] validPropertiesNFields;
     private Component[] componentArray;
     private int propertyCount;
     private List<PropertyInfo> validProperties;
     private List<FieldInfo> validFields;
     PropertyInfo[] propertyInfoArray;
 
     private void Awake()
     {
         if (runAtAwake)
         {
             GetPropsNFields();
         }
     }
     private void Start()
     {
         if (runAtStart)
         {
             GetPropsNFields();
         }
     }
     /// TODO: This could run more efficiently in a coroutine
     private void Update()
     {
         if (runAtRuntime)
         {
             if (obj != null)
             {
                 GetPropsNFields();
             }
         }
         else
         {
             Destroy(this);
         }
     }
 
     [ExecuteInEditMode]
     public void GetPropsNFields()
     {
         if (obj != null)
         { // If the developer has dragged in an object,
             // Get the list of components on the object
             componentArray = obj.GetComponents(typeof(Component));
             // Get the type of each component
             string[] componentTypeStrings = new string[componentArray.Length];
             for(int i = 0; i < componentArray.Length; i++)
             {
                 componentTypeStrings[i] = componentArray[i].GetType().ToString();
             }
             // Display the components to the developer
             components = componentTypeStrings;
             // Get all public, non-inherited properties and fields from the component that the developer has selected
             PropertyInfo[] propertyInfoArray = componentArray[componentSelected].GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
             FieldInfo[] fieldInfoArray = componentArray[componentSelected].GetType().GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
             // Check each property and field on the component to see if it is "valid". 
             List<string> validPropertiesNFieldsNameList = new List<string>();
             validProperties = new List<PropertyInfo>();
             for(int i = 0; i < propertyInfoArray.Length; i++)
             { // Check each property
                 if (propertyInfoArray[i].PropertyType.Equals(activeValue.GetType()))
                 { // If the current property matches the type of activeValue, 
                     // Store the property in the list of valid properties and store its name in a grand list of valid properties AND fields
                     validProperties.Add(propertyInfoArray[i]);
                     validPropertiesNFieldsNameList.Add(propertyInfoArray[i].Name + ": <" + propertyInfoArray[i].PropertyType + ">");
                 }
             } // finally, store the number of valid properties so that we can differentiate between properties and fields later on
             propertyCount = validPropertiesNFieldsNameList.Count;
             validFields = new List<FieldInfo>();
             for (int i = 0; i < fieldInfoArray.Length; i++)
             { // Check each field
                 if (fieldInfoArray[i].FieldType.Equals(activeValue.GetType()))
                 { // If the current field matches the type of activeValue, 
                     // Store the field in the list of valid fields and store its name in a grand list of valid properties AND fields
                     validPropertiesNFieldsNameList.Add(fieldInfoArray[i].Name + ": <" + fieldInfoArray[i - propertyInfoArray.Length].FieldType + ">");
                     validFields.Add(fieldInfoArray[i]);
                 }
             }
             // Expose the valid properties and fields to be displayed to the developer
             validPropertiesNFields = validPropertiesNFieldsNameList.ToArray();
             // Store the property/field value locally
             GetSelectedValue();
         }
     }
     private void GetSelectedValue()
     {
         if (validPropertiesNFields.Length == 0)
         { // If we have NO properties OR fields, set float to default value
             activeValue = 0f;
         }
         else
         { // If there ARE some valid properties or fields
             if(componentSelected > (componentArray.Length - 1))
             { // Prevent out of bounds
                 componentSelected = 0;
             }
             if (propertyOrFieldSelected < propertyCount)
             { // If the developer has selected a property,
                 if (validProperties.Count > 0)
                 { // If there are any valid properties,
                     // Store the value of the selected property
                     if(propertyOrFieldSelected > validProperties.Count)
                     { // Prevent out of bounds
                         propertyOrFieldSelected = 0;
                     }
                     activeValue = (float)validProperties[propertyOrFieldSelected].GetValue(componentArray[componentSelected]);
                 }
             }
             else
             { // Otherwise the developer has selected a field, 
                 if (validFields.Count > 0)
                 { // So if there are any valid fields,
                   // Store the value of the selected field
                     int fieldSelected = propertyOrFieldSelected - propertyCount;
                     if (fieldSelected > (validFields.Count - 1) || fieldSelected < 0)
                     { // Prevent out of bounds
                         fieldSelected = 0;
                     }                        
                     activeValue = (float)validFields[fieldSelected].GetValue(componentArray[componentSelected]);
                 }
             }
         }
     }
 }
 #if UNITY_EDITOR
 [CustomEditor(typeof(FloatHolder))]
 public class FloatHolderEditor : Editor
 {
     public override void OnInspectorGUI()
     {
        // var offsetProperty = serializedObject.FindProperty("Offset");
     //    EditorGUILayout.PropertyField(offsetProperty);
       //  serializedObject.ApplyModifiedProperties();
         // Get the current script
         FloatHolder script = (FloatHolder)target;
         // Draw the rest standard variables
         DrawDefaultInspector();
         // Collect the valid properties and fields
         script.GetPropsNFields();
         // Draw custom drop-down menus for the selected object's components and the fields/properties of that selected component
         if (script.components.Length > 0)
         { // If we have any components,
           // Draw the drop-down menu for them
             var componentSelectedProperty = serializedObject.FindProperty("componentSelected");
             //EditorGUILayout.PropertyField(componentSelectedProperty);
             componentSelectedProperty.intValue = EditorGUILayout.Popup("Components", componentSelectedProperty.intValue, script.components);
         }
         if (script.validPropertiesNFields.Length > 0)
         { // if the selected component has any valid properties/fields,
             // Draw the drop-down menu for them
             var propertyOrFieldSelectedProperty = serializedObject.FindProperty("propertyOrFieldSelected");
             propertyOrFieldSelectedProperty.intValue = EditorGUILayout.Popup("Members", propertyOrFieldSelectedProperty.intValue, script.validPropertiesNFields);
         }
         serializedObject.ApplyModifiedProperties();
     }
 }
 #endif   
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

193 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

One Script Referred by Another Script used by Multiple Objects 0 Answers

Object reference not set to an instance of an object (Scripts calling objects which only exist in certain scenes) 1 Answer

Passing multiple parameters into InputField AddListener 1 Answer

event is called twice 1 Answer

Losing object reference 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges