How to make decimal variables visible in Inspector?
Asked Answered
T

3

0

I want to change a decimal variables in runtime in the inspector or just watch it without using

Debug.Log(variable);

Is this anyhow possible?

Ting answered 4/4 at 10:46 Comment(5)

You need to give a bit more information here, I assume you are using C# as by default unityscript variables are already public? Search for public here, I think this is a duplicate question.

Genova

It's a public variable already, but it's still not visible in the inspector. And yes i use c#. public decimal playtime = 0M;

Ting

Hi, I think Unity can't serialize the decimal type, that's why you can't see it in the inspector. Float and double are supported but if you have to use decimal, I think you can write your own serializer for it to work in Unity. You can also write custom inspectors or PropertyDrawer that could help display/edit this type, like it's done here: http://wiki.unity3d.com/index.php/EnumFlagPropertyDrawer

Chloropicrin

I have tried to make a serialize container but still the decimals wont show up. [System.Serializable] public class Decimals { public decimal playtime, bla, blu; } public class GameManagerScript : MonoBehaviour { public Decimals showall; } Also my programming skills aint that good.

Ting

Well, what you wrote is correct to serialize a custom class non derived from Monobehavior. The problem here is that Unity can't serialize the decimal type at all. Finding a way to serialize this type is more complicated, you can read this blog about Unity serialization here: http://blogs.unity3d.com/2014/06/24/serialization-in-unity/ . They give an example of custom serialization and information on how to do this. This is not an quick/easy task, maybe some scripts in the asset store already manage this case. May I ask why you want to use decimal type ?

Chloropicrin
C
0

Like dns said, Unity can’t serialize the decimal type on it’s own. If you want Unity to actually “store” (serialize) the value, you have to provide your own way to serialize the value. This wrapper class does this:

//SerializableDecimal.cs
using UnityEngine;
using System.Collections;

[System.Serializable]
public class SerializableDecimal : ISerializationCallbackReceiver
{
	public decimal value;
	[SerializeField]
	private int[] data;

	public void OnBeforeSerialize ()
	{
		data = decimal.GetBits(value);
	}
	public void OnAfterDeserialize ()
	{
		if (data != null && data.Length == 4)
		{
			value = new decimal(data);
		}
	}
}

The field “value” is what you would use at runtime. Whenever Unity wants to serialize this custom class, it first copies the 4 internal uint fields into fields that unity can serialize. Now Unity will store those 4 values which made up a decimal value. When deserializing it restores those values.

Of course the inspector can only “show” serialized data, so it will show by default the data array with the 4 internal fields (hi, mid, lo and flags) which are difficult to edit. To get a visual representation you have to provide a PropertyDrawer like this:

//SerializableDecimalDrawer.cs
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(SerializableDecimal))]
public class SerializableDecimalDrawer : PropertyDrawer
{
	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label)
	{
		var obj = property.serializedObject.targetObject;
		var inst = (SerializableDecimal)this.fieldInfo.GetValue(obj);
		var fieldRect = EditorGUI.PrefixLabel(position, label);
		string text = GUI.TextField(fieldRect, inst.value.ToString());
		if (GUI.changed)
		{
			decimal val;
			if(decimal.TryParse(text, out val))
			{
				inst.value = val;
				property.serializedObject.ApplyModifiedProperties();
			}
		}
	}
}

With those two classes you have a decimal type that is serialized and can be edited / viewed in the inspector.

Keep in mind that the PropertyDrawer class need to be inside an “/Editor/” folder since it’s an editor class.

edit
Just noticed that i probably should also give a usage example :wink: Here it is:

public class DecimalTestScript : MonoBehaviour
{
	public SerializableDecimal val;
	void Start ()
	{
		Debug.Log("Val: " + val.value);
	}
}

So the only difference from using a “normal” decimal value is, that you have to use:

    val.value

instead of

    val

Of course you can implement implicit and explicit cast operators to use a SerializableDecimal just like a decimal, but such operators don’t work in all cases.

Courbet answered 6/6, 2023 at 2:41 Comment(4)

Wow thanks alot for that much effort, i didn't expect so much trouble with such small task. I thought it would be way easier. I may test it out later.

Ting

I just realized i had a copy&paste error ^^ I somehow inserted the same script twice. I've updated the scripts. I also searched a bit the documentation and found the GetBits method, so there's no need for using reflection. Now the SerializableDecimal class is much shorter.

Courbet

Looks way shorter now :P . Thanks again.

Ting

Cool, that's a nice and short example of a custom serialization with PropertyDrawer! This could also go on the Wiki as ISerializationCallbackReceiver was introduced with Unity 4.5 and is not very well documented yet! Ok, now, what about this new Quantum bit type ? ;-)

Chloropicrin
V
0

It does not work if I use SerializableDecimal class within a ScriptableObject inherited class =(

Varico answered 17/7, 2023 at 17:17 Comment(0)
E
0

Changed Bunny83’s property drawer for SerializableDecimal so it could work in inherited class.

using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(SerializableDecimal))]
public class SerializableDecimalDrawer : PropertyDrawer
{
	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
	{
		var dataProp = property.FindPropertyRelative("data");

		dataProp.arraySize = 4;
		var dataArray0 = dataProp.GetArrayElementAtIndex(0);
		var dataArray1 = dataProp.GetArrayElementAtIndex(1);
		var dataArray2 = dataProp.GetArrayElementAtIndex(2);
		var dataArray3 = dataProp.GetArrayElementAtIndex(3);

		var currentValue = new decimal(new int[] { dataArray0.intValue, dataArray1.intValue, dataArray2.intValue, dataArray3.intValue });

		var fieldRect = EditorGUI.PrefixLabel(position, label);
		string text = GUI.TextField(fieldRect, currentValue.ToString("F"));
		if (GUI.changed)
		{
            decimal val;
            if (decimal.TryParse(text, out val))
            {
                var bits = decimal.GetBits(val);

                dataArray0.intValue = bits[0];
                dataArray1.intValue = bits[1];
                dataArray2.intValue = bits[2];
                dataArray3.intValue = bits[3];

                property.serializedObject.ApplyModifiedProperties();
            }
        }
	}
}
Escalate answered 4/4 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.