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.

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][1]

![alt text][2]

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 _= componentArray*.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 validPropertiesNFieldsNameList = new List();
validProperties = new List();
for(int i = 0; i < propertyInfoArray.Length; i++)
{ // Check each property
if (propertyInfoArray*.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*);*
validPropertiesNFieldsNameList.Add(propertyInfoArray_.Name + “: <” + propertyInfoArray*.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();
for (int i = 0; i < fieldInfoArray.Length; i++)
{ // Check each field
if (fieldInfoArray*.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*.Name + “: <” + fieldInfoArray[i - propertyInfoArray.Length].FieldType + “>”);*
validFields.Add(fieldInfoArray*);*
}
}
// 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
[1]: https://i.imgur.com/1VZYWAz.png*_
_[2]: https://i.imgur.com/z3nOXx6.png*_

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();
			}
		}
	}
}